From bc3a61462a951a3597c1bc1a31be2f062ee2dae2 Mon Sep 17 00:00:00 2001 From: jhartworks Date: Tue, 16 Jun 2026 11:00:24 +0200 Subject: [PATCH] Initial commit --- .gitignore | 3 + README.md | 38 + blablub.json | 119 ++ main.js | 76 + package-lock.json | 4855 +++++++++++++++++++++++++++++++++++++++++++ package.json | 45 + preload.js | 941 +++++++++ src/app.js | 2376 +++++++++++++++++++++ src/index.html | 333 +++ src/styles.css | 482 +++++ start-time4midi.cmd | 16 + 11 files changed, 9284 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 blablub.json create mode 100644 main.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 preload.js create mode 100644 src/app.js create mode 100644 src/index.html create mode 100644 src/styles.css create mode 100644 start-time4midi.cmd diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f94630c --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +dist/ +.env \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..3d8b37c --- /dev/null +++ b/README.md @@ -0,0 +1,38 @@ +# Time4Midi + +Desktop-Prototyp fuer einen plattformuebergreifenden Timeline-Editor fuer: + +- MIDI Notes +- Art-Net DMX Values +- externe Synchronisation via MIDI Timecode (MTC) +- externe Synchronisation via Art-Net Timecode + +## Aktueller Stand + +Die erste Version enthaelt bereits: + +- mehrere Timelines pro Projekt +- Speichern und Laden als JSON +- Tracks fuer `MIDI Note` und `Art-Net Channel` +- Punkt-/Keyframe-Editing direkt auf dem Zeitstrahl +- `Hold` und `Linear` pro Segment +- konfigurierbare MIDI In/Out Ports +- konfigurierbares Art-Net Ziel inklusive Broadcast/Unicast +- Live-Playhead fuer `Internal`, `MTC` und `Art-Net Timecode` + +## Starten + +```bash +npm install +npm start +``` + +Unter Windows kannst du alternativ einfach die Datei `start-time4midi.cmd` im Projektordner doppelklicken. + +## Naechste sinnvolle Ausbaustufen + +1. Mehr Track-Typen: MIDI CC, Program Change, komplette DMX-Ranges +2. Copy/Paste, Zoom, Snapping, Loop, Undo/Redo +3. bessere MTC-Unterstuetzung inkl. Full-Frame und Locate +4. Projektbrowser, mehrere Cue-Listen, Szenen und Trigger +5. Packaging fuer Windows, macOS und Linux diff --git a/blablub.json b/blablub.json new file mode 100644 index 0000000..61c5577 --- /dev/null +++ b/blablub.json @@ -0,0 +1,119 @@ +{ + "version": 2, + "name": "BlaBlub", + "timelines": [ + { + "id": "timeline-9lqfedny", + "name": "Timeline A", + "duration": 120, + "tracks": [ + { + "id": "track-c3e8j63s", + "name": "Kick Note", + "type": "midi-note", + "channel": 1, + "note": 36, + "interpolationDefault": "hold", + "points": [ + { + "id": "point-fg40zfhx", + "time": 2, + "value": 127, + "mode": "hold" + }, + { + "id": "point-mop7wxzy", + "time": 3, + "value": 0, + "mode": "hold" + }, + { + "id": "point-dtrij6iv", + "time": 15.55, + "value": 127, + "mode": "hold" + }, + { + "id": "point-jvnqrede", + "time": 25.15, + "value": 6, + "mode": "hold" + } + ] + }, + { + "id": "track-soj6wtrn", + "name": "Dimmer Ramp", + "type": "artnet-channel", + "universe": 0, + "address": 1, + "interpolationDefault": "linear", + "points": [ + { + "id": "point-s9nb1b40", + "time": 0.77, + "value": 0, + "mode": "linear" + }, + { + "id": "point-gfe4chv8", + "time": 10, + "value": 255, + "mode": "linear" + }, + { + "id": "point-x9fzsv8x", + "time": 18, + "value": 60, + "mode": "linear" + } + ] + }, + { + "id": "track-aq46qmrj", + "name": "MIDI CC 3", + "type": "midi-cc", + "channel": 1, + "controller": 1, + "interpolationDefault": "linear", + "points": [ + { + "id": "point-utl6tzni", + "time": 3.63, + "value": 127, + "mode": "linear" + }, + { + "id": "point-jacus9hx", + "time": 12.16, + "value": 0, + "mode": "linear" + }, + { + "id": "point-jh4u1bze", + "time": 20.17, + "value": 118, + "mode": "linear" + }, + { + "id": "point-penaibyl", + "time": 38.2, + "value": 10, + "mode": "linear" + } + ] + } + ] + } + ], + "selectedTimelineId": "timeline-9lqfedny", + "routing": { + "midiInputId": "input-2", + "midiOutputId": "output-4", + "artnetHost": "255.255.255.255", + "artnetPort": 6454, + "artnetUniverse": 0, + "artnetTimecodePort": 6454, + "artnetBroadcast": true + } +} \ No newline at end of file diff --git a/main.js b/main.js new file mode 100644 index 0000000..5362231 --- /dev/null +++ b/main.js @@ -0,0 +1,76 @@ +const { app, BrowserWindow, dialog, ipcMain } = require("electron"); +const fs = require("node:fs/promises"); +const path = require("node:path"); + +app.commandLine.appendSwitch("enable-blink-features", "WebMidi"); + +function createWindow() { + const win = new BrowserWindow({ + width: 1600, + height: 980, + minWidth: 1200, + minHeight: 760, + backgroundColor: "#0f1117", + webPreferences: { + contextIsolation: true, + sandbox: false, + preload: path.join(__dirname, "preload.js") + } + }); + + win.loadFile(path.join(__dirname, "src", "index.html")); +} + +ipcMain.handle("project:save", async (_event, payload) => { + if (payload?.filePath) { + await fs.writeFile(payload.filePath, payload.data, "utf8"); + return { canceled: false, filePath: payload.filePath }; + } + + const result = await dialog.showSaveDialog({ + defaultPath: payload?.defaultPath || "time4midi-project.json", + filters: [ + { name: "Time4Midi Project", extensions: ["json"] } + ] + }); + + if (result.canceled || !result.filePath) { + return { canceled: true }; + } + + await fs.writeFile(result.filePath, payload.data, "utf8"); + return { canceled: false, filePath: result.filePath }; +}); + +ipcMain.handle("project:load", async () => { + const result = await dialog.showOpenDialog({ + properties: ["openFile"], + filters: [ + { name: "Time4Midi Project", extensions: ["json"] } + ] + }); + + if (result.canceled || result.filePaths.length === 0) { + return { canceled: true }; + } + + const filePath = result.filePaths[0]; + const data = await fs.readFile(filePath, "utf8"); + return { canceled: false, filePath, data }; +}); + +app.whenReady().then(() => { + createWindow(); + + app.on("activate", () => { + if (BrowserWindow.getAllWindows().length === 0) { + createWindow(); + } + }); +}); + +app.on("window-all-closed", () => { + if (process.platform !== "darwin") { + app.quit(); + } +}); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..ea78004 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,4855 @@ +{ + "name": "time4midi", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "time4midi", + "version": "0.1.0", + "dependencies": { + "knx": "^2.5.4", + "modbus-serial": "^8.0.25", + "mqtt": "^5.10.4", + "serialport": "^13.0.0" + }, + "devDependencies": { + "electron": "^31.7.7", + "electron-builder": "^24.13.3" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@develar/schema-utils": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.6.5.tgz", + "integrity": "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.0", + "ajv-keywords": "^3.4.1" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/@electron/asar": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.4.1.tgz", + "integrity": "sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^5.0.0", + "glob": "^7.1.6", + "minimatch": "^3.0.4" + }, + "bin": { + "asar": "bin/asar.js" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/@electron/asar/node_modules/brace-expansion": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@electron/asar/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@electron/get": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz", + "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "got": "^11.8.5", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "global-agent": "^3.0.0" + } + }, + "node_modules/@electron/notarize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.2.1.tgz", + "integrity": "sha512-aL+bFMIkpR0cmmj5Zgy0LMKEpgy43/hw5zadEArgmAMWWlKc5buwFvFT9G/o/YJkvXAJm5q3iuTuLaiaXW39sg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "fs-extra": "^9.0.1", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/notarize/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@electron/notarize/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/notarize/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/osx-sign": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.0.5.tgz", + "integrity": "sha512-k9ZzUQtamSoweGQDV2jILiRIHUu7lYlJ3c6IEmjv1hC17rclE+eb9U+f6UFlOOETo0JzY1HNlXy4YOlCvl+Lww==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "compare-version": "^0.1.2", + "debug": "^4.3.4", + "fs-extra": "^10.0.0", + "isbinaryfile": "^4.0.8", + "minimist": "^1.2.6", + "plist": "^3.0.5" + }, + "bin": { + "electron-osx-flat": "bin/electron-osx-flat.js", + "electron-osx-sign": "bin/electron-osx-sign.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@electron/osx-sign/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@electron/osx-sign/node_modules/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/@electron/osx-sign/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/osx-sign/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/universal": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.5.1.tgz", + "integrity": "sha512-kbgXxyEauPJiQQUNG2VgUeyfQNFk6hBF11ISN2PNI6agUgPl55pv4eQmaqHzTAzchBvqZ2tQuRVaPStGf0mxGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron/asar": "^3.2.1", + "@malept/cross-spawn-promise": "^1.1.0", + "debug": "^4.3.1", + "dir-compare": "^3.0.0", + "fs-extra": "^9.0.1", + "minimatch": "^3.0.4", + "plist": "^3.0.4" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/@electron/universal/node_modules/brace-expansion": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@electron/universal/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@electron/universal/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/universal/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@electron/universal/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@malept/cross-spawn-promise": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", + "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/malept" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" + } + ], + "license": "Apache-2.0", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@malept/flatpak-bundler": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@malept/flatpak-bundler/-/flatpak-bundler-0.4.0.tgz", + "integrity": "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "fs-extra": "^9.0.0", + "lodash": "^4.17.15", + "tmp-promise": "^3.0.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@malept/flatpak-bundler/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@malept/flatpak-bundler/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@malept/flatpak-bundler/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@serialport/binding-mock": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@serialport/binding-mock/-/binding-mock-10.2.2.tgz", + "integrity": "sha512-HAFzGhk9OuFMpuor7aT5G1ChPgn5qSsklTFOTUX72Rl6p0xwcSVsRtG/xaGp6bxpN7fI9D/S8THLBWbBgS6ldw==", + "license": "MIT", + "dependencies": { + "@serialport/bindings-interface": "^1.2.1", + "debug": "^4.3.3" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@serialport/bindings-cpp": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/bindings-cpp/-/bindings-cpp-13.0.0.tgz", + "integrity": "sha512-r25o4Bk/vaO1LyUfY/ulR6hCg/aWiN6Wo2ljVlb4Pj5bqWGcSRC4Vse4a9AcapuAu/FeBzHCbKMvRQeCuKjzIQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@serialport/bindings-interface": "1.2.2", + "@serialport/parser-readline": "12.0.0", + "debug": "4.4.0", + "node-addon-api": "8.3.0", + "node-gyp-build": "4.8.4" + }, + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-delimiter": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-12.0.0.tgz", + "integrity": "sha512-gu26tVt5lQoybhorLTPsH2j2LnX3AOP2x/34+DUSTNaUTzu2fBXw+isVjQJpUBFWu6aeQRZw5bJol5X9Gxjblw==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-readline": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-12.0.0.tgz", + "integrity": "sha512-O7cywCWC8PiOMvo/gglEBfAkLjp/SENEML46BXDykfKP5mTPM46XMaX1L0waWU6DXJpBgjaL7+yX6VriVPbN4w==", + "license": "MIT", + "dependencies": { + "@serialport/parser-delimiter": "12.0.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/bindings-cpp/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@serialport/bindings-cpp/node_modules/node-addon-api": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.3.0.tgz", + "integrity": "sha512-8VOpLHFrOQlAH+qA0ZzuGRlALRA6/LVh8QJldbrC4DY0hXoMP0l4Acq8TzFC018HztWiRqyCEj2aTWY2UvnJUg==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/@serialport/bindings-interface": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@serialport/bindings-interface/-/bindings-interface-1.2.2.tgz", + "integrity": "sha512-CJaUd5bLvtM9c5dmO9rPBHPXTa9R2UwpkJ0wdh9JCYcbrPWsKz+ErvR0hBLeo7NPeiFdjFO4sonRljiw4d2XiA==", + "license": "MIT", + "engines": { + "node": "^12.22 || ^14.13 || >=16" + } + }, + "node_modules/@serialport/parser-byte-length": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-byte-length/-/parser-byte-length-13.0.0.tgz", + "integrity": "sha512-32yvqeTAqJzAEtX5zCrN1Mej56GJ5h/cVFsCDPbF9S1ZSC9FWjOqNAgtByseHfFTSTs/4ZBQZZcZBpolt8sUng==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-cctalk": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-cctalk/-/parser-cctalk-13.0.0.tgz", + "integrity": "sha512-RErAe57g9gvnlieVYGIn1xymb1bzNXb2QtUQd14FpmbQQYlcrmuRnJwKa1BgTCujoCkhtaTtgHlbBWOxm8U2uA==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-delimiter": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-13.0.0.tgz", + "integrity": "sha512-Qqyb0FX1avs3XabQqNaZSivyVbl/yl0jywImp7ePvfZKLwx7jBZjvL+Hawt9wIG6tfq6zbFM24vzCCK7REMUig==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-inter-byte-timeout": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-inter-byte-timeout/-/parser-inter-byte-timeout-13.0.0.tgz", + "integrity": "sha512-a0w0WecTW7bD2YHWrpTz1uyiWA2fDNym0kjmPeNSwZ2XCP+JbirZt31l43m2ey6qXItTYVuQBthm75sPVeHnGA==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-packet-length": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-packet-length/-/parser-packet-length-13.0.0.tgz", + "integrity": "sha512-60ZDDIqYRi0Xs2SPZUo4Jr5LLIjtb+rvzPKMJCohrO6tAqSDponcNpcB1O4W21mKTxYjqInSz+eMrtk0LLfZIg==", + "license": "MIT", + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/@serialport/parser-readline": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-13.0.0.tgz", + "integrity": "sha512-dov3zYoyf0dt1Sudd1q42VVYQ4WlliF0MYvAMA3MOyiU1IeG4hl0J6buBA2w4gl3DOCC05tGgLDN/3yIL81gsA==", + "license": "MIT", + "dependencies": { + "@serialport/parser-delimiter": "13.0.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-ready": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-ready/-/parser-ready-13.0.0.tgz", + "integrity": "sha512-JNUQA+y2Rfs4bU+cGYNqOPnNMAcayhhW+XJZihSLQXOHcZsFnOa2F9YtMg9VXRWIcnHldHYtisp62Etjlw24bw==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-regex": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-regex/-/parser-regex-13.0.0.tgz", + "integrity": "sha512-m7HpIf56G5XcuDdA3DB34Z0pJiwxNRakThEHjSa4mG05OnWYv0IG8l2oUyYfuGMowQWaVnQ+8r+brlPxGVH+eA==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-slip-encoder": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-slip-encoder/-/parser-slip-encoder-13.0.0.tgz", + "integrity": "sha512-fUHZEExm6izJ7rg0A1yjXwu4sOzeBkPAjDZPfb+XQoqgtKAk+s+HfICiYn7N2QU9gyaeCO8VKgWwi+b/DowYOg==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-spacepacket": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-spacepacket/-/parser-spacepacket-13.0.0.tgz", + "integrity": "sha512-DoXJ3mFYmyD8X/8931agJvrBPxqTaYDsPoly9/cwQSeh/q4EjQND9ySXBxpWz5WcpyCU4jOuusqCSAPsbB30Eg==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/stream": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/stream/-/stream-13.0.0.tgz", + "integrity": "sha512-F7xLJKsjGo2WuEWMSEO1SimRcOA+WtWICsY13r0ahx8s2SecPQH06338g28OT7cW7uRXI7oEQAk62qh5gHJW3g==", + "license": "MIT", + "dependencies": { + "@serialport/bindings-interface": "1.2.2", + "debug": "4.4.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/stream/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dev": true, + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@tootallnate/once": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.1.tgz", + "integrity": "sha512-HqmEUIGRJ5fSXchkVgR5F7qn48bDBzv0kWj/Kfu5e6uci4UlEeng4331LnBkWffb++Ei3FOVLxo8JJWMFBDMeQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "node_modules/@types/debug": { + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", + "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/fs-extra": { + "version": "9.0.13", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", + "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", + "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/plist": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.5.tgz", + "integrity": "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*", + "xmlbuilder": ">=11.0.1" + } + }, + "node_modules/@types/readable-stream": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.23.tgz", + "integrity": "sha512-wwXrtQvbMHxCbBgjHaMGEmImFTQxxpfMOR/ZoQnXxB1woqkUbdLGFDgauo00Py9IudiaqSeiBiulSV9i6XIPig==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/verror": { + "version": "1.10.11", + "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.11.tgz", + "integrity": "sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.9.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.10.tgz", + "integrity": "sha512-A9gOqLdi6cV4ibazAjcQufGj0B1y/vDqYrcuP6d/6x8P27gRS8643Dj9o1dEKtB6O7fwxb2FgBmJS2mX7gpvdw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.6" + } + }, + "node_modules/7zip-bin": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.2.0.tgz", + "integrity": "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/app-builder-bin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-4.0.0.tgz", + "integrity": "sha512-xwdG0FJPQMe0M0UA4Tz0zEB8rBJTRA5a476ZawAqiBkMv16GRK5xpXThOjMaEOFnZ6zabejjG4J3da0SXG63KA==", + "dev": true, + "license": "MIT" + }, + "node_modules/app-builder-lib": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-24.13.3.tgz", + "integrity": "sha512-FAzX6IBit2POXYGnTCT8YHFO/lr5AapAII6zzhQO3Rw4cEDOgK+t1xhLc5tNcKlicTHlo9zxIwnYCX9X2DLkig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@develar/schema-utils": "~2.6.5", + "@electron/notarize": "2.2.1", + "@electron/osx-sign": "1.0.5", + "@electron/universal": "1.5.1", + "@malept/flatpak-bundler": "^0.4.0", + "@types/fs-extra": "9.0.13", + "async-exit-hook": "^2.0.1", + "bluebird-lst": "^1.0.9", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", + "chromium-pickle-js": "^0.2.0", + "debug": "^4.3.4", + "ejs": "^3.1.8", + "electron-publish": "24.13.1", + "form-data": "^4.0.0", + "fs-extra": "^10.1.0", + "hosted-git-info": "^4.1.0", + "is-ci": "^3.0.0", + "isbinaryfile": "^5.0.0", + "js-yaml": "^4.1.0", + "lazy-val": "^1.0.5", + "minimatch": "^5.1.1", + "read-config-file": "6.3.2", + "sanitize-filename": "^1.6.3", + "semver": "^7.3.8", + "tar": "^6.1.12", + "temp-file": "^3.4.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "dmg-builder": "24.13.3", + "electron-builder-squirrel-windows": "24.13.3" + } + }, + "node_modules/app-builder-lib/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/app-builder-lib/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/app-builder-lib/node_modules/semver": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", + "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/app-builder-lib/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/archiver": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", + "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "archiver-utils": "^2.1.0", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^2.2.0", + "zip-stream": "^4.1.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/archiver-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", + "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "^7.1.4", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^2.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/archiver-utils/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/archiver-utils/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/archiver-utils/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-exit-hook": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", + "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/binary-parser": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/binary-parser/-/binary-parser-1.1.5.tgz", + "integrity": "sha512-KsW0sfQ3e5Lpk8EVP44okdpOUhUlVaQ5HQ/qBKLpuMgEV29pTIfPvFobs9j4kp/4BqOSZ+kZpC7f4mleVonTRw==", + "license": "MIT" + }, + "node_modules/binary-protocol": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/binary-protocol/-/binary-protocol-0.0.0.tgz", + "integrity": "sha512-kcFqbqtZnZEGSY8Jh6dEO6VbxJDZu1tqJc3LODqADPHrDwHQwoTNdGTHW4sLhmNXeWUOZKjfRfopAu5xpRQbPw==", + "license": "MIT", + "dependencies": { + "bluebird": "~2.2.2" + } + }, + "node_modules/binary-protocol/node_modules/bluebird": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.2.2.tgz", + "integrity": "sha512-NqzuS2y44FmP++Ihv6/PgXpNRB+c282TKq0URBTQdA+EqPpR1Qu44QY8H8667LA39iOIaVHpmRPI4gpthYT8Ug==", + "license": "MIT" + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/bluebird-lst": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/bluebird-lst/-/bluebird-lst-1.0.9.tgz", + "integrity": "sha512-7B1Rtx82hjnSD4PGLAjVWeYH3tHAcVUmChh85a3lltKQm6FresXh9ErQo6oAv6CqxttczC3/kEg8SY5NluPuUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bluebird": "^3.5.5" + } + }, + "node_modules/boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/brace-expansion": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/broker-factory": { + "version": "3.1.15", + "resolved": "https://registry.npmjs.org/broker-factory/-/broker-factory-3.1.15.tgz", + "integrity": "sha512-ko+aWvgNuP49meGrdjUu7rC+Y+Wai3cCPxP3xWwHsHfehFjOh5ZQM2yC4gEB2UddeZ/YXhm0K1eG/L6fxym2Og==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.7", + "fast-unique-numbers": "^9.0.27", + "tslib": "^2.8.1", + "worker-factory": "^7.0.50" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.1.tgz", + "integrity": "sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/builder-util": { + "version": "24.13.1", + "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-24.13.1.tgz", + "integrity": "sha512-NhbCSIntruNDTOVI9fdXz0dihaqX2YuE1D6zZMrwiErzH4ELZHE6mdiB40wEgZNprDia+FghRFgKoAqMZRRjSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/debug": "^4.1.6", + "7zip-bin": "~5.2.0", + "app-builder-bin": "4.0.0", + "bluebird-lst": "^1.0.9", + "builder-util-runtime": "9.2.4", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.3", + "debug": "^4.3.4", + "fs-extra": "^10.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-ci": "^3.0.0", + "js-yaml": "^4.1.0", + "source-map-support": "^0.5.19", + "stat-mode": "^1.0.0", + "temp-file": "^3.4.0" + } + }, + "node_modules/builder-util-runtime": { + "version": "9.2.4", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.4.tgz", + "integrity": "sha512-upp+biKpN/XZMLim7aguUyW8s0FUpDvOtK6sbanMFDAMBzpHDqdhgVYm6zc9HJ6nWo7u2Lxk60i2M6Jd3aiNrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "sax": "^1.2.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/builder-util/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/builder-util/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/builder-util/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/chromium-pickle-js": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz", + "integrity": "sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/commist": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/commist/-/commist-3.2.0.tgz", + "integrity": "sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==", + "license": "MIT" + }, + "node_modules/compare-version": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", + "integrity": "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/compress-commons": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", + "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "^0.2.13", + "crc32-stream": "^4.0.2", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/config-file-ts": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/config-file-ts/-/config-file-ts-0.2.6.tgz", + "integrity": "sha512-6boGVaglwblBgJqGyxm4+xCmEGcWgnWHSWHY5jad58awQhB6gftq0G8HbzU39YqCIYHMLAiL1yjwiZ36m/CL8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "^10.3.10", + "typescript": "^5.3.3" + } + }, + "node_modules/config-file-ts/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/config-file-ts/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/config-file-ts/node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/crc": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", + "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "buffer": "^5.1.0" + } + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", + "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/dir-compare": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-3.3.0.tgz", + "integrity": "sha512-J7/et3WlGUCxjdnD3HAAzQ6nsnc0WL6DD7WcwJb7c39iH1+AWfg+9OqzJNaI6PkBwBvm1mhZNL9iY/nRiZXlPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-equal": "^1.0.0", + "minimatch": "^3.0.4" + } + }, + "node_modules/dir-compare/node_modules/brace-expansion": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/dir-compare/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/dmg-builder": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-24.13.3.tgz", + "integrity": "sha512-rcJUkMfnJpfCboZoOOPf4L29TRtEieHNOeAbYPWPxlaBw/Z1RKrRA86dOI9rwaI4tQSc/RD82zTNHprfUHXsoQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "app-builder-lib": "24.13.3", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", + "fs-extra": "^10.1.0", + "iconv-lite": "^0.6.2", + "js-yaml": "^4.1.0" + }, + "optionalDependencies": { + "dmg-license": "^1.0.11" + } + }, + "node_modules/dmg-builder/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dmg-builder/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/dmg-builder/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/dmg-license": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/dmg-license/-/dmg-license-1.0.11.tgz", + "integrity": "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "@types/plist": "^3.0.1", + "@types/verror": "^1.10.3", + "ajv": "^6.10.0", + "crc": "^3.8.0", + "iconv-corefoundation": "^1.1.7", + "plist": "^3.0.4", + "smart-buffer": "^4.0.2", + "verror": "^1.10.0" + }, + "bin": { + "dmg-license": "bin/dmg-license.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-9.0.2.tgz", + "integrity": "sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=10" + } + }, + "node_modules/dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron": { + "version": "31.7.7", + "resolved": "https://registry.npmjs.org/electron/-/electron-31.7.7.tgz", + "integrity": "sha512-HZtZg8EHsDGnswFt0QeV8If8B+et63uD6RJ7I4/xhcXqmTIbI08GoubX/wm+HdY0DwcuPe1/xsgqpmYvjdjRoA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@electron/get": "^2.0.0", + "@types/node": "^20.9.0", + "extract-zip": "^2.0.1" + }, + "bin": { + "electron": "cli.js" + }, + "engines": { + "node": ">= 12.20.55" + } + }, + "node_modules/electron-builder": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-24.13.3.tgz", + "integrity": "sha512-yZSgVHft5dNVlo31qmJAe4BVKQfFdwpRw7sFp1iQglDRCDD6r22zfRJuZlhtB5gp9FHUxCMEoWGq10SkCnMAIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "app-builder-lib": "24.13.3", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", + "chalk": "^4.1.2", + "dmg-builder": "24.13.3", + "fs-extra": "^10.1.0", + "is-ci": "^3.0.0", + "lazy-val": "^1.0.5", + "read-config-file": "6.3.2", + "simple-update-notifier": "2.0.0", + "yargs": "^17.6.2" + }, + "bin": { + "electron-builder": "cli.js", + "install-app-deps": "install-app-deps.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/electron-builder-squirrel-windows": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-24.13.3.tgz", + "integrity": "sha512-oHkV0iogWfyK+ah9ZIvMDpei1m9ZRpdXcvde1wTpra2U8AFDNNpqJdnin5z+PM1GbQ5BoaKCWas2HSjtR0HwMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "app-builder-lib": "24.13.3", + "archiver": "^5.3.1", + "builder-util": "24.13.1", + "fs-extra": "^10.1.0" + } + }, + "node_modules/electron-builder-squirrel-windows/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/electron-builder-squirrel-windows/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-builder-squirrel-windows/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/electron-builder/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/electron-builder/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-builder/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/electron-publish": { + "version": "24.13.1", + "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-24.13.1.tgz", + "integrity": "sha512-2ZgdEqJ8e9D17Hwp5LEq5mLQPjqU3lv/IALvgp+4W8VeNhryfGhYEQC/PgDPMrnWUp+l60Ou5SJLsu+k4mhQ8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/fs-extra": "^9.0.11", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", + "chalk": "^4.1.2", + "fs-extra": "^10.1.0", + "lazy-val": "^1.0.5", + "mime": "^2.5.2" + } + }, + "node_modules/electron-publish/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/electron-publish/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-publish/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extsprintf": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", + "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "optional": true + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-unique-numbers": { + "version": "9.0.27", + "resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-9.0.27.tgz", + "integrity": "sha512-nDA9ADeINN8SA2u2wCtU+siWFTTDqQR37XvgPIDDmboWQeExz7X0mImxuaN+kJddliIqy2FpVRmnvRZ+j8i1/A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18.2.0" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/filelist": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz", + "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.4", + "mime-types": "^2.1.35" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true, + "license": "MIT" + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "engines": { + "node": ">=10.0" + } + }, + "node_modules/global-agent/node_modules/semver": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", + "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", + "dev": true, + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/help-me": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==", + "license": "MIT" + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/iconv-corefoundation": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", + "integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "cli-truncate": "^2.1.0", + "node-addon-api": "^1.6.3" + }, + "engines": { + "node": "^8.11.2 || >=10" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ip-address": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", + "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.2.0.tgz", + "integrity": "sha512-ku//LD7ie/m9UVGCm9KweBIIHP4mB0maNGvav6Hz778fQCNLQF7iZ+H/UuHuqmjJCHCpA5hw8hOeRKxZl8IlXw==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-ci": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", + "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ci-info": "^3.2.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/isbinaryfile": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.7.tgz", + "integrity": "sha512-gnWD14Jh3FzS3CPhF0AxNOJ8CxqeblPTADzI38r0wt8ZyQl5edpy75myt08EG2oKvpyiqSqsx+Wkz9vtkbTqYQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jake": { + "version": "10.9.4", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", + "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.6", + "filelist": "^1.0.4", + "picocolors": "^1.1.1" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/js-sdsl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", + "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/js-yaml": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", + "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/knx": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/knx/-/knx-2.5.4.tgz", + "integrity": "sha512-h+WMbx2NPgCL1Vr4X8xAsTV9E+72XjPviKRv67iJLomapENO25ZsRGq8egvqUJwtnsordJGOxxUA4o1jMOzQYw==", + "license": "MIT", + "dependencies": { + "@types/node": "^6.14.4", + "binary-parser": "1.1.5", + "binary-protocol": "0.0.0", + "ipaddr.js": "1.2.0", + "log-driver": "1.2.7", + "machina": "^4.0.2" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/knx/node_modules/@types/node": { + "version": "6.14.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-6.14.13.tgz", + "integrity": "sha512-J1F0XJ/9zxlZel5ZlbeSuHW2OpabrUAqpFuC2sm2I3by8sERQ8+KCjNKUcq8QHuzpGMWiJpo9ZxeHrqrP2KzQw==", + "license": "MIT" + }, + "node_modules/lazy-val": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz", + "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "license": "MIT" + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.union": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", + "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-driver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz", + "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==", + "license": "ISC", + "engines": { + "node": ">=0.8.6" + } + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/machina": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/machina/-/machina-4.0.2.tgz", + "integrity": "sha512-OOlFrW1rd783S6tF36v5Ie/TM64gfvSl9kYLWL2cPA31J71HHWW3XrgSe1BZSFAPkh8532CMJMLv/s9L2aopiA==", + "dependencies": { + "lodash": "^4.17.5" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/modbus-serial": { + "version": "8.0.25", + "resolved": "https://registry.npmjs.org/modbus-serial/-/modbus-serial-8.0.25.tgz", + "integrity": "sha512-T6OHW80k7DtYZF96onavw84IXNu44EW+fybgVftWAGOraL8vTmMZod8w6thOrWj2I2qHC9Gsn2nitVTUDih+6A==", + "license": "ISC", + "dependencies": { + "debug": "^4.4.3" + }, + "optionalDependencies": { + "serialport": "^13.0.0" + } + }, + "node_modules/mqtt": { + "version": "5.15.1", + "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.15.1.tgz", + "integrity": "sha512-V1WnkGuJh3ec9QXzy5Iylw8OOBK+Xu1WhxcQ9mMpLThG+/JZIMV1PgLNRgIiqXhZnvnVLsuyxHl5A/3bHHbcAA==", + "license": "MIT", + "dependencies": { + "@types/readable-stream": "^4.0.21", + "@types/ws": "^8.18.1", + "commist": "^3.2.0", + "concat-stream": "^2.0.0", + "debug": "^4.4.1", + "help-me": "^5.0.0", + "lru-cache": "^10.4.3", + "minimist": "^1.2.8", + "mqtt-packet": "^9.0.2", + "number-allocator": "^1.0.14", + "readable-stream": "^4.7.0", + "rfdc": "^1.4.1", + "socks": "^2.8.6", + "split2": "^4.2.0", + "worker-timers": "^8.0.23", + "ws": "^8.18.3" + }, + "bin": { + "mqtt": "build/bin/mqtt.js", + "mqtt_pub": "build/bin/pub.js", + "mqtt_sub": "build/bin/sub.js" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/mqtt-packet": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-9.0.2.tgz", + "integrity": "sha512-MvIY0B8/qjq7bKxdN1eD+nrljoeaai+qjLJgfRn3TiMuz0pamsIWY2bFODPZMSNmabsLANXsLl4EMoWvlaTZWA==", + "license": "MIT", + "dependencies": { + "bl": "^6.0.8", + "debug": "^4.3.4", + "process-nextick-args": "^2.0.1" + } + }, + "node_modules/mqtt-packet/node_modules/bl": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/bl/-/bl-6.1.6.tgz", + "integrity": "sha512-jLsPgN/YSvPUg9UX0Kd73CXpm2Psg9FxMeCSXnk3WBO3CMT10JMwijubhGfHCnFu6TPn1ei3b975dxv7K2pWVg==", + "license": "MIT", + "dependencies": { + "@types/readable-stream": "^4.0.0", + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^4.2.0" + } + }, + "node_modules/mqtt-packet/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/mqtt-packet/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/mqtt/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/mqtt/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/mqtt/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/node-addon-api": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", + "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/number-allocator": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.14.tgz", + "integrity": "sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.1", + "js-sdsl": "4.3.0" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/plist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.1.tgz", + "integrity": "sha512-ZIfcLJC+7E7FBFnDxm9MPmt7D+DidyQ26lewieO75AdhA2ayMtsJSES0iWzqJQbcVRSrTufQoy0DR94xHue0oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@xmldom/xmldom": "^0.9.10", + "base64-js": "^1.5.1", + "xmlbuilder": "^15.1.1" + }, + "engines": { + "node": ">=10.4.0" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-config-file": { + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-6.3.2.tgz", + "integrity": "sha512-M80lpCjnE6Wt6zb98DoW8WHR09nzMSpu8XHtPkiTHrJ5Az9CybfeQhTJ8D7saeBHpGhLPIVyA8lcL6ZmdKwY6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "config-file-ts": "^0.2.4", + "dotenv": "^9.0.2", + "dotenv-expand": "^5.1.0", + "js-yaml": "^4.1.0", + "json5": "^2.2.0", + "lazy-val": "^1.0.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "license": "MIT" + }, + "node_modules/roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sanitize-filename": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.4.tgz", + "integrity": "sha512-9ZyI08PsvdQl2r/bBIGubpVdR3RR9sY6RDiWFPreA21C/EFlQhmgo20UZlNjZMMZNubusLhAQozkA0Od5J21Eg==", + "dev": true, + "license": "WTFPL OR ISC", + "dependencies": { + "truncate-utf8-bytes": "^1.0.0" + } + }, + "node_modules/sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serialport": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/serialport/-/serialport-13.0.0.tgz", + "integrity": "sha512-PHpnTd8isMGPfFTZNCzOZp9m4mAJSNWle9Jxu6BPTcWq7YXl5qN7tp8Sgn0h+WIGcD6JFz5QDgixC2s4VW7vzg==", + "license": "MIT", + "dependencies": { + "@serialport/binding-mock": "10.2.2", + "@serialport/bindings-cpp": "13.0.0", + "@serialport/parser-byte-length": "13.0.0", + "@serialport/parser-cctalk": "13.0.0", + "@serialport/parser-delimiter": "13.0.0", + "@serialport/parser-inter-byte-timeout": "13.0.0", + "@serialport/parser-packet-length": "13.0.0", + "@serialport/parser-readline": "13.0.0", + "@serialport/parser-ready": "13.0.0", + "@serialport/parser-regex": "13.0.0", + "@serialport/parser-slip-encoder": "13.0.0", + "@serialport/parser-spacepacket": "13.0.0", + "@serialport/stream": "13.0.0", + "debug": "4.4.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/serialport/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/simple-update-notifier/node_modules/semver": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", + "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.9.tgz", + "integrity": "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==", + "license": "MIT", + "dependencies": { + "ip-address": "^10.1.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/stat-mode": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz", + "integrity": "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.1.0" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/temp-file": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz", + "integrity": "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-exit-hook": "^2.0.1", + "fs-extra": "^10.0.0" + } + }, + "node_modules/temp-file/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/temp-file/node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/temp-file/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/tmp": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz", + "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/tmp-promise": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", + "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tmp": "^0.2.0" + } + }, + "node_modules/truncate-utf8-bytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", + "dev": true, + "license": "WTFPL", + "dependencies": { + "utf8-byte-length": "^1.0.1" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "license": "MIT" + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/utf8-byte-length": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", + "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==", + "dev": true, + "license": "(WTFPL OR MIT)" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/verror": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", + "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/worker-factory": { + "version": "7.0.50", + "resolved": "https://registry.npmjs.org/worker-factory/-/worker-factory-7.0.50.tgz", + "integrity": "sha512-hhwc0G+sFwM4qBuhJIUBn2p1Jf8v/FwmLUANBf/Q+Lt2uI8mfIZQhXaZQACodQD4R7Zp6cn/6702bIvNn2puJQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.7", + "fast-unique-numbers": "^9.0.27", + "tslib": "^2.8.1" + } + }, + "node_modules/worker-timers": { + "version": "8.0.32", + "resolved": "https://registry.npmjs.org/worker-timers/-/worker-timers-8.0.32.tgz", + "integrity": "sha512-huEv5mB6xIqcadsZ8SuuFQH9Lg9Hq0h0xD9vAZZsLPNw0aLxoDWyjTALAUD3hAA4cuKbKaqrjbBcbEYClwSh3w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.7", + "tslib": "^2.8.1", + "worker-timers-broker": "^8.0.16", + "worker-timers-worker": "^9.0.14" + } + }, + "node_modules/worker-timers-broker": { + "version": "8.0.17", + "resolved": "https://registry.npmjs.org/worker-timers-broker/-/worker-timers-broker-8.0.17.tgz", + "integrity": "sha512-avGDVB9AP5k5eCAP2AiT/nwCHGCNla7Z+nMZWXpmhbiSTry6A7VwEzlffTO34/5Eo55O+XLbWQu0bBW3uEO0UA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.7", + "broker-factory": "^3.1.15", + "fast-unique-numbers": "^9.0.27", + "tslib": "^2.8.1", + "worker-timers-worker": "^9.0.14" + } + }, + "node_modules/worker-timers-worker": { + "version": "9.0.15", + "resolved": "https://registry.npmjs.org/worker-timers-worker/-/worker-timers-worker-9.0.15.tgz", + "integrity": "sha512-KKUe7lZ/Aignr51H6hOUik8LwTnIgojH/1lwhli8A8qIEIyewogZTpNpMW5B6BF7nmwBOkUoTYgf1H3QShcjSA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.7", + "tslib": "^2.8.1", + "worker-factory": "^7.0.50" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xmlbuilder": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/zip-stream": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", + "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "archiver-utils": "^3.0.4", + "compress-commons": "^4.1.2", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/zip-stream/node_modules/archiver-utils": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", + "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "^7.2.3", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..30f6b47 --- /dev/null +++ b/package.json @@ -0,0 +1,45 @@ +{ + "name": "time4midi", + "version": "0.1.0", + "description": "Cross-platform timeline editor for MIDI notes and Art-Net values, slaved to incoming timecode.", + "main": "main.js", + "author": "J.H. Artworks", + "scripts": { + "start": "electron .", + "dist:win": "set CSC_IDENTITY_AUTO_DISCOVERY=false&& electron-builder --win portable" + }, + "devDependencies": { + "electron": "^31.7.7", + "electron-builder": "^24.13.3" + }, + "dependencies": { + "knx": "^2.5.4", + "modbus-serial": "^8.0.25", + "mqtt": "^5.10.4", + "serialport": "^13.0.0" + }, + "build": { + "appId": "com.jhartworks.time4midi", + "productName": "Time4Midi", + "files": [ + "main.js", + "preload.js", + "src/**/*", + "package.json" + ], + "directories": { + "output": "dist" + }, + "win": { + "signAndEditExecutable": false, + "target": [ + { + "target": "portable", + "arch": [ + "x64" + ] + } + ] + } + } +} diff --git a/preload.js b/preload.js new file mode 100644 index 0000000..36f8d86 --- /dev/null +++ b/preload.js @@ -0,0 +1,941 @@ +const { contextBridge, ipcRenderer } = require("electron"); +const crypto = require("node:crypto"); +const dgram = require("node:dgram"); +const mqtt = require("mqtt"); +const { SerialPort } = require("serialport"); +const ModbusRTU = require("modbus-serial"); +const knx = require("knx"); + +const ARTNET_PORT = 6454; +const SACN_PORT = 5568; +const KNX_PORT = 3671; +const ARTNET_HEADER = Buffer.from("Art-Net\0", "ascii"); +const ARTNET_OPCODE_DMX = 0x5000; +const ARTNET_OPCODE_TIMECODE = 0x9700; + +function createStatusEmitter() { + return { + statusListeners: new Set(), + lastError: "" + }; +} + +function emitStatus(target, message) { + target.lastError = message || ""; + const payload = { + ok: !target.lastError, + error: target.lastError + }; + for (const listener of target.statusListeners) { + listener(payload); + } +} + +function addStatusApi(target, handler) { + target.statusListeners.add(handler); + handler({ + ok: !target.lastError, + error: target.lastError + }); + return () => { + target.statusListeners.delete(handler); + }; +} + +const artnet = { + ...createStatusEmitter(), + sendSocket: dgram.createSocket("udp4"), + listenSocket: null, + listeners: new Set(), + config: { + enabled: true, + host: "255.255.255.255", + port: ARTNET_PORT, + broadcast: true, + timecodeListenPort: ARTNET_PORT + }, + sequence: 1 +}; + +const sacn = { + ...createStatusEmitter(), + socket: dgram.createSocket("udp4"), + config: { + enabled: false, + host: "", + port: SACN_PORT, + multicast: true, + sourceName: "Time4Midi" + }, + sequence: 1, + cid: crypto.randomBytes(16) +}; + +const osc = { + ...createStatusEmitter(), + socket: dgram.createSocket("udp4"), + config: { + enabled: false, + host: "127.0.0.1", + port: 8000 + } +}; + +const mqttState = { + ...createStatusEmitter(), + client: null, + connected: false, + config: { + enabled: false, + brokerUrl: "mqtt://127.0.0.1:1883", + username: "", + password: "", + clientId: "" + } +}; + +const serialState = { + ...createStatusEmitter(), + port: null, + config: { + enabled: false, + path: "", + baudRate: 9600, + dataBits: 8, + stopBits: 1, + parity: "none", + mode: "rs232" + } +}; + +const modbusTcpState = { + ...createStatusEmitter(), + client: new ModbusRTU(), + connected: false, + signature: "", + config: { + enabled: false, + host: "127.0.0.1", + port: 502, + timeout: 1000 + } +}; + +const modbusRtuState = { + ...createStatusEmitter(), + client: new ModbusRTU(), + connected: false, + signature: "", + config: { + enabled: false, + path: "", + baudRate: 9600, + dataBits: 8, + stopBits: 1, + parity: "none", + timeout: 1000 + } +}; + +const knxState = { + ...createStatusEmitter(), + connection: null, + connecting: null, + connected: false, + config: { + enabled: false, + ipAddr: "224.0.23.12", + ipPort: KNX_PORT, + physAddr: "15.15.15", + forceTunneling: false + } +}; + +function normalizeArtNetConfig(nextConfig = {}) { + return { + enabled: nextConfig.enabled !== false, + host: nextConfig.host || "255.255.255.255", + port: Number.isFinite(Number(nextConfig.port)) ? Number(nextConfig.port) : ARTNET_PORT, + broadcast: Boolean(nextConfig.broadcast), + timecodeListenPort: Number.isFinite(Number(nextConfig.timecodeListenPort)) + ? Number(nextConfig.timecodeListenPort) + : ARTNET_PORT + }; +} + +function normalizeSacnConfig(nextConfig = {}) { + return { + enabled: Boolean(nextConfig.enabled), + host: String(nextConfig.host || ""), + port: Number.isFinite(Number(nextConfig.port)) ? Number(nextConfig.port) : SACN_PORT, + multicast: nextConfig.multicast !== false, + sourceName: String(nextConfig.sourceName || "Time4Midi").slice(0, 63) + }; +} + +function normalizeOscConfig(nextConfig = {}) { + return { + enabled: Boolean(nextConfig.enabled), + host: nextConfig.host || "127.0.0.1", + port: Number.isFinite(Number(nextConfig.port)) ? Number(nextConfig.port) : 8000 + }; +} + +function normalizeMqttConfig(nextConfig = {}) { + return { + enabled: Boolean(nextConfig.enabled), + brokerUrl: String(nextConfig.brokerUrl || "").trim() || "mqtt://127.0.0.1:1883", + username: String(nextConfig.username || ""), + password: String(nextConfig.password || ""), + clientId: String(nextConfig.clientId || "") + }; +} + +function normalizeSerialConfig(nextConfig = {}) { + return { + enabled: Boolean(nextConfig.enabled), + path: String(nextConfig.path || ""), + baudRate: Number.isFinite(Number(nextConfig.baudRate)) ? Number(nextConfig.baudRate) : 9600, + dataBits: [7, 8].includes(Number(nextConfig.dataBits)) ? Number(nextConfig.dataBits) : 8, + stopBits: [1, 2].includes(Number(nextConfig.stopBits)) ? Number(nextConfig.stopBits) : 1, + parity: ["none", "even", "odd"].includes(String(nextConfig.parity || "none")) ? String(nextConfig.parity || "none") : "none", + mode: String(nextConfig.mode || "rs232") + }; +} + +function normalizeModbusTcpConfig(nextConfig = {}) { + return { + enabled: Boolean(nextConfig.enabled), + host: String(nextConfig.host || "127.0.0.1"), + port: Number.isFinite(Number(nextConfig.port)) ? Number(nextConfig.port) : 502, + timeout: Number.isFinite(Number(nextConfig.timeout)) ? Number(nextConfig.timeout) : 1000 + }; +} + +function normalizeModbusRtuConfig(nextConfig = {}) { + return { + enabled: Boolean(nextConfig.enabled), + path: String(nextConfig.path || ""), + baudRate: Number.isFinite(Number(nextConfig.baudRate)) ? Number(nextConfig.baudRate) : 9600, + dataBits: [7, 8].includes(Number(nextConfig.dataBits)) ? Number(nextConfig.dataBits) : 8, + stopBits: [1, 2].includes(Number(nextConfig.stopBits)) ? Number(nextConfig.stopBits) : 1, + parity: ["none", "even", "odd"].includes(String(nextConfig.parity || "none")) ? String(nextConfig.parity || "none") : "none", + timeout: Number.isFinite(Number(nextConfig.timeout)) ? Number(nextConfig.timeout) : 1000 + }; +} + +function normalizeKnxConfig(nextConfig = {}) { + return { + enabled: Boolean(nextConfig.enabled), + ipAddr: String(nextConfig.ipAddr || "224.0.23.12"), + ipPort: Number.isFinite(Number(nextConfig.ipPort)) ? Number(nextConfig.ipPort) : KNX_PORT, + physAddr: String(nextConfig.physAddr || "15.15.15"), + forceTunneling: Boolean(nextConfig.forceTunneling) + }; +} + +function closeArtNetListener() { + if (artnet.listenSocket) { + try { + artnet.listenSocket.close(); + } catch (_error) { + // Ignore listener close races. + } + artnet.listenSocket = null; + } +} + +function parseArtNetTimecode(message) { + if (message.length < 19) { + return null; + } + if (!message.subarray(0, 8).equals(ARTNET_HEADER)) { + return null; + } + const opcode = message.readUInt16LE(8); + if (opcode !== ARTNET_OPCODE_TIMECODE) { + return null; + } + + const frames = message[14]; + const seconds = message[15]; + const minutes = message[16]; + const hours = message[17]; + const frameType = message[18] & 0x03; + const fpsMap = [24, 25, 29.97, 30]; + const fps = fpsMap[frameType] || 30; + const totalSeconds = (hours * 3600) + (minutes * 60) + seconds + (frames / fps); + return { hours, minutes, seconds, frames, fps, totalSeconds }; +} + +function ensureArtNetListener() { + if (!artnet.config.enabled || artnet.listenSocket) { + return; + } + + artnet.listenSocket = dgram.createSocket("udp4"); + artnet.listenSocket.on("error", (error) => { + emitStatus(artnet, `Art-Net Listen Fehler: ${error.message}`); + }); + artnet.listenSocket.on("listening", () => { + emitStatus(artnet, ""); + }); + artnet.listenSocket.on("message", (message) => { + const parsed = parseArtNetTimecode(message); + if (!parsed) return; + for (const listener of artnet.listeners) { + listener(parsed); + } + }); + + try { + artnet.listenSocket.bind(artnet.config.timecodeListenPort); + } catch (error) { + emitStatus(artnet, `Art-Net Listen Fehler: ${error.message}`); + } +} + +function configureArtNet(nextConfig) { + const previousPort = artnet.config.timecodeListenPort; + artnet.config = normalizeArtNetConfig(nextConfig); + + try { + artnet.sendSocket.setBroadcast(artnet.config.broadcast); + } catch (error) { + emitStatus(artnet, `Art-Net Konfig Fehler: ${error.message}`); + } + + if (!artnet.config.enabled) { + closeArtNetListener(); + emitStatus(artnet, ""); + return artnet.config; + } + + if (previousPort !== artnet.config.timecodeListenPort) { + closeArtNetListener(); + } + if (artnet.listeners.size > 0) { + ensureArtNetListener(); + } + return artnet.config; +} + +function sendArtNetDmx(payload) { + if (!artnet.config.enabled) { + return Promise.resolve(false); + } + + const universe = Math.max(0, Math.min(32767, Number(payload.universe) || 0)); + const values = Array.isArray(payload.values) ? payload.values : []; + const length = Math.max(2, Math.min(512, values.length || 512)); + const evenLength = length % 2 === 0 ? length : length + 1; + const packet = Buffer.alloc(18 + evenLength); + + ARTNET_HEADER.copy(packet, 0); + packet.writeUInt16LE(ARTNET_OPCODE_DMX, 8); + packet[10] = 0x00; + packet[11] = 0x0e; + packet[12] = artnet.sequence; + packet[13] = 0x00; + packet.writeUInt16LE(universe, 14); + packet.writeUInt16BE(evenLength, 16); + for (let index = 0; index < evenLength; index += 1) { + packet[18 + index] = Math.max(0, Math.min(255, Math.round(values[index] || 0))); + } + artnet.sequence = artnet.sequence >= 255 ? 1 : artnet.sequence + 1; + + return new Promise((resolve, reject) => { + artnet.sendSocket.send(packet, 0, packet.length, artnet.config.port, artnet.config.host, (error) => { + if (error) { + emitStatus(artnet, `Art-Net Send Fehler: ${error.message}`); + reject(error); + return; + } + emitStatus(artnet, ""); + resolve(true); + }); + }); +} + +function configureOsc(nextConfig) { + osc.config = normalizeOscConfig(nextConfig); + emitStatus(osc, ""); + return osc.config; +} + +function encodeOscString(value) { + const base = Buffer.from(`${value}\0`, "utf8"); + const padding = (4 - (base.length % 4)) % 4; + return padding === 0 ? base : Buffer.concat([base, Buffer.alloc(padding)]); +} + +function encodeOscArgument(value, argType) { + if (argType === "string") { + return { tag: "s", data: encodeOscString(String(value ?? "")) }; + } + if (argType === "int") { + const data = Buffer.alloc(4); + data.writeInt32BE(Math.round(Number(value) || 0), 0); + return { tag: "i", data }; + } + const data = Buffer.alloc(4); + data.writeFloatBE(Number(value) || 0, 0); + return { tag: "f", data }; +} + +function sendOsc(payload) { + if (!osc.config.enabled) { + return Promise.resolve(false); + } + const address = String(payload.address || "/time4midi"); + const normalizedAddress = address.startsWith("/") ? address : `/${address}`; + const argument = encodeOscArgument(payload.value, payload.argType || "float"); + const packet = Buffer.concat([ + encodeOscString(normalizedAddress), + encodeOscString(`,${argument.tag}`), + argument.data + ]); + return new Promise((resolve, reject) => { + osc.socket.send(packet, 0, packet.length, osc.config.port, osc.config.host, (error) => { + if (error) { + emitStatus(osc, `OSC Send Fehler: ${error.message}`); + reject(error); + return; + } + emitStatus(osc, ""); + resolve(true); + }); + }); +} + +function configureSacn(nextConfig) { + sacn.config = normalizeSacnConfig(nextConfig); + emitStatus(sacn, ""); + return sacn.config; +} + +function clampSacnUniverse(universe) { + return Math.max(1, Math.min(63999, Number(universe) || 1)); +} + +function sacnDestination(universe) { + const safeUniverse = clampSacnUniverse(universe); + if (!sacn.config.multicast) { + return sacn.config.host || "127.0.0.1"; + } + const high = (safeUniverse >> 8) & 0xff; + const low = safeUniverse & 0xff; + return `239.255.${high}.${low}`; +} + +function encodeFlagsAndLength(length) { + return 0x7000 | length; +} + +function writeFixedString(buffer, offset, size, value) { + buffer.fill(0, offset, offset + size); + Buffer.from(String(value || ""), "utf8").copy(buffer, offset, 0, size); +} + +function sendSacnDmx(payload) { + if (!sacn.config.enabled) { + return Promise.resolve(false); + } + + const universe = clampSacnUniverse(payload.universe); + const values = Array.isArray(payload.values) ? payload.values : []; + const slotCount = Math.max(1, Math.min(512, values.length || 512)); + const packetLength = 126 + slotCount; + const propertyValueCount = slotCount + 1; + const packet = Buffer.alloc(packetLength); + + packet.writeUInt16BE(0x0010, 0); + packet.writeUInt16BE(0x0000, 2); + writeFixedString(packet, 4, 12, "ASC-E1.17"); + + packet.writeUInt16BE(encodeFlagsAndLength(packetLength - 16), 16); + packet.writeUInt32BE(0x00000004, 18); + sacn.cid.copy(packet, 22); + + packet.writeUInt16BE(encodeFlagsAndLength(packetLength - 38), 38); + packet.writeUInt32BE(0x00000002, 40); + writeFixedString(packet, 44, 64, sacn.config.sourceName || "Time4Midi"); + packet[108] = 100; + packet.writeUInt16BE(0x0000, 109); + packet[111] = sacn.sequence; + packet[112] = 0x00; + packet.writeUInt16BE(universe, 113); + + packet.writeUInt16BE(encodeFlagsAndLength(packetLength - 115), 115); + packet[117] = 0x02; + packet[118] = 0xa1; + packet.writeUInt16BE(0x0000, 119); + packet.writeUInt16BE(0x0001, 121); + packet.writeUInt16BE(propertyValueCount, 123); + packet[125] = 0x00; + + for (let index = 0; index < slotCount; index += 1) { + packet[126 + index] = Math.max(0, Math.min(255, Math.round(values[index] || 0))); + } + + sacn.sequence = sacn.sequence >= 255 ? 1 : sacn.sequence + 1; + const host = sacnDestination(universe); + + return new Promise((resolve, reject) => { + sacn.socket.send(packet, 0, packet.length, sacn.config.port, host, (error) => { + if (error) { + emitStatus(sacn, `sACN Send Fehler: ${error.message}`); + reject(error); + return; + } + emitStatus(sacn, ""); + resolve(true); + }); + }); +} + +function disconnectMqtt() { + if (mqttState.client) { + try { + mqttState.client.removeAllListeners(); + mqttState.client.end(true); + } catch (_error) { + // Best effort disconnect. + } + } + mqttState.client = null; + mqttState.connected = false; +} + +function configureMqtt(nextConfig) { + mqttState.config = normalizeMqttConfig(nextConfig); + disconnectMqtt(); + + if (!mqttState.config.enabled) { + emitStatus(mqttState, ""); + return mqttState.config; + } + + const options = { reconnectPeriod: 1000 }; + if (mqttState.config.clientId) options.clientId = mqttState.config.clientId; + if (mqttState.config.username) options.username = mqttState.config.username; + if (mqttState.config.password) options.password = mqttState.config.password; + + try { + mqttState.client = mqtt.connect(mqttState.config.brokerUrl, options); + mqttState.client.on("connect", () => { + mqttState.connected = true; + emitStatus(mqttState, ""); + }); + mqttState.client.on("reconnect", () => { + mqttState.connected = false; + emitStatus(mqttState, "MQTT verbindet neu..."); + }); + mqttState.client.on("close", () => { + mqttState.connected = false; + }); + mqttState.client.on("error", (error) => { + mqttState.connected = false; + emitStatus(mqttState, `MQTT Fehler: ${error.message}`); + }); + } catch (error) { + emitStatus(mqttState, `MQTT Fehler: ${error.message}`); + } + + return mqttState.config; +} + +function publishMqtt(payload) { + if (!mqttState.config.enabled) { + return Promise.resolve(false); + } + return new Promise((resolve, reject) => { + if (!mqttState.client || !mqttState.connected) { + reject(new Error("MQTT nicht verbunden")); + return; + } + mqttState.client.publish(String(payload.topic || ""), String(payload.value ?? ""), {}, (error) => { + if (error) { + emitStatus(mqttState, `MQTT Send Fehler: ${error.message}`); + reject(error); + return; + } + emitStatus(mqttState, ""); + resolve(true); + }); + }); +} + +function closeSerialPort() { + return new Promise((resolve) => { + if (!serialState.port) { + resolve(); + return; + } + const port = serialState.port; + serialState.port = null; + if (!port.isOpen) { + resolve(); + return; + } + port.close(() => resolve()); + }); +} + +async function configureSerial(nextConfig) { + serialState.config = normalizeSerialConfig(nextConfig); + await closeSerialPort(); + emitStatus(serialState, ""); + return serialState.config; +} + +async function ensureSerialPortOpen() { + if (!serialState.config.enabled) { + throw new Error("Serial deaktiviert"); + } + if (!serialState.config.path) { + throw new Error("Serial Port nicht gesetzt"); + } + if (serialState.port && serialState.port.isOpen && serialState.port.path === serialState.config.path) { + return serialState.port; + } + await closeSerialPort(); + + serialState.port = new SerialPort({ + path: serialState.config.path, + baudRate: serialState.config.baudRate, + dataBits: serialState.config.dataBits, + stopBits: serialState.config.stopBits, + parity: serialState.config.parity, + autoOpen: false + }); + + serialState.port.on("error", (error) => { + emitStatus(serialState, `Serial Fehler: ${error.message}`); + }); + + await new Promise((resolve, reject) => { + serialState.port.open((error) => { + if (error) { + reject(error); + return; + } + resolve(true); + }); + }); + + emitStatus(serialState, ""); + return serialState.port; +} + +async function sendSerial(payload) { + if (!serialState.config.enabled) { + return false; + } + const port = await ensureSerialPortOpen(); + const text = String(payload.text ?? ""); + return new Promise((resolve, reject) => { + port.write(text, "utf8", (error) => { + if (error) { + emitStatus(serialState, `Serial Send Fehler: ${error.message}`); + reject(error); + return; + } + emitStatus(serialState, ""); + resolve(true); + }); + }); +} + +async function listSerialPorts() { + const ports = await SerialPort.list(); + return ports.map((port) => ({ + path: port.path, + manufacturer: port.manufacturer || "", + friendlyName: port.path + })); +} + +async function disconnectModbusClient(state) { + if (state.client?.isOpen) { + try { + await state.client.close(); + } catch (_error) { + // Best effort close. + } + } + state.connected = false; + state.signature = ""; +} + +async function configureModbusTcp(nextConfig) { + modbusTcpState.config = normalizeModbusTcpConfig(nextConfig); + await disconnectModbusClient(modbusTcpState); + emitStatus(modbusTcpState, ""); + return modbusTcpState.config; +} + +async function configureModbusRtu(nextConfig) { + modbusRtuState.config = normalizeModbusRtuConfig(nextConfig); + await disconnectModbusClient(modbusRtuState); + emitStatus(modbusRtuState, ""); + return modbusRtuState.config; +} + +async function ensureModbusTcpConnected() { + if (!modbusTcpState.config.enabled) { + throw new Error("Modbus TCP deaktiviert"); + } + const signature = `${modbusTcpState.config.host}:${modbusTcpState.config.port}:${modbusTcpState.config.timeout}`; + if (modbusTcpState.connected && modbusTcpState.signature === signature && modbusTcpState.client.isOpen) { + return modbusTcpState.client; + } + await disconnectModbusClient(modbusTcpState); + await modbusTcpState.client.connectTCP(modbusTcpState.config.host, { + port: modbusTcpState.config.port, + timeout: modbusTcpState.config.timeout + }); + modbusTcpState.client.setTimeout(modbusTcpState.config.timeout); + modbusTcpState.connected = true; + modbusTcpState.signature = signature; + emitStatus(modbusTcpState, ""); + return modbusTcpState.client; +} + +async function ensureModbusRtuConnected() { + if (!modbusRtuState.config.enabled) { + throw new Error("Modbus RTU deaktiviert"); + } + if (!modbusRtuState.config.path) { + throw new Error("Modbus RTU Port nicht gesetzt"); + } + const signature = `${modbusRtuState.config.path}:${modbusRtuState.config.baudRate}:${modbusRtuState.config.parity}:${modbusRtuState.config.dataBits}:${modbusRtuState.config.stopBits}`; + if (modbusRtuState.connected && modbusRtuState.signature === signature && modbusRtuState.client.isOpen) { + return modbusRtuState.client; + } + await disconnectModbusClient(modbusRtuState); + await modbusRtuState.client.connectRTUBuffered(modbusRtuState.config.path, { + baudRate: modbusRtuState.config.baudRate, + parity: modbusRtuState.config.parity, + dataBits: modbusRtuState.config.dataBits, + stopBits: modbusRtuState.config.stopBits + }); + modbusRtuState.client.setTimeout(modbusRtuState.config.timeout); + modbusRtuState.connected = true; + modbusRtuState.signature = signature; + emitStatus(modbusRtuState, ""); + return modbusRtuState.client; +} + +async function writeModbus(client, payload) { + client.setID(Number(payload.unitId) || 1); + const address = Number(payload.address) || 0; + const value = Number(payload.value) || 0; + if (payload.registerType === "coil") { + await client.writeCoil(address, value >= 1); + } else { + await client.writeRegister(address, Math.max(0, Math.min(65535, Math.round(value)))); + } +} + +async function sendModbusTcp(payload) { + const client = await ensureModbusTcpConnected(); + try { + await writeModbus(client, payload); + emitStatus(modbusTcpState, ""); + return true; + } catch (error) { + emitStatus(modbusTcpState, `Modbus TCP Fehler: ${error.message}`); + throw error; + } +} + +async function sendModbusRtu(payload) { + const client = await ensureModbusRtuConnected(); + try { + await writeModbus(client, payload); + emitStatus(modbusRtuState, ""); + return true; + } catch (error) { + emitStatus(modbusRtuState, `Modbus RTU Fehler: ${error.message}`); + throw error; + } +} + +function disconnectKnx() { + return new Promise((resolve) => { + if (!knxState.connection) { + knxState.connected = false; + knxState.connecting = null; + resolve(); + return; + } + + const connection = knxState.connection; + knxState.connection = null; + knxState.connected = false; + knxState.connecting = null; + try { + connection.Disconnect(() => resolve()); + } catch (_error) { + resolve(); + } + }); +} + +async function configureKnx(nextConfig) { + knxState.config = normalizeKnxConfig(nextConfig); + await disconnectKnx(); + emitStatus(knxState, ""); + return knxState.config; +} + +async function ensureKnxConnection() { + if (!knxState.config.enabled) { + throw new Error("KNX deaktiviert"); + } + if (knxState.connection && knxState.connected) { + return knxState.connection; + } + if (knxState.connecting) { + return knxState.connecting; + } + + knxState.connecting = new Promise((resolve, reject) => { + try { + const connection = knx.Connection({ + ipAddr: knxState.config.ipAddr, + ipPort: knxState.config.ipPort, + physAddr: knxState.config.physAddr, + forceTunneling: knxState.config.forceTunneling, + handlers: { + connected: function () { + knxState.connection = connection; + knxState.connected = true; + knxState.connecting = null; + emitStatus(knxState, ""); + resolve(connection); + }, + disconnected: function () { + knxState.connected = false; + knxState.connection = null; + }, + error: function (error) { + knxState.connected = false; + knxState.connection = null; + knxState.connecting = null; + const message = error?.message || String(error); + emitStatus(knxState, `KNX Fehler: ${message}`); + reject(error instanceof Error ? error : new Error(message)); + } + } + }); + knxState.connection = connection; + } catch (error) { + knxState.connecting = null; + emitStatus(knxState, `KNX Fehler: ${error.message}`); + reject(error); + } + }); + + return knxState.connecting; +} + +async function sendKnx(payload) { + const connection = await ensureKnxConnection(); + const dpt = String(payload.dpt || "DPT5.001"); + let value = payload.value; + if (dpt.startsWith("DPT1")) { + value = Number(value) >= 1; + } else if (dpt.startsWith("DPT16")) { + value = String(value ?? ""); + } else { + value = Number(value) || 0; + } + + return new Promise((resolve, reject) => { + try { + connection.write(String(payload.groupAddress || "1/1/1"), value, dpt, (error) => { + if (error) { + emitStatus(knxState, `KNX Send Fehler: ${error.message}`); + reject(error); + return; + } + emitStatus(knxState, ""); + resolve(true); + }); + } catch (error) { + emitStatus(knxState, `KNX Send Fehler: ${error.message}`); + reject(error); + } + }); +} + +artnet.sendSocket.on("error", (error) => { + emitStatus(artnet, `Art-Net Socket Fehler: ${error.message}`); +}); + +osc.socket.on("error", (error) => { + emitStatus(osc, `OSC Socket Fehler: ${error.message}`); +}); + +sacn.socket.on("error", (error) => { + emitStatus(sacn, `sACN Socket Fehler: ${error.message}`); +}); + +configureMqtt(mqttState.config); + +contextBridge.exposeInMainWorld("time4midi", { + project: { + save: (payload) => ipcRenderer.invoke("project:save", payload), + load: () => ipcRenderer.invoke("project:load") + }, + artnet: { + configure: (config) => configureArtNet(config), + sendDmx: (payload) => sendArtNetDmx(payload), + onTimecode: (handler) => { + artnet.listeners.add(handler); + ensureArtNetListener(); + return () => { + artnet.listeners.delete(handler); + }; + }, + onStatus: (handler) => addStatusApi(artnet, handler) + }, + sacn: { + configure: (config) => configureSacn(config), + sendDmx: (payload) => sendSacnDmx(payload), + onStatus: (handler) => addStatusApi(sacn, handler) + }, + osc: { + configure: (config) => configureOsc(config), + send: (payload) => sendOsc(payload), + onStatus: (handler) => addStatusApi(osc, handler) + }, + mqtt: { + configure: (config) => configureMqtt(config), + publish: (payload) => publishMqtt(payload), + onStatus: (handler) => addStatusApi(mqttState, handler) + }, + serial: { + configure: (config) => configureSerial(config), + send: (payload) => sendSerial(payload), + listPorts: () => listSerialPorts(), + onStatus: (handler) => addStatusApi(serialState, handler) + }, + modbusTcp: { + configure: (config) => configureModbusTcp(config), + send: (payload) => sendModbusTcp(payload), + onStatus: (handler) => addStatusApi(modbusTcpState, handler) + }, + modbusRtu: { + configure: (config) => configureModbusRtu(config), + send: (payload) => sendModbusRtu(payload), + onStatus: (handler) => addStatusApi(modbusRtuState, handler) + }, + knx: { + configure: (config) => configureKnx(config), + send: (payload) => sendKnx(payload), + onStatus: (handler) => addStatusApi(knxState, handler) + } +}); diff --git a/src/app.js b/src/app.js new file mode 100644 index 0000000..8c0066c --- /dev/null +++ b/src/app.js @@ -0,0 +1,2376 @@ +(function () { + const TRACK_HEIGHT = 96; + const RULER_HEIGHT = 34; + const POINT_RADIUS = 7; + const FPS_DEFAULT = 30; + const LEFT_GUTTER = 126; + const RIGHT_PADDING = 24; + const MIN_CANVAS_HEIGHT = 420; + const EXTERNAL_RESYNC_MS = 1000; + const EXTERNAL_IDLE_MS = 1500; + + const refs = { + appShell: document.querySelector(".app-shell"), + inspectorPane: document.getElementById("inspectorPane"), + inspectorToggleButton: document.getElementById("inspectorToggleButton"), + projectNameInput: document.getElementById("projectNameInput"), + projectPathLabel: document.getElementById("projectPathLabel"), + newProjectButton: document.getElementById("newProjectButton"), + saveProjectButton: document.getElementById("saveProjectButton"), + loadProjectButton: document.getElementById("loadProjectButton"), + addTimelineButton: document.getElementById("addTimelineButton"), + timelineList: document.getElementById("timelineList"), + addMidiTrackButton: document.getElementById("addMidiTrackButton"), + addMidiCcTrackButton: document.getElementById("addMidiCcTrackButton"), + addArtNetTrackButton: document.getElementById("addArtNetTrackButton"), + addSacnTrackButton: document.getElementById("addSacnTrackButton"), + addSerialTrackButton: document.getElementById("addSerialTrackButton"), + addModbusTcpTrackButton: document.getElementById("addModbusTcpTrackButton"), + addModbusRtuTrackButton: document.getElementById("addModbusRtuTrackButton"), + addKnxTrackButton: document.getElementById("addKnxTrackButton"), + addOscTrackButton: document.getElementById("addOscTrackButton"), + addMqttTrackButton: document.getElementById("addMqttTrackButton"), + trackList: document.getElementById("trackList"), + syncSourceSelect: document.getElementById("syncSourceSelect"), + playButton: document.getElementById("playButton"), + pauseButton: document.getElementById("pauseButton"), + stopButton: document.getElementById("stopButton"), + timecodeLabel: document.getElementById("timecodeLabel"), + transportStatusLabel: document.getElementById("transportStatusLabel"), + timelineTitle: document.getElementById("timelineTitle"), + timelineCanvas: document.getElementById("timelineCanvas"), + midiInputSelect: document.getElementById("midiInputSelect"), + midiOutputSelect: document.getElementById("midiOutputSelect"), + artnetHostInput: document.getElementById("artnetHostInput"), + artnetEnabledInput: document.getElementById("artnetEnabledInput"), + artnetPortInput: document.getElementById("artnetPortInput"), + artnetUniverseInput: document.getElementById("artnetUniverseInput"), + artnetTimecodePortInput: document.getElementById("artnetTimecodePortInput"), + artnetBroadcastInput: document.getElementById("artnetBroadcastInput"), + sacnHostInput: document.getElementById("sacnHostInput"), + sacnEnabledInput: document.getElementById("sacnEnabledInput"), + sacnPortInput: document.getElementById("sacnPortInput"), + sacnSourceNameInput: document.getElementById("sacnSourceNameInput"), + sacnMulticastInput: document.getElementById("sacnMulticastInput"), + oscHostInput: document.getElementById("oscHostInput"), + oscEnabledInput: document.getElementById("oscEnabledInput"), + oscPortInput: document.getElementById("oscPortInput"), + mqttBrokerInput: document.getElementById("mqttBrokerInput"), + mqttEnabledInput: document.getElementById("mqttEnabledInput"), + mqttUsernameInput: document.getElementById("mqttUsernameInput"), + mqttPasswordInput: document.getElementById("mqttPasswordInput"), + mqttClientIdInput: document.getElementById("mqttClientIdInput"), + serialPathInput: document.getElementById("serialPathInput"), + serialBaudInput: document.getElementById("serialBaudInput"), + serialParitySelect: document.getElementById("serialParitySelect"), + serialModeSelect: document.getElementById("serialModeSelect"), + serialEnabledInput: document.getElementById("serialEnabledInput"), + modbusTcpHostInput: document.getElementById("modbusTcpHostInput"), + modbusTcpPortInput: document.getElementById("modbusTcpPortInput"), + modbusTcpEnabledInput: document.getElementById("modbusTcpEnabledInput"), + modbusRtuPathInput: document.getElementById("modbusRtuPathInput"), + modbusRtuBaudInput: document.getElementById("modbusRtuBaudInput"), + modbusRtuParitySelect: document.getElementById("modbusRtuParitySelect"), + modbusRtuEnabledInput: document.getElementById("modbusRtuEnabledInput"), + knxIpInput: document.getElementById("knxIpInput"), + knxPortInput: document.getElementById("knxPortInput"), + knxPhysAddrInput: document.getElementById("knxPhysAddrInput"), + knxTunnelingInput: document.getElementById("knxTunnelingInput"), + knxEnabledInput: document.getElementById("knxEnabledInput"), + timelineInspector: document.getElementById("timelineInspector"), + selectionInspector: document.getElementById("selectionInspector") + }; + + if (!window.time4midi) { + refs.transportStatusLabel.textContent = "Bridge nicht geladen. Bitte App neu starten."; + return; + } + + const ctx = refs.timelineCanvas.getContext("2d"); + const runtime = { + midiAccess: null, + midiInputs: [], + midiOutputs: [], + drag: null, + animationFrameId: 0, + mtcNibbles: Array(8).fill(0), + outputState: { + midiNotes: new Map(), + midiCcs: new Map(), + oscValues: new Map(), + mqttValues: new Map(), + serialValues: new Map(), + modbusTcpValues: new Map(), + modbusRtuValues: new Map(), + knxValues: new Map() + } + }; + + const state = { + filePath: "", + project: createDefaultProject(), + selectedTimelineId: "", + selectedTrackId: "", + selectedPointId: "", + transport: { + syncSource: "internal", + playing: false, + currentTime: 0, + fps: FPS_DEFAULT, + lastExternalSource: "internal", + playStartPerf: 0, + playStartTime: 0, + externalAnchorPerf: 0, + externalAnchorTime: 0, + lastExternalUpdateAt: 0, + lastExternalCorrectionAt: 0 + }, + routing: createDefaultRouting(), + ui: { + inspectorCollapsed: false + } + }; + + state.selectedTimelineId = state.project.timelines[0].id; + + function uid(prefix) { + return `${prefix}-${Math.random().toString(36).slice(2, 10)}`; + } + + function createDefaultRouting() { + return { + midiInputId: "", + midiOutputId: "", + artnetEnabled: true, + artnetHost: "255.255.255.255", + artnetPort: 6454, + artnetUniverse: 0, + artnetTimecodePort: 6454, + artnetBroadcast: true, + sacnEnabled: false, + sacnHost: "", + sacnPort: 5568, + sacnSourceName: "Time4Midi", + sacnMulticast: true, + oscEnabled: false, + oscHost: "127.0.0.1", + oscPort: 8000, + mqttEnabled: false, + mqttBrokerUrl: "mqtt://127.0.0.1:1883", + mqttUsername: "", + mqttPassword: "", + mqttClientId: "time4midi-desktop", + serialEnabled: false, + serialPath: "", + serialBaud: 9600, + serialParity: "none", + serialMode: "rs232", + modbusTcpEnabled: false, + modbusTcpHost: "127.0.0.1", + modbusTcpPort: 502, + modbusRtuEnabled: false, + modbusRtuPath: "", + modbusRtuBaud: 9600, + modbusRtuParity: "none", + knxEnabled: false, + knxIp: "224.0.23.12", + knxPort: 3671, + knxPhysAddr: "15.15.15", + knxTunneling: false + }; + } + + function createDefaultProject() { + return { + version: 6, + name: "Neues Projekt", + timelines: [ + { + id: uid("timeline"), + name: "Timeline A", + duration: 120, + tracks: [ + { + id: uid("track"), + name: "Kick", + type: "midi-note", + channel: 1, + note: 36, + interpolationDefault: "hold", + points: [ + { id: uid("point"), time: 2, value: 110, mode: "hold" }, + { id: uid("point"), time: 3, value: 0, mode: "hold" }, + { id: uid("point"), time: 8, value: 127, mode: "hold" }, + { id: uid("point"), time: 9, value: 0, mode: "hold" } + ] + }, + { + id: uid("track"), + name: "Filter CC", + type: "midi-cc", + channel: 1, + controller: 74, + interpolationDefault: "linear", + points: [ + { id: uid("point"), time: 0, value: 10, mode: "linear" }, + { id: uid("point"), time: 6, value: 100, mode: "linear" }, + { id: uid("point"), time: 12, value: 40, mode: "linear" } + ] + }, + { + id: uid("track"), + name: "Dimmer", + type: "artnet-channel", + universe: 0, + address: 1, + interpolationDefault: "linear", + points: [ + { id: uid("point"), time: 0, value: 0, mode: "linear" }, + { id: uid("point"), time: 10, value: 255, mode: "linear" }, + { id: uid("point"), time: 18, value: 60, mode: "linear" } + ] + }, + { + id: uid("track"), + name: "sACN Wash", + type: "sacn-channel", + universe: 1, + address: 1, + interpolationDefault: "linear", + points: [ + { id: uid("point"), time: 0, value: 0, mode: "linear" }, + { id: uid("point"), time: 4, value: 180, mode: "linear" }, + { id: uid("point"), time: 10, value: 30, mode: "linear" } + ] + } + ] + } + ] + }; + } + + function clamp(value, min, max) { + return Math.max(min, Math.min(max, value)); + } + + function roundToStep(value, step) { + if (!step || step <= 0) { + return value; + } + return Math.round(value / step) * step; + } + + function trackRange(track) { + if (track.type === "artnet-channel" || track.type === "sacn-channel") { + return { min: 0, max: 255, step: 1, integer: true }; + } + if (track.type === "midi-note" || track.type === "midi-cc") { + return { min: 0, max: 127, step: 1, integer: true }; + } + if (track.type === "modbus-tcp" || track.type === "modbus-rtu") { + if (track.registerType === "coil") { + return { min: 0, max: 1, step: 1, integer: true }; + } + return { min: 0, max: 65535, step: 1, integer: true }; + } + if (track.type === "knx-value") { + if (String(track.dpt || "").startsWith("DPT1")) { + return { min: 0, max: 1, step: 1, integer: true }; + } + if (String(track.dpt || "").startsWith("DPT5")) { + return { min: 0, max: 255, step: 1, integer: true }; + } + } + + const minValue = Number.isFinite(Number(track.minValue)) ? Number(track.minValue) : 0; + const maxValue = Number.isFinite(Number(track.maxValue)) ? Number(track.maxValue) : 127; + return { + min: Math.min(minValue, maxValue), + max: Math.max(minValue, maxValue), + step: 0.01, + integer: false + }; + } + + function quantizeTrackValue(track, value) { + const range = trackRange(track); + const clamped = clamp(Number(value) || 0, range.min, range.max); + if (range.integer) { + return Math.round(clamped); + } + return Number(roundToStep(clamped, range.step).toFixed(3)); + } + + function sortTrackPoints(track) { + track.points.sort((left, right) => left.time - right.time); + } + + function normalizeTrack(track) { + const normalized = { + ...track, + interpolationDefault: track.interpolationDefault || (track.type === "midi-note" ? "hold" : "linear"), + points: Array.isArray(track.points) ? track.points.map((point) => ({ + id: point.id || uid("point"), + time: Number(point.time) || 0, + value: Number(point.value) || 0, + mode: point.mode || track.interpolationDefault || "hold" + })) : [] + }; + + if (track.type === "midi-note") { + normalized.channel = clamp(Number(track.channel) || 1, 1, 16); + normalized.note = clamp(Number(track.note) || 60, 0, 127); + normalized.points = normalized.points.map((point) => ({ ...point, mode: "hold" })); + } else if (track.type === "midi-cc") { + normalized.channel = clamp(Number(track.channel) || 1, 1, 16); + normalized.controller = clamp(Number(track.controller) || 1, 0, 127); + } else if (track.type === "artnet-channel" || track.type === "sacn-channel") { + normalized.universe = clamp(Number(track.universe) || 0, 0, 32767); + normalized.address = clamp(Number(track.address) || 1, 1, 512); + } else if (track.type === "osc-value") { + normalized.address = String(track.address || "/time4midi/value"); + normalized.argType = track.argType || "float"; + normalized.minValue = Number.isFinite(Number(track.minValue)) ? Number(track.minValue) : 0; + normalized.maxValue = Number.isFinite(Number(track.maxValue)) ? Number(track.maxValue) : 127; + } else if (track.type === "mqtt-value") { + normalized.topic = String(track.topic || "time4midi/value"); + normalized.minValue = Number.isFinite(Number(track.minValue)) ? Number(track.minValue) : 0; + normalized.maxValue = Number.isFinite(Number(track.maxValue)) ? Number(track.maxValue) : 127; + } else if (track.type === "serial-value") { + normalized.template = String(track.template || "{value}\r\n"); + normalized.minValue = Number.isFinite(Number(track.minValue)) ? Number(track.minValue) : 0; + normalized.maxValue = Number.isFinite(Number(track.maxValue)) ? Number(track.maxValue) : 255; + } else if (track.type === "modbus-tcp" || track.type === "modbus-rtu") { + normalized.unitId = clamp(Number(track.unitId) || 1, 1, 247); + normalized.address = clamp(Number(track.address) || 0, 0, 65535); + normalized.registerType = track.registerType || "holding-register"; + } else if (track.type === "knx-value") { + normalized.groupAddress = String(track.groupAddress || "1/1/1"); + normalized.dpt = String(track.dpt || "DPT5.001"); + normalized.minValue = Number.isFinite(Number(track.minValue)) ? Number(track.minValue) : 0; + normalized.maxValue = Number.isFinite(Number(track.maxValue)) ? Number(track.maxValue) : 255; + } + + normalized.points = normalized.points.map((point) => ({ + ...point, + value: quantizeTrackValue(normalized, point.value) + })); + sortTrackPoints(normalized); + return normalized; + } + + function getSelectedTimeline() { + return state.project.timelines.find((timeline) => timeline.id === state.selectedTimelineId) || null; + } + + function getSelectedTrack() { + const timeline = getSelectedTimeline(); + if (!timeline) { + return null; + } + return timeline.tracks.find((track) => track.id === state.selectedTrackId) || null; + } + + function getSelectedPoint() { + const track = getSelectedTrack(); + if (!track) { + return null; + } + return track.points.find((point) => point.id === state.selectedPointId) || null; + } + + function ensureSelectionIntegrity() { + if (!state.project.timelines.some((timeline) => timeline.id === state.selectedTimelineId)) { + state.selectedTimelineId = state.project.timelines[0]?.id || ""; + } + + const timeline = getSelectedTimeline(); + if (!timeline) { + state.selectedTrackId = ""; + state.selectedPointId = ""; + return; + } + + if (!timeline.tracks.some((track) => track.id === state.selectedTrackId)) { + state.selectedTrackId = timeline.tracks[0]?.id || ""; + state.selectedPointId = ""; + } + + const track = getSelectedTrack(); + if (track && !track.points.some((point) => point.id === state.selectedPointId)) { + state.selectedPointId = ""; + } + } + + function getTrackKindLabel(track) { + if (track.type === "midi-note") return "NOTE"; + if (track.type === "midi-cc") return "CC"; + if (track.type === "artnet-channel") return "ART"; + if (track.type === "sacn-channel") return "sACN"; + if (track.type === "serial-value") return "SER"; + if (track.type === "modbus-tcp") return "MBT"; + if (track.type === "modbus-rtu") return "MBR"; + if (track.type === "knx-value") return "KNX"; + if (track.type === "osc-value") return "OSC"; + return "MQTT"; + } + + function getTrackColor(track) { + if (track.type === "midi-note") return "#61c8ff"; + if (track.type === "midi-cc") return "#8fd86f"; + if (track.type === "artnet-channel") return "#ffb35c"; + if (track.type === "sacn-channel") return "#9ca8ff"; + if (track.type === "serial-value") return "#ff9e6b"; + if (track.type === "modbus-tcp") return "#63e0d3"; + if (track.type === "modbus-rtu") return "#49b6ab"; + if (track.type === "knx-value") return "#f26f7d"; + if (track.type === "osc-value") return "#ff8fd1"; + return "#f7df68"; + } + + function describeTrack(track) { + if (track.type === "midi-note") return `Ch ${track.channel} / Note ${track.note}`; + if (track.type === "midi-cc") return `Ch ${track.channel} / CC ${track.controller}`; + if (track.type === "artnet-channel") return `Uni ${track.universe} / Addr ${track.address}`; + if (track.type === "sacn-channel") return `Uni ${track.universe} / Addr ${track.address}`; + if (track.type === "serial-value") return escapeHtml(track.template); + if (track.type === "modbus-tcp" || track.type === "modbus-rtu") return `ID ${track.unitId} / Addr ${track.address}`; + if (track.type === "knx-value") return `${escapeHtml(track.groupAddress)} / ${escapeHtml(track.dpt)}`; + if (track.type === "osc-value") return escapeHtml(track.address); + return escapeHtml(track.topic); + } + + function basenameFromPath(filePath) { + if (!filePath) { + return "Noch nicht gespeichert"; + } + const parts = String(filePath).split(/[\\/]/); + return parts[parts.length - 1] || filePath; + } + + function escapeHtml(value) { + return String(value) + .replaceAll("&", "&") + .replaceAll("<", "<") + .replaceAll(">", ">"); + } + + function escapeAttribute(value) { + return escapeHtml(value).replaceAll("\"", """); + } + + function chooseTimeStep(duration) { + if (duration <= 15) return 0.5; + if (duration <= 60) return 1; + if (duration <= 300) return 5; + if (duration <= 1800) return 15; + return 60; + } + + function ensureCanvasLayout() { + const timeline = getSelectedTimeline(); + const laneCount = timeline?.tracks.length || 1; + const desiredHeight = Math.max(MIN_CANVAS_HEIGHT, RULER_HEIGHT + (laneCount * TRACK_HEIGHT) + 8); + if (refs.timelineCanvas.style.height !== `${desiredHeight}px`) { + refs.timelineCanvas.style.height = `${desiredHeight}px`; + } + } + + function resizeCanvas() { + const rect = refs.timelineCanvas.getBoundingClientRect(); + const dpr = window.devicePixelRatio || 1; + const nextWidth = Math.max(1, Math.floor(rect.width * dpr)); + const nextHeight = Math.max(1, Math.floor(rect.height * dpr)); + if (refs.timelineCanvas.width !== nextWidth || refs.timelineCanvas.height !== nextHeight) { + refs.timelineCanvas.width = nextWidth; + refs.timelineCanvas.height = nextHeight; + } + ctx.setTransform(dpr, 0, 0, dpr, 0, 0); + } + + function getPlotMetrics() { + const width = refs.timelineCanvas.clientWidth; + const height = refs.timelineCanvas.clientHeight; + const plotWidth = Math.max(1, width - LEFT_GUTTER - RIGHT_PADDING); + return { + width, + height, + plotWidth, + left: LEFT_GUTTER, + right: width - RIGHT_PADDING + }; + } + + function timeToX(time, duration, metrics) { + const safeDuration = Math.max(1, duration); + return metrics.left + ((clamp(time, 0, safeDuration) / safeDuration) * metrics.plotWidth); + } + + function xToTime(x, duration, metrics) { + const safeDuration = Math.max(1, duration); + const relative = clamp(x - metrics.left, 0, metrics.plotWidth); + return (relative / metrics.plotWidth) * safeDuration; + } + + function valueToY(track, value, trackIndex) { + const range = trackRange(track); + const spread = Math.max(0.0001, range.max - range.min); + const norm = clamp((value - range.min) / spread, 0, 1); + const top = RULER_HEIGHT + (trackIndex * TRACK_HEIGHT) + 14; + const usableHeight = TRACK_HEIGHT - 28; + return top + ((1 - norm) * usableHeight); + } + + function yToValue(track, y, trackIndex) { + const range = trackRange(track); + const top = RULER_HEIGHT + (trackIndex * TRACK_HEIGHT) + 14; + const usableHeight = TRACK_HEIGHT - 28; + const norm = 1 - clamp((y - top) / usableHeight, 0, 1); + return quantizeTrackValue(track, range.min + ((range.max - range.min) * norm)); + } + + function formatTimecode(totalSeconds, fps = FPS_DEFAULT) { + const safe = Math.max(0, totalSeconds || 0); + const hours = Math.floor(safe / 3600); + const minutes = Math.floor((safe % 3600) / 60); + const seconds = Math.floor(safe % 60); + const frames = Math.floor((safe - Math.floor(safe)) * fps); + return [hours, minutes, seconds, frames] + .map((value) => String(value).padStart(2, "0")) + .join(":"); + } + + function drawEmptyCanvas(width, height) { + ctx.fillStyle = "#091018"; + ctx.fillRect(0, 0, width, height); + ctx.fillStyle = "rgba(255,255,255,0.72)"; + ctx.font = "16px Trebuchet MS"; + ctx.fillText("Keine Timeline vorhanden. Links eine Timeline anlegen.", 24, 40); + } + + function drawTrackLaneLabel(track, trackIndex) { + const top = RULER_HEIGHT + (trackIndex * TRACK_HEIGHT); + const color = getTrackColor(track); + + ctx.fillStyle = color; + ctx.beginPath(); + ctx.roundRect(18, top + 14, 44, 24, 8); + ctx.fill(); + + ctx.fillStyle = "#091018"; + ctx.font = "bold 11px Trebuchet MS"; + ctx.textAlign = "center"; + ctx.fillText(getTrackKindLabel(track), 40, top + 30); + + ctx.save(); + ctx.translate(92, top + (TRACK_HEIGHT / 2)); + ctx.rotate(-Math.PI / 2); + ctx.fillStyle = "rgba(255,255,255,0.86)"; + ctx.font = "bold 13px Trebuchet MS"; + ctx.textAlign = "center"; + ctx.fillText(track.name, 0, 0); + ctx.restore(); + + if (track.id === state.selectedTrackId) { + ctx.strokeStyle = "rgba(110,242,198,0.7)"; + ctx.lineWidth = 2; + ctx.strokeRect(4, top + 4, LEFT_GUTTER - 8, TRACK_HEIGHT - 8); + ctx.lineWidth = 1; + } + ctx.textAlign = "left"; + } + + function drawGrid(duration, tracks, metrics) { + ctx.fillStyle = "#0b111a"; + ctx.fillRect(0, 0, metrics.width, metrics.height); + ctx.fillStyle = "rgba(255,255,255,0.03)"; + ctx.fillRect(0, 0, LEFT_GUTTER, metrics.height); + + const step = chooseTimeStep(duration); + for (let time = 0; time <= duration + 0.0001; time += step) { + const x = timeToX(time, duration, metrics); + ctx.strokeStyle = time % (step * 4) === 0 ? "rgba(255,255,255,0.18)" : "rgba(255,255,255,0.08)"; + ctx.beginPath(); + ctx.moveTo(x, 0); + ctx.lineTo(x, metrics.height); + ctx.stroke(); + + ctx.fillStyle = "rgba(255,255,255,0.65)"; + ctx.font = "12px Trebuchet MS"; + ctx.fillText(`${time.toFixed(time < 10 ? 1 : 0)}s`, x + 6, 20); + } + + ctx.strokeStyle = "rgba(255,255,255,0.08)"; + ctx.beginPath(); + ctx.moveTo(LEFT_GUTTER, 0); + ctx.lineTo(LEFT_GUTTER, metrics.height); + ctx.stroke(); + + for (let index = 0; index < tracks.length; index += 1) { + const top = RULER_HEIGHT + (index * TRACK_HEIGHT); + ctx.fillStyle = index % 2 === 0 ? "rgba(255,255,255,0.015)" : "rgba(255,255,255,0.035)"; + ctx.fillRect(LEFT_GUTTER, top, metrics.width - LEFT_GUTTER, TRACK_HEIGHT); + ctx.strokeStyle = "rgba(255,255,255,0.08)"; + ctx.beginPath(); + ctx.moveTo(0, top); + ctx.lineTo(metrics.width, top); + ctx.stroke(); + drawTrackLaneLabel(tracks[index], index); + } + } + + function drawTracks(duration, tracks, metrics) { + tracks.forEach((track, trackIndex) => { + sortTrackPoints(track); + const color = getTrackColor(track); + ctx.strokeStyle = color; + ctx.lineWidth = 2; + ctx.beginPath(); + + track.points.forEach((point, pointIndex) => { + const x = timeToX(point.time, duration, metrics); + const y = valueToY(track, point.value, trackIndex); + if (pointIndex === 0) { + ctx.moveTo(x, y); + } else { + const previous = track.points[pointIndex - 1]; + const prevY = valueToY(track, previous.value, trackIndex); + if (track.type !== "midi-note" && previous.mode === "linear") { + ctx.lineTo(x, y); + } else { + ctx.lineTo(x, prevY); + ctx.lineTo(x, y); + } + } + }); + ctx.stroke(); + + track.points.forEach((point) => { + const x = timeToX(point.time, duration, metrics); + const y = valueToY(track, point.value, trackIndex); + const isSelected = point.id === state.selectedPointId && track.id === state.selectedTrackId; + ctx.fillStyle = isSelected ? "#ffffff" : color; + ctx.beginPath(); + ctx.arc(x, y, isSelected ? POINT_RADIUS + 2 : POINT_RADIUS, 0, Math.PI * 2); + ctx.fill(); + }); + }); + } + + function drawPlayhead(duration, metrics) { + const x = timeToX(state.transport.currentTime, duration, metrics); + ctx.strokeStyle = "#6ef2c6"; + ctx.lineWidth = 2; + ctx.beginPath(); + ctx.moveTo(x, 0); + ctx.lineTo(x, metrics.height); + ctx.stroke(); + ctx.lineWidth = 1; + } + + function drawCanvas() { + ensureCanvasLayout(); + resizeCanvas(); + const timeline = getSelectedTimeline(); + const metrics = getPlotMetrics(); + ctx.clearRect(0, 0, metrics.width, metrics.height); + + if (!timeline) { + drawEmptyCanvas(metrics.width, metrics.height); + return; + } + + const duration = Math.max(1, timeline.duration); + drawGrid(duration, timeline.tracks, metrics); + drawTracks(duration, timeline.tracks, metrics); + drawPlayhead(duration, metrics); + } + + function renderTimelineList() { + refs.timelineList.innerHTML = ""; + for (const timeline of state.project.timelines) { + const card = document.createElement("div"); + card.className = `timeline-card selectable-card ${timeline.id === state.selectedTimelineId ? "active" : ""}`; + card.dataset.timelineId = timeline.id; + card.innerHTML = ` + ${escapeHtml(timeline.name)} +
+ ${timeline.tracks.length} Tracks + ${timeline.duration}s +
+ `; + refs.timelineList.appendChild(card); + } + } + + function pillClassForTrack(track) { + if (track.type === "artnet-channel") return "artnet"; + if (track.type === "sacn-channel") return "sacn"; + if (track.type === "midi-cc") return "midicc"; + if (track.type === "serial-value") return "serial"; + if (track.type === "modbus-tcp" || track.type === "modbus-rtu") return "modbus"; + if (track.type === "knx-value") return "knx"; + if (track.type === "osc-value") return "osc"; + if (track.type === "mqtt-value") return "mqtt"; + return "midi"; + } + + function renderTrackList() { + const timeline = getSelectedTimeline(); + refs.trackList.innerHTML = ""; + if (!timeline) { + refs.trackList.innerHTML = `
Lege zuerst eine Timeline an.
`; + return; + } + + for (const track of timeline.tracks) { + const card = document.createElement("div"); + card.className = `track-card ${track.id === state.selectedTrackId ? "active" : ""}`; + card.innerHTML = ` +
+
+
+ ${escapeHtml(track.name)} + ${getTrackKindLabel(track)} +
+
+ ${describeTrack(track)} + ${track.points.length} Punkte +
+
+
+ + + +
+
+ `; + refs.trackList.appendChild(card); + } + } + + function renderTimelineInspector() { + const timeline = getSelectedTimeline(); + refs.timelineTitle.textContent = timeline ? timeline.name : "Keine Timeline ausgewaehlt"; + if (!timeline) { + refs.timelineInspector.innerHTML = `
Keine Timeline ausgewaehlt.
`; + return; + } + + refs.timelineInspector.innerHTML = ` +
+ + +
+ `; + } + + function pointModeOptions(track, point) { + const linearAllowed = track.type !== "midi-note"; + return ` + + ${linearAllowed ? `` : ""} + `; + } + + function renderSelectionInspector() { + const track = getSelectedTrack(); + const point = getSelectedPoint(); + if (!track) { + refs.selectionInspector.innerHTML = `
Waehle einen Track oder Punkt aus.
`; + return; + } + + const range = trackRange(track); + let typeFields = ""; + + if (track.type === "midi-note") { + typeFields = ` +
+ + +
+ `; + } else if (track.type === "midi-cc") { + typeFields = ` +
+ + +
+ `; + } else if (track.type === "artnet-channel" || track.type === "sacn-channel") { + typeFields = ` +
+ + +
+ `; + } else if (track.type === "serial-value") { + typeFields = ` + +
+ + +
+ `; + } else if (track.type === "modbus-tcp" || track.type === "modbus-rtu") { + typeFields = ` +
+ + +
+ + `; + } else if (track.type === "knx-value") { + typeFields = ` + +
+ + +
+
+ + +
+ `; + } else if (track.type === "osc-value") { + typeFields = ` + +
+ + +
+ + `; + } else if (track.type === "mqtt-value") { + typeFields = ` + +
+ + +
+ `; + } + + const interpolationOptions = track.type === "midi-note" + ? `` + : ` + + + `; + + let pointFields = `
Waehle einen Punkt im Editor, um Zeit, Wert und Modus zu aendern.
`; + if (point) { + pointFields = ` +
Punkt
+
+ + +
+ + `; + } + + refs.selectionInspector.innerHTML = ` +
Track
+ + + ${typeFields} + ${pointFields} + `; + } + + function renderPortSelect(select, items, selectedId, emptyLabel) { + select.innerHTML = ""; + const emptyOption = document.createElement("option"); + emptyOption.value = ""; + emptyOption.textContent = emptyLabel; + select.appendChild(emptyOption); + items.forEach((entry) => { + const option = document.createElement("option"); + option.value = entry.id; + option.textContent = entry.name; + if (entry.id === selectedId) { + option.selected = true; + } + select.appendChild(option); + }); + } + + function renderMidiSelectors() { + renderPortSelect(refs.midiInputSelect, runtime.midiInputs, state.routing.midiInputId, "Kein MIDI Input"); + renderPortSelect(refs.midiOutputSelect, runtime.midiOutputs, state.routing.midiOutputId, "Kein MIDI Output"); + } + + function renderTransportState() { + refs.syncSourceSelect.value = state.transport.syncSource; + refs.timecodeLabel.textContent = formatTimecode(state.transport.currentTime, state.transport.fps); + refs.playButton.textContent = state.transport.playing ? "Resume" : "Play"; + if (state.transport.playing) { + refs.transportStatusLabel.textContent = `Playing (${state.transport.syncSource})`; + } else { + refs.transportStatusLabel.textContent = `Paused/Stopped (${state.transport.lastExternalSource})`; + } + } + + function renderAll() { + ensureSelectionIntegrity(); + refs.appShell.classList.toggle("inspector-collapsed", state.ui.inspectorCollapsed); + refs.inspectorToggleButton.textContent = state.ui.inspectorCollapsed ? "<" : ">"; + refs.inspectorToggleButton.title = state.ui.inspectorCollapsed + ? "Rechte Leiste ausklappen" + : "Rechte Leiste einklappen"; + refs.projectNameInput.value = state.project.name; + refs.projectPathLabel.textContent = basenameFromPath(state.filePath); + refs.projectPathLabel.title = state.filePath || ""; + refs.artnetEnabledInput.checked = Boolean(state.routing.artnetEnabled); + refs.artnetHostInput.value = state.routing.artnetHost; + refs.artnetPortInput.value = state.routing.artnetPort; + refs.artnetUniverseInput.value = state.routing.artnetUniverse; + refs.artnetTimecodePortInput.value = state.routing.artnetTimecodePort; + refs.artnetBroadcastInput.checked = state.routing.artnetBroadcast; + refs.sacnEnabledInput.checked = Boolean(state.routing.sacnEnabled); + refs.sacnHostInput.value = state.routing.sacnHost; + refs.sacnPortInput.value = state.routing.sacnPort; + refs.sacnSourceNameInput.value = state.routing.sacnSourceName; + refs.sacnMulticastInput.checked = state.routing.sacnMulticast; + refs.oscEnabledInput.checked = Boolean(state.routing.oscEnabled); + refs.oscHostInput.value = state.routing.oscHost; + refs.oscPortInput.value = state.routing.oscPort; + refs.mqttEnabledInput.checked = Boolean(state.routing.mqttEnabled); + refs.mqttBrokerInput.value = state.routing.mqttBrokerUrl; + refs.mqttUsernameInput.value = state.routing.mqttUsername; + refs.mqttPasswordInput.value = state.routing.mqttPassword; + refs.mqttClientIdInput.value = state.routing.mqttClientId; + refs.serialEnabledInput.checked = Boolean(state.routing.serialEnabled); + refs.serialPathInput.value = state.routing.serialPath; + refs.serialBaudInput.value = state.routing.serialBaud; + refs.serialParitySelect.value = state.routing.serialParity; + refs.serialModeSelect.value = state.routing.serialMode; + refs.modbusTcpEnabledInput.checked = Boolean(state.routing.modbusTcpEnabled); + refs.modbusTcpHostInput.value = state.routing.modbusTcpHost; + refs.modbusTcpPortInput.value = state.routing.modbusTcpPort; + refs.modbusRtuEnabledInput.checked = Boolean(state.routing.modbusRtuEnabled); + refs.modbusRtuPathInput.value = state.routing.modbusRtuPath; + refs.modbusRtuBaudInput.value = state.routing.modbusRtuBaud; + refs.modbusRtuParitySelect.value = state.routing.modbusRtuParity; + refs.knxEnabledInput.checked = Boolean(state.routing.knxEnabled); + refs.knxIpInput.value = state.routing.knxIp; + refs.knxPortInput.value = state.routing.knxPort; + refs.knxPhysAddrInput.value = state.routing.knxPhysAddr; + refs.knxTunnelingInput.checked = Boolean(state.routing.knxTunneling); + renderTransportState(); + renderTimelineList(); + renderTrackList(); + renderTimelineInspector(); + renderSelectionInspector(); + renderMidiSelectors(); + drawCanvas(); + } + + function configureArtNetTransport() { + window.time4midi.artnet.configure({ + enabled: state.routing.artnetEnabled, + host: state.routing.artnetHost, + port: state.routing.artnetPort, + broadcast: state.routing.artnetBroadcast, + timecodeListenPort: state.routing.artnetTimecodePort + }); + } + + function configureOscTransport() { + window.time4midi.osc.configure({ + enabled: state.routing.oscEnabled, + host: state.routing.oscHost, + port: state.routing.oscPort + }); + } + + function configureSacnTransport() { + window.time4midi.sacn.configure({ + enabled: state.routing.sacnEnabled, + host: state.routing.sacnHost, + port: state.routing.sacnPort, + sourceName: state.routing.sacnSourceName, + multicast: state.routing.sacnMulticast + }); + } + + function configureMqttTransport() { + window.time4midi.mqtt.configure({ + enabled: state.routing.mqttEnabled, + brokerUrl: state.routing.mqttBrokerUrl, + username: state.routing.mqttUsername, + password: state.routing.mqttPassword, + clientId: state.routing.mqttClientId + }); + } + + async function configureSerialTransport() { + try { + await window.time4midi.serial.configure({ + enabled: state.routing.serialEnabled, + path: state.routing.serialPath, + baudRate: state.routing.serialBaud, + parity: state.routing.serialParity, + mode: state.routing.serialMode + }); + } catch (error) { + refs.transportStatusLabel.textContent = `Serial Fehler: ${error.message}`; + } + } + + async function configureModbusTcpTransport() { + try { + await window.time4midi.modbusTcp.configure({ + enabled: state.routing.modbusTcpEnabled, + host: state.routing.modbusTcpHost, + port: state.routing.modbusTcpPort + }); + } catch (error) { + refs.transportStatusLabel.textContent = `Modbus TCP Fehler: ${error.message}`; + } + } + + async function configureModbusRtuTransport() { + try { + await window.time4midi.modbusRtu.configure({ + enabled: state.routing.modbusRtuEnabled, + path: state.routing.modbusRtuPath, + baudRate: state.routing.modbusRtuBaud, + parity: state.routing.modbusRtuParity + }); + } catch (error) { + refs.transportStatusLabel.textContent = `Modbus RTU Fehler: ${error.message}`; + } + } + + async function configureKnxTransport() { + try { + await window.time4midi.knx.configure({ + enabled: state.routing.knxEnabled, + ipAddr: state.routing.knxIp, + ipPort: state.routing.knxPort, + physAddr: state.routing.knxPhysAddr, + forceTunneling: state.routing.knxTunneling + }); + } catch (error) { + refs.transportStatusLabel.textContent = `KNX Fehler: ${error.message}`; + } + } + + function addTimeline() { + const timeline = { + id: uid("timeline"), + name: `Timeline ${String.fromCharCode(65 + state.project.timelines.length)}`, + duration: 120, + tracks: [] + }; + state.project.timelines.push(timeline); + state.selectedTimelineId = timeline.id; + state.selectedTrackId = ""; + state.selectedPointId = ""; + renderAll(); + } + + function addTrack(type) { + const timeline = getSelectedTimeline(); + if (!timeline) { + return; + } + + let track; + if (type === "midi-note") { + track = { + id: uid("track"), + name: `MIDI Note ${timeline.tracks.length + 1}`, + type, + channel: 1, + note: 60, + interpolationDefault: "hold", + points: [] + }; + } else if (type === "midi-cc") { + track = { + id: uid("track"), + name: `MIDI CC ${timeline.tracks.length + 1}`, + type, + channel: 1, + controller: 1, + interpolationDefault: "linear", + points: [] + }; + } else if (type === "artnet-channel") { + track = { + id: uid("track"), + name: `Art-Net ${timeline.tracks.length + 1}`, + type, + universe: state.routing.artnetUniverse, + address: clamp(timeline.tracks.length + 1, 1, 512), + interpolationDefault: "linear", + points: [] + }; + } else if (type === "sacn-channel") { + track = { + id: uid("track"), + name: `sACN ${timeline.tracks.length + 1}`, + type, + universe: 1, + address: clamp(timeline.tracks.length + 1, 1, 512), + interpolationDefault: "linear", + points: [] + }; + } else if (type === "serial-value") { + track = { + id: uid("track"), + name: `Serial ${timeline.tracks.length + 1}`, + type, + template: "{value}\\r\\n", + minValue: 0, + maxValue: 255, + interpolationDefault: "linear", + points: [] + }; + } else if (type === "modbus-tcp") { + track = { + id: uid("track"), + name: `Modbus TCP ${timeline.tracks.length + 1}`, + type, + unitId: 1, + address: 0, + registerType: "holding-register", + interpolationDefault: "linear", + points: [] + }; + } else if (type === "modbus-rtu") { + track = { + id: uid("track"), + name: `Modbus RTU ${timeline.tracks.length + 1}`, + type, + unitId: 1, + address: 0, + registerType: "holding-register", + interpolationDefault: "linear", + points: [] + }; + } else if (type === "knx-value") { + track = { + id: uid("track"), + name: `KNX ${timeline.tracks.length + 1}`, + type, + groupAddress: `1/1/${timeline.tracks.length + 1}`, + dpt: "DPT5.001", + minValue: 0, + maxValue: 255, + interpolationDefault: "linear", + points: [] + }; + } else if (type === "osc-value") { + track = { + id: uid("track"), + name: `OSC ${timeline.tracks.length + 1}`, + type, + address: `/time4midi/osc/${timeline.tracks.length + 1}`, + argType: "float", + minValue: 0, + maxValue: 127, + interpolationDefault: "linear", + points: [] + }; + } else { + track = { + id: uid("track"), + name: `MQTT ${timeline.tracks.length + 1}`, + type, + topic: `time4midi/value/${timeline.tracks.length + 1}`, + minValue: 0, + maxValue: 127, + interpolationDefault: "linear", + points: [] + }; + } + + timeline.tracks.push(track); + state.selectedTrackId = track.id; + state.selectedPointId = ""; + renderAll(); + } + + function moveTrack(trackId, direction) { + const timeline = getSelectedTimeline(); + if (!timeline) return; + const index = timeline.tracks.findIndex((track) => track.id === trackId); + if (index < 0) return; + const nextIndex = clamp(index + direction, 0, timeline.tracks.length - 1); + if (nextIndex === index) return; + const [track] = timeline.tracks.splice(index, 1); + timeline.tracks.splice(nextIndex, 0, track); + renderAll(); + } + + function clearOutputCaches(sendNoteOffs) { + if (sendNoteOffs) { + const timeline = getSelectedTimeline(); + if (timeline) { + for (const track of timeline.tracks) { + if (track.type !== "midi-note") continue; + const key = `${track.channel}:${track.note}`; + if (runtime.outputState.midiNotes.get(key)?.active) { + sendMidiNoteOff(track); + } + } + } + } + runtime.outputState.midiNotes.clear(); + runtime.outputState.midiCcs.clear(); + runtime.outputState.oscValues.clear(); + runtime.outputState.mqttValues.clear(); + runtime.outputState.serialValues.clear(); + runtime.outputState.modbusTcpValues.clear(); + runtime.outputState.modbusRtuValues.clear(); + runtime.outputState.knxValues.clear(); + } + + function deleteTrack(trackId) { + const timeline = getSelectedTimeline(); + if (!timeline) return; + timeline.tracks = timeline.tracks.filter((track) => track.id !== trackId); + if (state.selectedTrackId === trackId) { + state.selectedTrackId = timeline.tracks[0]?.id || ""; + state.selectedPointId = ""; + } + clearOutputCaches(true); + renderAll(); + } + + function evaluateTrackAt(track, time) { + if (!track.points.length) { + return trackRange(track).min; + } + + sortTrackPoints(track); + if (time <= track.points[0].time) { + return track.points[0].value; + } + + for (let index = 0; index < track.points.length - 1; index += 1) { + const current = track.points[index]; + const next = track.points[index + 1]; + if (time >= current.time && time <= next.time) { + if (track.type !== "midi-note" && current.mode === "linear" && next.time > current.time) { + const blend = (time - current.time) / (next.time - current.time); + return quantizeTrackValue(track, current.value + ((next.value - current.value) * blend)); + } + return current.value; + } + } + + return track.points[track.points.length - 1].value; + } + + function sendMidiMessage(bytes) { + const output = runtime.midiOutputs.find((entry) => entry.id === state.routing.midiOutputId); + if (!output) { + return; + } + output.send(bytes); + } + + function sendMidiNoteOn(track, velocity) { + const channel = clamp(track.channel - 1, 0, 15); + sendMidiMessage([0x90 + channel, clamp(track.note, 0, 127), clamp(Math.round(velocity), 1, 127)]); + } + + function sendMidiNoteOff(track) { + const channel = clamp(track.channel - 1, 0, 15); + sendMidiMessage([0x80 + channel, clamp(track.note, 0, 127), 0]); + } + + function sendMidiCc(track, value) { + const channel = clamp(track.channel - 1, 0, 15); + sendMidiMessage([0xb0 + channel, clamp(track.controller, 0, 127), clamp(Math.round(value), 0, 127)]); + } + + function decodeEscapedText(value) { + return String(value || "") + .replaceAll("\\r", "\r") + .replaceAll("\\n", "\n") + .replaceAll("\\t", "\t"); + } + + function formatSerialPayload(track, value) { + const template = String(track.template || "{value}\\r\\n"); + return decodeEscapedText( + template + .replaceAll("{value}", String(value)) + .replaceAll("{track}", String(track.name || "")) + .replaceAll("{time}", state.transport.currentTime.toFixed(3)) + ); + } + + async function applyOutputsAtCurrentTime() { + const timeline = getSelectedTimeline(); + if (!timeline) return; + + const artnetFrameBuckets = new Map(); + const sacnFrameBuckets = new Map(); + for (const track of timeline.tracks) { + const value = quantizeTrackValue(track, evaluateTrackAt(track, state.transport.currentTime)); + + if (track.type === "midi-note") { + const key = `${track.channel}:${track.note}`; + const previous = runtime.outputState.midiNotes.get(key) || { active: false, value: 0 }; + const nextActive = value > 0; + if (!previous.active && nextActive) { + sendMidiNoteOn(track, value); + } else if (previous.active && !nextActive) { + sendMidiNoteOff(track); + } else if (previous.active && nextActive && previous.value !== value) { + sendMidiNoteOn(track, value); + } + runtime.outputState.midiNotes.set(key, { active: nextActive, value }); + } else if (track.type === "midi-cc") { + const key = `${track.channel}:${track.controller}`; + const previousValue = runtime.outputState.midiCcs.get(key); + if (previousValue !== value) { + sendMidiCc(track, value); + runtime.outputState.midiCcs.set(key, value); + } + } else if (track.type === "artnet-channel") { + if (!state.routing.artnetEnabled) { + continue; + } + const universe = Number.isFinite(Number(track.universe)) ? Number(track.universe) : state.routing.artnetUniverse; + const address = clamp((Number(track.address) || 1) - 1, 0, 511); + if (!artnetFrameBuckets.has(universe)) { + artnetFrameBuckets.set(universe, Array(512).fill(0)); + } + artnetFrameBuckets.get(universe)[address] = Math.round(value); + } else if (track.type === "sacn-channel") { + if (!state.routing.sacnEnabled) { + continue; + } + const universe = clamp(Number(track.universe) || 1, 1, 63999); + const address = clamp((Number(track.address) || 1) - 1, 0, 511); + if (!sacnFrameBuckets.has(universe)) { + sacnFrameBuckets.set(universe, Array(512).fill(0)); + } + sacnFrameBuckets.get(universe)[address] = Math.round(value); + } else if (track.type === "osc-value") { + if (!state.routing.oscEnabled) { + continue; + } + const key = `${track.address}:${track.argType}`; + const previousValue = runtime.outputState.oscValues.get(key); + if (previousValue !== value) { + try { + await window.time4midi.osc.send({ + address: track.address, + value, + argType: track.argType + }); + runtime.outputState.oscValues.set(key, value); + } catch (error) { + refs.transportStatusLabel.textContent = `OSC Fehler: ${error.message}`; + } + } + } else if (track.type === "mqtt-value") { + if (!state.routing.mqttEnabled) { + continue; + } + const key = track.topic; + const previousValue = runtime.outputState.mqttValues.get(key); + if (previousValue !== value) { + try { + await window.time4midi.mqtt.publish({ + topic: track.topic, + value + }); + runtime.outputState.mqttValues.set(key, value); + } catch (error) { + refs.transportStatusLabel.textContent = `MQTT Fehler: ${error.message}`; + } + } + } else if (track.type === "serial-value") { + if (!state.routing.serialEnabled) { + continue; + } + const key = track.id; + const previousValue = runtime.outputState.serialValues.get(key); + if (previousValue !== value) { + try { + await window.time4midi.serial.send({ + text: formatSerialPayload(track, value) + }); + runtime.outputState.serialValues.set(key, value); + } catch (error) { + refs.transportStatusLabel.textContent = `Serial Fehler: ${error.message}`; + } + } + } else if (track.type === "modbus-tcp") { + if (!state.routing.modbusTcpEnabled) { + continue; + } + const key = track.id; + const previousValue = runtime.outputState.modbusTcpValues.get(key); + if (previousValue !== value) { + try { + await window.time4midi.modbusTcp.send({ + unitId: track.unitId, + address: track.address, + registerType: track.registerType, + value + }); + runtime.outputState.modbusTcpValues.set(key, value); + } catch (error) { + refs.transportStatusLabel.textContent = `Modbus TCP Fehler: ${error.message}`; + } + } + } else if (track.type === "modbus-rtu") { + if (!state.routing.modbusRtuEnabled) { + continue; + } + const key = track.id; + const previousValue = runtime.outputState.modbusRtuValues.get(key); + if (previousValue !== value) { + try { + await window.time4midi.modbusRtu.send({ + unitId: track.unitId, + address: track.address, + registerType: track.registerType, + value + }); + runtime.outputState.modbusRtuValues.set(key, value); + } catch (error) { + refs.transportStatusLabel.textContent = `Modbus RTU Fehler: ${error.message}`; + } + } + } else if (track.type === "knx-value") { + if (!state.routing.knxEnabled) { + continue; + } + const key = `${track.groupAddress}:${track.dpt}`; + const previousValue = runtime.outputState.knxValues.get(key); + if (previousValue !== value) { + try { + await window.time4midi.knx.send({ + groupAddress: track.groupAddress, + dpt: track.dpt, + value + }); + runtime.outputState.knxValues.set(key, value); + } catch (error) { + refs.transportStatusLabel.textContent = `KNX Fehler: ${error.message}`; + } + } + } + } + + for (const [universe, values] of artnetFrameBuckets.entries()) { + try { + await window.time4midi.artnet.sendDmx({ universe, values }); + } catch (error) { + refs.transportStatusLabel.textContent = `Art-Net Fehler: ${error.message}`; + } + } + + for (const [universe, values] of sacnFrameBuckets.entries()) { + try { + await window.time4midi.sacn.sendDmx({ universe, values }); + } catch (error) { + refs.transportStatusLabel.textContent = `sACN Fehler: ${error.message}`; + } + } + } + + function updateCurrentTime(seconds, source, fps) { + const timeline = getSelectedTimeline(); + const duration = timeline?.duration || seconds || 0; + state.transport.currentTime = clamp(seconds, 0, duration); + state.transport.lastExternalSource = source; + if (fps) { + state.transport.fps = fps; + } + renderTransportState(); + applyOutputsAtCurrentTime(); + drawCanvas(); + } + + function pauseTransport() { + state.transport.playing = false; + if (runtime.animationFrameId) { + cancelAnimationFrame(runtime.animationFrameId); + runtime.animationFrameId = 0; + } + renderTransportState(); + drawCanvas(); + } + + function stopTransport(resetTime) { + pauseTransport(); + clearOutputCaches(true); + if (resetTime) { + state.transport.currentTime = 0; + } + renderTransportState(); + drawCanvas(); + } + + function ensureTransportLoop() { + if (!runtime.animationFrameId) { + runtime.animationFrameId = requestAnimationFrame(tickTransport); + } + } + + function startInternalTransport() { + state.transport.playing = true; + state.transport.playStartPerf = performance.now(); + state.transport.playStartTime = state.transport.currentTime; + renderTransportState(); + ensureTransportLoop(); + } + + function startExternalTransport(anchorTime, source, fps, forceZero) { + const now = performance.now(); + const timeline = getSelectedTimeline(); + const duration = timeline?.duration || anchorTime || 0; + const nextAnchor = forceZero ? 0 : clamp(anchorTime, 0, duration || anchorTime); + state.transport.playing = true; + state.transport.lastExternalSource = source; + state.transport.fps = fps || state.transport.fps || FPS_DEFAULT; + state.transport.externalAnchorTime = nextAnchor; + state.transport.externalAnchorPerf = now; + state.transport.lastExternalUpdateAt = now; + state.transport.lastExternalCorrectionAt = now; + state.transport.currentTime = nextAnchor; + renderTransportState(); + drawCanvas(); + ensureTransportLoop(); + } + + function playTransport() { + if (state.transport.syncSource !== "internal") { + return; + } + startInternalTransport(); + } + + function tickTransport() { + runtime.animationFrameId = 0; + const timeline = getSelectedTimeline(); + if (!timeline || !state.transport.playing) { + return; + } + + const now = performance.now(); + if (state.transport.syncSource === "internal") { + const elapsed = (now - state.transport.playStartPerf) / 1000; + const nextTime = state.transport.playStartTime + elapsed; + if (nextTime >= timeline.duration) { + updateCurrentTime(timeline.duration, "internal", FPS_DEFAULT); + pauseTransport(); + return; + } + updateCurrentTime(nextTime, "internal", FPS_DEFAULT); + ensureTransportLoop(); + return; + } + + if ((now - state.transport.lastExternalUpdateAt) > EXTERNAL_IDLE_MS) { + pauseTransport(); + return; + } + + const nextTime = state.transport.externalAnchorTime + ((now - state.transport.externalAnchorPerf) / 1000); + updateCurrentTime(nextTime, state.transport.syncSource, state.transport.fps); + ensureTransportLoop(); + } + + function applyExternalTimecode(totalSeconds, source, fps) { + const now = performance.now(); + const predicted = state.transport.playing + ? state.transport.externalAnchorTime + ((now - state.transport.externalAnchorPerf) / 1000) + : totalSeconds; + const drift = Math.abs(predicted - totalSeconds); + const shouldResync = !state.transport.playing + || (now - state.transport.lastExternalCorrectionAt) >= EXTERNAL_RESYNC_MS + || drift > 0.2; + + state.transport.lastExternalUpdateAt = now; + state.transport.lastExternalSource = source; + state.transport.fps = fps || state.transport.fps || FPS_DEFAULT; + + if (shouldResync) { + state.transport.externalAnchorTime = totalSeconds; + state.transport.externalAnchorPerf = now; + state.transport.lastExternalCorrectionAt = now; + state.transport.currentTime = totalSeconds; + renderTransportState(); + drawCanvas(); + } + + state.transport.playing = true; + ensureTransportLoop(); + } + + function handleQuarterFrame(dataByte) { + const piece = (dataByte >> 4) & 0x07; + const nibble = dataByte & 0x0f; + runtime.mtcNibbles[piece] = nibble; + + const frames = runtime.mtcNibbles[0] | ((runtime.mtcNibbles[1] & 0x01) << 4); + const seconds = runtime.mtcNibbles[2] | ((runtime.mtcNibbles[3] & 0x03) << 4); + const minutes = runtime.mtcNibbles[4] | ((runtime.mtcNibbles[5] & 0x03) << 4); + const hours = runtime.mtcNibbles[6] | ((runtime.mtcNibbles[7] & 0x01) << 4); + const frameRateCode = (runtime.mtcNibbles[7] >> 1) & 0x03; + const fpsMap = [24, 25, 29.97, 30]; + const fps = fpsMap[frameRateCode] || FPS_DEFAULT; + const totalSeconds = (hours * 3600) + (minutes * 60) + seconds + (frames / fps); + + if (state.transport.syncSource === "mtc") { + applyExternalTimecode(totalSeconds, "mtc", fps); + } + } + + function handleMidiRealtime(status) { + if (state.transport.syncSource !== "mtc") { + return; + } + if (status === 0xfa) { + startExternalTransport(0, "mtc", state.transport.fps, true); + } else if (status === 0xfb) { + startExternalTransport(state.transport.currentTime, "mtc", state.transport.fps, false); + } else if (status === 0xfc) { + pauseTransport(); + } + } + + function handleMidiMessage(event) { + const data = Array.from(event.data || []); + if (data.length === 0) return; + if (data[0] === 0xf1 && data.length > 1) { + handleQuarterFrame(data[1]); + return; + } + if (data[0] >= 0xfa && data[0] <= 0xfc) { + handleMidiRealtime(data[0]); + } + } + + async function setupMidi() { + if (!navigator.requestMIDIAccess) { + refs.transportStatusLabel.textContent = "Web MIDI auf diesem System nicht verfuegbar"; + return; + } + + try { + runtime.midiAccess = await navigator.requestMIDIAccess(); + refreshMidiPorts(); + runtime.midiAccess.onstatechange = refreshMidiPorts; + } catch (error) { + refs.transportStatusLabel.textContent = `MIDI Fehler: ${error.message}`; + } + } + + function refreshMidiPorts() { + if (!runtime.midiAccess) return; + runtime.midiInputs = Array.from(runtime.midiAccess.inputs.values()); + runtime.midiOutputs = Array.from(runtime.midiAccess.outputs.values()); + if (!state.routing.midiInputId && runtime.midiInputs[0]) { + state.routing.midiInputId = runtime.midiInputs[0].id; + } + if (!state.routing.midiOutputId && runtime.midiOutputs[0]) { + state.routing.midiOutputId = runtime.midiOutputs[0].id; + } + attachSelectedMidiInput(); + renderAll(); + } + + function attachSelectedMidiInput() { + runtime.midiInputs.forEach((input) => { + input.onmidimessage = null; + }); + const selected = runtime.midiInputs.find((entry) => entry.id === state.routing.midiInputId); + if (selected) { + selected.onmidimessage = handleMidiMessage; + } + } + + function persistProjectPayload() { + return JSON.stringify({ + version: 6, + name: state.project.name, + timelines: state.project.timelines, + selectedTimelineId: state.selectedTimelineId, + routing: state.routing, + transport: { + syncSource: state.transport.syncSource + } + }, null, 2); + } + + async function saveProject(forceDialog = false) { + const result = await window.time4midi.project.save({ + defaultPath: state.filePath || `${state.project.name.replaceAll(/\s+/g, "-").toLowerCase() || "time4midi-project"}.json`, + filePath: forceDialog ? "" : state.filePath, + data: persistProjectPayload() + }); + + if (!result.canceled) { + state.filePath = result.filePath; + renderAll(); + } + } + + async function loadProject() { + const result = await window.time4midi.project.load(); + if (result.canceled) return; + + const parsed = JSON.parse(result.data); + state.project = { + version: parsed.version || 6, + name: parsed.name || "Geladenes Projekt", + timelines: Array.isArray(parsed.timelines) ? parsed.timelines.map((timeline) => ({ + id: timeline.id || uid("timeline"), + name: timeline.name || "Timeline", + duration: clamp(Number(timeline.duration) || 120, 1, 86400), + tracks: Array.isArray(timeline.tracks) ? timeline.tracks.map(normalizeTrack) : [] + })) : [] + }; + state.selectedTimelineId = parsed.selectedTimelineId || state.project.timelines[0]?.id || ""; + state.routing = { + ...createDefaultRouting(), + ...(parsed.routing || {}) + }; + state.transport.syncSource = parsed.transport?.syncSource || "internal"; + state.filePath = result.filePath; + state.selectedTrackId = ""; + state.selectedPointId = ""; + stopTransport(true); + configureArtNetTransport(); + configureSacnTransport(); + configureOscTransport(); + configureMqttTransport(); + await Promise.allSettled([ + configureSerialTransport(), + configureModbusTcpTransport(), + configureModbusRtuTransport(), + configureKnxTransport() + ]); + renderAll(); + } + + function findCanvasHit(x, y) { + const timeline = getSelectedTimeline(); + if (!timeline) return null; + const metrics = getPlotMetrics(); + const duration = Math.max(1, timeline.duration); + for (let trackIndex = 0; trackIndex < timeline.tracks.length; trackIndex += 1) { + const track = timeline.tracks[trackIndex]; + for (const point of track.points) { + const pointX = timeToX(point.time, duration, metrics); + const pointY = valueToY(track, point.value, trackIndex); + if (Math.hypot(pointX - x, pointY - y) <= 12) { + return { track, point, trackIndex }; + } + } + } + + if (y < RULER_HEIGHT) { + return { track: null, point: null, trackIndex: -1 }; + } + + const trackIndex = Math.floor((y - RULER_HEIGHT) / TRACK_HEIGHT); + const track = timeline.tracks[trackIndex] || null; + return { track, point: null, trackIndex }; + } + + function setPlayheadFromCanvas(x) { + const timeline = getSelectedTimeline(); + if (!timeline) return; + const metrics = getPlotMetrics(); + const clampedX = clamp(x, metrics.left, metrics.right); + const nextTime = xToTime(clampedX, timeline.duration, metrics); + updateCurrentTime(nextTime, state.transport.syncSource, state.transport.fps); + } + + function addPointFromCanvas(x, y) { + const timeline = getSelectedTimeline(); + if (!timeline) return; + const hit = findCanvasHit(x, y); + if (!hit || !hit.track) return; + const metrics = getPlotMetrics(); + const point = { + id: uid("point"), + time: Number(xToTime(x, timeline.duration, metrics).toFixed(2)), + value: yToValue(hit.track, y, hit.trackIndex), + mode: hit.track.interpolationDefault + }; + hit.track.points.push(point); + sortTrackPoints(hit.track); + state.selectedTrackId = hit.track.id; + state.selectedPointId = point.id; + renderAll(); + } + + function updateInspectorBinding(binding, rawValue, target) { + const timeline = getSelectedTimeline(); + const track = getSelectedTrack(); + const point = getSelectedPoint(); + if (!binding) return; + + if (binding === "timeline.name" && timeline) { + timeline.name = rawValue; + } else if (binding === "timeline.duration" && timeline) { + timeline.duration = clamp(Number(rawValue) || 1, 1, 86400); + } else if (binding === "track.name" && track) { + track.name = rawValue; + } else if (binding === "track.interpolationDefault" && track) { + track.interpolationDefault = rawValue; + if (track.type === "midi-note") { + track.points.forEach((entry) => { + entry.mode = "hold"; + }); + } + } else if (binding === "track.channel" && track) { + track.channel = clamp(Number(rawValue) || 1, 1, 16); + } else if (binding === "track.note" && track) { + track.note = clamp(Number(rawValue) || 60, 0, 127); + } else if (binding === "track.controller" && track) { + track.controller = clamp(Number(rawValue) || 1, 0, 127); + } else if (binding === "track.universe" && track) { + track.universe = track.type === "sacn-channel" + ? clamp(Number(rawValue) || 1, 1, 63999) + : clamp(Number(rawValue) || 0, 0, 32767); + } else if (binding === "track.address" && track) { + if (track.type === "artnet-channel" || track.type === "sacn-channel") { + track.address = clamp(Number(rawValue) || 1, 1, 512); + } else if (track.type === "modbus-tcp" || track.type === "modbus-rtu") { + track.address = clamp(Number(rawValue) || 0, 0, 65535); + } else { + track.address = rawValue; + } + } else if (binding === "track.topic" && track) { + track.topic = rawValue; + } else if (binding === "track.template" && track) { + track.template = rawValue; + } else if (binding === "track.unitId" && track) { + track.unitId = clamp(Number(rawValue) || 1, 1, 247); + } else if (binding === "track.registerType" && track) { + track.registerType = rawValue; + } else if (binding === "track.groupAddress" && track) { + track.groupAddress = rawValue; + } else if (binding === "track.dpt" && track) { + track.dpt = rawValue; + } else if (binding === "track.minValue" && track) { + track.minValue = Number(rawValue) || 0; + } else if (binding === "track.maxValue" && track) { + track.maxValue = Number(rawValue) || 0; + } else if (binding === "track.argType" && track) { + track.argType = rawValue; + } else if (binding === "point.time" && point && timeline && track) { + point.time = clamp(Number(rawValue) || 0, 0, timeline.duration); + sortTrackPoints(track); + } else if (binding === "point.value" && point && track) { + point.value = quantizeTrackValue(track, rawValue); + } else if (binding === "point.mode" && point) { + point.mode = rawValue; + } + + if (track && ["track.minValue", "track.maxValue", "track.registerType", "track.dpt"].includes(binding)) { + track.points = track.points.map((entry) => ({ + ...entry, + value: quantizeTrackValue(track, entry.value) + })); + if (point) { + point.value = quantizeTrackValue(track, point.value); + } + } + + if (target?.type === "number") { + target.value = rawValue; + } + renderAll(); + } + + async function handleRoutingChanges() { + state.routing.artnetEnabled = refs.artnetEnabledInput.checked; + state.routing.artnetHost = refs.artnetHostInput.value || "255.255.255.255"; + state.routing.artnetPort = clamp(Number(refs.artnetPortInput.value) || 6454, 1, 65535); + state.routing.artnetUniverse = clamp(Number(refs.artnetUniverseInput.value) || 0, 0, 32767); + state.routing.artnetTimecodePort = clamp(Number(refs.artnetTimecodePortInput.value) || 6454, 1, 65535); + state.routing.artnetBroadcast = refs.artnetBroadcastInput.checked; + state.routing.sacnEnabled = refs.sacnEnabledInput.checked; + state.routing.sacnHost = refs.sacnHostInput.value || ""; + state.routing.sacnPort = clamp(Number(refs.sacnPortInput.value) || 5568, 1, 65535); + state.routing.sacnSourceName = (refs.sacnSourceNameInput.value || "Time4Midi").slice(0, 63); + state.routing.sacnMulticast = refs.sacnMulticastInput.checked; + state.routing.oscEnabled = refs.oscEnabledInput.checked; + state.routing.oscHost = refs.oscHostInput.value || "127.0.0.1"; + state.routing.oscPort = clamp(Number(refs.oscPortInput.value) || 8000, 1, 65535); + state.routing.mqttEnabled = refs.mqttEnabledInput.checked; + state.routing.mqttBrokerUrl = refs.mqttBrokerInput.value || "mqtt://127.0.0.1:1883"; + state.routing.mqttUsername = refs.mqttUsernameInput.value || ""; + state.routing.mqttPassword = refs.mqttPasswordInput.value || ""; + state.routing.mqttClientId = refs.mqttClientIdInput.value || ""; + state.routing.serialEnabled = refs.serialEnabledInput.checked; + state.routing.serialPath = refs.serialPathInput.value || ""; + state.routing.serialBaud = clamp(Number(refs.serialBaudInput.value) || 9600, 300, 921600); + state.routing.serialParity = refs.serialParitySelect.value || "none"; + state.routing.serialMode = refs.serialModeSelect.value || "rs232"; + state.routing.modbusTcpEnabled = refs.modbusTcpEnabledInput.checked; + state.routing.modbusTcpHost = refs.modbusTcpHostInput.value || "127.0.0.1"; + state.routing.modbusTcpPort = clamp(Number(refs.modbusTcpPortInput.value) || 502, 1, 65535); + state.routing.modbusRtuEnabled = refs.modbusRtuEnabledInput.checked; + state.routing.modbusRtuPath = refs.modbusRtuPathInput.value || ""; + state.routing.modbusRtuBaud = clamp(Number(refs.modbusRtuBaudInput.value) || 9600, 300, 921600); + state.routing.modbusRtuParity = refs.modbusRtuParitySelect.value || "none"; + state.routing.knxEnabled = refs.knxEnabledInput.checked; + state.routing.knxIp = refs.knxIpInput.value || "224.0.23.12"; + state.routing.knxPort = clamp(Number(refs.knxPortInput.value) || 3671, 1, 65535); + state.routing.knxPhysAddr = refs.knxPhysAddrInput.value || "15.15.15"; + state.routing.knxTunneling = refs.knxTunnelingInput.checked; + clearOutputCaches(false); + configureArtNetTransport(); + configureSacnTransport(); + configureOscTransport(); + configureMqttTransport(); + await Promise.allSettled([ + configureSerialTransport(), + configureModbusTcpTransport(), + configureModbusRtuTransport(), + configureKnxTransport() + ]); + renderAll(); + } + + function attachEventListeners() { + refs.projectNameInput.addEventListener("input", () => { + state.project.name = refs.projectNameInput.value || "Unbenanntes Projekt"; + renderAll(); + }); + + refs.newProjectButton.addEventListener("click", () => { + state.project = createDefaultProject(); + state.routing = createDefaultRouting(); + state.selectedTimelineId = state.project.timelines[0]?.id || ""; + state.selectedTrackId = ""; + state.selectedPointId = ""; + state.filePath = ""; + stopTransport(true); + configureArtNetTransport(); + configureSacnTransport(); + configureOscTransport(); + configureMqttTransport(); + void configureSerialTransport(); + void configureModbusTcpTransport(); + void configureModbusRtuTransport(); + void configureKnxTransport(); + renderAll(); + }); + + refs.saveProjectButton.addEventListener("click", () => saveProject(false)); + refs.loadProjectButton.addEventListener("click", loadProject); + refs.addTimelineButton.addEventListener("click", addTimeline); + refs.addMidiTrackButton.addEventListener("click", () => addTrack("midi-note")); + refs.addMidiCcTrackButton.addEventListener("click", () => addTrack("midi-cc")); + refs.addArtNetTrackButton.addEventListener("click", () => addTrack("artnet-channel")); + refs.addSacnTrackButton.addEventListener("click", () => addTrack("sacn-channel")); + refs.addSerialTrackButton.addEventListener("click", () => addTrack("serial-value")); + refs.addModbusTcpTrackButton.addEventListener("click", () => addTrack("modbus-tcp")); + refs.addModbusRtuTrackButton.addEventListener("click", () => addTrack("modbus-rtu")); + refs.addKnxTrackButton.addEventListener("click", () => addTrack("knx-value")); + refs.addOscTrackButton.addEventListener("click", () => addTrack("osc-value")); + refs.addMqttTrackButton.addEventListener("click", () => addTrack("mqtt-value")); + refs.inspectorToggleButton.addEventListener("click", () => { + state.ui.inspectorCollapsed = !state.ui.inspectorCollapsed; + renderAll(); + }); + + refs.timelineList.addEventListener("click", (event) => { + const target = event.target.closest("[data-timeline-id]"); + if (!target) return; + state.selectedTimelineId = target.dataset.timelineId; + state.selectedTrackId = ""; + state.selectedPointId = ""; + stopTransport(true); + renderAll(); + }); + + refs.trackList.addEventListener("click", (event) => { + const actionButton = event.target.closest("[data-track-action]"); + if (actionButton) { + const trackId = actionButton.dataset.trackId; + const action = actionButton.dataset.trackAction; + if (action === "up") moveTrack(trackId, -1); + if (action === "down") moveTrack(trackId, 1); + if (action === "delete") deleteTrack(trackId); + return; + } + + const target = event.target.closest("[data-track-id]"); + if (!target) return; + state.selectedTrackId = target.dataset.trackId; + state.selectedPointId = ""; + renderAll(); + }); + + refs.syncSourceSelect.addEventListener("change", () => { + state.transport.syncSource = refs.syncSourceSelect.value; + pauseTransport(); + renderAll(); + }); + + refs.playButton.addEventListener("click", playTransport); + refs.pauseButton.addEventListener("click", pauseTransport); + refs.stopButton.addEventListener("click", () => stopTransport(true)); + + refs.midiInputSelect.addEventListener("change", () => { + state.routing.midiInputId = refs.midiInputSelect.value; + attachSelectedMidiInput(); + renderAll(); + }); + + refs.midiOutputSelect.addEventListener("change", () => { + state.routing.midiOutputId = refs.midiOutputSelect.value; + renderAll(); + }); + + [ + refs.artnetHostInput, + refs.artnetEnabledInput, + refs.artnetPortInput, + refs.artnetUniverseInput, + refs.artnetTimecodePortInput, + refs.artnetBroadcastInput, + refs.sacnHostInput, + refs.sacnEnabledInput, + refs.sacnPortInput, + refs.sacnSourceNameInput, + refs.sacnMulticastInput, + refs.oscHostInput, + refs.oscEnabledInput, + refs.oscPortInput, + refs.mqttBrokerInput, + refs.mqttEnabledInput, + refs.mqttUsernameInput, + refs.mqttPasswordInput, + refs.mqttClientIdInput, + refs.serialPathInput, + refs.serialBaudInput, + refs.serialParitySelect, + refs.serialModeSelect, + refs.serialEnabledInput, + refs.modbusTcpHostInput, + refs.modbusTcpPortInput, + refs.modbusTcpEnabledInput, + refs.modbusRtuPathInput, + refs.modbusRtuBaudInput, + refs.modbusRtuParitySelect, + refs.modbusRtuEnabledInput, + refs.knxIpInput, + refs.knxPortInput, + refs.knxPhysAddrInput, + refs.knxTunnelingInput, + refs.knxEnabledInput + ].forEach((element) => { + element.addEventListener("change", () => { + void handleRoutingChanges(); + }); + if (element.type === "text") { + element.addEventListener("blur", () => { + void handleRoutingChanges(); + }); + } + }); + + [refs.timelineInspector, refs.selectionInspector].forEach((container) => { + container.addEventListener("input", (event) => { + const target = event.target; + updateInspectorBinding(target.dataset.bind, target.value, target); + }); + container.addEventListener("change", (event) => { + const target = event.target; + updateInspectorBinding(target.dataset.bind, target.value, target); + }); + }); + + refs.timelineCanvas.addEventListener("dblclick", (event) => { + const rect = refs.timelineCanvas.getBoundingClientRect(); + addPointFromCanvas(event.clientX - rect.left, event.clientY - rect.top); + }); + + refs.timelineCanvas.addEventListener("mousedown", (event) => { + const rect = refs.timelineCanvas.getBoundingClientRect(); + const x = event.clientX - rect.left; + const y = event.clientY - rect.top; + const hit = findCanvasHit(x, y); + if (!hit) return; + + if (hit.track) { + state.selectedTrackId = hit.track.id; + state.selectedPointId = hit.point ? hit.point.id : ""; + } + + if (hit.point) { + runtime.drag = { + type: "point", + trackId: hit.track.id, + pointId: hit.point.id, + trackIndex: hit.trackIndex + }; + renderAll(); + return; + } + + if (!state.transport.playing && x >= getPlotMetrics().left) { + setPlayheadFromCanvas(x); + runtime.drag = { + type: "playhead" + }; + } else { + renderAll(); + } + }); + + window.addEventListener("mousemove", (event) => { + if (!runtime.drag) return; + const rect = refs.timelineCanvas.getBoundingClientRect(); + const x = event.clientX - rect.left; + const y = event.clientY - rect.top; + + if (runtime.drag.type === "playhead") { + setPlayheadFromCanvas(x); + return; + } + + const timeline = getSelectedTimeline(); + if (!timeline) return; + const track = timeline.tracks.find((entry) => entry.id === runtime.drag.trackId); + const point = track?.points.find((entry) => entry.id === runtime.drag.pointId); + if (!track || !point) return; + + const metrics = getPlotMetrics(); + const clampedX = clamp(x, metrics.left, metrics.right); + const clampedY = clamp(y, RULER_HEIGHT, refs.timelineCanvas.clientHeight); + point.time = Number(xToTime(clampedX, timeline.duration, metrics).toFixed(2)); + point.value = yToValue(track, clampedY, runtime.drag.trackIndex); + sortTrackPoints(track); + renderSelectionInspector(); + drawCanvas(); + }); + + window.addEventListener("mouseup", () => { + runtime.drag = null; + }); + + window.addEventListener("keydown", (event) => { + if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "s") { + event.preventDefault(); + saveProject(false); + return; + } + + if (event.key !== "Delete" && event.key !== "Backspace") { + return; + } + + const track = getSelectedTrack(); + if (!track || !state.selectedPointId) { + return; + } + + track.points = track.points.filter((point) => point.id !== state.selectedPointId); + state.selectedPointId = ""; + renderAll(); + }); + + window.addEventListener("resize", drawCanvas); + } + + window.time4midi.artnet.onStatus((status) => { + if (state.routing.artnetEnabled && !status.ok && status.error) { + refs.transportStatusLabel.textContent = status.error; + } else if (!state.transport.playing) { + renderTransportState(); + } + }); + + window.time4midi.osc.onStatus((status) => { + if (state.routing.oscEnabled && !status.ok && status.error) { + refs.transportStatusLabel.textContent = status.error; + } else if (!state.transport.playing) { + renderTransportState(); + } + }); + + window.time4midi.sacn.onStatus((status) => { + if (state.routing.sacnEnabled && !status.ok && status.error) { + refs.transportStatusLabel.textContent = status.error; + } else if (!state.transport.playing) { + renderTransportState(); + } + }); + + window.time4midi.mqtt.onStatus((status) => { + if (state.routing.mqttEnabled && !status.ok && status.error) { + refs.transportStatusLabel.textContent = status.error; + } else if (!state.transport.playing) { + renderTransportState(); + } + }); + + window.time4midi.serial.onStatus((status) => { + if (state.routing.serialEnabled && !status.ok && status.error) { + refs.transportStatusLabel.textContent = status.error; + } else if (!state.transport.playing) { + renderTransportState(); + } + }); + + window.time4midi.modbusTcp.onStatus((status) => { + if (state.routing.modbusTcpEnabled && !status.ok && status.error) { + refs.transportStatusLabel.textContent = status.error; + } else if (!state.transport.playing) { + renderTransportState(); + } + }); + + window.time4midi.modbusRtu.onStatus((status) => { + if (state.routing.modbusRtuEnabled && !status.ok && status.error) { + refs.transportStatusLabel.textContent = status.error; + } else if (!state.transport.playing) { + renderTransportState(); + } + }); + + window.time4midi.knx.onStatus((status) => { + if (state.routing.knxEnabled && !status.ok && status.error) { + refs.transportStatusLabel.textContent = status.error; + } else if (!state.transport.playing) { + renderTransportState(); + } + }); + + window.time4midi.artnet.onTimecode((payload) => { + if (state.transport.syncSource === "artnet") { + applyExternalTimecode(payload.totalSeconds, "artnet", payload.fps); + } + }); + + attachEventListeners(); + setupMidi(); + configureArtNetTransport(); + configureSacnTransport(); + configureOscTransport(); + configureMqttTransport(); + void configureSerialTransport(); + void configureModbusTcpTransport(); + void configureModbusRtuTransport(); + void configureKnxTransport(); + renderAll(); +})(); diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..6458ed8 --- /dev/null +++ b/src/index.html @@ -0,0 +1,333 @@ + + + + + + Time4Midi + + + +
+ + +
+
+
+
+ Quelle + +
+
+ Timecode + 00:00:00:00 +
+
+ Status + Idle +
+
+ + + +
+
+
+ +
+
+
+
Editor
+

Keine Timeline ausgewählt

+
+
+ Doppelklick: Punkt setzen + Ziehen: Zeit/Wert ändern + Entf: Punkt löschen +
+
+
+ +
+
+
+ + +
+ + + + diff --git a/src/styles.css b/src/styles.css new file mode 100644 index 0000000..afe56af --- /dev/null +++ b/src/styles.css @@ -0,0 +1,482 @@ +:root { + color-scheme: dark; + --bg: #0b0e14; + --bg-elevated: #141a24; + --bg-soft: #1b2331; + --panel: rgba(20, 26, 36, 0.86); + --panel-border: rgba(255, 255, 255, 0.08); + --text: #ecf2ff; + --muted: #a4b2cc; + --accent: #6ef2c6; + --accent-strong: #17c98d; + --danger: #ff7e87; + --grid: rgba(255, 255, 255, 0.08); + --midi: #61c8ff; + --artnet: #ffb35c; + --shadow: 0 20px 60px rgba(0, 0, 0, 0.35); +} + +* { + box-sizing: border-box; +} + +html, +body { + margin: 0; + min-height: 100%; + background: + radial-gradient(circle at top left, rgba(110, 242, 198, 0.14), transparent 32%), + radial-gradient(circle at top right, rgba(97, 200, 255, 0.14), transparent 28%), + linear-gradient(180deg, #0a0d13 0%, #111722 100%); + color: var(--text); + font-family: "Trebuchet MS", "Segoe UI Variable Text", "Aptos", sans-serif; +} + +body { + min-height: 100vh; +} + +button, +input, +select { + font: inherit; +} + +button { + border: 1px solid rgba(255, 255, 255, 0.12); + border-radius: 12px; + background: linear-gradient(180deg, rgba(110, 242, 198, 0.2), rgba(23, 201, 141, 0.15)); + color: var(--text); + padding: 0.65rem 0.95rem; + cursor: pointer; + transition: transform 120ms ease, border-color 120ms ease, background 120ms ease; +} + +button:hover { + transform: translateY(-1px); + border-color: rgba(110, 242, 198, 0.45); +} + +button.ghost { + background: rgba(255, 255, 255, 0.04); +} + +input, +select { + width: 100%; + border: 1px solid rgba(255, 255, 255, 0.12); + border-radius: 12px; + padding: 0.7rem 0.8rem; + background: rgba(0, 0, 0, 0.22); + color: var(--text); +} + +.app-shell { + display: grid; + grid-template-columns: 332px minmax(0, 1fr) 292px; + gap: 18px; + min-height: 100vh; + padding: 18px; +} + +.app-shell.inspector-collapsed { + grid-template-columns: 332px minmax(0, 1fr) 54px; +} + +.sidebar, +.workspace, +.inspector { + display: flex; + flex-direction: column; + gap: 18px; +} + +.panel { + background: var(--panel); + border: 1px solid var(--panel-border); + border-radius: 24px; + box-shadow: var(--shadow); + backdrop-filter: blur(18px); + padding: 18px; +} + +.inspector { + position: relative; + gap: 12px; + min-width: 0; + overflow-y: auto; + padding-right: 2px; +} + +.inspector .panel { + padding: 12px; + border-radius: 20px; +} + +.inspector-toggle { + align-self: flex-end; + width: 40px; + height: 40px; + padding: 0; + border-radius: 12px; +} + +.app-shell.inspector-collapsed .inspector .panel { + display: none; +} + +.app-shell.inspector-collapsed .inspector { + align-items: center; +} + +.brand-panel { + overflow: hidden; + position: relative; +} + +.brand-lockup { + display: flex; + align-items: center; + gap: 14px; +} + +.logo-mark { + width: 64px; + height: 64px; + border-radius: 18px; + position: relative; + background: + linear-gradient(160deg, rgba(110, 242, 198, 0.3), rgba(97, 200, 255, 0.18)), + rgba(255, 255, 255, 0.04); + border: 1px solid rgba(255, 255, 255, 0.12); +} + +.logo-mark::before, +.logo-mark::after, +.logo-mark span { + content: ""; + position: absolute; + bottom: 12px; + width: 10px; + border-radius: 999px; + background: linear-gradient(180deg, #6ef2c6, #61c8ff); +} + +.logo-mark::before { + left: 16px; + height: 22px; +} + +.logo-mark span { + left: 28px; + height: 34px; +} + +.logo-mark::after { + left: 40px; + height: 46px; +} + +.brand-panel::after { + content: ""; + position: absolute; + inset: auto -80px -100px auto; + width: 220px; + height: 220px; + background: radial-gradient(circle, rgba(110, 242, 198, 0.24), transparent 68%); + pointer-events: none; +} + +.eyebrow { + text-transform: uppercase; + letter-spacing: 0.18em; + font-size: 0.72rem; + color: var(--accent); +} + +h1, +h2 { + margin: 0; +} + +h1 { + font-size: 1.6rem; + line-height: 1.15; +} + +.brand-credit { + margin-top: 0.35rem; + color: var(--muted); + font-size: 0.72rem; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +h2 { + font-size: 1.02rem; +} + +p { + color: var(--muted); + line-height: 1.5; +} + +.panel-header, +.transport-main, +.editor-header { + display: flex; + justify-content: space-between; + align-items: center; + gap: 12px; +} + +.panel-actions, +.transport-controls, +.editor-hints { + display: flex; + gap: 8px; + flex-wrap: wrap; +} + +.editor-hints span, +.meta-row span, +.transport-label { + font-size: 0.78rem; + color: var(--muted); +} + +.field, +.field-grid { + display: grid; + gap: 0.45rem; +} + +.field { + margin-top: 12px; +} + +.checkbox-row { + grid-template-columns: 1fr auto; + align-items: center; +} + +.checkbox-row input { + width: 20px; + height: 20px; +} + +.field span { + font-size: 0.82rem; + color: var(--muted); +} + +.field-grid { + grid-template-columns: 1fr 1fr; + gap: 12px; +} + +.checkbox { + align-content: end; +} + +.checkbox input { + width: 20px; + height: 20px; +} + +.meta-row { + margin-top: 12px; + display: grid; + gap: 0.3rem; +} + +.path-label { + display: block; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.timeline-list, +.track-list, +#timelineInspector, +#selectionInspector { + display: grid; + gap: 10px; + margin-top: 12px; +} + +.timeline-card, +.track-card, +.info-card { + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 18px; + padding: 12px 14px; + background: rgba(255, 255, 255, 0.03); +} + +.selectable-card { + cursor: pointer; +} + +.timeline-card.active, +.track-card.active { + border-color: rgba(110, 242, 198, 0.52); + background: rgba(110, 242, 198, 0.09); +} + +.track-pill { + display: inline-flex; + align-items: center; + border-radius: 999px; + padding: 0.2rem 0.55rem; + font-size: 0.72rem; + font-weight: 700; + color: #081118; +} + +.track-pill.midi { + background: var(--midi); +} + +.track-pill.artnet { + background: var(--artnet); +} + +.track-pill.midicc { + background: #8fd86f; +} + +.track-pill.osc { + background: #ff8fd1; +} + +.track-pill.sacn { + background: #9ca8ff; +} + +.track-pill.serial { + background: #ff9e6b; +} + +.track-pill.modbus { + background: #63e0d3; +} + +.track-pill.knx { + background: #f26f7d; +} + +.track-pill.mqtt { + background: #f7df68; +} + +.track-card-top { + display: flex; + gap: 10px; + align-items: flex-start; +} + +.track-card-body { + flex: 1; +} + +.track-card-actions { + display: flex; + gap: 6px; +} + +.track-action { + min-width: 34px; + height: 34px; + padding: 0; + border-radius: 10px; +} + +.workspace { + min-width: 0; +} + +.transport-bar { + padding: 16px 18px; +} + +.transport-main { + align-items: end; +} + +.transport-block { + display: grid; + gap: 4px; +} + +#timecodeLabel { + font-size: 1.55rem; + letter-spacing: 0.08em; +} + +.editor { + flex: 1; + display: flex; + flex-direction: column; + min-height: 0; +} + +.canvas-frame { + margin-top: 14px; + flex: 1; + min-height: 0; + border-radius: 20px; + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.03), transparent), + rgba(5, 8, 12, 0.62); + overflow: auto; + border: 1px solid rgba(255, 255, 255, 0.08); +} + +#timelineCanvas { + display: block; + width: 100%; + height: 100%; +} + +.empty-state { + color: var(--muted); + font-size: 0.92rem; + line-height: 1.5; +} + +.inspector-grid { + display: grid; + gap: 10px; +} + +.section-label { + color: var(--muted); + font-size: 0.8rem; + text-transform: uppercase; + letter-spacing: 0.14em; +} + +@media (max-width: 1320px) { + .app-shell { + grid-template-columns: 320px minmax(0, 1fr); + } + + .inspector { + grid-column: 1 / -1; + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + } +} + +@media (max-width: 980px) { + .app-shell { + grid-template-columns: 1fr; + } + + .inspector { + grid-template-columns: 1fr; + } +} diff --git a/start-time4midi.cmd b/start-time4midi.cmd new file mode 100644 index 0000000..a8a3ddd --- /dev/null +++ b/start-time4midi.cmd @@ -0,0 +1,16 @@ +@echo off +setlocal +cd /d "%~dp0" + +if not exist node_modules ( + echo Installing dependencies... + call npm install + if errorlevel 1 ( + echo. + echo npm install failed. + pause + exit /b 1 + ) +) + +call npm start