[FL-3918] Full-fledged JS SDK + npm packages (#3963)

* feat: js sdk
* refactor: move js back where it belongs
* docs: generate docs using typedoc
* feat: sdk versioning scheme
* ci: silence pvs warning
* docs: bring back old incomplete js docs
* style: readAnalog naming
* fix: rename script compatibility screens

Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
Anna Antonenko
2024-10-31 05:42:57 +03:00
committed by GitHub
parent e4c8270824
commit 85e5642b2a
62 changed files with 3150 additions and 419 deletions

View File

@@ -0,0 +1 @@
docs/

View File

@@ -0,0 +1,31 @@
# Flipper Zero JavaScript SDK
This package contains official tooling and typings for developing Flipper Zero
applications in JavaScript.
## Getting started
Create your application using the interactive wizard:
```shell
npx @flipperdevices/create-fz-app@latest
```
Then, enter the directory with your application and launch it:
```shell
cd my-flip-app
npm start
```
You are free to use `pnpm` or `yarn` instead of `npm`.
## Versioning
For each version of this package, the major and minor components match those of
the Flipper Zero JS SDK version that that package version targets. This version
follows semver. For example, apps compiled with SDK version `0.1.0` will be
compatible with SDK versions `0.1`...`1.0` (not including `1.0`).
Every API has a version history reflected in its JSDoc comment. It is heavily
recommended to check SDK compatibility using a combination of
`sdkCompatibilityStatus`, `isSdkCompatible`, `assertSdkCompatibility` depending
on your use case.
## Documentation
Check out the [JavaScript section in the Developer Documentation](https://developer.flipper.net/flipperzero/doxygen/js.html)

View File

@@ -0,0 +1,91 @@
/**
* @brief Special key codes that this module recognizes
* @version Added in JS SDK 0.1
*/
export type ModifierKey = "CTRL" | "SHIFT" | "ALT" | "GUI";
/** @version Added in JS SDK 0.1 */
export type MainKey =
"DOWN" | "LEFT" | "RIGHT" | "UP" |
"ENTER" | "PAUSE" | "CAPSLOCK" | "DELETE" | "BACKSPACE" | "END" | "ESC" |
"HOME" | "INSERT" | "NUMLOCK" | "PAGEUP" | "PAGEDOWN" | "PRINTSCREEN" |
"SCROLLLOCK" | "SPACE" | "TAB" | "MENU" |
"F1" | "F2" | "F3" | "F4" | "F5" | "F6" | "F7" | "F8" | "F9" | "F10" |
"F11" | "F12" | "F13" | "F14" | "F15" | "F16" | "F17" | "F18" | "F19" |
"F20" | "F21" | "F22" | "F23" | "F24" |
"\n" | " " | "!" | "\"" | "#" | "$" | "%" | "&" | "'" | "(" | ")" | "*" |
"+" | "," | "-" | "." | "/" | ":" | ";" | "<" | ">" | "=" | "?" | "@" | "[" |
"]" | "\\" | "^" | "_" | "`" | "{" | "}" | "|" | "~" |
"0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" |
"A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" |
"M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" |
"Y" | "Z" |
"a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" |
"m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" |
"y" | "z";
/** @version Added in JS SDK 0.1 */
export type KeyCode = MainKey | ModifierKey | number;
/**
* @brief Initializes the module
* @param settings USB device settings. Omit to select default parameters
* @version Added in JS SDK 0.1
*/
export declare function setup(settings?: { vid: number, pid: number, mfrName?: string, prodName?: string }): void;
/**
* @brief Tells whether the virtual USB HID device has successfully connected
* @version Added in JS SDK 0.1
*/
export declare function isConnected(): boolean;
/**
* @brief Presses one or multiple keys at once, then releases them
* @param keys The arguments represent a set of keys to. Out of that set, only
* one of the keys may represent a "main key" (see `MainKey`), with
* the rest being modifier keys (see `ModifierKey`).
* @version Added in JS SDK 0.1
*/
export declare function press(...keys: KeyCode[]): void;
/**
* @brief Presses one or multiple keys at once without releasing them
* @param keys The arguments represent a set of keys to. Out of that set, only
* one of the keys may represent a "main key" (see `MainKey`), with
* the rest being modifier keys (see `ModifierKey`).
* @version Added in JS SDK 0.1
*/
export declare function hold(...keys: KeyCode[]): void;
/**
* @brief Releases one or multiple keys at once
* @param keys The arguments represent a set of keys to. Out of that set, only
* one of the keys may represent a "main key" (see `MainKey`), with
* the rest being modifier keys (see `ModifierKey`).
* @version Added in JS SDK 0.1
*/
export declare function release(...keys: KeyCode[]): void;
/**
* @brief Prints a string by repeatedly pressing and releasing keys
* @param string The string to print
* @param delay How many milliseconds to wait between key presses
* @version Added in JS SDK 0.1
*/
export declare function print(string: string, delay?: number): void;
/**
* @brief Prints a string by repeatedly pressing and releasing keys. Presses
* "Enter" after printing the string
* @param string The string to print
* @param delay How many milliseconds to wait between key presses
* @version Added in JS SDK 0.1
*/
export declare function println(string: string, delay?: number): void;

View File

@@ -0,0 +1 @@
# Welcome

View File

@@ -0,0 +1,182 @@
/**
* Module for dealing with events
*
* ```js
* let eventLoop = require("event_loop");
* ```
*
* The event loop is central to event-based programming in many frameworks, and
* our JS subsystem is no exception. It is a good idea to familiarize yourself
* with the event loop first before using any of the advanced modules (e.g. GPIO
* and GUI).
*
* # Conceptualizing the event loop
* If you ever wrote JavaScript before, you have definitely seen callbacks. It's
* when a function accepts another function (usually an anonymous one) as one of
* the arguments, which it will call later on, e.g. when an event happens or
* when data becomes ready:
* ```js
* setTimeout(function() { console.log("Hello, World!") }, 1000);
* ```
*
* Many JavaScript engines employ a queue that the runtime fetches events from
* as they occur, subsequently calling the corresponding callbacks. This is done
* in a long-running loop, hence the name "event loop". Here's the pseudocode
* for a typical event loop:
* ```js
* while(loop_is_running()) {
* if(event_available_in_queue()) {
* let event = fetch_event_from_queue();
* let callback = get_callback_associated_with(event);
* if(callback)
* callback(get_extra_data_for(event));
* } else {
* // avoid wasting CPU time
* sleep_until_any_event_becomes_available();
* }
* }
* ```
*
* Most JS runtimes enclose the event loop within themselves, so that most JS
* programmers does not even need to be aware of its existence. This is not the
* case with our JS subsystem.
*
* # Example
* This is how one would write something similar to the `setTimeout` example
* above:
* ```js
* // import module
* let eventLoop = require("event_loop");
*
* // create an event source that will fire once 1 second after it has been created
* let timer = eventLoop.timer("oneshot", 1000);
*
* // subscribe a callback to the event source
* eventLoop.subscribe(timer, function(_subscription, _item, eventLoop) {
* print("Hello, World!");
* eventLoop.stop();
* }, eventLoop); // notice this extra argument. we'll come back to this later
*
* // run the loop until it is stopped
* eventLoop.run();
*
* // the previous line will only finish executing once `.stop()` is called, hence
* // the following line will execute only after "Hello, World!" is printed
* print("Stopped");
* ```
*
* I promised you that we'll come back to the extra argument after the callback
* function. Our JavaScript engine does not support closures (anonymous
* functions that access values outside of their arguments), so we ask
* `subscribe` to pass an outside value (namely, `eventLoop`) as an argument to
* the callback so that we can access it. We can modify this extra state:
* ```js
* // this timer will fire every second
* let timer = eventLoop.timer("periodic", 1000);
* eventLoop.subscribe(timer, function(_subscription, _item, counter, eventLoop) {
* print("Counter is at:", counter);
* if(counter === 10)
* eventLoop.stop();
* // modify the extra arguments that will be passed to us the next time
* return [counter + 1, eventLoop];
* }, 0, eventLoop);
* ```
*
* Because we have two extra arguments, if we return anything other than an
* array of length 2, the arguments will be kept as-is for the next call.
*
* The first two arguments that get passed to our callback are:
* - The subscription manager that lets us `.cancel()` our subscription
* - The event item, used for events that have extra data. Timer events do
* not, they just produce `undefined`.
*
* @version Added in JS SDK 0.1
* @module
*/
/**
* @ignore
*/
type Lit = undefined | null | {};
/**
* Subscription control interface
* @version Added in JS SDK 0.1
*/
export interface Subscription {
/**
* Cancels the subscription, preventing any future events managed by the
* subscription from firing
* @version Added in JS SDK 0.1
*/
cancel(): void;
}
/**
* Opaque event source identifier
* @version Added in JS SDK 0.1
*/
export type Contract<Item = undefined> = symbol & { "__tag__": "contract" };
// introducing a nominal type in a hacky way; the `__tag__` property doesn't really exist.
/**
* A callback can be assigned to an event loop to listen to an event. It may
* return an array with values that will be passed to it as arguments the next
* time that it is called. The first argument is always the subscription
* manager, and the second argument is always the item that trigged the event.
* The type of the item is defined by the event source.
* @version Added in JS SDK 0.1
*/
export type Callback<Item, Args extends Lit[]> = (subscription: Subscription, item: Item, ...args: Args) => Args | undefined | void;
/**
* Subscribes a callback to an event
* @param contract Event identifier
* @param callback Function to call when the event is triggered
* @param args Initial arguments passed to the callback
* @version Added in JS SDK 0.1
*/
export function subscribe<Item, Args extends Lit[]>(contract: Contract<Item>, callback: Callback<Item, Args>, ...args: Args): Subscription;
/**
* Runs the event loop until it is stopped (potentially never)
* @version Added in JS SDK 0.1
*/
export function run(): void | never;
/**
* Stops the event loop
* @version Added in JS SDK 0.1
*/
export function stop(): void;
/**
* Creates a timer event that can be subscribed to just like any other event
* @param mode Either `"oneshot"` or `"periodic"`
* @param interval Timer interval in milliseconds
* @version Added in JS SDK 0.1
*/
export function timer(mode: "oneshot" | "periodic", interval: number): Contract;
/**
* Message queue
* @version Added in JS SDK 0.1
*/
export declare class Queue<T> {
/**
* Message event
* @version Added in JS SDK 0.1
*/
input: Contract<T>;
/**
* Sends a message to the queue
* @param message message to send
* @version Added in JS SDK 0.1
*/
send(message: T): void;
}
/**
* Creates a message queue
* @param length maximum queue capacity
* @version Added in JS SDK 0.1
*/
export function queue<T>(length: number): Queue<T>;

View File

@@ -0,0 +1,41 @@
/**
* Module for querying device properties
* @version Added in JS SDK 0.1
* @module
*/
/**
* @brief Returns the device model
* @version Added in JS SDK 0.1
*/
export declare function getModel(): string;
/**
* @brief Returns the name of the virtual dolphin
* @version Added in JS SDK 0.1
*/
export declare function getName(): string;
/**
* @brief Returns the battery charge percentage
* @version Added in JS SDK 0.1
*/
export declare function getBatteryCharge(): number;
/**
* @warning Do **NOT** use this to check the presence or absence of features. If
* you do, I'm gonna be sad :( Instead, refer to `checkSdkFeatures` and
* other similar mechanisms.
* @note Original firmware reports `"flipperdevices"`.
* @version Added in JS SDK 0.1
*/
export declare const firmwareVendor: string;
/**
* @warning Do **NOT** use this to check the presence or absence of features. If
* you do, I'm gonna be sad :( Instead, refer to
* `checkSdkCompatibility` and other similar mechanisms.
* @note You're looking at JS SDK 0.1
* @version Added in JS SDK 0.1
*/
export declare const jsSdkVersion: [number, number];

View File

@@ -0,0 +1,344 @@
/**
* Things from this module are automatically available to you without having to
* explicitly import anything.
*
* # SDK versioning and features
*
* ## Motivation
* It is very important that you check that features are implemented before you
* use them. By adding the necessary checks, you ensure that your users get a
* clear warning instead of a cryptic error message when running the script.
*
* This system has been designed in collaboration with our community in order to
* make things better for everybody involved. You can find out more in this
* discussion: https://github.com/flipperdevices/flipperzero-firmware/pull/3961
*
* ## Community agreement
* Each interpreter implementation (aka "JS SDK", aka "JS API"), including
* those found in third-party firmware distributions, defines two markers for
* signaling what it supports: the **SDK version** and the
* **extra feature set**.
*
* The **SDK version** consists of two semver-like integer components: the major
* version and the minor version. Like semver, the major version is bumped when
* a breaking change is introduced (i.e. one that would require correction of
* apps by their developers), and the minor version is bumped when a new
* non-breaking feature is introduced. Because we have adopted TypeScript,
* the https://www.semver-ts.org/ standard is used to determine whether a change
* is breaking or not. The basis of `semver-ts` is the "no new red squiggles"
* rule.
*
* Every major version is associated with a set of **extra features** that are
* present in some firmware distributions but not others. Distributions may
* cross-port features between each other, until at some point they get ported
* into the upstream firmware distribution. With the next major release of the
* JS SDK, all extra features present in the upstream distribution are now
* declared **baseline features**, and thus no longer recognized as "extra
* features".
*
* Before using a feature, you must check that the interpreter that you're
* running on actually supports it. If you don't, the portability of your
* application will suffer.
*
* ## Implementation
* Use the following functions to check version compatibility:
* - `checkSdkCompatibility` when your script absolutely cannot function on an
* incompatible interpreter
* - `isSdkCompatible` when your script can leverage multiple interpreter
* editions to its advantage
* - `sdkCompatibilityStatus` when you need a detailed status on compatibility
*
* Use the following functions to check feature compatibility:
* - `checkSdkFeatures` when your script absolutely cannot function on an
* incompatible interpreter
* - `doesSdkSupport` when your script can leverage multiple interpreter
* editions to its advantage
*
* ## Automatic version enforcement
* The SDK will automatically insert a call to `checkSdkCompatibility` in the
* beginning of the resulting script. If you would like to disable this check
* and instead use other manual compatibility checking facilities, edit your
* `fz-sdk.config.json5`.
*
* # Standard library
* Standard library features are mostly unimplemented. This module defines,
* among other things, the features that _are_ implemented.
*
* @version Added in JS SDK 0.1
* @module
*/
/**
* @brief Checks compatibility between the script and the JS SDK that the
* firmware provides
*
* @note You're looking at JS SDK v0.1
*
* @param expectedMajor JS SDK major version expected by the script
* @param expectedMinor JS SDK minor version expected by the script
* @returns Compatibility status:
* - `"compatible"` if the script and the JS SDK are compatible
* - `"firmwareTooOld"` if the expected major version is larger than the
* version of the firmware, or if the expected minor version is larger than
* the version of the firmware
* - `"firmwareTooNew"` if the expected major version is lower than the
* version of the firmware
* @version Added in JS SDK 0.1
*/
declare function sdkCompatibilityStatus(expectedMajor: number, expectedMinor: number):
"compatible" | "firmwareTooOld" | "firmwareTooNew";
/**
* @brief Checks compatibility between the script and the JS SDK that the
* firmware provides in a boolean fashion
*
* @note You're looking at JS SDK v0.1
*
* @param expectedMajor JS SDK major version expected by the script
* @param expectedMinor JS SDK minor version expected by the script
* @returns `true` if the two are compatible, `false` otherwise
* @version Added in JS SDK 0.1
*/
declare function isSdkCompatible(expectedMajor: number, expectedMinor: number): boolean;
/**
* @brief Asks the user whether to continue executing the script if the versions
* are not compatible. Does nothing if they are.
*
* @note You're looking at JS SDK v0.1
*
* @param expectedMajor JS SDK major version expected by the script
* @param expectedMinor JS SDK minor version expected by the script
* @version Added in JS SDK 0.1
*/
declare function checkSdkCompatibility(expectedMajor: number, expectedMinor: number): void | never;
/**
* @brief Checks whether all of the specified extra features are supported by
* the interpreter.
* @warning This function will return `false` if a queried feature is now
* recognized as a baseline feature. For more info, consult the module
* documentation.
* @param features Array of named features to query
*/
declare function doesSdkSupport(features: string[]): boolean;
/**
* @brief Checks whether all of the specified extra features are supported by
* the interpreter, asking the user if they want to continue running the
* script if they're not.
* @warning This function will act as if the feature is not implemented for
* features that are now recognized as baseline features. For more
* info, consult the module documentation.
* @param features Array of named features to query
*/
declare function checkSdkFeatures(features: string[]): void | never;
/**
* @brief Pauses JavaScript execution for a while
* @param ms How many milliseconds to pause the execution for
* @version Added in JS SDK 0.1
*/
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
* @version Added in JS SDK 0.1
*/
declare function print(...args: any[]): void;
/**
* @brief Converts a number to a string
* @param value The number to convert to a string
* @param base Integer base (`2`...`16`), default: 10
* @version Added in JS SDK 0.1
*/
declare function toString(value: number, base?: number): string;
/**
* @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
* @version Added in JS SDK 0.1
*/
declare function load(path: string): any;
/**
* @brief Loads a natively implemented module
* @param module The name of the module to load
* @version Added in JS SDK 0.1
*/
declare function require(module: string): any;
/**
* @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.
*
* @version Added in JS SDK 0.1
*/
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
* @version Added in JS SDK 0.1
*/
declare class ArrayBuffer {
/**
* @brief The pointer to the byte buffer
* @note Like other `RawPointer` values, this value is essentially useless
* to JS code.
* @version Added in JS SDK 0.1
*/
getPtr: RawPointer;
/**
* @brief The length of the buffer in bytes
* @version Added in JS SDK 0.1
*/
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
* @version Added in JS SDK 0.1
*/
slice(start: number, end?: number): ArrayBuffer;
}
declare function ArrayBuffer(): ArrayBuffer;
declare type ElementType = "u8" | "i8" | "u16" | "i16" | "u32" | "i32";
declare class TypedArray<E extends ElementType> {
/**
* @brief The length of the buffer in bytes
* @version Added in JS SDK 0.1
*/
byteLength: number;
/**
* @brief The length of the buffer in typed elements
* @version Added in JS SDK 0.1
*/
length: number;
/**
* @brief The underlying `ArrayBuffer`
* @version Added in JS SDK 0.1
*/
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
* @version Added in JS SDK 0.1
*/
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
* @version Added in JS SDK 0.1
*/
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
* @version Added in JS SDK 0.1
*/
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
* @version Added in JS SDK 0.1
*/
error(...args: any[]): void;
};
declare class Array<T> {
/**
* @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
* @version Added in JS SDK 0.1
*/
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
* @version Added in JS SDK 0.1
*/
push(value: T): number;
/**
* @brief How many elements there are in the array
* @version Added in JS SDK 0.1
*/
length: number;
}
declare class String {
/**
* @brief How many characters there are in the string
* @version Added in JS SDK 0.1
*/
length: number;
/**
* @brief Returns the character code at an index in the string
* @param index The index to consult
* @version Added in JS SDK 0.1
*/
charCodeAt(index: number): number;
/**
* See `charCodeAt`
* @version Added in JS SDK 0.1
*/
at(index: number): number;
}
declare class Boolean { }
declare class Function { }
declare class Number { }
declare class Object { }
declare class RegExp { }
declare interface IArguments { }
declare type Partial<O extends object> = { [K in keyof O]?: O[K] };

View File

@@ -0,0 +1,86 @@
/**
* Module for accessing the GPIO (General Purpose Input/Output) ports
*
* ```js
* let eventLoop = require("event_loop");
* let gpio = require("gpio");
* ```
*
* This module depends on the `event_loop` module, so it _must_ only be imported
* after `event_loop` is imported.
*
* # Example
* ```js
* let eventLoop = require("event_loop");
* let gpio = require("gpio");
*
* let led = gpio.get("pc3");
* led.init({ direction: "out", outMode: "push_pull" });
*
* led.write(true);
* delay(1000);
* led.write(false);
* delay(1000);
* ```
*
* @version Added in JS SDK 0.1
* @module
*/
import type { Contract } from "../event_loop";
/**
* @version Added in JS SDK 0.1
*/
export interface Mode {
direction: "in" | "out";
outMode?: "push_pull" | "open_drain";
inMode?: "analog" | "plain_digital" | "interrupt" | "event";
edge?: "rising" | "falling" | "both";
pull?: "up" | "down";
}
/**
* @version Added in JS SDK 0.1
*/
export interface Pin {
/**
* Configures a pin. This may be done several times.
* @param mode Pin configuration object
* @version Added in JS SDK 0.1
*/
init(mode: Mode): void;
/**
* Sets the output value of a pin if it's been configured with
* `direction: "out"`.
* @param value Logic value to output
* @version Added in JS SDK 0.1
*/
write(value: boolean): void;
/**
* Gets the input value of a pin if it's been configured with
* `direction: "in"`, but not `inMode: "analog"`.
* @version Added in JS SDK 0.1
*/
read(): boolean;
/**
* Gets the input voltage of a pin in millivolts if it's been configured
* with `direction: "in"` and `inMode: "analog"`
* @version Added in JS SDK 0.1
*/
readAnalog(): number;
/**
* Returns an `event_loop` event that can be used to listen to interrupts,
* as configured by `init`
* @version Added in JS SDK 0.1
*/
interrupt(): Contract;
}
/**
* Returns an object that can be used to manage a GPIO pin. For the list of
* available pins, see https://docs.flipper.net/gpio-and-modules#miFsS
* @param pin Pin name (e.g. `"PC3"`) or number (e.g. `7`)
* @version Added in JS SDK 0.1
*/
export function get(pin: string | number): Pin;

View File

@@ -0,0 +1,45 @@
/**
* Displays a dialog with up to three options.
*
* <img src="../images/dialog.png" width="200" alt="Sample screenshot of the view" />
*
* ```js
* let eventLoop = require("event_loop");
* let gui = require("gui");
* let dialogView = require("gui/dialog");
* ```
*
* This module depends on the `gui` module, which in turn depends on the
* `event_loop` module, so they _must_ be imported in this order. It is also
* recommended to conceptualize these modules first before using this one.
*
* # Example
* For an example refer to the `gui.js` example script.
*
* # View props
* - `header`: Text displayed in bold at the top of the screen
* - `text`: Text displayed in the middle of the string
* - `left`: Text for the left button
* - `center`: Text for the center button
* - `right`: Text for the right button
*
* @version Added in JS SDK 0.1
* @module
*/
import type { View, ViewFactory } from ".";
import type { Contract } from "../event_loop";
type Props = {
header: string,
text: string,
left: string,
center: string,
right: string,
}
declare class Dialog extends View<Props> {
input: Contract<"left" | "center" | "right">;
}
declare class DialogFactory extends ViewFactory<Props, Dialog> { }
declare const factory: DialogFactory;
export = factory;

View File

@@ -0,0 +1,32 @@
/**
* Displays nothing.
*
* <img src="../images/empty.png" width="200" alt="Sample screenshot of the view" />
*
* ```js
* let eventLoop = require("event_loop");
* let gui = require("gui");
* let emptyView = require("gui/empty_screen");
* ```
*
* This module depends on the `gui` module, which in turn depends on the
* `event_loop` module, so they _must_ be imported in this order. It is also
* recommended to conceptualize these modules first before using this one.
*
* # Example
* For an example refer to the GUI example.
*
* # View props
* This view does not have any props.
*
* @version Added in JS SDK 0.1
* @module
*/
import type { View, ViewFactory } from ".";
type Props = {};
declare class EmptyScreen extends View<Props> { }
declare class EmptyScreenFactory extends ViewFactory<Props, EmptyScreen> { }
declare const factory: EmptyScreenFactory;
export = factory;

View File

@@ -0,0 +1,171 @@
/**
* ```js
* let eventLoop = require("event_loop");
* let gui = require("gui");
* ```
*
* This module depends on the `event_loop` module, so it _must_ only be imported
* after `event_loop` is imported.
*
* ## Conceptualizing GUI
* ### Event loop
* It is highly recommended to familiarize yourself with the event loop first
* before doing GUI-related things.
*
* ### Canvas
* The canvas is just a drawing area with no abstractions over it. Drawing on
* the canvas directly (i.e. not through a viewport) is useful in case you want
* to implement a custom design element, but this is rather uncommon.
*
* ### Viewport
* A viewport is a window into a rectangular portion of the canvas. Applications
* always access the canvas through a viewport.
*
* ### View
* In Flipper's terminology, a "View" is a fullscreen design element that
* assumes control over the entire viewport and all input events. Different
* types of views are available (not all of which are unfortunately currently
* implemented in JS):
* | View | Has JS adapter? |
* |----------------------|------------------|
* | `button_menu` | ❌ |
* | `button_panel` | ❌ |
* | `byte_input` | ❌ |
* | `dialog_ex` | ✅ (as `dialog`) |
* | `empty_screen` | ✅ |
* | `file_browser` | ❌ |
* | `loading` | ✅ |
* | `menu` | ❌ |
* | `number_input` | ❌ |
* | `popup` | ❌ |
* | `submenu` | ✅ |
* | `text_box` | ✅ |
* | `text_input` | ✅ |
* | `variable_item_list` | ❌ |
* | `widget` | ❌ |
*
* In JS, each view has its own set of properties (or just "props"). The
* programmer can manipulate these properties in two ways:
* - Instantiate a `View` using the `makeWith(props)` method, passing an
* object with the initial properties
* - Call `set(name, value)` to modify a property of an existing `View`
*
* ### View Dispatcher
* The view dispatcher holds references to all the views that an application
* needs and switches between them as the application makes requests to do so.
*
* ### Scene Manager
* The scene manager is an optional add-on to the view dispatcher that makes
* managing applications with complex navigation flows easier. It is currently
* inaccessible from JS.
*
* ### Approaches
* In total, there are three different approaches that you may take when writing
* a GUI application:
* | Approach | Use cases | Available from JS |
* |----------------|------------------------------------------------------------------------------|-------------------|
* | ViewPort only | Accessing the graphics API directly, without any of the nice UI abstractions | ❌ |
* | ViewDispatcher | Common UI elements that fit with the overall look of the system | ✅ |
* | SceneManager | Additional navigation flow management for complex applications | ❌ |
*
* # Example
* An example with three different views using the ViewDispatcher approach:
* ```js
* let eventLoop = require("event_loop");
* let gui = require("gui");
* let loadingView = require("gui/loading");
* let submenuView = require("gui/submenu");
* let emptyView = require("gui/empty_screen");
*
* // Common pattern: declare all the views in an object. This is absolutely not
* // required, but adds clarity to the script.
* let views = {
* // the view dispatcher auto-✨magically✨ remembers views as they are created
* loading: loadingView.make(),
* empty: emptyView.make(),
* demos: submenuView.makeWith({
* items: [
* "Hourglass screen",
* "Empty screen",
* "Exit app",
* ],
* }),
* };
*
* // go to different screens depending on what was selected
* eventLoop.subscribe(views.demos.chosen, function (_sub, index, gui, eventLoop, views) {
* if (index === 0) {
* gui.viewDispatcher.switchTo(views.loading);
* } else if (index === 1) {
* gui.viewDispatcher.switchTo(views.empty);
* } else if (index === 2) {
* eventLoop.stop();
* }
* }, gui, eventLoop, views);
*
* // go to the demo chooser screen when the back key is pressed
* eventLoop.subscribe(gui.viewDispatcher.navigation, function (_sub, _, gui, views) {
* gui.viewDispatcher.switchTo(views.demos);
* }, gui, views);
*
* // run UI
* gui.viewDispatcher.switchTo(views.demos);
* eventLoop.run();
* ```
*
* @version Added in JS SDK 0.1
* @module
*/
import type { Contract } from "../event_loop";
type Properties = { [K: string]: any };
export declare class View<Props extends Properties> {
set<P extends keyof Props>(property: P, value: Props[P]): void;
}
export declare class ViewFactory<Props extends Properties, V extends View<Props>> {
make(): V;
makeWith(initial: Partial<Props>): V;
}
/**
* @version Added in JS SDK 0.1
*/
declare class ViewDispatcher {
/**
* Event source for `sendCustom` events
* @version Added in JS SDK 0.1
*/
custom: Contract<number>;
/**
* Event source for navigation events (back key presses)
* @version Added in JS SDK 0.1
*/
navigation: Contract;
/**
* Sends a number to the custom event handler
* @param event number to send
* @version Added in JS SDK 0.1
*/
sendCustom(event: number): void;
/**
* Switches to a view
* @param assoc View-ViewDispatcher association as returned by `add`
* @version Added in JS SDK 0.1
*/
switchTo(assoc: View<any>): void;
/**
* Sends this ViewDispatcher to the front or back, above or below all other
* GUI viewports
* @param direction Either `"front"` or `"back"`
* @version Added in JS SDK 0.1
*/
sendTo(direction: "front" | "back"): void;
}
/**
* @version Added in JS SDK 0.1
*/
export const viewDispatcher: ViewDispatcher;

View File

@@ -0,0 +1,33 @@
/**
* Displays an animated hourglass icon. Suppresses all `navigation` events,
* making it impossible for the user to exit the view by pressing the back key.
*
* <img src="../images/loading.png" width="200" alt="Sample screenshot of the view" />
*
* ```js
* let eventLoop = require("event_loop");
* let gui = require("gui");
* let loadingView = require("gui/loading");
* ```
*
* This module depends on the `gui` module, which in turn depends on the
* `event_loop` module, so they _must_ be imported in this order. It is also
* recommended to conceptualize these modules first before using this one.
*
* # Example
* For an example refer to the GUI example.
*
* # View props
* This view does not have any props.
*
* @version Added in JS SDK 0.1
* @module
*/
import type { View, ViewFactory } from ".";
type Props = {};
declare class Loading extends View<Props> { }
declare class LoadingFactory extends ViewFactory<Props, Loading> { }
declare const factory: LoadingFactory;
export = factory;

View File

@@ -0,0 +1,39 @@
/**
* Displays a scrollable list of clickable textual entries.
*
* <img src="../images/submenu.png" width="200" alt="Sample screenshot of the view" />
*
* ```js
* let eventLoop = require("event_loop");
* let gui = require("gui");
* let submenuView = require("gui/submenu");
* ```
*
* This module depends on the `gui` module, which in turn depends on the
* `event_loop` module, so they _must_ be imported in this order. It is also
* recommended to conceptualize these modules first before using this one.
*
* # Example
* For an example refer to the GUI example.
*
* # View props
* - `header`: Text displayed at the top of the screen in bold
* - `items`: Array of selectable textual items
*
* @version Added in JS SDK 0.1
* @module
*/
import type { View, ViewFactory } from ".";
import type { Contract } from "../event_loop";
type Props = {
header: string,
items: string[],
};
declare class Submenu extends View<Props> {
chosen: Contract<number>;
}
declare class SubmenuFactory extends ViewFactory<Props, Submenu> { }
declare const factory: SubmenuFactory;
export = factory;

View File

@@ -0,0 +1,41 @@
/**
* Displays a scrollable read-only text field.
*
* <img src="text_box.png" width="200" alt="Sample screenshot of the view" />
*
* ```js
* let eventLoop = require("event_loop");
* let gui = require("gui");
* let textBoxView = require("gui/text_box");
* ```
*
* This module depends on the `gui` module, which in turn depends on the
* `event_loop` module, so they _must_ be imported in this order. It is also
* recommended to conceptualize these modules first before using this one.
*
* # Example
* For an example refer to the `gui.js` example script.
*
* # View props
* - `text`: Text in the text box
* - `font`: The font to display the text in (`"text"` or `"hex"`)
* - `focus`: The initial focus of the text box (`"start"` or `"end"`)
*
* @version Added in JS SDK 0.1
* @module
*/
import type { View, ViewFactory } from ".";
import type { Contract } from "../event_loop";
type Props = {
text: string,
font: "text" | "hex",
focus: "start" | "end",
}
declare class TextBox extends View<Props> {
chosen: Contract<number>;
}
declare class TextBoxFactory extends ViewFactory<Props, TextBox> { }
declare const factory: TextBoxFactory;
export = factory;

View File

@@ -0,0 +1,41 @@
/**
* Displays a keyboard.
*
* <img src="../images/text_input.png" width="200" alt="Sample screenshot of the view" />
*
* ```js
* let eventLoop = require("event_loop");
* let gui = require("gui");
* let textInputView = require("gui/text_input");
* ```
*
* This module depends on the `gui` module, which in turn depends on the
* `event_loop` module, so they _must_ be imported in this order. It is also
* recommended to conceptualize these modules first before using this one.
*
* # Example
* For an example refer to the `gui.js` example script.
*
* # View props
* - `header`: Text displayed at the top of the screen
* - `minLength`: Minimum allowed text length
* - `maxLength`: Maximum allowed text length
*
* @version Added in JS SDK 0.1
* @module
*/
import type { View, ViewFactory } from ".";
import type { Contract } from "../event_loop";
type Props = {
header: string,
minLength: number,
maxLength: number,
}
declare class TextInput extends View<Props> {
input: Contract<string>;
}
declare class TextInputFactory extends ViewFactory<Props, TextInput> { }
declare const factory: TextInputFactory;
export = factory;

View File

@@ -0,0 +1,54 @@
/**
* Math operations
* @version Added in JS SDK 0.1
* @module
*/
/** @version Added in JS SDK 0.1 */
export function abs(n: number): number;
/** @version Added in JS SDK 0.1 */
export function acos(n: number): number;
/** @version Added in JS SDK 0.1 */
export function acosh(n: number): number;
/** @version Added in JS SDK 0.1 */
export function asin(n: number): number;
/** @version Added in JS SDK 0.1 */
export function asinh(n: number): number;
/** @version Added in JS SDK 0.1 */
export function atan(n: number): number;
/** @version Added in JS SDK 0.1 */
export function atan2(a: number, b: number): number;
/** @version Added in JS SDK 0.1 */
export function atanh(n: number): number;
/** @version Added in JS SDK 0.1 */
export function cbrt(n: number): number;
/** @version Added in JS SDK 0.1 */
export function ceil(n: number): number;
/** @version Added in JS SDK 0.1 */
export function clz32(n: number): number;
/** @version Added in JS SDK 0.1 */
export function cos(n: number): number;
/** @version Added in JS SDK 0.1 */
export function exp(n: number): number;
/** @version Added in JS SDK 0.1 */
export function floor(n: number): number;
/** @version Added in JS SDK 0.1 */
export function max(n: number, m: number): number;
/** @version Added in JS SDK 0.1 */
export function min(n: number, m: number): number;
/** @version Added in JS SDK 0.1 */
export function pow(n: number, m: number): number;
/** @version Added in JS SDK 0.1 */
export function random(): number;
/** @version Added in JS SDK 0.1 */
export function sign(n: number): number;
/** @version Added in JS SDK 0.1 */
export function sin(n: number): number;
/** @version Added in JS SDK 0.1 */
export function sqrt(n: number): number;
/** @version Added in JS SDK 0.1 */
export function trunc(n: number): number;
/** @version Added in JS SDK 0.1 */
declare const PI: number;
/** @version Added in JS SDK 0.1 */
declare const EPSILON: number;

View File

@@ -0,0 +1,31 @@
/**
* Module for using the color LED and vibration motor
* @version Added in JS SDK 0.1
* @module
*/
/**
* @brief Signals success to the user via the color LED, speaker and vibration
* motor
* @version Added in JS SDK 0.1
*/
export declare function success(): void;
/**
* @brief Signals failure to the user via the color LED, speaker and vibration
* motor
*/
export declare function error(): void;
/**
* @version Added in JS SDK 0.1
*/
export type Color = "red" | "green" | "blue" | "yellow" | "cyan" | "magenta";
/**
* @brief Displays a basic color on the color LED
* @param color The color to display, see `Color`
* @param duration The duration, either `"short"` (10ms) or `"long"` (100ms)
* @version Added in JS SDK 0.1
*/
export declare function blink(color: Color, duration: "short" | "long"): void;

View File

@@ -0,0 +1,29 @@
{
"name": "@flipperdevices/fz-sdk",
"version": "0.1.0",
"description": "Type declarations and documentation for native JS modules available on Flipper Zero",
"keywords": [
"flipper",
"flipper zero",
"framework"
],
"author": "Flipper Devices",
"license": "GPL-3.0-only",
"repository": {
"type": "git",
"url": "git+https://github.com/flipperdevices/flipperzero-firmware.git",
"directory": "applications/system/js_app/packages/fz-sdk"
},
"type": "module",
"devDependencies": {
"esbuild": "^0.24.0",
"esbuild-plugin-tsc": "^0.4.0",
"json5": "^2.2.3",
"typedoc": "^0.26.10",
"typedoc-material-theme": "^1.1.0"
},
"dependencies": {
"prompts": "^2.4.2",
"serialport": "^12.0.0"
}
}

View File

@@ -0,0 +1,896 @@
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
dependencies:
prompts:
specifier: ^2.4.2
version: 2.4.2
serialport:
specifier: ^12.0.0
version: 12.0.0
devDependencies:
esbuild:
specifier: ^0.24.0
version: 0.24.0
esbuild-plugin-tsc:
specifier: ^0.4.0
version: 0.4.0(typescript@5.6.3)
json5:
specifier: ^2.2.3
version: 2.2.3
typedoc:
specifier: ^0.26.10
version: 0.26.10(typescript@5.6.3)
typedoc-material-theme:
specifier: ^1.1.0
version: 1.1.0(typedoc@0.26.10(typescript@5.6.3))
packages:
'@esbuild/aix-ppc64@0.24.0':
resolution: {integrity: sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [aix]
'@esbuild/android-arm64@0.24.0':
resolution: {integrity: sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==}
engines: {node: '>=18'}
cpu: [arm64]
os: [android]
'@esbuild/android-arm@0.24.0':
resolution: {integrity: sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==}
engines: {node: '>=18'}
cpu: [arm]
os: [android]
'@esbuild/android-x64@0.24.0':
resolution: {integrity: sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [android]
'@esbuild/darwin-arm64@0.24.0':
resolution: {integrity: sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==}
engines: {node: '>=18'}
cpu: [arm64]
os: [darwin]
'@esbuild/darwin-x64@0.24.0':
resolution: {integrity: sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==}
engines: {node: '>=18'}
cpu: [x64]
os: [darwin]
'@esbuild/freebsd-arm64@0.24.0':
resolution: {integrity: sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==}
engines: {node: '>=18'}
cpu: [arm64]
os: [freebsd]
'@esbuild/freebsd-x64@0.24.0':
resolution: {integrity: sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==}
engines: {node: '>=18'}
cpu: [x64]
os: [freebsd]
'@esbuild/linux-arm64@0.24.0':
resolution: {integrity: sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==}
engines: {node: '>=18'}
cpu: [arm64]
os: [linux]
'@esbuild/linux-arm@0.24.0':
resolution: {integrity: sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==}
engines: {node: '>=18'}
cpu: [arm]
os: [linux]
'@esbuild/linux-ia32@0.24.0':
resolution: {integrity: sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==}
engines: {node: '>=18'}
cpu: [ia32]
os: [linux]
'@esbuild/linux-loong64@0.24.0':
resolution: {integrity: sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==}
engines: {node: '>=18'}
cpu: [loong64]
os: [linux]
'@esbuild/linux-mips64el@0.24.0':
resolution: {integrity: sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==}
engines: {node: '>=18'}
cpu: [mips64el]
os: [linux]
'@esbuild/linux-ppc64@0.24.0':
resolution: {integrity: sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==}
engines: {node: '>=18'}
cpu: [ppc64]
os: [linux]
'@esbuild/linux-riscv64@0.24.0':
resolution: {integrity: sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==}
engines: {node: '>=18'}
cpu: [riscv64]
os: [linux]
'@esbuild/linux-s390x@0.24.0':
resolution: {integrity: sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==}
engines: {node: '>=18'}
cpu: [s390x]
os: [linux]
'@esbuild/linux-x64@0.24.0':
resolution: {integrity: sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==}
engines: {node: '>=18'}
cpu: [x64]
os: [linux]
'@esbuild/netbsd-x64@0.24.0':
resolution: {integrity: sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==}
engines: {node: '>=18'}
cpu: [x64]
os: [netbsd]
'@esbuild/openbsd-arm64@0.24.0':
resolution: {integrity: sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==}
engines: {node: '>=18'}
cpu: [arm64]
os: [openbsd]
'@esbuild/openbsd-x64@0.24.0':
resolution: {integrity: sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==}
engines: {node: '>=18'}
cpu: [x64]
os: [openbsd]
'@esbuild/sunos-x64@0.24.0':
resolution: {integrity: sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==}
engines: {node: '>=18'}
cpu: [x64]
os: [sunos]
'@esbuild/win32-arm64@0.24.0':
resolution: {integrity: sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==}
engines: {node: '>=18'}
cpu: [arm64]
os: [win32]
'@esbuild/win32-ia32@0.24.0':
resolution: {integrity: sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==}
engines: {node: '>=18'}
cpu: [ia32]
os: [win32]
'@esbuild/win32-x64@0.24.0':
resolution: {integrity: sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==}
engines: {node: '>=18'}
cpu: [x64]
os: [win32]
'@material/material-color-utilities@0.2.7':
resolution: {integrity: sha512-0FCeqG6WvK4/Cc06F/xXMd/pv4FeisI0c1tUpBbfhA2n9Y8eZEv4Karjbmf2ZqQCPUWMrGp8A571tCjizxoTiQ==}
'@serialport/binding-mock@10.2.2':
resolution: {integrity: sha512-HAFzGhk9OuFMpuor7aT5G1ChPgn5qSsklTFOTUX72Rl6p0xwcSVsRtG/xaGp6bxpN7fI9D/S8THLBWbBgS6ldw==}
engines: {node: '>=12.0.0'}
'@serialport/bindings-cpp@12.0.1':
resolution: {integrity: sha512-r2XOwY2dDvbW7dKqSPIk2gzsr6M6Qpe9+/Ngs94fNaNlcTRCV02PfaoDmRgcubpNVVcLATlxSxPTIDw12dbKOg==}
engines: {node: '>=16.0.0'}
'@serialport/bindings-interface@1.2.2':
resolution: {integrity: sha512-CJaUd5bLvtM9c5dmO9rPBHPXTa9R2UwpkJ0wdh9JCYcbrPWsKz+ErvR0hBLeo7NPeiFdjFO4sonRljiw4d2XiA==}
engines: {node: ^12.22 || ^14.13 || >=16}
'@serialport/parser-byte-length@12.0.0':
resolution: {integrity: sha512-0ei0txFAj+s6FTiCJFBJ1T2hpKkX8Md0Pu6dqMrYoirjPskDLJRgZGLqoy3/lnU1bkvHpnJO+9oJ3PB9v8rNlg==}
engines: {node: '>=12.0.0'}
'@serialport/parser-cctalk@12.0.0':
resolution: {integrity: sha512-0PfLzO9t2X5ufKuBO34DQKLXrCCqS9xz2D0pfuaLNeTkyGUBv426zxoMf3rsMRodDOZNbFblu3Ae84MOQXjnZw==}
engines: {node: '>=12.0.0'}
'@serialport/parser-delimiter@11.0.0':
resolution: {integrity: sha512-aZLJhlRTjSmEwllLG7S4J8s8ctRAS0cbvCpO87smLvl3e4BgzbVgF6Z6zaJd3Aji2uSiYgfedCdNc4L6W+1E2g==}
engines: {node: '>=12.0.0'}
'@serialport/parser-delimiter@12.0.0':
resolution: {integrity: sha512-gu26tVt5lQoybhorLTPsH2j2LnX3AOP2x/34+DUSTNaUTzu2fBXw+isVjQJpUBFWu6aeQRZw5bJol5X9Gxjblw==}
engines: {node: '>=12.0.0'}
'@serialport/parser-inter-byte-timeout@12.0.0':
resolution: {integrity: sha512-GnCh8K0NAESfhCuXAt+FfBRz1Cf9CzIgXfp7SdMgXwrtuUnCC/yuRTUFWRvuzhYKoAo1TL0hhUo77SFHUH1T/w==}
engines: {node: '>=12.0.0'}
'@serialport/parser-packet-length@12.0.0':
resolution: {integrity: sha512-p1hiCRqvGHHLCN/8ZiPUY/G0zrxd7gtZs251n+cfNTn+87rwcdUeu9Dps3Aadx30/sOGGFL6brIRGK4l/t7MuQ==}
engines: {node: '>=8.6.0'}
'@serialport/parser-readline@11.0.0':
resolution: {integrity: sha512-rRAivhRkT3YO28WjmmG4FQX6L+KMb5/ikhyylRfzWPw0nSXy97+u07peS9CbHqaNvJkMhH1locp2H36aGMOEIA==}
engines: {node: '>=12.0.0'}
'@serialport/parser-readline@12.0.0':
resolution: {integrity: sha512-O7cywCWC8PiOMvo/gglEBfAkLjp/SENEML46BXDykfKP5mTPM46XMaX1L0waWU6DXJpBgjaL7+yX6VriVPbN4w==}
engines: {node: '>=12.0.0'}
'@serialport/parser-ready@12.0.0':
resolution: {integrity: sha512-ygDwj3O4SDpZlbrRUraoXIoIqb8sM7aMKryGjYTIF0JRnKeB1ys8+wIp0RFMdFbO62YriUDextHB5Um5cKFSWg==}
engines: {node: '>=12.0.0'}
'@serialport/parser-regex@12.0.0':
resolution: {integrity: sha512-dCAVh4P/pZrLcPv9NJ2mvPRBg64L5jXuiRxIlyxxdZGH4WubwXVXY/kBTihQmiAMPxbT3yshSX8f2+feqWsxqA==}
engines: {node: '>=12.0.0'}
'@serialport/parser-slip-encoder@12.0.0':
resolution: {integrity: sha512-0APxDGR9YvJXTRfY+uRGhzOhTpU5akSH183RUcwzN7QXh8/1jwFsFLCu0grmAUfi+fItCkR+Xr1TcNJLR13VNA==}
engines: {node: '>=12.0.0'}
'@serialport/parser-spacepacket@12.0.0':
resolution: {integrity: sha512-dozONxhPC/78pntuxpz/NOtVps8qIc/UZzdc/LuPvVsqCoJXiRxOg6ZtCP/W58iibJDKPZPAWPGYeZt9DJxI+Q==}
engines: {node: '>=12.0.0'}
'@serialport/stream@12.0.0':
resolution: {integrity: sha512-9On64rhzuqKdOQyiYLYv2lQOh3TZU/D3+IWCR5gk0alPel2nwpp4YwDEGiUBfrQZEdQ6xww0PWkzqth4wqwX3Q==}
engines: {node: '>=12.0.0'}
'@shikijs/core@1.22.0':
resolution: {integrity: sha512-S8sMe4q71TJAW+qG93s5VaiihujRK6rqDFqBnxqvga/3LvqHEnxqBIOPkt//IdXVtHkQWKu4nOQNk0uBGicU7Q==}
'@shikijs/engine-javascript@1.22.0':
resolution: {integrity: sha512-AeEtF4Gcck2dwBqCFUKYfsCq0s+eEbCEbkUuFou53NZ0sTGnJnJ/05KHQFZxpii5HMXbocV9URYVowOP2wH5kw==}
'@shikijs/engine-oniguruma@1.22.0':
resolution: {integrity: sha512-5iBVjhu/DYs1HB0BKsRRFipRrD7rqjxlWTj4F2Pf+nQSPqc3kcyqFFeZXnBMzDf0HdqaFVvhDRAGiYNvyLP+Mw==}
'@shikijs/types@1.22.0':
resolution: {integrity: sha512-Fw/Nr7FGFhlQqHfxzZY8Cwtwk5E9nKDUgeLjZgt3UuhcM3yJR9xj3ZGNravZZok8XmEZMiYkSMTPlPkULB8nww==}
'@shikijs/vscode-textmate@9.3.0':
resolution: {integrity: sha512-jn7/7ky30idSkd/O5yDBfAnVt+JJpepofP/POZ1iMOxK59cOfqIgg/Dj0eFsjOTMw+4ycJN0uhZH/Eb0bs/EUA==}
'@types/hast@3.0.4':
resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
'@types/mdast@4.0.4':
resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==}
'@types/unist@3.0.3':
resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
'@ungap/structured-clone@1.2.0':
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
brace-expansion@2.0.1:
resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
ccount@2.0.1:
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
character-entities-html4@2.1.0:
resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==}
character-entities-legacy@3.0.0:
resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==}
comma-separated-tokens@2.0.3:
resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==}
debug@4.3.4:
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
engines: {node: '>=6.0'}
peerDependencies:
supports-color: '*'
peerDependenciesMeta:
supports-color:
optional: true
dequal@2.0.3:
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
engines: {node: '>=6'}
devlop@1.1.0:
resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
entities@4.5.0:
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
engines: {node: '>=0.12'}
esbuild-plugin-tsc@0.4.0:
resolution: {integrity: sha512-q9gWIovt1nkwchMLc2zhyksaiHOv3kDK4b0AUol8lkMCRhJ1zavgfb2fad6BKp7FT9rh/OHmEBXVjczLoi/0yw==}
peerDependencies:
typescript: ^4.0.0 || ^5.0.0
esbuild@0.24.0:
resolution: {integrity: sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==}
engines: {node: '>=18'}
hasBin: true
hast-util-to-html@9.0.3:
resolution: {integrity: sha512-M17uBDzMJ9RPCqLMO92gNNUDuBSq10a25SDBI08iCCxmorf4Yy6sYHK57n9WAbRAAaU+DuR4W6GN9K4DFZesYg==}
hast-util-whitespace@3.0.0:
resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==}
html-void-elements@3.0.0:
resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==}
json5@2.2.3:
resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
engines: {node: '>=6'}
hasBin: true
kleur@3.0.3:
resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
engines: {node: '>=6'}
linkify-it@5.0.0:
resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==}
lunr@2.3.9:
resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==}
markdown-it@14.1.0:
resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==}
hasBin: true
mdast-util-to-hast@13.2.0:
resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==}
mdurl@2.0.0:
resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==}
micromark-util-character@2.1.0:
resolution: {integrity: sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==}
micromark-util-encode@2.0.0:
resolution: {integrity: sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==}
micromark-util-sanitize-uri@2.0.0:
resolution: {integrity: sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==}
micromark-util-symbol@2.0.0:
resolution: {integrity: sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==}
micromark-util-types@2.0.0:
resolution: {integrity: sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==}
minimatch@9.0.5:
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
engines: {node: '>=16 || 14 >=14.17'}
ms@2.1.2:
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
node-addon-api@7.0.0:
resolution: {integrity: sha512-vgbBJTS4m5/KkE16t5Ly0WW9hz46swAstv0hYYwMtbG7AznRhNyfLRe8HZAiWIpcHzoO7HxhLuBQj9rJ/Ho0ZA==}
node-gyp-build@4.6.0:
resolution: {integrity: sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==}
hasBin: true
oniguruma-to-js@0.4.3:
resolution: {integrity: sha512-X0jWUcAlxORhOqqBREgPMgnshB7ZGYszBNspP+tS9hPD3l13CdaXcHbgImoHUHlrvGx/7AvFEkTRhAGYh+jzjQ==}
prompts@2.4.2:
resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==}
engines: {node: '>= 6'}
property-information@6.5.0:
resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==}
punycode.js@2.3.1:
resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==}
engines: {node: '>=6'}
regex@4.3.3:
resolution: {integrity: sha512-r/AadFO7owAq1QJVeZ/nq9jNS1vyZt+6t1p/E59B56Rn2GCya+gr1KSyOzNL/er+r+B7phv5jG2xU2Nz1YkmJg==}
serialport@12.0.0:
resolution: {integrity: sha512-AmH3D9hHPFmnF/oq/rvigfiAouAKyK/TjnrkwZRYSFZxNggJxwvbAbfYrLeuvq7ktUdhuHdVdSjj852Z55R+uA==}
engines: {node: '>=16.0.0'}
shiki@1.22.0:
resolution: {integrity: sha512-/t5LlhNs+UOKQCYBtl5ZsH/Vclz73GIqT2yQsCBygr8L/ppTdmpL4w3kPLoZJbMKVWtoG77Ue1feOjZfDxvMkw==}
sisteransi@1.0.5:
resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
space-separated-tokens@2.0.2:
resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
stringify-entities@4.0.4:
resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==}
strip-comments@2.0.1:
resolution: {integrity: sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==}
engines: {node: '>=10'}
trim-lines@3.0.1:
resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==}
typedoc-material-theme@1.1.0:
resolution: {integrity: sha512-LLWGVb8w+i+QGnsu/a0JKjcuzndFQt/UeGVOQz0HFFGGocROEHv5QYudIACrj+phL2LDwH05tJx0Ob3pYYH2UA==}
engines: {node: '>=18.0.0', npm: '>=8.6.0'}
peerDependencies:
typedoc: ^0.25.13 || ^0.26.3
typedoc@0.26.10:
resolution: {integrity: sha512-xLmVKJ8S21t+JeuQLNueebEuTVphx6IrP06CdV7+0WVflUSW3SPmR+h1fnWVdAR/FQePEgsSWCUHXqKKjzuUAw==}
engines: {node: '>= 18'}
hasBin: true
peerDependencies:
typescript: 4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x
typescript@5.6.3:
resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==}
engines: {node: '>=14.17'}
hasBin: true
uc.micro@2.1.0:
resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==}
unist-util-is@6.0.0:
resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==}
unist-util-position@5.0.0:
resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==}
unist-util-stringify-position@4.0.0:
resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==}
unist-util-visit-parents@6.0.1:
resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==}
unist-util-visit@5.0.0:
resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==}
vfile-message@4.0.2:
resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==}
vfile@6.0.3:
resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
yaml@2.6.0:
resolution: {integrity: sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==}
engines: {node: '>= 14'}
hasBin: true
zwitch@2.0.4:
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
snapshots:
'@esbuild/aix-ppc64@0.24.0':
optional: true
'@esbuild/android-arm64@0.24.0':
optional: true
'@esbuild/android-arm@0.24.0':
optional: true
'@esbuild/android-x64@0.24.0':
optional: true
'@esbuild/darwin-arm64@0.24.0':
optional: true
'@esbuild/darwin-x64@0.24.0':
optional: true
'@esbuild/freebsd-arm64@0.24.0':
optional: true
'@esbuild/freebsd-x64@0.24.0':
optional: true
'@esbuild/linux-arm64@0.24.0':
optional: true
'@esbuild/linux-arm@0.24.0':
optional: true
'@esbuild/linux-ia32@0.24.0':
optional: true
'@esbuild/linux-loong64@0.24.0':
optional: true
'@esbuild/linux-mips64el@0.24.0':
optional: true
'@esbuild/linux-ppc64@0.24.0':
optional: true
'@esbuild/linux-riscv64@0.24.0':
optional: true
'@esbuild/linux-s390x@0.24.0':
optional: true
'@esbuild/linux-x64@0.24.0':
optional: true
'@esbuild/netbsd-x64@0.24.0':
optional: true
'@esbuild/openbsd-arm64@0.24.0':
optional: true
'@esbuild/openbsd-x64@0.24.0':
optional: true
'@esbuild/sunos-x64@0.24.0':
optional: true
'@esbuild/win32-arm64@0.24.0':
optional: true
'@esbuild/win32-ia32@0.24.0':
optional: true
'@esbuild/win32-x64@0.24.0':
optional: true
'@material/material-color-utilities@0.2.7': {}
'@serialport/binding-mock@10.2.2':
dependencies:
'@serialport/bindings-interface': 1.2.2
debug: 4.3.4
transitivePeerDependencies:
- supports-color
'@serialport/bindings-cpp@12.0.1':
dependencies:
'@serialport/bindings-interface': 1.2.2
'@serialport/parser-readline': 11.0.0
debug: 4.3.4
node-addon-api: 7.0.0
node-gyp-build: 4.6.0
transitivePeerDependencies:
- supports-color
'@serialport/bindings-interface@1.2.2': {}
'@serialport/parser-byte-length@12.0.0': {}
'@serialport/parser-cctalk@12.0.0': {}
'@serialport/parser-delimiter@11.0.0': {}
'@serialport/parser-delimiter@12.0.0': {}
'@serialport/parser-inter-byte-timeout@12.0.0': {}
'@serialport/parser-packet-length@12.0.0': {}
'@serialport/parser-readline@11.0.0':
dependencies:
'@serialport/parser-delimiter': 11.0.0
'@serialport/parser-readline@12.0.0':
dependencies:
'@serialport/parser-delimiter': 12.0.0
'@serialport/parser-ready@12.0.0': {}
'@serialport/parser-regex@12.0.0': {}
'@serialport/parser-slip-encoder@12.0.0': {}
'@serialport/parser-spacepacket@12.0.0': {}
'@serialport/stream@12.0.0':
dependencies:
'@serialport/bindings-interface': 1.2.2
debug: 4.3.4
transitivePeerDependencies:
- supports-color
'@shikijs/core@1.22.0':
dependencies:
'@shikijs/engine-javascript': 1.22.0
'@shikijs/engine-oniguruma': 1.22.0
'@shikijs/types': 1.22.0
'@shikijs/vscode-textmate': 9.3.0
'@types/hast': 3.0.4
hast-util-to-html: 9.0.3
'@shikijs/engine-javascript@1.22.0':
dependencies:
'@shikijs/types': 1.22.0
'@shikijs/vscode-textmate': 9.3.0
oniguruma-to-js: 0.4.3
'@shikijs/engine-oniguruma@1.22.0':
dependencies:
'@shikijs/types': 1.22.0
'@shikijs/vscode-textmate': 9.3.0
'@shikijs/types@1.22.0':
dependencies:
'@shikijs/vscode-textmate': 9.3.0
'@types/hast': 3.0.4
'@shikijs/vscode-textmate@9.3.0': {}
'@types/hast@3.0.4':
dependencies:
'@types/unist': 3.0.3
'@types/mdast@4.0.4':
dependencies:
'@types/unist': 3.0.3
'@types/unist@3.0.3': {}
'@ungap/structured-clone@1.2.0': {}
argparse@2.0.1: {}
balanced-match@1.0.2: {}
brace-expansion@2.0.1:
dependencies:
balanced-match: 1.0.2
ccount@2.0.1: {}
character-entities-html4@2.1.0: {}
character-entities-legacy@3.0.0: {}
comma-separated-tokens@2.0.3: {}
debug@4.3.4:
dependencies:
ms: 2.1.2
dequal@2.0.3: {}
devlop@1.1.0:
dependencies:
dequal: 2.0.3
entities@4.5.0: {}
esbuild-plugin-tsc@0.4.0(typescript@5.6.3):
dependencies:
strip-comments: 2.0.1
typescript: 5.6.3
esbuild@0.24.0:
optionalDependencies:
'@esbuild/aix-ppc64': 0.24.0
'@esbuild/android-arm': 0.24.0
'@esbuild/android-arm64': 0.24.0
'@esbuild/android-x64': 0.24.0
'@esbuild/darwin-arm64': 0.24.0
'@esbuild/darwin-x64': 0.24.0
'@esbuild/freebsd-arm64': 0.24.0
'@esbuild/freebsd-x64': 0.24.0
'@esbuild/linux-arm': 0.24.0
'@esbuild/linux-arm64': 0.24.0
'@esbuild/linux-ia32': 0.24.0
'@esbuild/linux-loong64': 0.24.0
'@esbuild/linux-mips64el': 0.24.0
'@esbuild/linux-ppc64': 0.24.0
'@esbuild/linux-riscv64': 0.24.0
'@esbuild/linux-s390x': 0.24.0
'@esbuild/linux-x64': 0.24.0
'@esbuild/netbsd-x64': 0.24.0
'@esbuild/openbsd-arm64': 0.24.0
'@esbuild/openbsd-x64': 0.24.0
'@esbuild/sunos-x64': 0.24.0
'@esbuild/win32-arm64': 0.24.0
'@esbuild/win32-ia32': 0.24.0
'@esbuild/win32-x64': 0.24.0
hast-util-to-html@9.0.3:
dependencies:
'@types/hast': 3.0.4
'@types/unist': 3.0.3
ccount: 2.0.1
comma-separated-tokens: 2.0.3
hast-util-whitespace: 3.0.0
html-void-elements: 3.0.0
mdast-util-to-hast: 13.2.0
property-information: 6.5.0
space-separated-tokens: 2.0.2
stringify-entities: 4.0.4
zwitch: 2.0.4
hast-util-whitespace@3.0.0:
dependencies:
'@types/hast': 3.0.4
html-void-elements@3.0.0: {}
json5@2.2.3: {}
kleur@3.0.3: {}
linkify-it@5.0.0:
dependencies:
uc.micro: 2.1.0
lunr@2.3.9: {}
markdown-it@14.1.0:
dependencies:
argparse: 2.0.1
entities: 4.5.0
linkify-it: 5.0.0
mdurl: 2.0.0
punycode.js: 2.3.1
uc.micro: 2.1.0
mdast-util-to-hast@13.2.0:
dependencies:
'@types/hast': 3.0.4
'@types/mdast': 4.0.4
'@ungap/structured-clone': 1.2.0
devlop: 1.1.0
micromark-util-sanitize-uri: 2.0.0
trim-lines: 3.0.1
unist-util-position: 5.0.0
unist-util-visit: 5.0.0
vfile: 6.0.3
mdurl@2.0.0: {}
micromark-util-character@2.1.0:
dependencies:
micromark-util-symbol: 2.0.0
micromark-util-types: 2.0.0
micromark-util-encode@2.0.0: {}
micromark-util-sanitize-uri@2.0.0:
dependencies:
micromark-util-character: 2.1.0
micromark-util-encode: 2.0.0
micromark-util-symbol: 2.0.0
micromark-util-symbol@2.0.0: {}
micromark-util-types@2.0.0: {}
minimatch@9.0.5:
dependencies:
brace-expansion: 2.0.1
ms@2.1.2: {}
node-addon-api@7.0.0: {}
node-gyp-build@4.6.0: {}
oniguruma-to-js@0.4.3:
dependencies:
regex: 4.3.3
prompts@2.4.2:
dependencies:
kleur: 3.0.3
sisteransi: 1.0.5
property-information@6.5.0: {}
punycode.js@2.3.1: {}
regex@4.3.3: {}
serialport@12.0.0:
dependencies:
'@serialport/binding-mock': 10.2.2
'@serialport/bindings-cpp': 12.0.1
'@serialport/parser-byte-length': 12.0.0
'@serialport/parser-cctalk': 12.0.0
'@serialport/parser-delimiter': 12.0.0
'@serialport/parser-inter-byte-timeout': 12.0.0
'@serialport/parser-packet-length': 12.0.0
'@serialport/parser-readline': 12.0.0
'@serialport/parser-ready': 12.0.0
'@serialport/parser-regex': 12.0.0
'@serialport/parser-slip-encoder': 12.0.0
'@serialport/parser-spacepacket': 12.0.0
'@serialport/stream': 12.0.0
debug: 4.3.4
transitivePeerDependencies:
- supports-color
shiki@1.22.0:
dependencies:
'@shikijs/core': 1.22.0
'@shikijs/engine-javascript': 1.22.0
'@shikijs/engine-oniguruma': 1.22.0
'@shikijs/types': 1.22.0
'@shikijs/vscode-textmate': 9.3.0
'@types/hast': 3.0.4
sisteransi@1.0.5: {}
space-separated-tokens@2.0.2: {}
stringify-entities@4.0.4:
dependencies:
character-entities-html4: 2.1.0
character-entities-legacy: 3.0.0
strip-comments@2.0.1: {}
trim-lines@3.0.1: {}
typedoc-material-theme@1.1.0(typedoc@0.26.10(typescript@5.6.3)):
dependencies:
'@material/material-color-utilities': 0.2.7
typedoc: 0.26.10(typescript@5.6.3)
typedoc@0.26.10(typescript@5.6.3):
dependencies:
lunr: 2.3.9
markdown-it: 14.1.0
minimatch: 9.0.5
shiki: 1.22.0
typescript: 5.6.3
yaml: 2.6.0
typescript@5.6.3: {}
uc.micro@2.1.0: {}
unist-util-is@6.0.0:
dependencies:
'@types/unist': 3.0.3
unist-util-position@5.0.0:
dependencies:
'@types/unist': 3.0.3
unist-util-stringify-position@4.0.0:
dependencies:
'@types/unist': 3.0.3
unist-util-visit-parents@6.0.1:
dependencies:
'@types/unist': 3.0.3
unist-util-is: 6.0.0
unist-util-visit@5.0.0:
dependencies:
'@types/unist': 3.0.3
unist-util-is: 6.0.0
unist-util-visit-parents: 6.0.1
vfile-message@4.0.2:
dependencies:
'@types/unist': 3.0.3
unist-util-stringify-position: 4.0.0
vfile@6.0.3:
dependencies:
'@types/unist': 3.0.3
vfile-message: 4.0.2
yaml@2.6.0: {}
zwitch@2.0.4: {}

