diff --git a/applications/system/js_app/examples/apps/Scripts/interactive.js b/applications/system/js_app/examples/apps/Scripts/interactive.js deleted file mode 100644 index 40ca98c30..000000000 --- a/applications/system/js_app/examples/apps/Scripts/interactive.js +++ /dev/null @@ -1,93 +0,0 @@ -let eventLoop = require("event_loop"); -let gui = require("gui"); -let dialog = require("gui/dialog"); -let textInput = require("gui/text_input"); -let loading = require("gui/loading"); -let storage = require("storage"); - -// No eval() or exec() so need to run code from file, and filename must be unique -storage.makeDirectory("/ext/.tmp"); -storage.makeDirectory("/ext/.tmp/js"); -storage.rmrf("/ext/.tmp/js/repl") -storage.makeDirectory("/ext/.tmp/js/repl") -let ctx = { - tmpTemplate: "/ext/.tmp/js/repl/", - tmpNumber: 0, - persistentScope: {}, -}; - -let views = { - dialog: dialog.makeWith({ - header: "Interactive Console", - text: "Press OK to Start", - center: "Run Some JS" - }), - textInput: textInput.makeWith({ - header: "Type JavaScript Code:", - minLength: 0, - maxLength: 256, - defaultText: "2+2", - defaultTextClear: true, - }), - loading: loading.make(), -}; - -eventLoop.subscribe(views.dialog.input, function (_sub, button, gui, views) { - if (button === "center") { - gui.viewDispatcher.switchTo(views.textInput); - } -}, gui, views); - -eventLoop.subscribe(views.textInput.input, function (_sub, text, gui, views, ctx) { - gui.viewDispatcher.switchTo(views.loading); - - let path = ctx.tmpTemplate + (ctx.tmpNumber++).toString(); - let file = storage.openFile(path, "w", "create_always"); - file.write(text); - file.close(); - - // Hide GUI before running, we want to see console and avoid deadlock if code fails - gui.viewDispatcher.sendTo("back"); - let result = load(path, ctx.persistentScope); // Load runs JS and returns last value on stack - storage.remove(path); - - // Must convert to string explicitly - if (result === null) { // mJS: typeof null === "null", ECMAScript: typeof null === "object", IDE complains when checking "null" type - result = "null"; - } else if (typeof result === "string") { - result = "'" + result + "'"; - } else if (typeof result === "number") { - result = result.toString(); - } else if (typeof result === "bigint") { // mJS doesn't support BigInt() but might aswell check - result = "bigint"; - } else if (typeof result === "boolean") { - result = result ? "true" : "false"; - } else if (typeof result === "symbol") { // mJS doesn't support Symbol() but might aswell check - result = "symbol"; - } else if (typeof result === "undefined") { - result = "undefined"; - } else if (typeof result === "object") { - result = "object"; // JSON.stringify() is not implemented - } else if (typeof result === "function") { - result = "function"; - } else { - result = "unknown type: " + typeof result; - } - - gui.viewDispatcher.sendTo("front"); - views.dialog.set("header", "JS Returned:"); - views.dialog.set("text", result); - gui.viewDispatcher.switchTo(views.dialog); - views.textInput.set("defaultText", text); -}, gui, views, ctx); - -eventLoop.subscribe(gui.viewDispatcher.navigation, function (_sub, _, eventLoop) { - eventLoop.stop(); -}, eventLoop); - -gui.viewDispatcher.switchTo(views.dialog); - -// Message behind GUI if something breaks -print("If you're stuck here, something went wrong, re-run the script") -eventLoop.run(); -print("\n\nFinished correctly :)") diff --git a/applications/system/js_app/examples/apps/Scripts/load.js b/applications/system/js_app/examples/apps/Scripts/load.js deleted file mode 100644 index 82b2d2046..000000000 --- a/applications/system/js_app/examples/apps/Scripts/load.js +++ /dev/null @@ -1,3 +0,0 @@ -let math = load(__dirname + "/load_api.js"); -let result = math.add(5, 10); -print(result); diff --git a/applications/system/js_app/examples/apps/Scripts/path.js b/applications/system/js_app/examples/apps/Scripts/path.js deleted file mode 100644 index 0be31b81d..000000000 --- a/applications/system/js_app/examples/apps/Scripts/path.js +++ /dev/null @@ -1,9 +0,0 @@ -let storage = require("storage"); - -print("script has __dirname of" + __dirname); -print("script has __filename of" + __filename); -if (storage.fileExists(__dirname + "/math.js")) { - print("math.js exist here."); -} else { - print("math.js does not exist here."); -} diff --git a/applications/system/js_app/examples/apps/Scripts/storage.js b/applications/system/js_app/examples/apps/Scripts/storage.js deleted file mode 100644 index c0ec8bfa4..000000000 --- a/applications/system/js_app/examples/apps/Scripts/storage.js +++ /dev/null @@ -1,29 +0,0 @@ -let storage = require("storage"); -let path = "/ext/storage.test"; - -print("File exists:", storage.fileExists(path)); - -print("Writing..."); -let file = storage.openFile(path, "w", "create_always"); -file.write("Hello "); -file.close(); - -print("File exists:", storage.fileExists(path)); - -file = storage.openFile(path, "w", "open_append"); -file.write("World!"); -file.close(); - -print("Reading..."); -file = storage.openFile(path, "r", "open_existing"); -let text = file.read("ascii", 128); -file.close(); -print(text); - -print("Removing...") -storage.remove(path); - -print("Done") - -// You don't need to close the file after each operation, this is just to show some different ways to use the API -// There's also many more functions and options, check type definitions in firmware repo \ No newline at end of file diff --git a/applications/system/js_app/examples/apps/Scripts/stringutils.js b/applications/system/js_app/examples/apps/Scripts/stringutils.js deleted file mode 100644 index b2facb237..000000000 --- a/applications/system/js_app/examples/apps/Scripts/stringutils.js +++ /dev/null @@ -1,19 +0,0 @@ -let sampleText = "Hello, World!"; - -let lengthOfText = "Length of text: " + sampleText.length.toString(); -print(lengthOfText); - -let start = 7; -let end = 12; -let substringResult = sampleText.slice(start, end); -print(substringResult); - -let searchStr = "World"; -let result2 = sampleText.indexOf(searchStr).toString(); -print(result2); - -let upperCaseText = "Text in upper case: " + sampleText.toUpperCase(); -print(upperCaseText); - -let lowerCaseText = "Text in lower case: " + sampleText.toLowerCase(); -print(lowerCaseText); diff --git a/applications/system/js_app/js_thread.c b/applications/system/js_app/js_thread.c index b433e9edf..600c2676e 100644 --- a/applications/system/js_app/js_thread.c +++ b/applications/system/js_app/js_thread.c @@ -244,30 +244,6 @@ static int32_t js_thread(void* arg) { struct mjs* mjs = mjs_create(worker); worker->modules = js_modules_create(mjs, worker->resolver); mjs_val_t global = mjs_get_global(mjs); - if(worker->path) { - FuriString* dirpath = furi_string_alloc(); - path_extract_dirname(furi_string_get_cstr(worker->path), dirpath); - mjs_set( - mjs, - global, - "__filename", - ~0, - mjs_mk_string( - mjs, furi_string_get_cstr(worker->path), furi_string_size(worker->path), true)); - mjs_set( - mjs, - global, - "__dirname", - ~0, - mjs_mk_string(mjs, furi_string_get_cstr(dirpath), furi_string_size(dirpath), true)); - furi_string_free(dirpath); - } - mjs_set(mjs, global, "print", ~0, MJS_MK_FN(js_print)); - mjs_set(mjs, global, "delay", ~0, MJS_MK_FN(js_delay)); - mjs_set(mjs, global, "ffi_address", ~0, MJS_MK_FN(js_ffi_address)); - mjs_set(mjs, global, "require", ~0, MJS_MK_FN(js_require)); - mjs_set(mjs, global, "parseInt", ~0, MJS_MK_FN(js_parse_int)); - mjs_val_t console_obj = mjs_mk_object(mjs); if(worker->path) { @@ -292,7 +268,6 @@ static int32_t js_thread(void* arg) { JS_ASSIGN_MULTI(mjs, global) { JS_FIELD("print", MJS_MK_FN(js_print)); JS_FIELD("delay", MJS_MK_FN(js_delay)); - JS_FIELD("toString", MJS_MK_FN(js_global_to_string)); JS_FIELD("parseInt", MJS_MK_FN(js_parse_int)); JS_FIELD("ffi_address", MJS_MK_FN(js_ffi_address)); JS_FIELD("require", MJS_MK_FN(js_require)); diff --git a/applications/system/js_app/packages/fz-sdk/global.d.ts b/applications/system/js_app/packages/fz-sdk/global.d.ts index 953afc30d..d2e73f7de 100644 --- a/applications/system/js_app/packages/fz-sdk/global.d.ts +++ b/applications/system/js_app/packages/fz-sdk/global.d.ts @@ -120,6 +120,7 @@ declare function checkSdkCompatibility(expectedMajor: number, expectedMinor: num * recognized as a baseline feature. For more info, consult the module * documentation. * @param features Array of named features to query + * @version Added in JS SDK 0.1 */ declare function doesSdkSupport(features: string[]): boolean; @@ -131,6 +132,7 @@ declare function doesSdkSupport(features: string[]): boolean; * features that are now recognized as baseline features. For more * info, consult the module documentation. * @param features Array of named features to query + * @version Added in JS SDK 0.1 */ declare function checkSdkFeatures(features: string[]): void | never; @@ -150,15 +152,48 @@ declare function delay(ms: number): void; declare function print(...args: any[]): void; /** - * @brief Reads a JS value from a file - * - * Reads a file at the specified path, interprets it as a JS value and returns - * said value. - * - * @param path The path to the file + * @brief Converts a string to a number + * @param text The string to convert to a number + * @param base Integer base (`2`...`16`), default: 10 * @version Added in JS SDK 0.1 */ -declare function load(path: string): any; +declare function parseInt(text: string, base?: number): number; + +/** + * @brief Path to the directory containing the current script + * @version Added in JS SDK 0.1 + */ +declare const __dirname: string; + +/** + * @brief Path to the current script file + * @version Added in JS SDK 0.1 + */ +declare const __filename: string; + +/** + * @brief Runs a JS file and returns value from it + * + * Reads a file at the specified path and runs it as JS, returning the last evaluated value. + * + * The result is cached and this filepath will not re-evaluated on future + * load() calls for this session. + * + * @param path The path to the file + * @param scope An object to use as global scope while running this file + * @version Added in JS SDK 0.1 + */ +declare function load(path: string, scope?: object): any; + +/** + * @brief Return 1-byte string whose ASCII code is the integer `n` + * + * If `n` is not numeric or outside of `0-255` range, `null` is returned + * + * @param n The ASCII code to convert to string + * @version Added in JS SDK 0.1 + */ +declare function chr(n: number): string | null; /** * @brief Loads a natively implemented module diff --git a/applications/system/js_app/packages/fz-sdk/gui/index.d.ts b/applications/system/js_app/packages/fz-sdk/gui/index.d.ts index 3184a5718..93a6846c2 100644 --- a/applications/system/js_app/packages/fz-sdk/gui/index.d.ts +++ b/applications/system/js_app/packages/fz-sdk/gui/index.d.ts @@ -30,7 +30,7 @@ * |----------------------|------------------| * | `button_menu` | ❌ | * | `button_panel` | ❌ | - * | `byte_input` | ❌ | + * | `byte_input` | ✅ | * | `dialog_ex` | ✅ (as `dialog`) | * | `empty_screen` | ✅ | * | `file_browser` | ❌ | @@ -122,11 +122,26 @@ import type { Contract } from "../event_loop"; type Properties = { [K: string]: any }; export declare class View { + /** + * Assign value to property by name + * @param property Name of the property + * @param value Value to assign + * @version Added in JS SDK 0.1 + */ set

