Cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

TypeError: Expected a function / File system

Hi all,

 

I want to handle settings updates through files not sockets because it's not stable.

I saved the settings in a separate file and used a separate file called common/device-file-settings.js to handle the new received settings file.

 

I am receiving a strange "TypeError: Expected a function." on line 39, an exception printed on line 45.

I am passing the fs from app/index.js together with the settings callback function in order to use it here.

 

Screenshot 2022-05-06 at 21.16.34.png

Best Answer
0 Votes
16 REPLIES 16

Are you sure you imported fileSystem and not fs?

Peter McLennan
Gondwana Software
Best Answer

Hi @Gondwana,

 

Yes, this is the code:

 

app/index.js

 

import * as fs from "fs";
...
deviceFileSettings.initialize(settingsCallback, fs);

 

 

common/device-file-settings.js

 

import { inbox } from "file-transfer";

export const SETTINGS_FILE_NAME = "settings";
export const SETTINGS_TYPE = "cbor";
export const SETTINGS_FILE = "settings.cbor";

let settings, onsettingschange, fileSystem;

export function initialize(callback, fs) {
  fileSystem = fs;
  onsettingschange = callback;
  inbox.addEventListener("newfile", processAllFiles);
}

...

function loadSettings(fileName) {
  try {
    var filePath = `/private/data/${fileName}`;
    if (fileSystem.existsSync(filePath)) {
      return fileSystem.readFileSync(filePath, SETTINGS_TYPE);
    } else {
      return fileSystem.readFileSync(`/private/data/`+SETTINGS_FILE, SETTINGS_TYPE);
    }
  } catch (ex) {
    console.log(ex);
    // return {};
  }
}

 

Best Answer
0 Votes

Without digging into it too much, I'm now wondering whether the use of the 'common' directory might be problematic. I think 'common' is for code that is to be imported into both the device (watch) and companion (phone). However, the fs API is only relevant to the device. I could be totally wrong that this is the issue, but maybe try moving device-file-settings.js out of common.

Also try using console.log to see if/when fileSystem is being initialised, and whether it has been defined when being used in loadSettings.

Peter McLennan
Gondwana Software
Best Answer

Hi @Gondwana,

 

Thank you for the detailed description.

 

Pretty valid point, but I tried that already and no success.

I will dig more to see why the fs is not behaving as it should.

Will share whatever I found.

 

Cheers 🍻

Best Answer
0 Votes

If you put device-file-settings.js in the app folder, it should be able to import fs directly. That would simplify things. Simple is good.

Peter McLennan
Gondwana Software
Best Answer
0 Votes

I've moved it directly to the app folder, and when I try to import the fs, a build cannot be created:

"TypeError: Cannot create property 'code' on boolean 'true'"

 

I am missing some important points.

Best Answer

That's probably something simple, like a typo. If you're using .code in your source, backtrack from there. It sounds like .code is being applied to a simple variable or function return value, rather than an object.

I think you've already discovered the ossapps repo. There's bound to be several things in there that use file transfer (eg, BIG TIME). You could refer to code from there. (Beware of simply pasting: that can create more problems than it solves.)

Peter McLennan
Gondwana Software
Best Answer
0 Votes

Hi @Gondwana,

 

Thank you for the thoughts, ideas and suggestions, but it's not a typo.

 

The setting file names I am transferring are with "_" and I am not keeping them like "settings.color.cbor", but "settings_color.cbor".

The issue is the file system import.

 

I tried everything, I was moving the "device-file-settings.js" file in different directories without success:

  • app directory
    • Error: "Unhandled exception: TypeError: Cannot read property 'readFileSync' of undefined"
  • "custom" directory
    • Error: Unhandled exception: TypeError: Expected a function.

 

I am posting here the directory structure and also the code of the files:

 

app/index.js

 

import clock from "clock";
import document from "document";
import * as util from "../common/utils";
import { me as appbit } from "appbit";
import { today } from 'user-activity';
import { HeartRateSensor } from "heart-rate";
import * as deviceSettings from "../common/device-settings.js";
import * as deviceFileSettings from "./custom/device-file-settings.js";
import * as myClock from "./custom/clock.js";
import * as myHRM from "./custom/hrm.js";
import * as myActivity from "./custom/activity.js";
import * as fsys from "fs";