View File

@@ -0,0 +1,176 @@
#!/usr/bin/env node
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { SerialPort } from "serialport";
import prompts from "prompts";
import esbuild from "esbuild";
import json5 from "json5";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
async function build(config) {
await esbuild.build({
entryPoints: ["./dist/index.js"],
outfile: config.output,
tsconfig: "./tsconfig.json",
format: "cjs",
bundle: true,
minify: config.minify,
external: [
"@flipperdevices/fz-sdk/*"
],
supported: {
"array-spread": false,
"arrow": false,
"async-await": false,
"async-generator": false,
"bigint": false,
"class": false,
"const-and-let": true,
"decorators": false,
"default-argument": false,
"destructuring": false,
"dynamic-import": false,
"exponent-operator": false,
"export-star-as": false,
"for-await": false,
"for-of": false,
"function-name-configurable": false,
"function-or-class-property-access": false,
"generator": false,
"hashbang": false,
"import-assertions": false,
"import-meta": false,
"inline-script": false,
"logical-assignment": false,
"nested-rest-binding": false,
"new-target": false,
"node-colon-prefix-import": false,
"node-colon-prefix-require": false,
"nullish-coalescing": false,
"object-accessors": false,
"object-extensions": false,
"object-rest-spread": false,
"optional-catch-binding": false,
"optional-chain": false,
"regexp-dot-all-flag": false,
"regexp-lookbehind-assertions": false,
"regexp-match-indices": false,
"regexp-named-capture-groups": false,
"regexp-set-notation": false,
"regexp-sticky-and-unicode-flags": false,
"regexp-unicode-property-escapes": false,
"rest-argument": false,
"template-literal": false,
"top-level-await": false,
"typeof-exotic-object-is-object": false,
"unicode-escapes": false,
"using": false,
},
});
let outContents = fs.readFileSync(config.output, "utf8");
outContents = "let exports = {};\n" + outContents;
if (config.enforceSdkVersion) {
const version = json5.parse(fs.readFileSync(path.join(__dirname, "package.json"), "utf8")).version;
let [major, minor, _] = version.split(".");
outContents = `checkSdkCompatibility(${major}, ${minor});\n${outContents}`;
}
fs.writeFileSync(config.output, outContents);
}
async function upload(config) {
const appFile = fs.readFileSync(config.input, "utf8");
const flippers = (await SerialPort.list()).filter(x => x.serialNumber?.startsWith("flip_"));
if (!flippers) {
console.error("No Flippers found");
process.exit(1);
}
let portPath = flippers[0].path;
if (flippers.length > 1) {
port = (await prompts([{
type: "select",
name: "port",
message: "Select Flipper to run the app on",
choices: flippers.map(x => ({ title: x.serialNumber.replace("flip_", ""), value: x.path })),
}])).port;
}
console.log(`Connecting to Flipper at ${portPath}`);
let port = new SerialPort({ path: portPath, baudRate: 230400 });
let received = "";
let lastMatch = 0;
async function waitFor(string, timeoutMs) {
return new Promise((resolve, _reject) => {
let timeout = undefined;
if (timeoutMs) {
timeout = setTimeout(() => {
console.error("Error: timeout");
process.exit(1);
}, timeoutMs);
}
setInterval(() => {
let idx = received.indexOf(string, lastMatch);
if (idx !== -1) {
lastMatch = idx;
if (timeoutMs)
clearTimeout(timeout);
resolve();
}
}, 50);
});
}
port.on("data", (data) => {
received += data.toString();
});
await waitFor(">: ", 1000);
console.log("Uploading application file");
port.write(`storage remove ${config.output}\x0d`);
port.drain();
await waitFor(">: ", 1000);
port.write(`storage write_chunk ${config.output} ${appFile.length}\x0d`);
await waitFor("Ready", 1000);
port.write(appFile);
port.drain();
await waitFor(">: ", 1000);
console.log("Launching application");
port.write(`js ${config.output}\x0d`);
port.drain();
await waitFor("Running", 1000);
process.stdout.write(received.slice(lastMatch));
port.on("data", (data) => {
process.stdout.write(data.toString());
});
process.on("exit", () => {
port.write("\x03");
});
await waitFor("Script done!", 0);
process.exit(0);
}
(async () => {
const commands = {
"build": build,
"upload": upload,
};
const config = json5.parse(fs.readFileSync("./fz-sdk.config.json5", "utf8"));
const command = process.argv[2];
if (!Object.keys(commands).includes(command)) {
console.error(`Unknown command ${command}. Supported: ${Object.keys(commands).join(", ")}`);
process.exit(1);
}
await commands[command](config[command]);
})();

