diff --git a/apps/btadv/app.ts b/apps/btadv/app.ts index c66ba3449..85fd3a5d3 100644 --- a/apps/btadv/app.ts +++ b/apps/btadv/app.ts @@ -686,7 +686,7 @@ const setIntervals = ( const redrawInterval = setInterval(redraw, /*replaced*/1000); Bangle.on("lock", locked => setIntervals(locked)); -let bleInterval: undefined | number; +let bleInterval: undefined | IntervalId; NRF.on("connect", () => setIntervals(undefined, true)); NRF.on("disconnect", () => setIntervals(undefined, false)); diff --git a/apps/clkinfostopw/clkinfo.ts b/apps/clkinfostopw/clkinfo.ts index 5a2fd7bd0..f0c2a6ccb 100644 --- a/apps/clkinfostopw/clkinfo.ts +++ b/apps/clkinfostopw/clkinfo.ts @@ -1,6 +1,6 @@ (() => { let durationOnPause = "---"; - let redrawInterval: number | undefined; + let redrawInterval: IntervalId | undefined; let startTime: number | undefined; let showMillis = true; const milliTime = 60; @@ -16,7 +16,7 @@ if (startTime) { if (showMillis && Date.now() - startTime > milliTime * 1000) { showMillis = false; - changeInterval(redrawInterval, 1000); + changeInterval(redrawInterval!, 1000); } } else { unqueueRedraw(); diff --git a/apps/clkinfostopw/settings.ts b/apps/clkinfostopw/settings.ts index a9a35f245..a7bfce1e5 100644 --- a/apps/clkinfostopw/settings.ts +++ b/apps/clkinfostopw/settings.ts @@ -10,8 +10,10 @@ type StopWatchSettings = { const SETTINGS_FILE = "clkinfostopw.setting.json"; const storage = require("Storage"); - const settings: StopWatchSettings = storage.readJSON(SETTINGS_FILE, true) || {}; - settings.format ??= StopWatchFormat.HMS; + const settings: StopWatchSettings = Object.assign( + { format: StopWatchFormat.HMS }, + storage.readJSON(SETTINGS_FILE, true), + ); const save = () => { storage.writeJSON(SETTINGS_FILE, settings) diff --git a/apps/drained/app.ts b/apps/drained/app.ts index 3adfbec02..f4d33bc44 100644 --- a/apps/drained/app.ts +++ b/apps/drained/app.ts @@ -1,7 +1,7 @@ const app = "drained"; // from boot.js -declare var drainedInterval: number | undefined; +declare var drainedInterval: IntervalId | undefined; if(typeof drainedInterval !== "undefined") drainedInterval = clearInterval(drainedInterval) as undefined; @@ -41,7 +41,7 @@ Bangle.setOptions({ }); // clock -let nextDraw: number | undefined; +let nextDraw: TimeoutId | undefined; const draw = () => { const x = g.getWidth() / 2; const y = g.getHeight() / 2 - 48; diff --git a/apps/folderlaunch/ChangeLog b/apps/folderlaunch/ChangeLog index 759f68777..8a32760a4 100644 --- a/apps/folderlaunch/ChangeLog +++ b/apps/folderlaunch/ChangeLog @@ -1 +1,2 @@ -0.01: New app! \ No newline at end of file +0.01: New app! +0.02: Handle files potentially not existing diff --git a/apps/folderlaunch/app.ts b/apps/folderlaunch/app.ts index 3e2dbacc9..cab686f61 100644 --- a/apps/folderlaunch/app.ts +++ b/apps/folderlaunch/app.ts @@ -115,7 +115,7 @@ // Get the icon and text, skip if the space is empty. Always draw text for folders even if disabled switch (entry.type) { case 'app': - let app: AppInfo = storage.readJSON(entry.id + '.info', false); + let app = storage.readJSON(entry.id + '.info', false) as AppInfo; icon = storage.read(app.icon!)!; text = app.name; empty = false; @@ -186,7 +186,7 @@ switch (entry.type) { case "app": Bangle.buzz(); - let infoFile = storage.readJSON(entry.id + '.info', false); + let infoFile = storage.readJSON(entry.id + '.info', false) as AppInfo; load(infoFile.src); break; case "folder": diff --git a/apps/folderlaunch/configLoad.js b/apps/folderlaunch/configLoad.js index 2c27df1b4..f0610bf62 100644 --- a/apps/folderlaunch/configLoad.js +++ b/apps/folderlaunch/configLoad.js @@ -28,7 +28,7 @@ function cleanAndSave(config) { var installedAppIds = []; for (var _i = 0, infoFiles_1 = infoFiles; _i < infoFiles_1.length; _i++) { var infoFile = infoFiles_1[_i]; - installedAppIds.push(storage.readJSON(infoFile, true).id); + installedAppIds.push((storage.readJSON(infoFile, true) || {}).id); } var toRemove = []; for (var appId in config.apps) @@ -67,7 +67,7 @@ module.exports = { infoFiles.sort(infoFileSorter); for (var _i = 0, infoFiles_2 = infoFiles; _i < infoFiles_2.length; _i++) { var infoFile = infoFiles_2[_i]; - var app_1 = storage.readJSON(infoFile, false); + var app_1 = storage.readJSON(infoFile, false) || {}; if ((!config.showClocks && app_1.type == 'clock') || (!config.showLaunchers && app_1.type == 'launch') || (app_1.type == 'widget') || diff --git a/apps/folderlaunch/configLoad.ts b/apps/folderlaunch/configLoad.ts index 0cb9d59f6..0c1e3c0c1 100644 --- a/apps/folderlaunch/configLoad.ts +++ b/apps/folderlaunch/configLoad.ts @@ -53,7 +53,7 @@ function cleanAndSave(config: Config): Config { let infoFiles: Array = storage.list(/\.info$/); let installedAppIds: Array = []; for (let infoFile of infoFiles) - installedAppIds.push(storage.readJSON(infoFile, true).id); + installedAppIds.push(((storage.readJSON(infoFile, true) || {}) as AppInfo).id); // Remove nonexistent apps from appInfo let toRemove: Array = []; @@ -77,8 +77,8 @@ function cleanAndSave(config: Config): Config { * @return negative if a should go first, positive if b should go first, zero if equivalent. */ let infoFileSorter = (a: string, b: string): number => { - let aJson: AppInfo = storage.readJSON(a, false); - let bJson: AppInfo = storage.readJSON(b, false); + let aJson = storage.readJSON(a, false) as AppInfo; + let bJson = storage.readJSON(b, false) as AppInfo; var n = (0 | aJson.sortorder!) - (0 | bJson.sortorder!); if (n) return n; // do sortorder first if (aJson.name < bJson.name) return -1; @@ -97,7 +97,7 @@ export = { * @return the loaded configuration */ getConfig: (): Config => { - let config = storage.readJSON(SETTINGS_FILE, true) || DEFAULT_CONFIG; + let config = (storage.readJSON(SETTINGS_FILE, true) as Config | undefined) || DEFAULT_CONFIG; // We only need to load data from the filesystem if there is a change if (config.hash == storage.hash(/\.info$/)) { @@ -110,7 +110,7 @@ export = { infoFiles.sort(infoFileSorter); for (let infoFile of infoFiles) { - let app: AppInfo = storage.readJSON(infoFile, false); + let app = storage.readJSON(infoFile, false) as AppInfo | undefined || ({} as AppInfo); // If the app is to be hidden by policy, exclude it completely if ( @@ -138,12 +138,12 @@ export = { // Note: Relies on curFolder secretly being a reference rather than a copy let curFolder: Folder = config.rootFolder; let depth = 0; - for (let folderName of config.apps[app.id].folder) { + for (let folderName of config.apps[app.id]!.folder) { if (curFolder.folders.hasOwnProperty(folderName)) { curFolder = curFolder.folders[folderName]!; depth++; } else { - config.apps[app.id].folder = config.apps[app.id].folder.slice(0, depth); + config.apps[app.id]!.folder = config.apps[app.id]!.folder.slice(0, depth); break; } } diff --git a/apps/folderlaunch/metadata.json b/apps/folderlaunch/metadata.json index c3c8db75b..b5508bb59 100644 --- a/apps/folderlaunch/metadata.json +++ b/apps/folderlaunch/metadata.json @@ -1,7 +1,7 @@ { "id": "folderlaunch", "name": "Folder launcher", - "version": "0.01", + "version": "0.02", "description": "Launcher that allows you to put your apps into folders", "icon": "icon.png", "type": "launch", @@ -45,4 +45,4 @@ "url": "screenshot2.png" } ] -} \ No newline at end of file +} diff --git a/apps/folderlaunch/settings.ts b/apps/folderlaunch/settings.ts index 4433b09a2..8848b7cf8 100644 --- a/apps/folderlaunch/settings.ts +++ b/apps/folderlaunch/settings.ts @@ -24,7 +24,7 @@ onchange // Do nothing, but stop typescript from yelling at me for this function being unused. It gets used by eval. I know eval is evil, but the menus are a bit limited. for (let app in config.apps) { - let appInfo: AppInfo = storage.readJSON(app + '.info', false); + let appInfo = storage.readJSON(app + '.info', false) as AppInfo; menu[appInfo.name] = { value: config.hidden.includes(app), format: (value: boolean) => (value ? 'Yes' : 'No'), @@ -36,7 +36,7 @@ }; let getAppInfo = (id: string): AppInfo => { - return storage.readJSON(id + '.info', false); + return storage.readJSON(id + '.info', false) as AppInfo; } let showFolderMenu = (path: Array) => { @@ -142,7 +142,7 @@ } } for (let appId of folder.apps) { - menu[storage.readJSON(appId + '.info', false).name] = () => { }; + menu[(storage.readJSON(appId + '.info', false) as AppInfo).name] = () => { }; } E.showMenu(menu); } diff --git a/apps/folderlaunch/types.d.ts b/apps/folderlaunch/types.d.ts index 9d22b9ff5..fee36ed98 100644 --- a/apps/folderlaunch/types.d.ts +++ b/apps/folderlaunch/types.d.ts @@ -21,7 +21,6 @@ type Config = { apps: { // Saved info for each app [key: string]: { folder: FolderList, // Folder path - fast: boolean, // Whether the app should be fast launched nagged: boolean // Whether the app's fast launch setting was configured } }, diff --git a/apps/popconlaunch/ChangeLog b/apps/popconlaunch/ChangeLog index e174349bf..ec9bfa232 100644 --- a/apps/popconlaunch/ChangeLog +++ b/apps/popconlaunch/ChangeLog @@ -2,3 +2,4 @@ 0.02: Trim old entries from the popcon app cache 0.03: Avoid polluting global scope 0.04: Add settings app for resetting popcon +0.05: Handle info files potentially not existing diff --git a/apps/popconlaunch/boot.js b/apps/popconlaunch/boot.js index 60c8ffcb7..e760b501e 100644 --- a/apps/popconlaunch/boot.js +++ b/apps/popconlaunch/boot.js @@ -26,7 +26,7 @@ trimCache(cache); require("Storage").writeJSON("popcon.cache.json", cache); if (orderChanged) { - var info = oldRead("popconlaunch.info", true); + var info = oldRead("popconlaunch.info", true) || { cacheBuster: true }; info.cacheBuster = !info.cacheBuster; require("Storage").writeJSON("popconlaunch.info", info); } @@ -65,7 +65,7 @@ require("Storage").readJSON = (function (fname, skipExceptions) { var _a; var j = oldRead(fname, skipExceptions); - if (/\.info$/.test(fname)) { + if (j && /\.info$/.test(fname)) { var cache_1 = ensureCache(); var so = void 0; if (j.src && (so = (_a = cache_1[j.src]) === null || _a === void 0 ? void 0 : _a.sortorder) != null) diff --git a/apps/popconlaunch/boot.ts b/apps/popconlaunch/boot.ts index a6b52fb0f..2802be59f 100644 --- a/apps/popconlaunch/boot.ts +++ b/apps/popconlaunch/boot.ts @@ -15,7 +15,7 @@ let cache: undefined | Cache; const ensureCache = (): NonNull => { if(!cache){ - cache = oldRead("popcon.cache.json", true); + cache = oldRead("popcon.cache.json", true) as Cache | undefined; if(!cache) cache = {}; } @@ -38,7 +38,7 @@ const saveCache = (cache: Cache, orderChanged: boolean) => { require("Storage").writeJSON("popcon.cache.json", cache); if(orderChanged){ // ensure launchers reload their caches: - const info: AppInfo & { cacheBuster?: boolean } = oldRead("popconlaunch.info", true); + const info = (oldRead("popconlaunch.info", true) as undefined | AppInfo & { cacheBuster?: boolean }) || {cacheBuster:true}; info.cacheBuster = !info.cacheBuster; require("Storage").writeJSON("popconlaunch.info", info); } @@ -80,10 +80,10 @@ const sortCache = () => { }; require("Storage").readJSON = ((fname, skipExceptions) => { - const j: AppInfo = oldRead(fname, skipExceptions); - // ^ technically only AppInfo if we're "*.info" + const j = oldRead(fname, skipExceptions) as AppInfo | undefined; + // technically only AppInfo if we're "*.info" ^ - if(/\.info$/.test(fname)){ + if(j && /\.info$/.test(fname)){ const cache = ensureCache(); let so; diff --git a/apps/popconlaunch/metadata.json b/apps/popconlaunch/metadata.json index f3e8ef4fb..012ca6765 100644 --- a/apps/popconlaunch/metadata.json +++ b/apps/popconlaunch/metadata.json @@ -2,7 +2,7 @@ "id": "popconlaunch", "name": "Popcon Launcher", "shortName": "Popcon", - "version": "0.04", + "version": "0.05", "description": "Launcher modification - your launchers will display your favourite (popular) apps first. Overrides `readJSON`, may slow down your watch", "readme": "README.md", "icon": "app.png", diff --git a/apps/popconlaunch/settings.ts b/apps/popconlaunch/settings.ts index e301df9b2..6f02dc438 100644 --- a/apps/popconlaunch/settings.ts +++ b/apps/popconlaunch/settings.ts @@ -6,7 +6,7 @@ const S = require("Storage"); S.erase("popcon.cache.json"); - const info: AppInfo & { cacheBuster?: boolean } = S.readJSON("popconlaunch.info", true); + const info = S.readJSON("popconlaunch.info", true) as AppInfo & { cacheBuster?: boolean }; info.cacheBuster = !info.cacheBuster; S.writeJSON("popconlaunch.info", info); diff --git a/apps/widhid/wid.ts b/apps/widhid/wid.ts index e788be1a8..1de293b36 100644 --- a/apps/widhid/wid.ts +++ b/apps/widhid/wid.ts @@ -1,5 +1,5 @@ (() => { - const settings: Settings = require("Storage").readJSON("setting.json", true) || { HID: false } as Settings; + const settings = require("Storage").readJSON("setting.json", true) as Settings || ({ HID: false } as Settings); if (settings.HID !== "kbmedia") { console.log("widhid: can't enable, HID setting isn't \"kbmedia\""); return; @@ -10,7 +10,7 @@ let anchor = {x:0,y:0}; let start = {x:0,y:0}; let dragging = false; - let activeTimeout: number | undefined; + let activeTimeout: TimeoutId | undefined; let waitForRelease = true; const onSwipe = ((_lr, ud) => { diff --git a/typescript/types/main.d.ts b/typescript/types/main.d.ts index 59fb18d30..ecf509f70 100644 --- a/typescript/types/main.d.ts +++ b/typescript/types/main.d.ts @@ -260,6 +260,9 @@ type Theme = { dark: boolean; }; +type IntervalId = number & { _brand: "interval" }; +type TimeoutId = number & { _brand: "timeout" }; + type PinMode = | "analog" | "input" @@ -3863,7 +3866,7 @@ declare class Bangle { * of the BPM reading. * * `hrmSportMode` - on the newest Bangle.js 2 builds with with the proprietary * heart rate algorithm, this is the sport mode passed to the algorithm. See `libs/misc/vc31_binary/algo.h` - * for more info. 0 = normal (default), 1 = running, 2 = ... + * for more info. -1 = auto, 0 = normal (default), 1 = running, 2 = ... * * `seaLevelPressure` (Bangle.js 2) Normally 1013.25 millibars - this is used for * calculating altitude with the pressure sensor * Where accelerations are used they are in internal units, where `8192 = 1g` @@ -4092,7 +4095,8 @@ declare class Bangle { static getHealthStatus(range?: "current" | "last" | "day"): HealthStatus; /** - * Reads debug info. Exposes the current values of `accHistoryIdx`, `accGestureCount`, `accIdleCount` and `pollInterval`. + * Reads debug info. Exposes the current values of `accHistoryIdx`, `accGestureCount`, `accIdleCount`, `pollInterval` and others. + * Please see the declaration of this function for more information (click the `==>` link above [this description](http://www.espruino.com/Reference#l_Bangle_dbg)) * @returns {any} * @url http://www.espruino.com/Reference#l_Bangle_dbg */ @@ -4297,6 +4301,13 @@ declare class Bangle { */ static showClock(): void; + /** + * Show a 'recovery' menu that allows you to perform certain tasks on your Bangle. + * You can also enter this menu by restarting while holding down the `BTN1` + * @url http://www.espruino.com/Reference#l_Bangle_showRecoveryMenu + */ + static showRecoveryMenu(): void; + /** * This behaves the same as the global `load()` function, but if fast * loading is possible (`Bangle.setUI` was called with a `remove` handler) @@ -4971,11 +4982,19 @@ declare class Graphics { * XXXXXXXXX * `); * g.drawImage(img, x,y); + * var img = Graphics.createImage(` + * ..... + * .XXX. + * .X.X. + * .XXX. + * ..... + * `); + * g.drawImage(img, x,y); * ``` * If the characters at the beginning and end of the string are newlines, they will * be ignored. Spaces are treated as `0`, and any other character is a `1` * - * @param {any} str - A String containing a newline-separated image - space is 0, anything else is 1 + * @param {any} str - A String containing a newline-separated image - space/. is 0, anything else is 1 * @returns {any} An Image object that can be used with `Graphics.drawImage` * @url http://www.espruino.com/Reference#l_Graphics_createImage */ @@ -5352,6 +5371,14 @@ declare class Graphics { */ setFontCustom(bitmap: ArrayBuffer, firstChar: number, width: number | string, height: number): Graphics; + /** + * + * @param {any} file - The font as a PBF file + * @returns {any} The instance of Graphics this was called on, to allow call chaining + * @url http://www.espruino.com/Reference#l_Graphics_setFontPBF + */ + setFontPBF(file: any): Graphics; + /** * Set the alignment for subsequent calls to `drawString` * @@ -8097,21 +8124,65 @@ declare class E { static toArrayBuffer(str: string): ArrayBuffer; /** - * Returns a 'flat' string representing the data in the arguments, or return - * `undefined` if a flat string cannot be created. - * This creates a string from the given arguments. If an argument is a String or an - * Array, each element is traversed and added as an 8 bit character. If it is - * anything else, it is converted to a character directly. + * Returns a `String` representing the data in the arguments. + * This creates a string from the given arguments in the same way as `E.toUint8Array`. If each argument is: + * * A String or an Array, each element is traversed and added as an 8 bit character + * * `{data : ..., count : N}` causes `data` to be repeated `count` times + * * `{callback : fn}` calls the function and adds the result + * * Anything else is converted to a character directly. * In the case where there's one argument which is an 8 bit typed array backed by a * flat string of the same length, the backing string will be returned without * doing a copy or other allocation. The same applies if there's a single argument * which is itself a flat string. + * ```JS + * E.toString(0,1,2,"Hi",3) + * "\0\1\2Hi\3" + * E.toString(1,2,{data:[3,4], count:4},5,6) + * "\1\2\3\4\3\4\3\4\3\4\5\6" + * >E.toString(1,2,{callback : () => "Hello World"},5,6) + * ="\1\2Hello World\5\6" + * ``` + * **Note:** Prior to Espruino 2v18 `E.toString` would always return a flat string, + * or would return `undefined` if one couldn't be allocated. Now, it will return + * a normal (fragmented) String if a contiguous chunk of memory cannot be allocated. + * You can still check if the returned value is a Flat string using `E.getAddressOf(str, true)!=0`, + * or can use `E.toFlatString` instead. * * @param {any} args - The arguments to convert to a String - * @returns {any} A String (or `undefined` if a Flat String cannot be created) + * @returns {any} A String * @url http://www.espruino.com/Reference#l_E_toString */ - static toString(...args: any[]): string | undefined; + static toString(...args: any[]): string; + + /** + * Returns a Flat `String` representing the data in the arguments, or `undefined` if one can't be allocated. + * This provides the same behaviour that `E.toString` had in Espruino before 2v18 - see `E.toString` for + * more information. + * + * @param {any} args - The arguments to convert to a Flat String + * @returns {any} A Flat String (or undefined) + * @url http://www.espruino.com/Reference#l_E_toFlatString + */ + static toFlatString(...args: any[]): string | undefined; + + /** + * By default, strings in Espruino are standard 8 bit binary strings. + * However calling E.asUTF8 will convert one of those strings to + * UTF8. + * ``` + * var s = String.fromCharCode(0xF0,0x9F,0x8D,0x94); + * var u = E.asUTF8(s); + * s.length // 4 + * s[0] // "\xF0" + * u.length // 1 + * u[0] // hamburger emoji + * ``` + * + * @param {any} str - The string to turn into a UTF8 Unicode String + * @returns {any} A String + * @url http://www.espruino.com/Reference#l_E_asUTF8 + */ + static asUTF8(str: any): string; /** * This creates a Uint8Array from the given arguments. These are handled as @@ -11226,7 +11297,7 @@ declare function getSerial(): any; * @returns {any} An ID that can be passed to clearInterval * @url http://www.espruino.com/Reference#l__global_setInterval */ -declare function setInterval(func: any, timeout: number, ...args: any[]): any; +declare function setInterval(func: string | Function, timeout: number, ...args: any[]): IntervalId; /** * Call the function (or evaluate the string) specified ONCE after the timeout in @@ -11261,7 +11332,7 @@ declare function setInterval(func: any, timeout: number, ...args: any[]): any; * @returns {any} An ID that can be passed to clearTimeout * @url http://www.espruino.com/Reference#l__global_setTimeout */ -declare function setTimeout(func: any, timeout: number, ...args: any[]): any; +declare function setTimeout(func: string | Function, timeout: number, ...args: any[]): TimeoutId; /** * Clear the Interval that was created with `setInterval`, for example: @@ -11273,7 +11344,7 @@ declare function setTimeout(func: any, timeout: number, ...args: any[]): any; * @param {any} id - The id returned by a previous call to setInterval. **Only one argument is allowed.** * @url http://www.espruino.com/Reference#l__global_clearInterval */ -declare function clearInterval(...id: any[]): void; +declare function clearInterval(id: IntervalId): void; /** * Clear the Timeout that was created with `setTimeout`, for example: @@ -11285,7 +11356,7 @@ declare function clearInterval(...id: any[]): void; * @param {any} id - The id returned by a previous call to setTimeout. **Only one argument is allowed.** * @url http://www.espruino.com/Reference#l__global_clearTimeout */ -declare function clearTimeout(...id: any[]): void; +declare function clearTimeout(id: TimeoutId): void; /** * Change the Interval on a callback created with `setInterval`, for example: @@ -11299,7 +11370,7 @@ declare function clearTimeout(...id: any[]): void; * @param {number} time - The new time period in ms * @url http://www.espruino.com/Reference#l__global_changeInterval */ -declare function changeInterval(id: any, time: number): void; +declare function changeInterval(id: IntervalId, time: number): void; /** * Read 8 bits of memory at the given location - DANGEROUS! @@ -13155,10 +13226,13 @@ declare module "tensorflow" { * Functions here take and return buffers of data. There is no support for * streaming, so both the compressed and decompressed data must be able to fit in * memory at the same time. + * If you'd like a way to perform compression/decompression on desktop, check out https://github.com/espruino/EspruinoWebTools#heatshrinkjs * @url http://www.espruino.com/Reference#heatshrink */ declare module "heatshrink" { /** + * Compress the heatshrink-encoded data supplied as input. + * If you'd like a way to perform compression/decompression on desktop, check out https://github.com/espruino/EspruinoWebTools#heatshrinkjs * * @param {any} data - The data to compress * @returns {any} Returns the result as an ArrayBuffer @@ -13167,6 +13241,8 @@ declare module "heatshrink" { function compress(data: any): ArrayBuffer; /** + * Decompress the heatshrink-encoded data supplied as input. + * If you'd like a way to perform compression/decompression on desktop, check out https://github.com/espruino/EspruinoWebTools#heatshrinkjs * * @param {any} data - The data to decompress * @returns {any} Returns the result as an ArrayBuffer @@ -13316,7 +13392,8 @@ declare module "Storage" { * @returns {any} An object containing parsed JSON from the file, or undefined * @url http://www.espruino.com/Reference#l_Storage_readJSON */ - function readJSON(name: string, noExceptions: ShortBoolean): any; + function readJSON(name: string, noExceptions?: false | 0): unknown; + function readJSON(name: string, noExceptions?: ShortBoolean): unknown | undefined; /** * Read a file from the flash storage area that has been written with