.....

deviceFileSettings.initialize(settingsCallback, fsys);

 

 

app/custom/device-file-settings.js

 

import { inbox } from "file-transfer";

export const SETTINGS_FILE_NAME = "settings";
export const SETTINGS_TYPE = "cbor";
export const SETTINGS_FILE = "settings.cbor";

let settings, onsettingschange, fs;

export function initialize(callback, fileSys) {
  fs = fileSys;
  onsettingschange = callback;
  inbox.addEventListener("newfile", processAllFiles);
}

function processAllFiles() {
  let fileName;
  while (fileName = inbox.nextFile()) {
    processSettingFile(fileName);
  }
}

function processSettingFile(fileName) {
  settings = loadSettings(fileName);
  onsettingschange(settings);
  fs.unlinkSync(fileName);
}

// Load settings from filesystem
function loadSettings(fileName) {
  try {
    var filePath = `/private/data/${fileName}`;
    return fs.readFileSync(filePath, SETTINGS_TYPE);
  } catch (ex) {
    console.log(ex);
    return fs.readFileSync(filePath, SETTINGS_TYPE);
    // return {};
  }
}

 

 

everytime it fails on this line:

"return fs.readFileSync(filePath, SETTINGS_TYPE);"

 

There is some dependency on the import so the fs cannot be recognized.

Best Answer
0 Votes

Why don't you just

import * as fsys from "fs";

in device-file-settings.js? Try to simplify things and eliminate fs and fileSys.

At very least, console.log(fs) in loadSettings to see whether it's actually defined.

Peter McLennan
Gondwana Software
Best Answer
0 Votes

Hi @Gondwana,

 

Thank you for your prompt reply.

 

The build is failing if I use the imported fs, I tried that already.

 

Here is the code:

import { inbox } from "file-transfer";
import * as fsys from "fs";

export const SETTINGS_FILE_NAME = "settings";
export const SETTINGS_TYPE = "cbor";
export const SETTINGS_FILE = "settings.cbor";

let settings, onsettingschange, fs;

export function initialize(callback) {
  // fs = fileSys;
  onsettingschange = callback;
  inbox.addEventListener("newfile", processAllFiles);
}

...

// Load settings from filesystem
function loadSettings(fileName) {
  try {
    // console.log(fsys);
    var filePath = `/private/data/${fileName}`;
    return fsys.readFileSync(filePath, SETTINGS_TYPE);
  } catch (ex) {
    // console.log(ex);
    // return fsys.readFileSync(filePath, SETTINGS_TYPE);
    // return {};
  }
}

 

The error:

Screenshot 2022-05-10 at 11.30.36.png

Best Answer
0 Votes

Unless I'm misinterpreting, that error message is referring to companion code. fs doesn't exist in the Companion API. You can only access the fs on the device (watch). Don't try to use fs in companion or common directories.

Peter McLennan
Gondwana Software
Best Answer
0 Votes

Hi @Gondwana,

 

I think what is presented in the log is just the app flow through the watch files:

  • app/index.js
  • companion/companion-settings.js
  • app/device-file-sharing.js

 

I do not have any import of the fs into the companion, but only import { outbox } from "file-transfer";

Best Answer
0 Votes

I just tried to duplicate getting a build error reported after "Building companion". The ony way I could do it is if there was a build error in the code that was being built for the companion (ie, /companion/... or /common/...). Build errors in device-only (ie, watch-only) code were always emitted prior to "Building companion".

Peter McLennan
Gondwana Software
Best Answer

Hi @Gondwana,

I agree, but the behaviour is weird because I am not changing anything in companion components when I edit in the device-file-settings.js and when I import fs into it, the build fails.

Best Answer
0 Votes

PM sent.

Peter McLennan
Gondwana Software
Best Answer
0 Votes

I found the reason why the build is failing.

 

The fs was loaded in the companion/index.js and I missed it because I was assuming only the import is there:

import * as mySettings from "./custom/companion-settings";

mySettings.initialize();

I was having the file like this :

import * as mySettings from "./custom/companion-settings";
import * as fs from "fs";

mySettings.initialize();

 

This was causing the build to fail because companion cannot import fs.

Best Answer