View File

@@ -0,0 +1,89 @@
/**
* Module for accessing the serial port
* @version Added in JS SDK 0.1
* @module
*/
/**
* @brief Initializes the serial port
* @param port The port to initialize (`"lpuart"` or `"start"`)
* @param baudRate
* @version Added in JS SDK 0.1
*/
export declare function setup(port: "lpuart" | "usart", baudRate: number): void;
/**
* @brief Writes data to the serial port
* @param value The data to write:
* - Strings will get sent as ASCII.
* - Numbers will get sent as a single byte.
* - Arrays of numbers will get sent as a sequence of bytes.
* - `ArrayBuffer`s and `TypedArray`s will be sent as a sequence
* of bytes.
* @version Added in JS SDK 0.1
*/
export declare function write<E extends ElementType>(value: string | number | number[] | ArrayBuffer | TypedArray<E>): void;
/**
* @brief Reads data from the serial port
* @param length The number of bytes to read
* @param timeout The number of time, in milliseconds, after which this function
* will give up and return what it read up to that point. If
* unset, the function will wait forever.
* @returns The received data interpreted as ASCII, or `undefined` if 0 bytes
* were read.
* @version Added in JS SDK 0.1
*/
export declare function read(length: number, timeout?: number): string | undefined;
/**
* @brief Reads data from the serial port
*
* Data is read one character after another until either a `\r` or `\n`
* character is received, neither of which is included in the result.
*
* @param timeout The number of time, in milliseconds, after which this function
* will give up and return what it read up to that point. If
* unset, the function will wait forever. The timeout only
* applies to characters, not entire strings.
* @returns The received data interpreted as ASCII, or `undefined` if 0 bytes
* were read.
* @version Added in JS SDK 0.1
*/
export declare function readln(timeout?: number): string;
/**
* @brief Reads data from the serial port
* @param length The number of bytes to read
* @param timeout The number of time, in milliseconds, after which this function
* will give up and return what it read up to that point. If
* unset, the function will wait forever.
* @returns The received data as an ArrayBuffer, or `undefined` if 0 bytes were
* read.
* @version Added in JS SDK 0.1
*/
export declare function readBytes(length: number, timeout?: number): ArrayBuffer;
/**
* @brief Reads data from the serial port, trying to match it to a pattern
* @param patterns A single pattern or an array of patterns:
* - If the argument is a single `string`, this function will
* match against the given string.
* - If the argument is an array of `number`s, this function
* will match against the given sequence of bytes,
* - If the argument is an array of `string`s, this function
* will match against any string out of the ones that were
* provided.
* - If the argument is an array of arrays of `number`s, this
* function will match against any sequence of bytes out of
* the ones that were provided.
* @param timeout The number of time, in milliseconds, after which this function
* will give up and return what it read up to that point. If
* unset, the function will wait forever. The timeout only
* applies to characters, not entire strings.
* @returns The index of the matched pattern if multiple were provided, or 0 if
* only one was provided and it matched, or `undefined` if none of the
* patterns matched.
* @version Added in JS SDK 0.1
*/
export declare function expect(patterns: string | number[] | string[] | number[][], timeout?: number): number | undefined;

