mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2025-12-12 20:49:49 +04:00
[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:
176
applications/system/js_app/packages/fz-sdk/sdk.js
Normal file
176
applications/system/js_app/packages/fz-sdk/sdk.js
Normal 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]);
|
||||
})();
|
||||
Reference in New Issue
Block a user