(property: P, value: Props[P]): void; } export declare class ViewFactory> { + /** + * Create view instance with default values, can be changed later with set() + * @version Added in JS SDK 0.1 + */ make(): V; + /** + * Create view instance with custom values, can be changed later with set() + * @param initial Dictionary of property names to values + * @version Added in JS SDK 0.1 + */ makeWith(initial: Partial): V; } @@ -144,6 +159,11 @@ declare class ViewDispatcher { * @version Added in JS SDK 0.1 */ navigation: Contract; + /** + * View object currently shown + * @version Added in JS SDK 0.1 + */ + currentView: View; /** * Sends a number to the custom event handler * @param event number to send diff --git a/applications/system/js_app/types/i2c/index.d.ts b/applications/system/js_app/packages/fz-sdk/i2c/index.d.ts similarity index 100% rename from applications/system/js_app/types/i2c/index.d.ts rename to applications/system/js_app/packages/fz-sdk/i2c/index.d.ts diff --git a/applications/system/js_app/packages/fz-sdk/notification/index.d.ts b/applications/system/js_app/packages/fz-sdk/notification/index.d.ts index 2199a1479..499994d3a 100644 --- a/applications/system/js_app/packages/fz-sdk/notification/index.d.ts +++ b/applications/system/js_app/packages/fz-sdk/notification/index.d.ts @@ -14,6 +14,7 @@ export declare function success(): void; /** * @brief Signals failure to the user via the color LED, speaker and vibration * motor + * @version Added in JS SDK 0.1 */ export declare function error(): void; diff --git a/applications/system/js_app/types/spi/index.d.ts b/applications/system/js_app/packages/fz-sdk/spi/index.d.ts similarity index 100% rename from applications/system/js_app/types/spi/index.d.ts rename to applications/system/js_app/packages/fz-sdk/spi/index.d.ts diff --git a/applications/system/js_app/types/global.d.ts b/applications/system/js_app/types/global.d.ts deleted file mode 100644 index a55dae7d9..000000000 --- a/applications/system/js_app/types/global.d.ts +++ /dev/null @@ -1,226 +0,0 @@ -/** - * @brief Pauses JavaScript execution for a while - * @param ms How many milliseconds to pause the execution for - */ -declare function delay(ms: number): void; - -/** - * @brief Prints to the GUI console view - * @param args The arguments are converted to strings, concatenated without any - * spaces in between and printed to the console view - */ -declare function print(...args: any[]): void; - -/** - * @brief Converts a string to a number - * @param text The string to convert to a number - * @param base Integer base (`2`...`16`), default: 10 - */ -declare function parseInt(text: string, base?: number): number; - -/** - * @brief Path to the directory containing the current script - */ -declare const __dirname: string; - -/** - * @brief Path to the current script file - */ -declare const __filename: string; - -/** - * @brief Reads a JS value from a file - * - * Reads a file at the specified path and runs it as JS, returning the last evaluated value. - * - * The result is cached and this filepath will not re-evaluated on future - * load() calls for this session. - * - * @param path The path to the file - * @param scope An object to use as global scope while running this file - */ -declare function load(path: string, scope?: object): any; - -/** - * @brief Return 1-byte string whose ASCII code is the integer `n` - * - * If `n` is not numeric or outside of `0-255` range, `null` is returned - * - * @param n The ASCII code to convert to string - */ -declare function chr(n: number): string | null; - -/** - * @brief mJS Foreign Pointer type - * - * JavaScript code cannot do anything with values of `RawPointer` type except - * acquire them from native code and pass them right back to other parts of - * native code. These values cannot be turned into something meaningful, nor can - * be they modified. - */ -declare type RawPointer = symbol & { "__tag__": "raw_ptr" }; -// introducing a nominal type in a hacky way; the `__tag__` property doesn't really exist. - -/** - * @brief Holds raw bytes - */ -declare class ArrayBuffer { - /** - * @brief The pointer to the byte buffer - * @note Like other `RawPointer` values, this value is essentially useless - * to JS code. - */ - getPtr: RawPointer; - /** - * @brief The length of the buffer in bytes - */ - byteLength: number; - /** - * @brief Creates an `ArrayBuffer` that contains a sub-part of the buffer - * @param start The index of the byte in the source buffer to be used as the - * start for the new buffer - * @param end The index of the byte in the source buffer that follows the - * byte to be used as the last byte for the new buffer - */ - slice(start: number, end?: number): ArrayBuffer; -} - -declare function ArrayBuffer(): ArrayBuffer; - -declare type ElementType = "u8" | "i8" | "u16" | "i16" | "u32" | "i32"; - -declare class TypedArray { - /** - * @brief The length of the buffer in bytes - */ - byteLength: number; - /** - * @brief The length of the buffer in typed elements - */ - length: number; - /** - * @brief The underlying `ArrayBuffer` - */ - buffer: ArrayBuffer; -} - -declare class Uint8Array extends TypedArray<"u8"> { } -declare class Int8Array extends TypedArray<"i8"> { } -declare class Uint16Array extends TypedArray<"u16"> { } -declare class Int16Array extends TypedArray<"i16"> { } -declare class Uint32Array extends TypedArray<"u32"> { } -declare class Int32Array extends TypedArray<"i32"> { } - -declare function Uint8Array(data: ArrayBuffer | number | number[]): Uint8Array; -declare function Int8Array(data: ArrayBuffer | number | number[]): Int8Array; -declare function Uint16Array(data: ArrayBuffer | number | number[]): Uint16Array; -declare function Int16Array(data: ArrayBuffer | number | number[]): Int16Array; -declare function Uint32Array(data: ArrayBuffer | number | number[]): Uint32Array; -declare function Int32Array(data: ArrayBuffer | number | number[]): Int32Array; - -declare const console: { - /** - * @brief Prints to the UART logs at the `[I]` level - * @param args The arguments are converted to strings, concatenated without any - * spaces in between and printed to the logs - */ - log(...args: any[]): void; - /** - * @brief Prints to the UART logs at the `[D]` level - * @param args The arguments are converted to strings, concatenated without any - * spaces in between and printed to the logs - */ - debug(...args: any[]): void; - /** - * @brief Prints to the UART logs at the `[W]` level - * @param args The arguments are converted to strings, concatenated without any - * spaces in between and printed to the logs - */ - warn(...args: any[]): void; - /** - * @brief Prints to the UART logs at the `[E]` level - * @param args The arguments are converted to strings, concatenated without any - * spaces in between and printed to the logs - */ - error(...args: any[]): void; -}; - -declare class Array { - /** - * @brief Takes items out of the array - * - * Removes elements from the array and returns them in a new array - * - * @param start The index to start taking elements from - * @param deleteCount How many elements to take - * @returns The elements that were taken out of the original array as a new - * array - */ - splice(start: number, deleteCount: number): T[]; - /** - * @brief Adds a value to the end of the array - * @param value The value to add - * @returns New length of the array - */ - push(value: T): number; - /** - * @brief How many elements there are in the array - */ - length: number; -} - -declare class String { - /** - * @brief How many characters there are in the string - */ - length: number; - /** - * @brief Returns the character code at an index in the string - * @param index The index to consult - */ - charCodeAt(index: number): number; - /** - * See `charCodeAt` - */ - at(index: number): number; - /** - * @brief Return index of first occurence of substr within the string or `-1` if not found - * @param substr The string to search for - * @param fromIndex The index to start searching from - */ - indexOf(substr: string, fromIndex?: number): number; - /** - * @brief Return a substring between two indices - * @param start The index to start substring at - * @param end The index to end substring at - */ - slice(start: number, end?: number): string; - /** - * @brief Return this string transformed to upper case - */ - toUpperCase(): string; - /** - * @brief Return this string transformed to lower case - */ - toLowerCase(): string; -} - -declare class Boolean { } - -declare class Function { } - -declare class Number { - /** - * @brief Converts this number to a string - * @param base Integer base (`2`...`16`), default: 10 - */ - toString(base?: number): string; -} - -declare class Object { } - -declare class RegExp { } - -declare interface IArguments { } - -declare type Partial = { [K in keyof O]?: O[K] }; diff --git a/applications/system/js_app/types/gui/byte_input.d.ts b/applications/system/js_app/types/gui/byte_input.d.ts deleted file mode 100644 index 92bd4c5a8..000000000 --- a/applications/system/js_app/types/gui/byte_input.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -import type { View, ViewFactory } from "."; -import type { Contract } from "../event_loop"; - -type Props = { - header: string, - length: number, - defaultData: Uint8Array | ArrayBuffer, -} -declare class ByteInput extends View { - input: Contract; -} -declare class ByteInputFactory extends ViewFactory { } -declare const factory: ByteInputFactory; -export = factory; diff --git a/applications/system/js_app/types/gui/file_picker.d.ts b/applications/system/js_app/types/gui/file_picker.d.ts deleted file mode 100644 index 2051e1432..000000000 --- a/applications/system/js_app/types/gui/file_picker.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** - * @brief Displays a file picker and returns the selected file, or undefined if cancelled - * @param basePath The path to start at - * @param extension The file extension(s) to show (like `.sub`, `.iso|.img`, `*`) - */ -export declare function pickFile(basePath: string, extension: string): string | undefined; diff --git a/applications/system/js_app/types/gui/index.d.ts b/applications/system/js_app/types/gui/index.d.ts deleted file mode 100644 index d4cdc6bc9..000000000 --- a/applications/system/js_app/types/gui/index.d.ts +++ /dev/null @@ -1,57 +0,0 @@ -import type { Contract } from "../event_loop"; - -type Properties = { [K: string]: any }; - -export declare class View { - /** - * Assign value to property by name - * @param property Name of the property - * @param value Value to assign - */ - set

(property: P, value: Props[P]): void; -} - -export declare class ViewFactory> { - /** - * Create view instance with default values, can be changed later with set() - */ - make(): V; - /** - * Create view instance with custom values, can be changed later with set() - * @param initial Dictionary of property names to values - */ - makeWith(initial: Partial): V; -} - -declare class ViewDispatcher { - /** - * Event source for `sendCustom` events - */ - custom: Contract; - /** - * Event source for navigation events (back key presses) - */ - navigation: Contract; - /** - * View object currently shown - */ - currentView: View; - /** - * Sends a number to the custom event handler - * @param event number to send - */ - sendCustom(event: number): void; - /** - * Switches to a view - * @param assoc View-ViewDispatcher association as returned by `add` - */ - switchTo(assoc: View): void; - /** - * Sends this ViewDispatcher to the front or back, above or below all other - * GUI viewports - * @param direction Either `"front"` or `"back"` - */ - sendTo(direction: "front" | "back"): void; -} - -export const viewDispatcher: ViewDispatcher;