View File

@@ -0,0 +1,293 @@
/**
* Module for accessing the filesystem
* @version Added in JS SDK 0.1
* @module
*/
/**
* File readability mode:
* - `"r"`: read-only
* - `"w"`: write-only
* - `"rw"`: read-write
* @version Added in JS SDK 0.1
*/
export type AccessMode = "r" | "w" | "rw";
/**
* File creation mode:
* - `"open_existing"`: open file or fail if it doesn't exist
* - `"open_always"`: open file or create a new empty one if it doesn't exist
* - `"open_append"`: open file and set r/w pointer to EOF, or create a new one if it doesn't exist
* - `"create_new"`: create new file or fail if it exists
* - `"create_always"`: truncate and open file, or create a new empty one if it doesn't exist
* @version Added in JS SDK 0.1
*/
export type OpenMode = "open_existing" | "open_always" | "open_append" | "create_new" | "create_always";
/**
* Standard UNIX timestamp
* @version Added in JS SDK 0.1
*/
export type Timestamp = number;
/**
* File information structure
* @version Added in JS SDK 0.1
*/
export declare class FileInfo {
/**
* Full path (e.g. "/ext/test", returned by `stat`) or file name
* (e.g. "test", returned by `readDirectory`)
* @version Added in JS SDK 0.1
*/
path: string;
/**
* Is the file a directory?
* @version Added in JS SDK 0.1
*/
isDirectory: boolean;
/**
* File size in bytes, or 0 in the case of directories
* @version Added in JS SDK 0.1
*/
size: number;
/**
* Time of last access as a UNIX timestamp
* @version Added in JS SDK 0.1
*/
accessTime: Timestamp;
}
/**
* Filesystem information structure
* @version Added in JS SDK 0.1
*/
export declare class FsInfo {
/**
* Total size of the filesystem, in bytes
* @version Added in JS SDK 0.1
*/
totalSpace: number;
/**
* Free space in the filesystem, in bytes
* @version Added in JS SDK 0.1
*/
freeSpace: number;
}
// file operations
/**
* File class
* @version Added in JS SDK 0.1
*/
export declare class File {
/**
* Closes the file. After this method is called, all other operations
* related to this file become unavailable.
* @returns `true` on success, `false` on failure
* @version Added in JS SDK 0.1
*/
close(): boolean;
/**
* Is the file currently open?
* @version Added in JS SDK 0.1
*/
isOpen(): boolean;
/**
* Reads bytes from a file opened in read-only or read-write mode
* @param mode The data type to interpret the bytes as: a `string` decoded
* from ASCII data (`"ascii"`), or an `ArrayBuf` (`"binary"`)
* @param bytes How many bytes to read from the file
* @returns an `ArrayBuf` if the mode is `"binary"`, a `string` if the mode
* is `ascii`. The number of bytes that was actually read may be
* fewer than requested.
* @version Added in JS SDK 0.1
*/
read<T extends ArrayBuffer | string>(mode: T extends ArrayBuffer ? "binary" : "ascii", bytes: number): T;
/**
* Writes bytes to a file opened in write-only or read-write mode
* @param data The data to write: a string that will be ASCII-encoded, or an
* ArrayBuf
* @returns the amount of bytes that was actually written
* @version Added in JS SDK 0.1
*/
write(data: ArrayBuffer | string): number;
/**
* Moves the R/W pointer forward
* @param bytes How many bytes to move the pointer forward by
* @returns `true` on success, `false` on failure
* @version Added in JS SDK 0.1
*/
seekRelative(bytes: number): boolean;
/**
* Moves the R/W pointer to an absolute position inside the file
* @param bytes The position inside the file
* @returns `true` on success, `false` on failure
* @version Added in JS SDK 0.1
*/
seekAbsolute(bytes: number): boolean;
/**
* Gets the absolute position of the R/W pointer in bytes
* @version Added in JS SDK 0.1
*/
tell(): number;
/**
* Discards the data after the current position of the R/W pointer in a file
* opened in either write-only or read-write mode.
* @returns `true` on success, `false` on failure
* @version Added in JS SDK 0.1
*/
truncate(): boolean;
/**
* Reads the total size of the file in bytes
* @version Added in JS SDK 0.1
*/
size(): number;
/**
* Detects whether the R/W pointer has reached the end of the file
* @version Added in JS SDK 0.1
*/
eof(): boolean;
/**
* Copies bytes from the R/W pointer in the current file to the R/W pointer
* in another file
* @param dest The file to copy the bytes into
* @param bytes The number of bytes to copy
* @returns `true` on success, `false` on failure
* @version Added in JS SDK 0.1
*/
copyTo(dest: File, bytes: number): boolean;
}
/**
* Opens a file
* @param path The path to the file
* @param accessMode `"r"`, `"w"` or `"rw"`; see `AccessMode`
* @param openMode `"open_existing"`, `"open_always"`, `"open_append"`,
* `"create_new"` or `"create_always"`; see `OpenMode`
* @returns a `File` on success, or `undefined` on failure
* @version Added in JS SDK 0.1
*/
export declare function openFile(path: string, accessMode: AccessMode, openMode: OpenMode): File | undefined;
/**
* Detects whether a file exists
* @param path The path to the file
* @returns `true` on success, `false` on failure
* @version Added in JS SDK 0.1
*/
export declare function fileExists(path: string): boolean;
// directory operations
/**
* Reads the list of files in a directory
* @param path The path to the directory
* @returns Array of `FileInfo` structures with directory entries,
* or `undefined` on failure
* @version Added in JS SDK 0.1
*/
export declare function readDirectory(path: string): FileInfo[] | undefined;
/**
* Detects whether a directory exists
* @param path The path to the directory
* @version Added in JS SDK 0.1
*/
export declare function directoryExists(path: string): boolean;
/**
* Creates an empty directory
* @param path The path to the new directory
* @returns `true` on success, `false` on failure
* @version Added in JS SDK 0.1
*/
export declare function makeDirectory(path: string): boolean;
// common (file/dir) operations
/**
* Detects whether a file or a directory exists
* @param path The path to the file or directory
* @version Added in JS SDK 0.1
*/
export declare function fileOrDirExists(path: string): boolean;
/**
* Acquires metadata about a file or directory
* @param path The path to the file or directory
* @returns A `FileInfo` structure or `undefined` on failure
* @version Added in JS SDK 0.1
*/
export declare function stat(path: string): FileInfo | undefined;
/**
* Removes a file or an empty directory
* @param path The path to the file or directory
* @returns `true` on success, `false` on failure
* @version Added in JS SDK 0.1
*/
export declare function remove(path: string): boolean;
/**
* Removes a file or recursively removes a possibly non-empty directory
* @param path The path to the file or directory
* @returns `true` on success, `false` on failure
* @version Added in JS SDK 0.1
*/
export declare function rmrf(path: string): boolean;
/**
* Renames or moves a file or directory
* @param oldPath The old path to the file or directory
* @param newPath The new path that the file or directory will become accessible
* under
* @returns `true` on success, `false` on failure
* @version Added in JS SDK 0.1
*/
export declare function rename(oldPath: string, newPath: string): boolean;
/**
* Copies a file or recursively copies a possibly non-empty directory
* @param oldPath The original path to the file or directory
* @param newPath The new path that the copy of the file or directory will be
* accessible under
* @version Added in JS SDK 0.1
*/
export declare function copy(oldPath: string, newPath: string): boolean;
/**
* Fetches generic information about a filesystem
* @param filesystem The path to the filesystem (e.g. `"/ext"` or `"/int"`)
* @version Added in JS SDK 0.1
*/
export declare function fsInfo(filesystem: string): FsInfo | undefined;
/**
* Chooses the next available filename with a numeric suffix in a directory
*
* ```
* "/ext/example_dir/example_file123.txt"
* \______________/ \__________/\_/\__/
* dirPath fileName | |
* | +---- fileExt
* +------- selected by this function
* ```
*
* @param dirPath The directory to look in
* @param fileName The base of the filename (the part before the numeric suffix)
* @param fileExt The extension of the filename (the part after the numeric suffix)
* @param maxLen The maximum length of the filename with the numeric suffix
* @returns The base of the filename with the next available numeric suffix,
* without the extension or the base directory.
* @version Added in JS SDK 0.1
*/
export declare function nextAvailableFilename(dirPath: string, fileName: string, fileExt: string, maxLen: number): string;
// path operations that do not access the filesystem
/**
* Determines whether the two paths are equivalent. Respects filesystem-defined
* path equivalence rules.
* @version Added in JS SDK 0.1
*/
export declare function arePathsEqual(path1: string, path2: string): boolean;
/**
* Determines whether a path is a subpath of another path. Respects
* filesystem-defined path equivalence rules.
* @param parentPath The parent path
* @param childPath The child path
* @version Added in JS SDK 0.1
*/
export declare function isSubpathOf(parentPath: string, childPath: string): boolean;

View File

@@ -0,0 +1,10 @@
/**
* Unit test module. Only available if the firmware has been configured with
* `FIRMWARE_APP_SET=unit_tests`.
* @version Added in JS SDK 0.1
* @module
*/
export function fail(message: string): never;
export function assert_eq<T>(expected: T, result: T): void | never;
export function assert_float_close(expected: number, result: number, epsilon: number): void | never;

View File

@@ -0,0 +1,13 @@
{
"compilerOptions": {
"checkJs": true,
"module": "CommonJS",
"noLib": true,
},
"include": [
"./**/*.d.ts"
],
"exclude": [
"node_modules",
]
}

View File

@@ -0,0 +1,19 @@
{
"$schema": "https://typedoc.org/schema.json",
"name": "Flipper Zero JS API",
"excludePrivate": true,
"entryPointStrategy": "expand",
"entryPoints": [
".",
],
"exclude": [
"node_modules"
],
"cleanOutputDir": true,
"out": "./docs",
"plugin": [
"typedoc-material-theme",
],
"readme": "./docs_readme.md",
"themeColor": "#ff8200",
}