diff --git a/.github/workflows/companion-module-checks.yaml b/.github/workflows/companion-module-checks.yaml new file mode 100644 index 0000000..bc8d44e --- /dev/null +++ b/.github/workflows/companion-module-checks.yaml @@ -0,0 +1,18 @@ +name: Companion Module Checks + +on: + push: + +jobs: + check: + name: Check module + + if: ${{ !contains(github.repository, 'companion-module-template-') }} + + permissions: + packages: read + + uses: bitfocus/actions/.github/workflows/module-checks.yaml@main + # with: + # upload-artifact: true # uncomment this to upload the built package as an artifact to this workflow that you can download and share with others + diff --git a/.github/workflows/node.yaml b/.github/workflows/node.yaml new file mode 100644 index 0000000..ae9b49c --- /dev/null +++ b/.github/workflows/node.yaml @@ -0,0 +1,76 @@ +name: Node CI + +on: + push: + branches: + - '**' + tags: + - 'v[0-9]+.[0-9]+.[0-9]+*' + pull_request: + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + timeout-minutes: 15 + + steps: + - uses: actions/checkout@v4 + - name: Use Node.js 18.x + # This should match the version of Node.js you have defined in the manifest.json runtime field + uses: actions/setup-node@v4 + with: + node-version: 18.x + - name: Prepare Environment + run: | + corepack enable + - name: Prepare Environment (For template repository) + # Only run this step if the repository is a template repository + # If you are using this in a module, you can remove this step + if: ${{ contains(github.repository, 'companion-module-template-') }} + run: | + # Perform an install to generate the lockfile + yarn install + env: + CI: false + - name: Prepare module + run: | + yarn install + env: + CI: true + - name: Build and check types + run: | + yarn build + env: + CI: true + - name: Run lint + run: | + yarn lint + env: + CI: true + + # Uncomment this to enable running unit tests + # test: + # name: Test + # runs-on: ubuntu-latest + # timeout-minutes: 15 + + # steps: + # - uses: actions/checkout@v4 + # - name: Use Node.js 22.x + # uses: actions/setup-node@v4 + # with: + # node-version: 22.x + # - name: Prepare Environment + # run: | + # corepack enable + # yarn install + # env: + # CI: true + # - name: Run tests + # run: | + # yarn test + # env: + # CI: true + # - name: Send coverage + # uses: codecov/codecov-action@v5 diff --git a/.gitignore b/.gitignore index c1ee082..1854eda 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,7 @@ node_modules/ -floattest.js +package-lock.json +/pkg +/pkg.tgz +/dist +DEBUG-* +/.yarn \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..af5adff --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +lint-staged \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..f54ff83 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +package.json +/LICENSE.md \ No newline at end of file diff --git a/.yarnrc.yml b/.yarnrc.yml new file mode 100644 index 0000000..3186f3f --- /dev/null +++ b/.yarnrc.yml @@ -0,0 +1 @@ +nodeLinker: node-modules diff --git a/LICENSE b/LICENSE index c294579..0f6be20 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018 Bitfocus AS +Copyright (c) 2022 Bitfocus AS - Open Source Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index fbb40ec..2d89fe9 100644 --- a/README.md +++ b/README.md @@ -1,32 +1,64 @@ # companion-module-behringer-wing See also HELP.md and LICENSE -**V0.5.0** -* Initial release / testing -* Fader, Mute, LED, Colors, Solo, Sends -* With dynamic variables and Feedback -**V0.9.0** -* Protocol update +# Changelog -**V0.9.1** -* Feedback and Variables +## 2.0.0 -**V0.9.2** -* Talkback bus control +### Added +- Support for Companion 3.0 +- Device discovery listing Behringer Wing devices in configuration screen (inspired by [Behringer X32](https://github.com/bitfocus/companion-module-behringer-x32/tree/master)) +- Fade-Curve Easings (copied from [Behringer X32](https://github.com/bitfocus/companion-module-behringer-x32/tree/master)) +- Support for Wing Rack and Wing Comapct -**V1.0.0** -* Implement a 'Clear All Solo' function -* Cleanup: remove excess debugging code +### Changed +- Re-structued repository to accomodate for large number of commands (inspired by [ATEM](https://github.com/bitfocus/companion-module-bmd-atem)) + - chaned from `action.ts` to `action/` as directory with one file per category + - OSC commands are documented the `commands/` directory, with one file per category. Each command category is its own namespace. -**V1.0.1** -* Fix typos on certain mute actions +## 1.0.4 -**V1.0.2** -* Fix Repo URL in HELP.md +### Fixed +- Fix DCA Channel count (16 not 8) -**V1.0.3** -* Fix remaining switch/toggle actions +## 1.0.3 -**V1.0.4** -* Fix DCA Channel count (16 not 8) +### Fixed +- Fix remaining switch/toggle actions + +## 1.0.2 + +### Fixed +- Fix Repo URL in HELP.md + +## 1.0.1 + +### Fixed +- Fix typos on certain mute actions + +## 1.0.0 + +### Added +- Implement a 'Clear All Solo' function + +### Changed +- Cleanup: remove excess debugging code + +## 0.9.2 +### Added +- Talkback bus control + +## 0.9.1 +### Added +- Feedback and Variables + +## 0.9.0 +### Changed +- Protocol update + +## 0.5.0 +### Added +- Initial release / testing +- Fader, Mute, LED, Colors, Solo, Sends +- With dynamic variables and Feedback \ No newline at end of file diff --git a/companion/HELP.md b/companion/HELP.md new file mode 100644 index 0000000..e77197b --- /dev/null +++ b/companion/HELP.md @@ -0,0 +1,3 @@ +## Your module + +Write some help for your users here! diff --git a/companion/manifest.json b/companion/manifest.json new file mode 100644 index 0000000..25c0946 --- /dev/null +++ b/companion/manifest.json @@ -0,0 +1,26 @@ +{ + "id": "behringer-wing", + "name": "behringer-wing", + "shortname": "wing", + "description": "WING OSC", + "version": "0.0.0", + "license": "MIT", + "repository": "git+https://github.com/janikwitzig/companion-module-behringer-wing.git", + "bugs": "https://github.com/janikwitzig/companion-module-behringer-wing/issues", + "maintainers": [ + { + "name": "Janik Witzig", + "email": "janik.witzig@bluewin.ch" + } + ], + "runtime": { + "type": "node18", + "api": "nodejs-ipc", + "apiVersion": "0.0.0", + "entrypoint": "../dist/index.js" + }, + "legacyIds": [], + "manufacturer": "Behringer", + "products": ["WING"], + "keywords": [] +} diff --git a/defaction.json b/defaction.json deleted file mode 100644 index c01da9a..0000000 --- a/defaction.json +++ /dev/null @@ -1,140 +0,0 @@ - -{ - "baseActions": { - "fdr": { - "label": "Fader Set", - "vSfx": "f", - "inType": "fader" - }, - "fdr_a": { - "label": "Fader Adjust", - "vSfx": "f", - "inType": "fader_a" - }, - "fdr_s": { - "label": "Fader Store", - "vSfx": "f", - "inType": "fader_s" - }, - "fdr_r": { - "label": "Fader Recall", - "vSfx": "f", - "inType": "fader_r" - }, - "mute": { - "label": "Mute", - "inType": "onoff", - "fSfx": "mute", - "choiceType": "ON_OFF", - "default": "0" - }, - "$solo": { - "label": "Solo", - "inType": "solo", - "fSfx": "$solo", - "choiceType": "ON_OFF", - "default": "0" - }, - "name": { - "label": "Name", - "inType": "textinput", - "vSfx": "n", - "inMin": 0, - "inMax": 16, - "default": "" - }, - "icon": { - "label": "Icon", - "inType": "number", - "fSfx": "icon", - "inMin": 0, - "inMax": 999, - "default": 0 - }, - "col": { - "label": "Color", - "inType": "color", - "fSfx": "col", - "default": 1 - }, - "led": { - "label": "LED", - "inType": "led", - "fSfx": "led", - "choiceType": "ON_OFF", - "default": "0" - } - }, - "sendActions": { - "send_bm": { - "label": "Ch, Aux Send", - "sendType": "send_bm", - "fSfx": "", - "choiceType": "send_bm" - }, - "send_bmm": { - "label": "Bus Send", - "sendType": "send_bmm", - "fSfx": "", - "choiceType": "send_bmm" - }, - "send_m": { - "label": "Main Send", - "sendType": "send_m", - "fSfx": "", - "choiceType": "send_m" - }, - "direct": { - "label": "Matrix Direct In", - "sendType": "direct", - "fSfx": "", - "choiceType": "direct" - } - }, - "sendOptions": { - "on": { - "label": "On", - "sendType": "onoff", - "fSfx": "on", - "choiceType": "ON_OFF", - "default": "1" - }, - "lvl": { - "label": "Level Set", - "vSfx": "l", - "sendType": "lvl" - }, - "lvl_a": { - "label": "Level Adjust", - "vSfx": "l", - "sendType": "lvl_a" - }, - "lvl_s": { - "label": "Level Store", - "vSfx": "l", - "sendType": "lvl_s" - }, - "lvl_r": { - "label": "Level Recall", - "vSfx": "l", - "sendType": "lvl_r" - } - }, - "mgrpActions": { - "name": { - "label": "Name", - "inType": "textinput", - "vSfx": "n", - "inMin": 0, - "inMax": 8, - "default": "" - }, - "mute": { - "label": "On", - "inType": "onoff", - "fSfx": "mute", - "choiceType": "ON_OFF", - "default": "0" - } - } -} \ No newline at end of file diff --git a/defbus.json b/defbus.json deleted file mode 100644 index 7bde1d9..0000000 --- a/defbus.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "send_bm": { - "bus": 16, - "main": 4 - }, - "send_bmm": { - "main": 4, - "mtx": 8, - "bus": 8 - }, - "send_m": { - "mtx": 8 - }, - "direct": { - "dir": 2 - } -} diff --git a/defmons.json b/defmons.json deleted file mode 100644 index a45a42a..0000000 --- a/defmons.json +++ /dev/null @@ -1,25 +0,0 @@ -[ - { - "id": "solo_bus", - "prefix": "/cfg/solo/", - "comment": "the following actID will be prefixed with 'solo_'", - "cmdMap": [ - - { - "actID": "mute", - "description": "Mute", - "bg": [128,0,0] - }, - { - "actID": "$dim", - "description": "Dim", - "bg": [0,150,200] - }, - { - "actID": "$mono", - "description": "Mono", - "bg": [0,150,200] - } - ] - } -] \ No newline at end of file diff --git a/defstrip.json b/defstrip.json deleted file mode 100644 index 57ee6a9..0000000 --- a/defstrip.json +++ /dev/null @@ -1,88 +0,0 @@ -{ - "ch": { - "id": "ch", - "digits": 0, - "min": 1, - "max": 40, - "description": "Channel", - "label": "Channel", - "hasRel": true, - "act": "baseActions", - "send": "send_bm" - }, - "aux": { - "id": "aux", - "digits": 0, - "min": 1, - "max": 8, - "description": "Aux In", - "label": "Aux", - "hasRel": true, - "act": "baseActions", - "send": "send_bm" - }, - "bus": { - "id": "bus", - "digits": 0, - "min": 1, - "max": 16, - "description": "Bus Master", - "label": "Bus", - "hasRel": true, - "act": "baseActions", - "send": "send_bmm", - "sendID": "send/" - }, - "main": { - "id": "main", - "digits": 0, - "min": 1, - "max": 4, - "description": "Main Master", - "label": "Main", - "act": "baseActions", - "hasRel": true, - "send": "send_m", - "sendID": "main/" - }, - "mtx": { - "id": "mtx", - "digits": 0, - "min": 1, - "max": 8, - "description": "Matrix Master", - "label": "Matrix", - "hasRel": true, - "act": "baseActions", - "send": "direct", - "sendID": "send/MX" - }, - "dir": { - "id": "dir", - "digits": 0, - "min": 0, - "max": 0, - "description": "Direct In", - "label": "Direct", - "hasRel": false, - "sendID": "dir/" - }, - "dca": { - "id": "dca", - "digits": 0, - "min": 1, - "max": 16, - "description": "DCA Master", - "label": "DCA", - "act": "baseActions" - }, - "mgrp": { - "id": "mgrp", - "digits": 0, - "min": 1, - "max": 8, - "description": "Mute Group", - "label": "Mute Group", - "act": "mgrpActions" - } -} \ No newline at end of file diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..caf9a89 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,5 @@ +import { generateEslintConfig } from '@companion-module/tools/eslint/config.mjs' + +export default generateEslintConfig({ + enableTypescript: true, +}) diff --git a/index.js b/index.js deleted file mode 100644 index e05332e..0000000 --- a/index.js +++ /dev/null @@ -1,2038 +0,0 @@ -'use strict'; -var instance_skel = require('../../instance_skel'); -var OSC = require('osc'); -var stripDef = require('./defstrip.json'); -var monDef = require('./defmons.json'); -var actDef = require('./defaction.json'); -var busDef = require('./defbus.json'); - -var debug; -var log; - - -function instance(system, id, config) { - var self = this; - var po = 0; - - // self.currentSnapshot = { - // name: '', - // index: 0 - // }; - - self.myMixer = { - ip: '', - name: '', - model: '', - serial: '', - fw: '' - }; - - // mixer state - self.xStat = {}; - // level/fader value store - self.tempStore = {}; - // stat id from mixer address - self.fbToStat = {}; - - self.actionDefs = {}; - self.toggleFeedbacks = {}; - self.colorFeedbacks = {}; - self.variableDefs = []; - self.soloList = new Set(); - self.fLevels = {}; - self.FADER_STEPS = 1540; - self.fLevels[self.FADER_STEPS] = []; - self.blinkingFB = {}; - self.crossFades = {}; - self.needStats = true; - - self.PollTimeout = 800; - self.PollCount = 7; - - self.build_choices(); - - // super-constructor - instance_skel.apply(this, arguments); - - if (process.env.DEVELOPER) { - self.config._configIdx = -1; - } - - // each instance needs a separate local port - id.split('').forEach(function (c) { - po += c.charCodeAt(0); - }); - self.port_offset = po; - - return self; -} - -instance.GetUpgradeScripts = function() { - return [ - function() { - // 'old' script that does nothing, but cannot be removed - return false - } - ] -} - -instance.prototype.ICON_SOLO = - 'iVBORw0KGgoAAAANSUhEUgAAAEgAAAA6CAYAAAATBx+NAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEwAACxMBAJqcGAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAUcSURBVHic7ZpLaFxVGIC//96ZTNKkqSnGN+JzUS3oyqi1ogstqKAIuhIVFEUUtOBGhC5UxI2FgigYKLqpFRdV8FFsN1rFbtoGWwyl1lStsa+0NjNJzsy5M7+LuXdyZzKT00zSJpTzwYU573O/+c859w4jeQ0UTytMsNgzWOp4QQ4y6YQijyrl4cWazFIgIHgHeDJJ1wkKKf/dLRy64LNaQuSV8XTaLzEHXpADL8iBF+TAC3LgBTnwghx4QQ68IAdekAMvyIEX5MALcuAFOfCCHHhBDrwgB16QAy/IgRfkwAty4AU58IIceEEOvCAHXpADL8iBF+TAC3LgBTnwghx4QQ68IAcNf8GjfzEmoUpfucgalJUIY2VhpKODYRHa+gduZPhY4XpVjnd08dS8JpfXQJNrXIORgnL5vDqcIyXDC9ZQsAZtuE5Ehofa6dMafrUGLRlG5to2r8FgyslUbYmJgsB1SrBzXLm0nYnNldIkAwIfAd1NivsryhUXYh6zURPUYStINaBXC8GOs8rK8z54wLOpOWxEuVfgEYQ3YYn8mTQJpzgkdaJcC699/yl953Nsa9gRL6eTjWWqBKqsaMjrskXesIY91jBqDfut4T1tmGerJaZKr53i7bh81BqGbJENqtMR3LjE6gTNlBT+clJZvtBiUjeyLR43iqZ4WpVsq7qqdFjDj032KrWG31S5JNXvDEGqLLeGoabti+xWpbOZoBnHvABZWyGoKKB3dhJuP6H0LKya2mB74k+hCp9GJf61hi+iIk+okktXjYq8CqyN5/g7yrvAUFy8qlzkrdmGiopsAG6Lkwfi9gcBUAaiEq83bZjXQAupCEpfE2VJImnXMW26kc4LVfoiw+EWUbHfGG5I6iZRYQ1lY7gxbr/CGsbj/NOq1f2sWQRZw7G43pSOVw8hneQaa7Bx/sHYx+wRlKbDKmE1ku7pRrYlYbhQiHAmLHEHsAk401C8OqzmEy+9W+P84c5ODsftzwI/xfl9xnBts3F0giuh9viyW3o5BSDLOIrWovBmbRIEVUGzPI5la5LkgQLyZWPozxfpZSzbyWuZHJeh3CfC5lTxOlVCoIfp024s3V6lerMAYVi/qScUQ3pTyVN1hVLrT5isqwec46tG1iphWQFZV0C2zraZtosIUbaLHzI5ngN2JUMzQT8wwfTXWHdiSeoEq1TIN+s7V6GQStafzOkTcNnM9uf8LpaNapIeiyVlnI0cWMPGkuFlVTpq863KTx5UK3QzLkKJZEOFW3SSq+O6XcCaOH88l+PPpgN1MZqKlAGNT2bN049wO4DAiEidSGCOL6spSY8XCLbMV5IqVwl8EBX5xxq+iopsjooMAaviKvtEmKR6B1vivDAK+LpkWB8V+Y44IgQ+F6HcbBwRVJTP4mRPVGJ7ybA+yvAtVL8c1Vr/9eQ10EKl+SnW6pqMJHl3+yQ5OdqhNMXWWcYpR4aHUzKXWcPeZnVLhiNamH6HbPEctDIyHGox1oEkquZ0irUiiSSBZyYIBtuVlLW8ovAS8L3AH0CJ6mm2A+XBTCffJHVFmMzkuB94X+EIYIFRgcFsmbukh+O1urAb2Inyc6r96dByt8CHwFHAKvwFbMrkWCvSfP+SvAYqCrlSZc43GGWEKBSAwR4qL7b788RSIq/BIPB8nDTzEgQQhUKUEUCHQfYu1EQXkQHgpvjztKDqq0V7VAJBZUEmt9QwGQAVKIcX5x3Ol4xUH8LaRqt9CNVN86JCYep/T6xGm2u0hEsAAAAASUVORK5CYII='; - - -instance.prototype.updateConfig = function(config) { - var self = this; - - self.config = config; - if (config.analyze) { - self.analyzing = true; - self.detVars = { startCount: 5, startTimeout: 200, endCount: 12, endTimeout: 4000 } - self.passCount = 5; - self.PollCount = 5; - self.passTimeout = 200; - self.PollTimeout = 200; - self.varCounter = 0; - self.firstPoll(); - self.log('info', `Sync Started (${self.PollCount}/${self.PollTimeout})`); - self.config.analyze = false; - } else { - self.PollCount = 7; - self.PollTimeout = 800; - self.init(); - } -}; - -instance.prototype.init = function() { - var self = this; - - debug = self.debug; - log = self.log; - - // cross-fade steps per second - self.fadeResolution = 20; - - self.build_strips(); - self.build_monitor(); - self.build_talk(); - self.init_actions(); - self.init_variables(); - self.init_feedbacks(); -// self.init_presets(); - debug(Object.keys(self.xStat).length + " status addresses generated"); - self.init_osc(); - -}; - -/** - * heartbeat to request updates, subscription expires every 10 seconds - */ -instance.prototype.pulse = function () { - var self = this; - self.sendOSC("/*s", []); - self.debug('re-subscribe'); - // any leftover status needed? - if (self.myMixer.model == '') { - self.sendOSC("/?", []); - } - if (self.needStats) { - self.pollStats(); - } - -}; - -/** - * blink feedbacks - */ -instance.prototype.blink = function () { - var self = this; - for (var f in self.blinkingFB) { - self.checkFeedbacks(f); - } -}; - -/** - * timed fades - */ -instance.prototype.doFades = function () { - var self = this; - var arg = { type: 'f' }; - var fadeDone = []; - - for (var f in self.crossFades) { - var c = self.crossFades[f]; - c.atStep++; - var atStep = c.atStep; - var newVal = c.startVal + (c.delta * atStep) - var v = (Math.sign(c.delta)>0) ? Math.min(c.finalVal, newVal) : Math.max(c.finalVal, newVal); - - arg.value = self.faderToDB(v); - - self.sendOSC(f, arg); - self.debug(f + ": ", arg); - self.setVariable(self.xStat[f].dvID + '_p',Math.round(v * 100)); - self.setVariable(self.xStat[f].dvID + '_d',self.faderToDB(v,true)); - - if (atStep > c.steps) { - fadeDone.push(f); - } - } - - // delete completed fades - for (f in fadeDone) { - self.sendOSC(fadeDone[f],[]); - delete self.crossFades[fadeDone[f]]; - } -} - -instance.prototype.init_presets = function () { - var self = this; - - var presets = [ - { - category: 'Channels', - label: 'Channel 1 Label\nIncludes Label, Color, Mute toggle, Mute feedback, Solo feedback', - bank: { - style: 'png', - text: '$(wing:l_ch1)', - size: '18', - color: self.rgb(255,255,255), - bgcolor: 0 - }, - actions: [ - { - action: 'mute', - options: { - type: '/ch/', - num: 1, - mute: 2 - } - } - ], - feedbacks: [ - { - type: 'c_ch', - options: { - theChannel: 1 - } - }, - { - type: 'ch', - options: { - fg: 16777215, - bg: self.rgb(128,0,0), - theChannel: 1 - } - }, - { - type: 'solosw_ch', - options: { - theChannel: 1 - } - } - ] - }, - { - category: 'Channels', - label: 'Channel 1 Level\nIncludes Fader dB, Color, Solo toggle, Solo feedback', - bank: { - style: 'png', - text: '$(wing:f_ch1_d)', - size: '18', - color: self.rgb(255,255,255), - bgcolor: 0 - }, - actions: [ - { - action: 'solosw_ch', - options: { - num: 1, - solo: 2 - } - } - ], - feedbacks: [ - { - type: 'c_ch', - options: { - theChannel: 1 - } - }, - { - type: 'solosw_ch', - options: { - theChannel: 1 - } - } - ] - } - ]; - // self.setPresetDefinitions(presets); -}; - - -instance.prototype.build_strips = function () { - var self = this; - - var i, b; - - var stat = {}; - var fb2stat = {}; - var defVariables = []; - - function capFirst(string) { - return string.charAt(0).toUpperCase() + string.slice(1); - } - - function deslash(s) { - return s.split('/').join(''); - } - - function undollar(s) { - return s.split('$').join('_'); - } - - function cloneObject(oldObject) { - return JSON.parse(JSON.stringify(oldObject)); - } - - var baseAct = actDef['baseActions']; - var mgrpAct = actDef['mgrpActions']; - var sendAct = actDef['sendActions']; - var sendOpt = actDef['sendOptions']; - - var a, i, s, b; - var strip, busStrip, statActs, busActs, buses, act, path, fbStatID, fbID, dvID, defaultLabel; - - // build stats & dynamic variables - for (s in stripDef) { - strip = stripDef[s]; - defaultLabel = strip.label + ' '; - statActs = actDef[strip.act]; - // console.log(`action: ${strip.act}`); - for (i = strip.min; i<= strip.max; i++) { - for (a in statActs) { - if (!a.match(/_/)) { // fader has extra actions - act = statActs[a]; - path = `/${strip.id}/${i}/${a}`; - dvID = `${strip.id}${i}` - fbStatID = `${strip.id}${i}_${act.fSfx}`; - fbID = act.fSfx; - if (act.fSfx) { - fb2stat[fbStatID] = path; - } - stat[path] = { - valid: false, - polled: 0 - } - switch (a) { - case 'fdr': - stat[path].idx = 0; - stat[path].fdr = 0.0; - defVariables.push({ - label: strip.description + ' ' + i + ' dB', - name: dvID + "_d" - }); - defVariables.push({ - label: strip.description + ' ' + i + ' %', - name: dvID + "_p" - }); - stat[path].dvID = dvID; - if (strip.hasRel) { - // add relative $fdr variable here - } - break; - case 'name': - dvID = dvID + '_' + act.vSfx; - stat[path].defaultName = defaultLabel + i; - stat[path].name = defaultLabel + 1; - stat[path].dvID = dvID; - defVariables.push( { - label: strip.description + ' ' + i + ' ' + act.label, - name: dvID - }); - break; - - case 'col': - stat[path].color = 1; - stat[path].fbID = fbID; - break; - case 'icon': - stat[path].icon = 0; - stat[path].fbID = fbID; - break - case 'led': - case '$solo': - case 'mute': - stat[path].isOn = false; - stat[path].fbID = fbID; - break; - } - } - } - buses = busDef[strip.send]; - for (b in buses){ - busStrip = stripDef[b]; - var fbIDbase = `${strip.send}_`; - for (var bs=1; bs <= buses[b]; bs++) { - busActs = actDef['sendOptions']; - for (a in busActs) { - if (!a.match('_')) { - act = busActs[a]; - path = `/${strip.id}/${i}/${busStrip.sendID}${bs}/${a}`; - fbID = fbIDbase + a; - dvID = `${strip.id}${i}_${b}${bs}`; - if (act.fSfx) { - fb2stat[fbID] = path; - } - stat[path] = { - valid: false, - polled: 0 - } - switch (a) { - case 'lvl': - stat[path].idx = 0; - stat[path].lvl = 0.0; - defVariables.push({ - label: strip.description + ' ' + i + ' to ' + busStrip.label + ' ' + bs + ' dB', - name: dvID + "_d" - }); - defVariables.push({ - label: strip.description + ' ' + i + ' to ' + busStrip.label + ' ' + bs + ' %', - name: dvID + "_p" - }); - stat[path].dvID = dvID; - break; - case 'on': - stat[path].isOn = false; - stat[path].fbID = fbID; - } - } - } - } - } - } - } - - var acts = {}; - var newAct; - var newMute; - var newColor; - var newOn; - var lbl; - var toggleFeedbacks = {}; - var colorFeedbacks = {}; - var onFeedbacks = {}; - - // build channel actions - for (var a in baseAct) { - lbl = baseAct[a].label; - newAct = { - id: a, - label: lbl, - options: [ ] - }; - newMute = undefined; - newColor = undefined; - newOn = undefined; - if (mgrpAct[a] === undefined) { - newAct.options.push(self.OPTIONS_STRIP_BASE); - } else { - newAct.options.push(self.OPTIONS_STRIP_ALL); - } - var newOpts = null; - switch (baseAct[a].inType) { - case 'fader': - newOpts = { - type: 'number', - label: 'Fader Level', - id: 'fad', - default: 0.0, - min: -144, - max: 10 - }; - case 'fader_a': - if (newOpts === null) { - newOpts = { - type: 'number', - tooltip: 'Adjust fader +/- percent.\n0% = -oo, 75% = 0db, 100% = +10db', - label: 'Adjust', - id: 'ticks', - min: -100, - max: 100, - default: 1 - } - } - case 'fader_r': - if (newOpts === null) { - newOpts = { - type: 'dropdown', - tooltip: 'Recall stored fader value', - label: 'From', - id: 'store', - default: 'me', - choices: [ - { id: 'me', - label: "This Strip" - }, - ...self.STORE_LOCATION - ] - } - } - newAct.options.push( - newOpts, - { - type: 'number', - label: 'Fade Duration (ms)', - id: 'duration', - default: 0, - min: 0, - step: 10, - max: 60000 - }); - break; - case 'fader_s': - newAct.options.push( { - type: 'dropdown', - tooltip: 'Store fader value for later recall', - label: 'Where', - id: 'store', - default: 'me', - choices: [ - { id: 'me', - label: "This Strip" - }, - ...self.STORE_LOCATION - ] - }); - break; - case 'color': - newColor = cloneObject(newAct); - newColor.id = a; - newColor.label = 'Color of Strip'; - newColor.description = 'Set button text to Color of Strip'; - newColor.callback = function(feedback, bank) { - var theChannel = feedback.options.strip + '/' + feedback.type; - var stat = self.xStat[theChannel]; - return { color: self.COLOR_VALUES[stat.color - 1].fg }; - }; - - newAct.options.push( { - id: a, - type: 'dropdown', - label: baseAct[a].label, - default: baseAct[a].default, - choices: self.COLOR_VALUES - }); - break; - case 'led': - newColor = cloneObject(newAct); - newColor.id = a; - newColor.label = 'Color on LED'; - newColor.description = 'Set button color when LED On'; - newColor.callback = function(feedback, bank) { - var color = self.xStat[feedback.options.strip + '/col'].color; - var stat = self.xStat[feedback.options.strip + '/led'] - if (stat.isOn) { - return { bgcolor: self.COLOR_VALUES[color - 1].fg }; - } - }; - newAct.options.push({ - id: 'on', - type: 'dropdown', - label: 'State', - choices: self.CHOICES_ON_OFF, - default: '1' - }); - break; - case 'number': - newAct.options.push( { - id: a, - type: 'number', - label: baseAct[a].label + ' ID', - min: baseAct[a].inMin, - max: baseAct[a].inMax, - default: baseAct[a].default, - range: false, - required: true - }); - break; - case 'solo': - newMute = cloneObject(newAct); - newMute.id = a; - newMute.label = `Show Strip ${lbl}`; - newMute.description = `Show border if Strip is Soloed` - newMute.callback = function(feedback, bank) { - var theNode = feedback.options.strip + '/' + feedback.type; - var stat = self.xStat[theNode]; - if (stat.isOn) { - return { png64: self.ICON_SOLO }; - } - }; - newAct.options.push({ - id: 'on', - type: 'dropdown', - label: 'State', - choices: self.CHOICES_ON_OFF, - default: '1' - }); - break; - case 'onoff': - newMute = cloneObject(newAct); - newMute.id = a; - newMute.label = `Color on Strip ${lbl}`; - newMute.description = `Set button color if Strip ${lbl} is On` - newMute.options.push( - { - type: 'colorpicker', - label: 'Foreground color', - id: 'fg', - default: '16777215' - }, - { - type: 'colorpicker', - label: 'Background color', - id: 'bg', - default: self.rgb(128,0, 0) - } - ); - newMute.callback = function(feedback, bank) { - var theNode = feedback.options.strip + '/' + feedback.type; - var stat = self.xStat[theNode]; - if (stat.isOn) { - return { color: feedback.options.fg, bgcolor: feedback.options.bg }; - } - }; - newAct.options.push({ - id: 'on', - type: 'dropdown', - label: 'State', - choices: self.CHOICES_ON_OFF, - default: '1' - }); - break; - case 'textinput': - newAct.options.push( { - id: a, - type: 'textinput', - label: baseAct[a].label, - tooltip: 'Maximum ' + baseAct[a].inMax + ' characters' - }); - break; - } - acts[a] = newAct; - if (newColor) { - colorFeedbacks[newColor.id] = newColor; - } - if (newMute) { - toggleFeedbacks[newMute.id] = newMute; - } - } - - // build send actions - for (a in sendAct) { - newAct = { - id: a, - label: sendAct[a].label, - options: [ ] - }; - var st = sendAct[a].sendType; - switch (st) { - case 'send_bm': - newAct.options.push( - { - type: 'dropdown', - tooltip: 'Source strip', - label: 'source', - id: 'source', - default: self.CHOICES_STRIP['ch'][0].id, - choices: [ - ...self.CHOICES_STRIP['ch'], - ...self.CHOICES_STRIP['aux'] - ] - }, - { - type: 'dropdown', - tooltip: 'Destination', - label: 'Destination', - id: 'bus', - default: self.CHOICES_BUS[st][0].id, - choices: self.CHOICES_BUS[st] - } - ); - break; - case 'send_bmm': - newAct.options.push( - { - type: 'dropdown', - tooltip: 'Source bus', - label: 'source', - id: 'source', - default: self.CHOICES_STRIP['bus'][0].id, - choices: self.CHOICES_STRIP['bus'] - }, - { - type: 'dropdown', - tooltip: 'Destination', - label: 'Destination', - id: 'bus', - default: self.CHOICES_BUS[st][0].id, - choices: self.CHOICES_BUS[st] - } - ); - break; - case 'send_m': - newAct.options.push( - { - type: 'dropdown', - tooltip: 'Source Main', - label: 'source', - id: 'source', - default: self.CHOICES_STRIP['main'][0].id, - choices: self.CHOICES_STRIP['main'] - }, - { - type: 'dropdown', - tooltip: 'Destination', - label: 'Destination', - id: 'bus', - default: self.CHOICES_BUS[st][0].id, - choices: self.CHOICES_BUS[st] - } - ); - break; - case 'direct': - newAct.options.push( - { - type: 'dropdown', - tooltip: 'Matrix', - label: 'Matrix', - id: 'matrix', - default: self.CHOICES_STRIP['mtx'][0].id, - choices: self.CHOICES_STRIP['mtx'] - }, - { - TYPE: 'dropdown', - tooltip: 'Direct Input', - label: 'Input', - id: 'source', - default: self.CHOICES_BUS[st][0].id, - choices: self.CHOICES_BUS[st] - } - ); - break; - } - - for (var sub in sendOpt) { - var subAct = cloneObject(newAct); - subAct.id = subAct.id + '_' + sub; - subAct.label = subAct.label + ' ' + sendOpt[sub].label; - newOpts = null; - newOn = undefined; - switch (sendOpt[sub].sendType) { - case 'onoff': - newOn = cloneObject(subAct); - newOn.id = subAct.id; - newOn.label = `Color on ${newAct.label} OFF`; - newOn.description = `Set button color if ${newAct.label} is OFF` - newOn.options.push( - { - type: 'colorpicker', - label: 'Foreground color', - id: 'fg', - default: '16777215' - }, - { - type: 'colorpicker', - label: 'Background color', - id: 'bg', - default: self.rgb(128,0, 0) - } - ); - newOn.callback = function(feedback, bank) { - var theNode = feedback.options.source + feedback.options.bus + '/on'; - var stat = self.xStat[theNode]; - if (!stat.isOn) { - return { color: feedback.options.fg, bgcolor: feedback.options.bg }; - } - }; - subAct.options.push( - { - id: 'on', - type: 'dropdown', - label: 'State', - choices: self.CHOICES_ON_OFF, - default: '1' - } - ); - break; - case 'lvl_s': - subAct.options.push( { - type: 'dropdown', - tooltip: 'Store level for later recall', - label: 'Where', - id: 'store', - default: 'me', - choices: [ - { id: 'me', - label: "This Send" - }, - ...self.STORE_LOCATION - ] - }); - break; - case 'lvl': - newOpts = { - type: 'number', - label: 'Level', - id: 'fad', - default: 0.0, - min: -144, - max: 10 - }; - // no break - case 'lvl_a': - if (newOpts === null) { - newOpts = { - type: 'number', - tooltip: 'Adjust level +/- percent.\n0% = -oo, 75% = 0db, 100% = +10db', - label: 'Adjust', - id: 'ticks', - min: -100, - max: 100, - default: 1 - } - } - // no break - case 'lvl_r': - if (newOpts === null) { - newOpts = { - type: 'dropdown', - tooltip: 'Recall stored value', - label: 'From', - id: 'store', - default: 'me', - choices: [ - { id: 'me', - label: "This Send" - }, - ...self.STORE_LOCATION - ] - } - } - subAct.options.push( - newOpts, - { - type: 'number', - label: 'Fade Duration (ms)', - id: 'duration', - default: 0, - min: 0, - step: 10, - max: 60000 - }); - break; - } - acts[subAct.id] = subAct; - if (newOn) { - onFeedbacks[newOn.id] = newOn; - } - } - } - - self.xStat = stat; - self.variableDefs = defVariables; - self.actionDefs = acts; - self.fbToStat = fb2stat; - self.colorFeedbacks = colorFeedbacks; - self.toggleFeedbacks = toggleFeedbacks; - Object.assign(self.toggleFeedbacks, onFeedbacks); - -}; - -instance.prototype.build_monitor = function () { - var self = this; - var c, i, ch, cm, cMap, id, actID, soloID, cmd, pfx; - - var stat = {}; - var fb2stat = self.fbToStat; - var soloActions = {}; - var soloFeedbacks = {}; - - var def = monDef; - - for (id in def) { - cmd = def[id]; - pfx = cmd.prefix; - cMap = cmd.cmdMap; - for (cm in cmd.cmdMap) { - ch = cMap[cm]; - actID = 'solo_' + ch.actID; - soloID = 'f_solo'; - c = pfx + ch.actID; - stat[c] = { - fbID: actID, - varID: soloID, - valid: false, - polled: 0 - }; - fb2stat[actID] = c; - soloActions[actID] = { - label: "Solo " + ch.description, - options: [] - }; - soloActions[actID].options.push( { - type: 'dropdown', - label: 'Value', - id: 'set', - default: '2', - choices: [ - {id: '1', label: 'On'}, - {id: '0', label: 'Off'}, - {id: '2', label: 'Toggle'} - ] - } ); - stat[c].isOn = false; - soloFeedbacks[actID] = { - label: "Solo Bus" + ch.description + " on", - description: "Color on Solo Bus" + ch.description, - options: [ - { - type: 'colorpicker', - label: 'Foreground color', - id: 'fg', - default: '16777215' - }, - { - type: 'colorpicker', - label: 'Background color', - id: 'bg', - default: self.rgb.apply(this, ch.bg) - }, - ], - callback: function(feedback, bank) { - var fbType = feedback.type; - var stat = self.xStat[fb2stat[fbType]]; - if (stat.isOn) { - return { color: feedback.options.fg, bgcolor: feedback.options.bg }; - } - } - }; - } - } - actID = 'clearsolo'; - soloID = '/$stat/solo' - soloActions[actID] = { - label: 'Solo Clear', - description: 'Clear all active Solos', - options: [] - }; - stat[soloID] = { - fbID: actID, - isOn: false, - valid: false, - polled: 0 - }; - self.fbToStat[actID] = soloID; - soloFeedbacks[actID] = { - label: 'Any Solo Active', - options: [ - { - type: 'checkbox', - label: 'Blink?', - id: 'blink', - default: 0 - }, - { - type: 'colorpicker', - label: 'Foreground color', - id: 'fg', - default: 0 - }, - { - type: 'colorpicker', - label: 'Background color', - id: 'bg', - default: self.rgb(168, 168, 0) - }, - ], - callback: function(feedback, bank) { - var opt = feedback.options; - var fbType = feedback.type; - var stat = self.xStat[self.fbToStat[fbType]]; - - if (stat.isOn) { - if (opt.blink) { // wants blink - if (self.blinkingFB[stat.fbID]) { - self.blinkingFB[stat.fbID] = false; - // blink off - return; - } else { - self.blinkingFB[stat.fbID] = true; - } - } - return { color: opt.fg, bgcolor: opt.bg }; - } else if (self.blinkingFB[stat.fbID]) { - delete self.blinkingFB[stat.fbID]; - } - - } - }; - - Object.assign(self.xStat, stat); - Object.assign(self.actionDefs, soloActions); - Object.assign(self.toggleFeedbacks, soloFeedbacks); -}; - -instance.prototype.build_talk = function () { - var self = this; - var basePfx = '/cfg/talk/' - var baseID = 'talk'; - var talkActions = {}; - var stat = {}; - var talkFeedbacks = {}; - var newAct; - var newFB; - - var talkBus = { - id: 'bus', - type: 'dropdown', - label: 'Bus', - default: 'A', - choices: [ - { id: 'A', label: 'Talkback A' }, - { id: 'B', label: 'Talkback B' } - ] - } - - var talkDest = { - id: 'dest', - type: 'dropdown', - label: 'Destination', - choices: [] - } - - for (var bus of ['A','B']) { - stat[basePfx + bus + '/$on'] = { - fbID: baseID, - isOn: false, - polled: 0, - valid: false - } - for (var n=1; n<=16; n++) { - var dest = 'B' + n; - stat[basePfx + bus + '/' + dest ] ={ - fbID: baseID + '_d', - isOn: false, - polled: 0, - valid: false - }; - if ('A' == bus) { - talkDest.choices.push({ - id: dest, - label: 'Bus ' + n - }); - } - } - for (var n=1; n<=4; n++) { - var dest = 'M' + n; - stat[basePfx + bus + '/' + dest ] = { - fbID: baseID + '_d', - isOn: false, - polled: 0, - valid: false - }; - if ('A' == bus) { - talkDest.choices.push({ - id: dest, - label: 'Main ' + n - }); - } - } - } - - talkDest.default = 'B1'; - - // TB A/B on/off - newAct = { - id: 'talk', - label: 'Talkback', - description: 'Turn Talkback On/Off', - options: [ - talkBus, - { - id: 'on', - type: 'dropdown', - label: 'State', - default: '1', - choices: self.CHOICES_ON_OFF - } - ] - } - - talkActions[newAct.id] = newAct; - newAct = { - id: 'talk_d', - label: 'Talkback Destination', - description: 'Enable Talkback Destination', - options: [ - talkBus, - talkDest, - { - id: 'on', - type: 'dropdown', - label: 'State', - default: '1', - choices: self.CHOICES_ON_OFF - } - ] - } - talkActions[newAct.id] = newAct; - - var newFB = { - id: 'talk', - label: 'Color for Talkback On', - options: [ - talkBus, - { - type: 'colorpicker', - label: 'Foreground color', - id: 'fg', - default: '16777215' - }, - { - type: 'colorpicker', - label: 'Background color', - id: 'bg', - default: self.rgb(128, 0, 0) - } - ], - callback: function(feedback, bank) { - var theNode = '/cfg/talk/' + feedback.options.bus + '/$on'; - var stat = self.xStat[theNode]; - if (stat.isOn) { - return { color: feedback.options.fg, bgcolor: feedback.options.bg }; - } - } - } - - talkFeedbacks[newFB.id] = newFB; - - newFB = { - id: 'talk_d', - label: 'Color for Talkback Destination On', - options: [ - talkBus, - talkDest, - { - type: 'colorpicker', - label: 'Foreground color', - id: 'fg', - default: '16777215' - }, - { - type: 'colorpicker', - label: 'Background color', - id: 'bg', - default: self.rgb(0 , 102, 0) - } - ], - callback: function(feedback, bank) { - var theNode = '/cfg/talk/' + feedback.options.bus + '/' + feedback.options.dest; - var stat = self.xStat[theNode]; - if (stat.isOn) { - return { color: feedback.options.fg, bgcolor: feedback.options.bg }; - } - } - }; - - talkFeedbacks[newFB.id] = newFB; - - Object.assign(self.xStat, stat); - Object.assign(self.actionDefs, talkActions); - Object.assign(self.toggleFeedbacks, talkFeedbacks); -}; - -instance.prototype.pollStats = function () { - var self = this; - var stillNeed = false; - var counter = 0; - var timeNow = Date.now(); - var timeOut = timeNow - self.PollTimeout; - var varCounter = self.varCounter; - - function ClearVars() { - for (var id in self.xStat) { - self.xStat[id].polled = 0; - self.xStat[id].valid = false; - } - } - - var id; - - for (id in self.xStat) { - if (!self.xStat[id].valid) { - stillNeed = true; - if (self.xStat[id].polled < timeOut) { - self.sendOSC(id); - // self.debug("sending " + id); - self.xStat[id].polled = timeNow; - counter++; - if (counter > self.PollCount) { - break; - } - } - } - } - - if (self.analyzing) { - if (varCounter < 200) { - self.varCounter = varCounter; - } else { - stillNeed = false; - } - } - - if (!stillNeed) { - if (self.analyzing) { - //pause counting while resetting data - self.needStats = false; - var d = (timeNow - self.timeStart) / 1000; - self.log('info', 'Pass complete (' + varCounter + '@' + (varCounter / d).toFixed(1) + ')'); - if (self.passTimeout < self.detVars.endTimeout) { - self.passTimeout += 200; - self.PollTimeout = self.passTimeout; - self.varCounter = 0; - self.timeStart = Date.now(); - stillNeed = true; - } else if (self.passCount < self.detVars.endCount) { - self.passTimeout = self.detVars.startTimeout; - self.PollTimeout = self.passTimeout; - self.passCount += 1; - self.varCounter = 0; - self.PollCount = self.passCount; - self.timeStart = Date.now(); - stillNeed = true; - } else { - self.analyzing = false; - } - if (self.analyzing) { - ClearVars(); - self.log('info', `Sync Started (${self.PollCount}/${self.PollTimeout})`); - } - } else { - self.status(self.STATUS_OK,"Mixer Status loaded"); - var c = Object.keys(self.xStat).length; - var d = (timeNow - self.timeStart) / 1000; - self.log('info', 'Sync complete (' + c + '@' + (c / d).toFixed(1) + ')'); - } - } - self.needStats = stillNeed; -}; - -instance.prototype.firstPoll = function () { - var self = this; - var id; - - self.timeStart = Date.now(); - self.sendOSC('/?',[]); - self.pollStats(); - self.pulse(); -}; - -instance.prototype.stepsToFader = function (i, steps) { - var res = i / ( steps - 1 ); - - return Math.floor(res * 10000) / 10000; -} - -instance.prototype.faderToDB = function ( f, asString ) { -// “f” represents OSC float data. f: [0.0, 1.0] -// “d” represents the dB float data. d:[-oo, +10] - - // float Lin2db(float lin) { - // if (lin <= 0.0) return -144.0; - // if (lin < 0.062561095) return (lin - 0.1875) * 30. / 0.0625; - // if (lin < 0.250244379) return (lin - 0.4375) / 0.00625; - // if (lin < 0.500488759) return (lin - 0.6250) / 0.0125; - // if (lin < 1.0) return (lin - 0.750) / 0.025; - // return 10.; - - var self = this; - var d = 0; - var steps = self.FADER_STEPS; - - if (f <= 0.0) { - d = -144; - } else if (f < 0.062561095) { - d = (f - 0.1875) * 30.0 / 0.0625; - } else if (f < 0.250244379) { - d = (f - 0.4375) / 0.00625; - } else if (f < 0.500488759) { - d = (f - 0.6250) / 0.0125; - } else if (f < 1.0) { - d = (f - 0.750) / 0.025; - } else { - d = 10.0; - } - - d = (Math.round(d * (steps - 0.5)) / steps) - - if (asString) { - return (f==0 ? "-oo" : (d>=0 ? '+':'') + d.toFixed(1)); - } else { - return d; - } -}; - -instance.prototype.dbToFloat = function ( d ) { - // “d” represents the dB float data. d:[-144, +10] - // “f” represents OSC float data. f: [0.0, 1.0] - var f = 0; - - if (d <= -90) { - f = 0; - } else if (d < -60.) { - f = (d + 90.) / 480.; - } else if (d < -30.) { - f = (d + 70.) / 160.; - } else if (d < -10.) { - f = (d + 50.) / 80.; - } else if (d <= 10.) { - f = (d + 30.) / 40.; - } - - return f; - -}; - -instance.prototype.init_osc = function() { - var self = this; - var host = self.config.host; - - if (self.oscPort) { - self.oscPort.close(); - } - if (self.config.host === undefined) { - self.status(self.STATUS_ERROR,'No host IP'); - self.log('error','No host IP'); - } else { - self.oscPort = new OSC.UDPPort ({ - localAddress: "0.0.0.0", - localPort: 2223 + self.port_offset, - remoteAddress: self.config.host, - remotePort: 2223, - metadata: true - }); - - // listen for incoming messages - self.oscPort.on('message', function(message, timeTag, info) { - var args = message.args; - var node = message.address; - var leaf = node.split('/').pop(); - var v = 0; - - if (!self.needStats) { - debug("received ", message, "from", info); - } - if (self.xStat[node] !== undefined) { - if (args.length>1) { - v = args[1].value; - } else { - v = args[0].value; - } - switch (leaf) { - case 'on': - self.xStat[node].isOn = (v == 1); - self.checkFeedbacks(self.xStat[node].fbID); - break; - case 'mute': - case 'led': - case '$solo': - self.xStat[node].isOn = (v == 1); - if ('led' == leaf) { - self.checkFeedbacks('col'); - } - if ('$solo' == leaf) { - var gs = true; - if (v == 1){ - self.soloList.add(node); - } else { - self.soloList.delete(node); - gs = (self.soloList.size > 0); - } - self.xStat['/$stat/solo'].isOn = gs; - } - self.checkFeedbacks(self.xStat[node].fbID); - break; - case 'fdr': - case 'lvl': - v = Math.floor(v * 10000) / 10000; - self.xStat[node][leaf] = v; - self.setVariable(self.xStat[node].dvID + '_p',Math.round(v * 100)); - self.setVariable(self.xStat[node].dvID + '_d',self.faderToDB(v,true)); - self.xStat[node].idx = self.fLevels[self.FADER_STEPS].findIndex((i) => i >= v); - break; - case 'name': - // no name, use behringer default - if (v=='') { - v = self.xStat[node].defaultName; - } - if (node.match(/\/main/)) { - v = v; - } - self.xStat[node].name = v; - self.setVariable(self.xStat[node].dvID, v); - break; - case 'col': - self.xStat[node].color = parseInt(args[0].value) - self.checkFeedbacks(self.xStat[node].fbID); - self.checkFeedbacks('led'); - break; - case '$mono': - case '$dim': - self.xStat[node].isOn = (v == 1); - self.checkFeedbacks(self.xStat[node].fbID); - break; - default: - if ( node.match(/\$solo/) - || node.match(/^\/cfg\/talk\//) - || node.match(/^\/\$stat\/solo/) ) { - self.xStat[node].isOn = (v == 1); - self.checkFeedbacks(self.xStat[node].fbID); - } - } - self.xStat[node].valid = true; - self.varCounter += 1; - if (self.needStats) { - self.pollStats(); - } else { - // debug(message); - } - } else if (leaf == '*') { - // /?~~,s~~WING,192.168.1.71,PGM,ngc‐full,NO_SERIAL,1.07.2‐40‐g1b1b292b:develop~~~~ - var mixer_info = args[0].value.split(','); - self.myMixer.ip = mixer_info[1] - self.myMixer.name = mixer_info[2]; - self.myMixer.model = mixer_info[3]; - self.myMixer.serial = mixer_info[4]; - self.myMixer.fw = mixer_info[5]; - if ('WING_EMU' == mixer_info[4]) { - self.PollTimeout = 3200; - self.PollCount = 7; - } - self.setVariable('m_ip', self.myMixer.ip); - self.setVariable('m_name', self.myMixer.name); - self.setVariable('m_model', self.myMixer.model); - self.setVariable('m_serial', self.myMixer.serial); - self.setVariable('m_fw', self.myMixer.fw); - } - // else { - // debug(message.address, args); - // } - }); - - self.oscPort.on('ready', function() { - self.status(self.STATUS_WARNING,"Loading status"); - self.log('info', `Sync Started (${self.PollCount}/${self.PollTimeout})`); - self.firstPoll(); - self.heartbeat = setInterval( function () { self.pulse(); }, 9000); - self.blinker = setInterval( function() { self.blink(); }, 1000); - self.fader = setInterval( function() { self.doFades(); }, 1000 / self.fadeResolution); - }); - - self.oscPort.on('close', function() { - if (self.heartbeat) { - clearInterval(self.heartbeat); - delete self.heartbeat; - } - if (self.blinker) { - clearInterval(self.blinker); - delete self.blinker; - } - if (self.fader) { - clearInterval(self.fader); - delete self.fader; - } - }); - - self.oscPort.on('error', function(err) { - self.log('error', "Error: " + err.message); - self.status(self.STATUS_ERROR, err.message); - if (self.heartbeat) { - clearInterval(self.heartbeat); - delete self.heartbeat; - } - if (self.blinker) { - clearInterval(self.blinker); - delete self.blinker; - } - if (self.fader) { - clearInterval(self.fader); - delete self.fader; - } - }); - - self.oscPort.open(); - } -}; - -// define instance variables -instance.prototype.init_variables = function() { - var self = this; - - var variables = [ - { - label: 'WING IP Address', - name: 'm_ip', - }, - { - label: 'WING Name', - name: 'm_name' - }, - { - label: 'WING Model', - name: 'm_model' - }, - { - label: 'WING Serial Number', - name: 'm_serial' - }, - { - label: 'WING Firmware', - name: 'm_fw' - // }, - // { - // label: 'Current Snapshot Name', - // name: 's_name' - // }, - // { - // label: 'Current Snapshot Index', - // name: 's_index' - } - ]; - variables.push.apply(variables, self.variableDefs); - - for (var i in variables) { - self.setVariable(variables[i].name); - } - self.setVariableDefinitions(variables); -}; - -// define instance feedbacks -instance.prototype.init_feedbacks = function() { - var self = this; - - var feedbacks = { - // snap_color: { - // label: 'Color on Current Snapshot', - // description: 'Set Button colors when this Snapshot is loaded', - // options: [ - // { - // type: 'colorpicker', - // label: 'Foreground color', - // id: 'fg', - // default: '16777215' - // }, - // { - // type: 'colorpicker', - // label: 'Background color', - // id: 'bg', - // default: self.rgb(0, 128, 0) - // }, - // { - // type: 'number', - // label: 'Snapshot to match', - // id: 'theSnap', - // default: 1, - // min: 1, - // max: 64, - // range: false, - // required: true - // } - // ], - // callback: function(feedback, bank) { - // if (feedback.options.theSnap == self.currentSnapshot.index) { - // return { color: feedback.options.fg, bgcolor: feedback.options.bg }; - // } - // } - // } - }; - Object.assign(feedbacks,this.toggleFeedbacks); - Object.assign(feedbacks,this.colorFeedbacks); - this.setFeedbackDefinitions(feedbacks); -}; - -// Return config fields for web config -instance.prototype.config_fields = function () { - return [ - { - type: 'textinput', - id: 'host', - label: 'Target IP', - tooltip: 'The IP of the WING console', - width: 6, - regex: this.REGEX_IP - } - // , - // { - // type: 'checkbox', - // label: 'Analyze', - // id: 'analyze', - // tooltip: 'Cycle through console sync timing variables\nThis will temporarily disable the module', - // default: 0 - // } - ]; -}; - -// When module gets deleted -instance.prototype.destroy = function() { - if (this.heartbeat) { - clearInterval(this.heartbeat); - delete this.heartbeat; - } - if (this.blinker) { - clearInterval(this.blinker); - delete this.blinker; - } - if (this.fader) { - clearInterval(this.fader); - delete this.fader; - } - if (this.oscPort) { - this.oscPort.close(); - } - debug("destroy", this.id); -}; - -instance.prototype.sendOSC = function (node, arg) { - var self = this; - - if (self.oscPort) { - self.oscPort.send({ - address: node, - args: arg - }); - // self.debug('sending ',node, (arg? arg:'')); - } -}; - -instance.prototype.sendUDP = function (data) { - var self = this; - var bytes = Buffer.from(data, 'hex'); - - if (self.udpPort) { - self.udpPort.send(data); - } -}; - -instance.prototype.build_choices = function() { - var self = this; - var strips; - var buses; - var bMax; - - // discreet float values for faders (1540) - for (var i = 0; i < self.FADER_STEPS; i++) { - self.fLevels[self.FADER_STEPS][i] = self.stepsToFader(i,self.FADER_STEPS); - } - - self.STORE_LOCATION = []; - - for (var i = 1; i <=10; i++) { - var i2 = ('0' + i.toString()).slice(-2); - - self.STORE_LOCATION.push( { - label: `Global ${i}`, - id: `gs_${i2}` - }) - } - - self.CHOICES_ON_OFF = [ - {id: '1', label: 'On'}, - {id: '0', label: 'Off'}, - {id: '2', label: 'Toggle'} - ] - - strips = { - type: 'dropdown', - label: 'Strip', - id: 'strip', - choices: [ ], - default: '' - }; - - self.CHOICES_STRIP = {}; - - for (var d in stripDef) { - var s = stripDef[d]; - self.CHOICES_STRIP[d] = []; - for (var i = s.min; i <= s.max; i++) { - self.CHOICES_STRIP[d].push( { - id: '/' + s.id + '/' + i, - label: s.label + ' ' + i - }); - } - } - - for (var d in stripDef) { - var s = stripDef[d]; - for (var i=s.min; i <= s.max; i++) { - if (s.act == 'baseActions') { - strips.choices.push( { - id: '/' + s.id + '/' + i, - label: s.label + ' ' + i - }); - } - } - } - - strips.default = strips.choices[0].id; - - self.OPTIONS_STRIP_BASE = { ...strips }; - - strips.choices = []; - for (var d in stripDef) { - var s = stripDef[d]; - for (var i=s.min; i <= s.max; i++) { - strips.choices.push( { - id: '/' + s.id + '/' + i, - label: s.label + ' ' + i - }); - } - } - - self.OPTIONS_STRIP_ALL = { ...strips }; - - self.CHOICES_BUS = {}; - - for (var b in busDef) { - var bus = busDef[b]; - self.CHOICES_BUS[b] = []; - for (var d in bus) { - var s = stripDef[d]; - for (var i=1; i<=bus[d]; i++) { - self.CHOICES_BUS[b].push( { - id: '/' + s.sendID + i, - label: s.label + ' ' + i - }); - } - } - } - - self.FADER_VALUES = [ - { label: '- ∞', id: '0.0' }, - { label: '-50 dB: ', id: '0.1251' }, - { label: '-30 dB', id: '0.251' }, - { label: '-20 dB', id: '0.375' }, - { label: '-18 dB', id: '0.4' }, - { label: '-15 dB', id: '0.437' }, - { label: '-12 dB', id: '0.475' }, - { label: '-9 dB', id: '0.525' }, - { label: '-6 dB', id: '0.6' }, - { label: '-3 dB', id: '0.675' }, - { label: '-2 dB', id: '0.7' }, - { label: '-1 dB', id: '0.725' }, - { label: '0 dB', id: '0.75' }, - { label: '+1 dB', id: '0.775' }, - { label: '+2 dB', id: '0.8' }, - { label: '+3 dB', id: '0.825' }, - { label: '+4 dB', id: '0.85' }, - { label: '+5 dB', id: '0.875' }, - { label: '+6 dB', id: '0.9' }, - { label: '+9 dB', id: '0.975' }, - { label: '+10 dB', id: '1.0' } - ]; - - self.COLOR_VALUES = [ - { label: 'Gray blue', id: '1', fg: self.rgb(162, 224, 235) }, - { label: 'Medium blue', id: '2', fg: self.rgb( 64, 242, 252) }, - { label: 'Dark blue', id: '3', fg: self.rgb( 64, 181, 235) }, - { label: 'Turquoise', id: '4', fg: self.rgb( 36, 252, 237) }, - { label: 'Green', id: '5', fg: self.rgb( 1, 242, 49) }, - { label: 'Olive green', id: '6', fg: self.rgb(197, 223, 61) }, - { label: 'Yellow', id: '7', fg: self.rgb(254, 242, 0) }, - { label: 'Orange', id: '8', fg: self.rgb(252, 141, 51) }, - { label: 'Red', id: '9', fg: self.rgb(252, 50, 50) }, - { label: 'Coral', id: '10', fg: self.rgb(254, 145, 104) }, - { label: 'Pink', id: '11', fg: self.rgb(251, 161, 249) }, - { label: 'Mauve', id: '12', fg: self.rgb(161, 141, 254) } - ]; - - self.TAPE_FUNCITONS = [ - { label: 'STOP', id: '0' }, - { label: 'PLAY PAUSE', id: '1' }, - { label: 'PLAY', id: '2' }, - { label: 'RECORD PAUSE', id: '3' }, - { label: 'RECORD', id: '4' }, - { label: 'FAST FORWARD', id: '5' }, - { label: 'REWIND', id: '6' } - ]; -} - -instance.prototype.init_actions = function(system) { - var self = this; - var newActions = {}; - - Object.assign(newActions, self.actionDefs); - - self.setActions(newActions); -}; - -instance.prototype.action = function(action) { - var self = this; - var cmd; - var subAct = action.action.slice(-2); - var opt = action.options; - var fVal; - var needEcho = true; - var arg = []; - - // calculate new fader/level float - // returns a 'new' float value - // or undefined for store or crossfade - function fadeTo(cmd, opt) { - var stat = self.xStat[cmd] - var node = cmd.split('/').pop(); - var opTicks = parseInt(opt.ticks); - var steps = self.FADER_STEPS; - var span = parseFloat(opt.duration); - var oldVal = stat[node]; - var oldIdx = stat.idx; - var byVal = opTicks * steps / 100; - var newIdx = Math.min(steps-1,Math.max(0, oldIdx + Math.round(byVal))); - var slot = opt.store == 'me' ? cmd : opt.store; - var r, byVal, newIdx; - - switch (subAct) { - case '_a': // adjust +/- (pseudo %) - byVal = opTicks * steps / 100; - newIdx = Math.min(steps-1,Math.max(0, oldIdx + Math.round(byVal))); - r = self.fLevels[steps][newIdx]; - break; - case '_r': // restore - r = slot && self.tempStore[slot] !== undefined ? self.tempStore[slot] : -1; - break; - case '_s': // store - if (slot) { // sanity check - self.tempStore[slot] = stat[node]; - } - r = undefined; - // the 'store' actions are internal to this module only - // r is left undefined since there is nothing to send - break; - default: // set new value - r = self.dbToFloat(opt.fad); - } - // set up cross fade? - if (span>0 && r >= 0) { - var xSteps = span / (1000 / self.fadeResolution); - var xDelta = Math.floor((r - oldVal) / xSteps * 10000) / 10000; - if (xDelta == 0) { // already there - r = undefined; - } else { - self.crossFades[cmd] = { - steps: xSteps, - delta: xDelta, - startVal: oldVal, - finalVal: r, - atStep: 1 - } - // start the xfade - r = oldVal + xDelta; - needEcho = false; - } - } - self.debug(`---------- ${oldIdx}:${oldVal} by ${byVal}(${opTicks}) fadeTo ${newIdx}:${r} ----------`); - if (r !== undefined) { - r = self.faderToDB(r) - } - return r; - } - - // internal function for action (not anonymous) - // self is properly scoped to next outer closure - function setToggle(cmd, opt) { - return 2 == parseInt(opt) ? (1-(self.xStat[cmd].isOn ? 1 : 0)) : parseInt(opt); - } - - switch (action.action){ - - case 'mute': - cmd = opt.strip + '/mute'; - arg = { - type: 'i', - value: setToggle(cmd, opt.on) - }; - break; - - case 'fdr': - case 'fdr_a': - case 'fdr_s': - case 'fdr_r': - cmd = opt.strip + '/fdr'; - if ((fVal = fadeTo(cmd, opt)) === undefined) { - cmd = undefined; - } else { - arg = { - type: 'f', - value: fVal - }; - } - break; - - case 'send_bm_on': - case 'send_bmm_on': - case 'send_m_on': - cmd = opt.source + opt.bus + '/on'; - arg = { - type: 'i', - value: setToggle(cmd, opt.on) - }; - break; - - case 'send_bm_lvl': - case 'send_bmm_lvl': - case 'send_m_lvl': - case 'send_bm_lvl_a': - case 'send_bmm_lvl_a': - case 'send_m_lvl_a': - case 'send_bm_lvl_s': - case 'send_bmm_lvl_s': - case 'send_m_lvl_s': - case 'send_bm_lvl_r': - case 'send_bmm_lvl_r': - case 'send_m_lvl_r': - cmd = opt.source + opt.bus + '/lvl'; - if ((fVal = fadeTo(cmd, opt)) === undefined) { - cmd = undefined; - } else { - arg = { - type: 'f', - value: fVal - }; - } - break; - - /* don't have details for this, yet - * It's probably in mon/1..2/level - */ - // case 'solo_level': - // case 'solo_level_a': - // cmd = '/config/solo/level'; - // if ((fVal = fadeTo(cmd, opt)) < 0) { - // cmd = undefined; - // } else { - // arg = { - // type: 'f', - // value: fVal - // }; - // } - // break; - - case '$solo': - cmd = opt.strip + '/$solo'; - arg = { - type: 'i', - value: setToggle(cmd, opt.on) - }; - - break; - - case 'led': - cmd = opt.strip + '/led'; - arg = { - type: 'i', - value: setToggle(cmd, opt.on) - }; - break; - - case 'name': - arg = { - type: "s", - value: "" + opt.name - }; - cmd = opt.strip + '/name'; - break; - - case 'col': - arg = { - type: 'i', - value: parseInt(opt.col) - }; - cmd = opt.strip + '/col'; - break; - - case 'icon': - arg = { - type: 'i', - value: parseInt(opt.icon) - }; - cmd = opt.strip + '/icon'; - break; - - case 'solo_mute': - case 'solo_$dim': - case 'solo_$mono': - var cfg = action.action.split('_')[1] - cmd = '/cfg/solo/' + cfg; - arg = { - type: 'i', - value: setToggle(cmd, opt.set) - }; - break; - - case 'talk': - cmd = '/cfg/talk/' + opt.bus + '/$on' - arg = { - type: 'i', - value: setToggle(cmd, opt.on) - } - break; - - case 'talk_d': - cmd = '/cfg/talk/' + opt.bus + '/' + opt.dest; - arg = { - type: 'i', - value: setToggle(cmd, opt.on) - } - break; - - case 'clearsolo': - // WING does not have this as a command - // so we keep track of 'solos' to reset each one - for (var s of self.soloList) { - self.sendOSC(s, {type: 'i', value: 0 }); - self.sendOSC(s,[]); - } - - break; - - // case 'load_snap': - // arg = { - // type: 'i', - // value: parseInt(opt.snap) - // }; - // cmd = '/-snap/load'; - // break; - - // case 'tape': - // arg = { - // type: 'i', - // value: parseInt(opt.tFunc) - // }; - // cmd = '/-stat/tape/state'; - // break; - } - - if (cmd !== undefined) { - self.sendOSC(cmd,arg); - self.debug(cmd, arg); - // force a reply - if (needEcho) { - self.sendOSC(cmd,[]); - } - } -}; - -instance_skel.extendedBy(instance); -exports = module.exports = instance; diff --git a/package.json b/package.json index a85a491..057d245 100644 --- a/package.json +++ b/package.json @@ -1,31 +1,49 @@ { - "name": "behringer-wing", - "legacy": [ - "wing" - ], - "version": "1.0.8", - "api_version": "1.0.0", - "keywords": [ - "Audio", - "Mixer", - "Console" - ], - "manufacturer": "Behringer", - "product": "WING", - "shortname": "wing", - "description": "Module to control Behringer WING consoles", - "main": "index.js", + "name": "wing-companion", + "version": "2.0.0", + "main": "dist/index.js", + "type": "module", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "postinstall": "husky", + "format": "prettier -w .", + "package": "run build && companion-module-build", + "build": "rimraf dist && run build:main", + "build:main": "tsc -p tsconfig.build.json", + "dev": "tsc -p tsconfig.build.json --watch", + "lint:raw": "eslint", + "lint": "run lint:raw ." }, - "author": "John Knight, Jr ", "license": "MIT", - "homepage": "https://github.com/bitfocus/companion-module-behringer-wing#readme", - "bugs": { - "url": "https://github.com/bitfocus/companion-module-behringer-wing/issues" - }, "repository": { "type": "git", - "url": "git+https://github.com/bitfocus/companion-module-behringer-wing.git" - } + "url": "git+https://github.com/bitfocus/companion-module-wing-companion.git" + }, + "dependencies": { + "@companion-module/base": "~1.11.2", + "debounce-fn": "^6.0.0", + "osc": "^2.4.5", + "p-queue": "^8.0.1", + "type-fest": "^4.31.0" + }, + "devDependencies": { + "@companion-module/tools": "^2.1.1", + "@types/node": "^22.10.2", + "eslint": "^9.17.0", + "husky": "^9.1.7", + "lint-staged": "^15.2.11", + "prettier": "^3.4.2", + "rimraf": "^6.0.1", + "typescript": "~5.5.4", + "typescript-eslint": "^8.18.1" + }, + "prettier": "@companion-module/tools/.prettierrc.json", + "lint-staged": { + "*.{css,json,md,scss}": [ + "prettier --write" + ], + "*.{ts,tsx,js,jsx}": [ + "yarn lint:raw --fix" + ] + }, + "packageManager": "yarn@4.5.3" } diff --git a/src/@types/osc.d.ts b/src/@types/osc.d.ts new file mode 100644 index 0000000..e18985f --- /dev/null +++ b/src/@types/osc.d.ts @@ -0,0 +1,133 @@ +// Copied from https://github.com/colinbdclark/osc.js/pull/105 + +declare module 'osc' { + export class EventEmitter { + addListener(event: E, listener: T[E]): this + + on(event: E, listener: T[E]): this + + once(event: E, listener: T[E]): this + + removeListener(event: E, listener: T[E]): this + + removeAllListeners(event?: keyof T): this + + setMaxListeners(n: number): this + + getMaxListeners(): number + + listeners(event: E): T[E][] + + emit(event: string | symbol, ...args: any[]): boolean + listenerCount(type: keyof T): number + + // Added in Node 6... + prependListener(event: E, listener: T[E]): this + + prependOnceListener(event: E, listener: T[E]): this + + eventNames(): Array + } + + export const defaults: { + metadata: boolean + unpackSingleArgs: boolean + } + export type Argument = number | string | Uint8Array + export type MetaArgument = + | { type: 'i' | 'f'; value: number } + | { type: 's'; value: string } + | { type: 'b'; value: Uint8Array } + + export abstract class SLIPPort {} + + export interface OscMessage { + address: string + args: Argument | Array | MetaArgument | Array + } + + // eslint-disable-next-line @typescript-eslint/no-empty-object-type + export interface OscBundle {} + + export interface SenderInfo { + address: string + port: number + size: number + family: 'IPv4' | 'IPv6' + } + + export interface PortEvents { + ready: () => void + message: (message: OscMessage, timeTag: number | undefined, info: SenderInfo) => void + bundle: (bundle: OscBundle, timeTag: number, info: SenderInfo) => void + osc: (packet: OscBundle | OscMessage, info: SenderInfo) => void + raw: (data: Uint8Array, info: SenderInfo) => void + error: (err: Error) => void + } + + export interface UdpOptions { + /** + * The port to listen on + */ + localPort?: number // 57121 + /** + * The local address to bind to + */ + localAddress?: string // '127.0.0.1' + /** + * The remote port to send messages to + */ + remotePort?: number + /** + * The remote address to send messages to + */ + remoteAddress?: string + broadcast?: boolean // false + /** + * The time to live (number of hops) for a multicast connection + */ + multicastTTL?: number + /** + * An array of multicast addresses to join when listening for multicast messages + */ + multicastMembership?: string[] + + socket?: any + + /** + * should message arguments be wrapped with type? + */ + metadata?: boolean + unpackSingleArgs?: boolean + } + + export interface OscSender { + send(msg: OscMessage, address?: string, port?: number): void + } + + export abstract class Port extends EventEmitter implements OscSender { + send(msg: OscMessage, address?: string, port?: number): void + } + + export class SerialPort extends SLIPPort { + open(): void + + close(): void + + listen(): void + } + + export class UDPPort extends Port { + static setupMulticast(that: UDPPort): void + + options: UdpOptions + + constructor(options: UdpOptions) + + open(): void + + close(): void + + listen(): void + } +} diff --git a/src/actions/aux.ts b/src/actions/aux.ts new file mode 100644 index 0000000..775ae83 --- /dev/null +++ b/src/actions/aux.ts @@ -0,0 +1,12 @@ +import { CompanionActionDefinitions } from '@companion-module/base' +import { CompanionActionWithCallback } from './common.js' +import { InstanceBaseExt } from '../types.js' +import { WingConfig } from '../config.js' + +export enum AuxActions {} + +export function createAuxActions(_self: InstanceBaseExt): CompanionActionDefinitions { + const actions: { [id in AuxActions]: CompanionActionWithCallback | undefined } = {} + + return actions +} diff --git a/src/actions/bus.ts b/src/actions/bus.ts new file mode 100644 index 0000000..e908c22 --- /dev/null +++ b/src/actions/bus.ts @@ -0,0 +1,51 @@ +import { CompanionActionDefinitions } from '@companion-module/base' +import { GetFaderInputField, GetDropdown, GetMuteDropdown } from '../choices/common.js' +import { getNodeNumber, runTransition } from './utils.js' +import { CompanionActionWithCallback } from './common.js' +import { BusCommands as Commands } from '../commands/bus.js' +import { InstanceBaseExt } from '../types.js' +import { WingConfig } from '../config.js' + +export enum BusActions { + SetBusToMatrixLevel = 'set-bus-to-matrix-level', + SetBusToMatrixMute = 'set-bus-to-matrix-mute', +} + +export function createBusActions(self: InstanceBaseExt): CompanionActionDefinitions { + const send = self.sendCommand + const ensureLoaded = self.ensureLoaded + const state = self.state + const transitions = self.transitions + + const actions: { [id in BusActions]: CompanionActionWithCallback | undefined } = { + [BusActions.SetBusToMatrixLevel]: { + name: 'Set Bus to Matrix Level', + options: [ + GetDropdown('From Bus', 'bus', state.namedChoices.busses), + GetDropdown('To Matrix', 'matrix', state.namedChoices.matrices), + ...GetFaderInputField('level'), + ], + callback: async (event) => { + const cmd = Commands.MatrixSendLevel(getNodeNumber(event, 'bus'), getNodeNumber(event, 'matrix')) + runTransition(cmd, 'level', event, state, transitions) + }, + subscribe: (event) => { + ensureLoaded(Commands.MatrixSendLevel(getNodeNumber(event, 'bus'), getNodeNumber(event, 'matrix'))) + }, + }, + [BusActions.SetBusToMatrixMute]: { + name: 'Set Bus to Matrix Mute', + options: [ + GetDropdown('From Bus', 'bus', state.namedChoices.busses), + GetDropdown('To Matrix', 'matrix', state.namedChoices.matrices), + GetMuteDropdown('mute'), + ], + callback: async (event) => { + const cmd = Commands.MatrixSendOn(getNodeNumber(event, 'bus'), getNodeNumber(event, 'matrix')) + send(cmd, event.options.mute as number) + }, + }, + } + + return actions +} diff --git a/src/actions/channel.ts b/src/actions/channel.ts new file mode 100644 index 0000000..02b60ef --- /dev/null +++ b/src/actions/channel.ts @@ -0,0 +1,111 @@ +import { CompanionActionDefinitions } from '@companion-module/base' +import { GetDropdown, GetNumberField, getIconChoices } from '../choices/common.js' +import { getSourceGroupChoices } from '../choices/common.js' +import { getChannelProcessOrderChoices, getFilterModelOptions } from '../choices/channel.js' +import { ChannelCommands as Commands } from '../commands/channel.js' +import * as ActionUtil from './utils.js' +import { CompanionActionWithCallback } from './common.js' +import { EqModelDropdown, EqParameterDropdown } from '../choices/eq.js' +import { EqModelChoice } from '../choices/eq.js' +import { InstanceBaseExt } from '../types.js' +import { WingConfig } from '../config.js' +import { getStringFromState } from '../state/utils.js' + +export enum ChannelActions { + SetChannelMainConnection = 'set-channel-main-connection', + SetChannelFilterModel = 'set-channel-filter-model', + SetChannelEqType = 'set-channel-eq-type', + SetChannelEqParameter = 'set-channel-eq-parameter', + SetChannelProcessOrder = 'set-channel-process-order', + SetChannelIcon = 'set-channel-icon', +} + +export function createChannelActions(self: InstanceBaseExt): CompanionActionDefinitions { + const send = self.sendCommand + const ensureLoaded = self.ensureLoaded + const state = self.state + + const actions: { [id in ChannelActions]: CompanionActionWithCallback | undefined } = { + [ChannelActions.SetChannelMainConnection]: { + name: 'Set Channel Main Connection', + options: [ + GetDropdown('Channel', 'channel', state.namedChoices.channels), + GetDropdown('Group', 'group', getSourceGroupChoices()), + GetNumberField('Index', 'index', 1, 64, 1, 1), + ], + callback: async (event) => { + let cmd = Commands.MainInputConnectionGroup(ActionUtil.getNodeNumber(event, 'channel')) + send(cmd, event.options.group as string) + cmd = Commands.MainInputConnectionIndex(ActionUtil.getNodeNumber(event, 'channel')) + send(cmd, ActionUtil.getNumber(event, 'index')) + }, + }, + [ChannelActions.SetChannelFilterModel]: { + name: 'Set Channel Filter Model', + options: [ + GetDropdown('Channel', 'channel', state.namedChoices.channels), + GetDropdown('Filter', 'filter', getFilterModelOptions()), + ], + callback: async (event) => { + const cmd = Commands.FilterModel(ActionUtil.getNodeNumber(event, 'channel')) + send(cmd, ActionUtil.getString(event, 'filter')) + }, + }, + [ChannelActions.SetChannelEqType]: { + name: 'Set Channel EQ Model', + options: [ + GetDropdown('Channel', 'channel', state.namedChoices.channels), + GetDropdown('EQ Model', 'model', EqModelChoice), + ], + callback: async (event) => { + const cmd = Commands.EqModel(ActionUtil.getNodeNumber(event, 'channel')) + send(cmd, ActionUtil.getString(event, 'model')) + }, + }, + [ChannelActions.SetChannelEqParameter]: { + name: 'Set Channel EQ Parameter', + options: [ + GetDropdown('Channel', 'channel', state.namedChoices.channels), + ...EqModelDropdown('model'), + ...EqParameterDropdown('band', 'model'), + ], + callback: async (event) => { + const cmd = Commands.EqModel(ActionUtil.getNodeNumber(event, 'channel')) + send(cmd, ActionUtil.getString(event, 'model')) + }, + subscribe: (event) => { + ensureLoaded(Commands.EqModel(ActionUtil.getNodeNumber(event, 'channel'))) + }, + learn: (event) => { + return { + ...event.options, + model: getStringFromState(Commands.EqModel(ActionUtil.getNodeNumber(event, 'channel')), state), + } + }, + }, + [ChannelActions.SetChannelProcessOrder]: { + name: 'Set Channel Process Order', + options: [ + GetDropdown('Channel', 'channel', state.namedChoices.channels), + GetDropdown('Order', 'order', getChannelProcessOrderChoices()), + ], + callback: async (event) => { + const cmd = Commands.ProcessOrder(ActionUtil.getNodeNumber(event, 'channel')) + send(cmd, ActionUtil.getString(event, 'order')) + }, + }, + [ChannelActions.SetChannelIcon]: { + name: 'Set Channel Icon', + options: [ + GetDropdown('Channel', 'channel', state.namedChoices.channels), + GetDropdown('Icon', 'icon', getIconChoices()), + ], + callback: async (event) => { + const cmd = Commands.Icon(ActionUtil.getNodeNumber(event, 'channel')) + send(cmd, ActionUtil.getNumber(event, 'icon')) + }, + }, + } + + return actions +} diff --git a/src/actions/common.ts b/src/actions/common.ts new file mode 100644 index 0000000..cd24006 --- /dev/null +++ b/src/actions/common.ts @@ -0,0 +1,651 @@ +import { CompanionActionDefinition } from '@companion-module/base' +import { SetRequired } from 'type-fest' // eslint-disable-line n/no-missing-import + +export type CompanionActionWithCallback = SetRequired + +import { CompanionActionDefinitions } from '@companion-module/base' +import { + GetFaderInputField, + GetDropdown, + GetPanoramaSlider, + GetMuteDropdown, + GetTextField, + GetFaderDeltaInputField, + GetPanoramaDeltaSlider, + GetColorDropdown, + GetNumberField, +} from '../choices/common.js' +import { getNodeNumber, getNumber, runTransition } from './utils.js' +import { InstanceBaseExt } from '../types.js' +import { WingConfig } from '../config.js' +import * as ActionUtil from './utils.js' +import { StateUtil } from '../state/index.js' +import { FadeDurationChoice } from '../choices/fades.js' + +export enum CommonActions { + SetColor = 'set-color', + SetName = 'set-name', + // Gain + SetGain = 'set-gain', + StoreGain = 'store-gain', + RestoreGain = 'restore-gain', + DeltaGain = 'delta-gain', + UndoDeltaGain = 'undo-delta-gain', + //////////// NORMAL + SetMute = 'set-mute', + // Fader + SetFader = 'set-fader', + StoreFader = 'store-fader', + RestoreFader = 'restore-fader', + DeltaFader = 'fader-delta', + UndoDeltaFader = 'undo-fader-delta', + // Panorama + SetPanorama = 'set-panorama', + StorePanorama = 'store-panorama', + RestorePanorama = 'restore-panorama', + DeltaPanorama = 'panorama-delta', + UndoDeltaPanorama = 'undo-panorama', + //////////// SEND + SetSendMute = 'set-send-mute', + // Send Fader + SetSendFader = 'set-send-fader', + StoreSendFader = 'store-send-fader', + RestoreSendFader = 'restore-send-fader', + DeltaSendFader = 'delta-send-fader', + UndoDeltaSendFader = 'undo-send-fader', + // Send Panorama + SetSendPanorama = 'set-send-panorama', + StoreSendPanorama = 'store-send-panorama', + RestoreSendPanorama = 'restore-send-panorama', + DeltaSendPanorama = 'delta-send-panorama', + UndoDeltaSendPanorama = 'undo-send-panorama', +} + +export function createCommonActions(self: InstanceBaseExt): CompanionActionDefinitions { + const send = self.sendCommand + const ensureLoaded = self.ensureLoaded + const state = self.state + const transitions = self.transitions + + const allChannels = [ + ...state.namedChoices.channels, + ...state.namedChoices.auxes, + ...state.namedChoices.busses, + ...state.namedChoices.matrices, + ...state.namedChoices.mains, + ] + + const allSendSources = [...state.namedChoices.channels, ...state.namedChoices.auxes, ...state.namedChoices.busses] + + const actions: { [id in CommonActions]: CompanionActionWithCallback | undefined } = { + [CommonActions.SetColor]: { + name: 'Set Color', + description: 'Set the scribble strip color of a channel, aux, bus, matrix or main.', + options: [GetDropdown('Selection', 'sel', allChannels), GetColorDropdown('color', 'Color')], + callback: async (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getColorCommand(sel, getNodeNumber(event, 'sel')) + send(cmd, getNumber(event, 'color')) + }, + }, + [CommonActions.SetName]: { + name: 'Set Name', + options: [GetDropdown('Selection', 'sel', allChannels), GetTextField('Name', 'name')], + callback: async (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getNameCommand(sel, getNodeNumber(event, 'sel')) + send(cmd, event.options.name as string) + }, + }, + //////////////////////////////////////////////////////////////// + // Gain + //////////////////////////////////////////////////////////////// + [CommonActions.SetGain]: { + name: 'Set Gain', + description: 'Set the gain of a channel or aux.', + options: [ + GetDropdown('Channel', 'channel', [...state.namedChoices.channels, ...state.namedChoices.auxes]), + GetNumberField('Gain (dB)', 'gain', -3.0, 45.5, 0.5, 0, true), + ...FadeDurationChoice, + ], + callback: async (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getGainCommand(sel, getNodeNumber(event, 'sel')) + ActionUtil.runTransition(cmd, 'gain', event, state, transitions) + }, + subscribe: (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getGainCommand(sel, getNodeNumber(event, 'sel')) + ensureLoaded(cmd) + }, + learn: (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getGainCommand(sel, getNodeNumber(event, 'sel')) + return { level: StateUtil.getNumberFromState(cmd, state) } + }, + }, + [CommonActions.StoreGain]: { + name: 'Store Gain', + description: 'Store the gain of a channel or aux.', + options: [GetDropdown('Selection', 'sel', allChannels)], + callback: async (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getGainCommand(sel, getNodeNumber(event, 'sel')) + StateUtil.storeValueForCommand(cmd, state) + }, + subscribe: (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getGainCommand(sel, getNodeNumber(event, 'sel')) + ensureLoaded(cmd) + }, + }, + [CommonActions.RestoreGain]: { + name: 'Restore Gain', + description: 'Restore the gain of a channel or aux.', + options: [GetDropdown('Selection', 'sel', allChannels), ...FadeDurationChoice], + callback: async (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getGainCommand(sel, getNodeNumber(event, 'sel')) + const restoreVal = StateUtil.getValueFromKey(cmd, state) + ActionUtil.runTransition(cmd, 'gain', event, state, transitions, restoreVal) + }, + }, + [CommonActions.DeltaGain]: { + name: 'Adjust Gain', + description: 'Adjust the gain of a channel or aux.', + options: [ + GetDropdown('Selection', 'sel', allChannels), + GetNumberField('Gain (dB)', 'gain', -48.5, 48.5, 0.5, 0, true), + ], + callback: async (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getGainCommand(sel, getNodeNumber(event, 'sel')) + let targetValue = StateUtil.getNumberFromState(cmd, state) + const delta = event.options.delta as number + state.storeDelta(cmd, delta) + if (targetValue != undefined) { + targetValue += delta + console.log('targetValue', targetValue) + ActionUtil.runTransition(cmd, 'gain', event, state, transitions, targetValue) + } + }, + subscribe: (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getGainCommand(sel, getNodeNumber(event, 'sel')) + ensureLoaded(cmd) + }, + }, + [CommonActions.UndoDeltaGain]: { + name: 'Undo Gain Adjust', + description: 'Undo the previous gain adjustment on a channel or aux.', + options: [GetDropdown('Selection', 'sel', allChannels), ...FadeDurationChoice], + callback: async (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getGainCommand(sel, getNodeNumber(event, 'sel')) + let targetValue = StateUtil.getNumberFromState(cmd, state) + const delta = state.restoreDelta(cmd) + if (targetValue != undefined) { + targetValue -= delta + ActionUtil.runTransition(cmd, 'gain', event, state, transitions, targetValue) + } + }, + subscribe: (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getGainCommand(sel, getNodeNumber(event, 'sel')) + ensureLoaded(cmd) + }, + }, + //////////////////////////////////////////////////////////////// + // NORMAL + //////////////////////////////////////////////////////////////// + [CommonActions.SetMute]: { + name: 'Set Mute', + description: 'Set or toggle the mute state of a channel, aux, bus, matrix or main.', + options: [GetDropdown('Selection', 'sel', allChannels), GetMuteDropdown('mute')], + callback: async (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getMuteCommand(sel, getNodeNumber(event, 'sel')) + const val = ActionUtil.getNumber(event, 'mute') + const currentVal = StateUtil.getBooleanFromState(cmd, state) + if (val < 2) { + send(cmd, val) + } else { + send(cmd, Number(!currentVal)) + } + }, + subscribe: (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getMuteCommand(sel, getNodeNumber(event, 'sel')) + ensureLoaded(cmd) + }, + }, + //////////////////////////////////////////////////////////////// + // Fader + //////////////////////////////////////////////////////////////// + [CommonActions.SetFader]: { + name: 'Set Level', + description: 'Set the fader level of a channel, aux, bus, matrix or main to a value.', + options: [GetDropdown('Selection', 'sel', allChannels), ...GetFaderInputField('level')], + callback: async (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getFaderCommand(sel, getNodeNumber(event, 'sel')) + runTransition(cmd, 'level', event, state, transitions) + }, + subscribe: (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getFaderCommand(sel, getNodeNumber(event, 'sel')) + ensureLoaded(cmd) + }, + learn: (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getFaderCommand(sel, getNodeNumber(event, 'sel')) + return { level: StateUtil.getNumberFromState(cmd, state) } + }, + }, + [CommonActions.StoreFader]: { + name: 'Store Level', + description: 'Store the fader level of a channel, aux, bus, matrix or main.', + options: [GetDropdown('Selection', 'sel', allChannels)], + callback: async (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getFaderCommand(sel, getNodeNumber(event, 'sel')) + StateUtil.storeValueForCommand(cmd, state) + }, + subscribe: (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getFaderCommand(sel, getNodeNumber(event, 'sel')) + ensureLoaded(cmd) + }, + }, + [CommonActions.RestoreFader]: { + name: 'Restore Level', + description: 'Restore the fader level of a channel, aux, bus, matrix or main.', + options: [GetDropdown('Selection', 'sel', allChannels), ...FadeDurationChoice], + callback: async (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getFaderCommand(sel, getNodeNumber(event, 'sel')) + const restoreVal = StateUtil.getValueFromKey(cmd, state) + ActionUtil.runTransition(cmd, 'level', event, state, transitions, restoreVal) + }, + }, + [CommonActions.DeltaFader]: { + name: 'Adjust Level', + description: 'Adjust the level of a channel, aux, bus, matrix or main.', + options: [GetDropdown('Selection', 'sel', allChannels), ...GetFaderDeltaInputField('delta', 'Adjust (dB)')], + callback: async (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getFaderCommand(sel, getNodeNumber(event, 'sel')) + let targetValue = StateUtil.getNumberFromState(cmd, state) + const delta = event.options.delta as number + state.storeDelta(cmd, delta) + if (targetValue != undefined) { + targetValue += delta + console.log('targetValue', targetValue) + ActionUtil.runTransition(cmd, 'level', event, state, transitions, targetValue) + } + }, + subscribe: (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getFaderCommand(sel, getNodeNumber(event, 'sel')) + ensureLoaded(cmd) + }, + }, + [CommonActions.UndoDeltaFader]: { + name: 'Undo Level Adjust', + description: 'Undo the previous level adjustment on a channel, aux, bus, matrix or main.', + options: [GetDropdown('Selection', 'sel', allChannels), ...FadeDurationChoice], + callback: async (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getFaderCommand(sel, getNodeNumber(event, 'sel')) + let targetValue = StateUtil.getNumberFromState(cmd, state) + const delta = state.restoreDelta(cmd) + if (targetValue != undefined) { + targetValue -= delta + ActionUtil.runTransition(cmd, 'level', event, state, transitions, targetValue) + } + }, + subscribe: (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getFaderCommand(sel, getNodeNumber(event, 'sel')) + ensureLoaded(cmd) + }, + }, + + //////////////////////////////////////////////////////////////// + // Panorama + //////////////////////////////////////////////////////////////// + [CommonActions.SetPanorama]: { + name: 'Set Panorama', + description: 'Set the panorama of a channel, aux, bus, matrix or main.', + options: [GetDropdown('Selection', 'sel', allChannels), ...GetPanoramaSlider('pan')], + callback: async (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getPanoramaCommand(sel, getNodeNumber(event, 'sel')) + runTransition(cmd, 'pan', event, state, transitions) + }, + subscribe: (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getPanoramaCommand(sel, getNodeNumber(event, 'sel')) + ensureLoaded(cmd) + }, + learn: (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getPanoramaCommand(sel, getNodeNumber(event, 'sel')) + return { pan: StateUtil.getNumberFromState(cmd, state) } + }, + }, + [CommonActions.StorePanorama]: { + name: 'Store Panorama', + description: 'Store the panorama of a channel, aux, bus, matrix or main.', + options: [GetDropdown('Selection', 'sel', allChannels)], + callback: async (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getPanoramaCommand(sel, getNodeNumber(event, 'sel')) + StateUtil.storeValueForCommand(cmd, state) + }, + subscribe: (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getPanoramaCommand(sel, getNodeNumber(event, 'sel')) + ensureLoaded(cmd) + }, + }, + [CommonActions.RestorePanorama]: { + name: 'Restore Panorama', + description: 'Restore the panorama of a channel, aux, bus, matrix or main.', + options: [GetDropdown('Selection', 'sel', allChannels), ...FadeDurationChoice], + callback: async (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getPanoramaCommand(sel, getNodeNumber(event, 'sel')) + const restoreVal = StateUtil.getValueFromKey(cmd, state) + ActionUtil.runTransition(cmd, 'pan', event, state, transitions, restoreVal) + }, + }, + [CommonActions.DeltaPanorama]: { + name: 'Adjust Panorama', + description: 'Adjust the panorama of a channel, aux, bus, matrix or main.', + options: [GetDropdown('Selection', 'sel', allChannels), ...GetPanoramaDeltaSlider('pan', 'Panorama')], + callback: async (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getPanoramaCommand(sel, getNodeNumber(event, 'sel')) + let targetValue = StateUtil.getNumberFromState(cmd, state) + const delta = event.options.delta as number + state.storeDelta(cmd, delta) + if (targetValue != undefined) { + targetValue += delta + console.log('targetValue', targetValue) + ActionUtil.runTransition(cmd, 'pan', event, state, transitions, targetValue) + } + }, + subscribe: (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getPanoramaCommand(sel, getNodeNumber(event, 'sel')) + ensureLoaded(cmd) + }, + }, + [CommonActions.UndoDeltaPanorama]: { + name: 'Undo Panorama Adjust', + description: 'Undo the previous adjustment on the panorama of a channel, aux, bus, matrix or main.', + options: [GetDropdown('Selection', 'sel', allChannels), ...FadeDurationChoice], + callback: async (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getPanoramaCommand(sel, getNodeNumber(event, 'sel')) + let targetValue = StateUtil.getNumberFromState(cmd, state) + const delta = state.restoreDelta(cmd) + if (targetValue != undefined) { + targetValue -= delta + ActionUtil.runTransition(cmd, 'pan', event, state, transitions, targetValue) + } + }, + subscribe: (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getPanoramaCommand(sel, getNodeNumber(event, 'sel')) + ensureLoaded(cmd) + }, + }, + + //////////////////////////////////////////////////////////////// + // Send Fader + //////////////////////////////////////////////////////////////// + [CommonActions.SetSendFader]: { + name: 'Set Send Level', + description: 'Set the send level from a channel, aux or bus to a bus.', + options: [ + GetDropdown('From', 'src', allSendSources), + GetDropdown('To Bus', 'dest', state.namedChoices.busses), + ...GetFaderInputField('level'), + ], + callback: async (event) => { + const src = event.options.src as string + const cmd = ActionUtil.getSendLevelCommand(src, getNodeNumber(event, 'src'), getNodeNumber(event, 'dest')) + runTransition(cmd, 'level', event, state, transitions) + }, + subscribe: (event) => { + const src = event.options.src as string + const cmd = ActionUtil.getSendLevelCommand(src, getNodeNumber(event, 'src'), getNodeNumber(event, 'dest')) + ensureLoaded(cmd) + }, + }, + [CommonActions.StoreSendFader]: { + name: 'Store Send Level', + description: 'Store the send level from a channel, aux or bus to a bus.', + options: [ + GetDropdown('From', 'src', allSendSources), + GetDropdown('To Bus', 'dest', state.namedChoices.busses), + ...GetFaderDeltaInputField('delta', 'Adjust (dB)'), + ], + callback: async (event) => { + const src = event.options.src as string + const cmd = ActionUtil.getSendLevelCommand(src, getNodeNumber(event, 'src'), getNodeNumber(event, 'dest')) + StateUtil.storeValueForCommand(cmd, state) + }, + subscribe: (event) => { + const src = event.options.src as string + const cmd = ActionUtil.getSendLevelCommand(src, getNodeNumber(event, 'src'), getNodeNumber(event, 'dest')) + ensureLoaded(cmd) + }, + }, + [CommonActions.RestoreSendFader]: { + name: 'Restore Send Level', + description: 'Restore the send level from a channel, aux or bus to a bus.', + options: [ + GetDropdown('From', 'src', allSendSources), + GetDropdown('To Bus', 'dest', state.namedChoices.busses), + ...GetFaderDeltaInputField('delta', 'Adjust (dB)'), + ], + callback: async (event) => { + const src = event.options.src as string + const cmd = ActionUtil.getSendLevelCommand(src, getNodeNumber(event, 'src'), getNodeNumber(event, 'dest')) + const restoreVal = StateUtil.getValueFromKey(cmd, state) + ActionUtil.runTransition(cmd, 'level', event, state, transitions, restoreVal) + }, + }, + [CommonActions.DeltaSendFader]: { + name: 'Adjust Send Level', + description: 'Adjust the send level from a channel, aux or bus to a bus.', + options: [ + GetDropdown('From', 'src', allSendSources), + GetDropdown('To Bus', 'dest', state.namedChoices.busses), + ...GetFaderDeltaInputField('delta', 'Adjust (dB)'), + ], + callback: async (event) => { + const src = event.options.src as string + const cmd = ActionUtil.getSendLevelCommand(src, getNodeNumber(event, 'src'), getNodeNumber(event, 'dest')) + let targetValue = StateUtil.getNumberFromState(cmd, state) + const delta = event.options.delta as number + state.storeDelta(cmd, delta) + if (targetValue != undefined) { + targetValue += delta + console.log('targetValue', targetValue) + ActionUtil.runTransition(cmd, 'level', event, state, transitions, targetValue) + } + }, + subscribe: (event) => { + const src = event.options.src as string + const cmd = ActionUtil.getSendLevelCommand(src, getNodeNumber(event, 'src'), getNodeNumber(event, 'dest')) + ensureLoaded(cmd) + }, + }, + [CommonActions.UndoDeltaSendFader]: { + name: 'Undo Send Level Adjust', + description: 'Undo the previous send level adjustment from a channel, aux or bus to a bus.', + options: [ + GetDropdown('From', 'src', allSendSources), + GetDropdown('To Bus', 'dest', state.namedChoices.busses), + ...FadeDurationChoice, + ], + callback: async (event) => { + const src = event.options.src as string + const cmd = ActionUtil.getSendLevelCommand(src, getNodeNumber(event, 'src'), getNodeNumber(event, 'dest')) + let targetValue = StateUtil.getNumberFromState(cmd, state) + const delta = state.restoreDelta(cmd) + if (targetValue != undefined) { + targetValue -= delta + ActionUtil.runTransition(cmd, 'level', event, state, transitions, targetValue) + } + }, + subscribe: (event) => { + const src = event.options.src as string + const cmd = ActionUtil.getSendLevelCommand(src, getNodeNumber(event, 'src'), getNodeNumber(event, 'dest')) + ensureLoaded(cmd) + }, + }, + [CommonActions.SetSendMute]: { + name: 'Set Send Mute', + description: 'Set or toggle the mute state of a send from a channel, aux or bus to a bus.', + options: [ + GetDropdown('Selection', 'src', allSendSources), + GetDropdown('To Bus', 'dest', state.namedChoices.busses), + GetMuteDropdown('mute'), + ], + callback: async (event) => { + const sel = event.options.src as string + const cmd = ActionUtil.getSendMuteCommand(sel, getNodeNumber(event, 'src'), getNodeNumber(event, 'dest')) + const val = ActionUtil.getNumber(event, 'mute') + const currentVal = StateUtil.getBooleanFromState(cmd, state) + // The Send mutes need to be sent inverted becauxe it is an 'on' command + if (val >= 2) { + send(cmd, Number(!currentVal)) + } else { + if (val < 1) { + send(cmd, 1) + } else { + send(cmd, 0) + } + } + }, + subscribe: (event) => { + const sel = event.options.sel as string + const cmd = ActionUtil.getSendMuteCommand(sel, getNodeNumber(event, 'src'), getNodeNumber(event, 'dest')) + ensureLoaded(cmd) + }, + }, + //////////////////////////////////////////////////////////////// + // Send Panorama + //////////////////////////////////////////////////////////////// + [CommonActions.SetSendPanorama]: { + name: 'Set Send Panorama', + description: 'Set the panorama of a send from a channel or aux to a bus.', + options: [ + GetDropdown('From', 'src', [...state.namedChoices.channels, ...state.namedChoices.auxes]), + GetDropdown('To Bus', 'dest', state.namedChoices.busses), + ...GetPanoramaSlider('pan'), + ], + callback: async (event) => { + const src = event.options.src as string + const cmd = ActionUtil.getSendPanoramaCommand(src, getNodeNumber(event, 'src'), getNodeNumber(event, 'dest')) + runTransition(cmd, 'pan', event, state, transitions) + }, + subscribe: (event) => { + const src = event.options.src as string + const cmd = ActionUtil.getSendPanoramaCommand(src, getNodeNumber(event, 'src'), getNodeNumber(event, 'dest')) + ensureLoaded(cmd) + }, + }, + [CommonActions.StoreSendPanorama]: { + name: 'Store Send Panorama', + description: 'Store the panorama of a send from a channel or aux to a bus.', + options: [ + GetDropdown('From', 'src', [...state.namedChoices.channels, ...state.namedChoices.auxes]), + GetDropdown('To Bus', 'dest', state.namedChoices.busses), + ...GetPanoramaDeltaSlider('delta', 'Panorama'), + ], + callback: async (event) => { + const src = event.options.src as string + const cmd = ActionUtil.getSendPanoramaCommand(src, getNodeNumber(event, 'src'), getNodeNumber(event, 'dest')) + StateUtil.storeValueForCommand(cmd, state) + }, + subscribe: (event) => { + const src = event.options.src as string + const cmd = ActionUtil.getSendPanoramaCommand(src, getNodeNumber(event, 'src'), getNodeNumber(event, 'dest')) + ensureLoaded(cmd) + }, + }, + [CommonActions.RestoreSendPanorama]: { + name: 'Restore Send Panorama', + description: 'Restore the panorama of a send from a channel or aux to a bus.', + options: [ + GetDropdown('From', 'src', [...state.namedChoices.channels, ...state.namedChoices.auxes]), + GetDropdown('To Bus', 'dest', state.namedChoices.busses), + ...GetPanoramaDeltaSlider('delta', 'Panorama'), + ], + callback: async (event) => { + const src = event.options.src as string + const cmd = ActionUtil.getSendPanoramaCommand(src, getNodeNumber(event, 'src'), getNodeNumber(event, 'dest')) + const restoreVal = StateUtil.getValueFromKey(cmd, state) + ActionUtil.runTransition(cmd, 'pan', event, state, transitions, restoreVal) + }, + }, + [CommonActions.DeltaSendPanorama]: { + name: 'Adjust Send Panorama', + description: 'Adjust the panorama of a send from a channel or aux to a bus.', + options: [ + GetDropdown('From', 'src', [...state.namedChoices.channels, ...state.namedChoices.auxes]), + GetDropdown('To Bus', 'dest', state.namedChoices.busses), + ...GetPanoramaDeltaSlider('delta', 'Panorama'), + ], + callback: async (event) => { + const src = event.options.src as string + const cmd = ActionUtil.getSendPanoramaCommand(src, getNodeNumber(event, 'src'), getNodeNumber(event, 'dest')) + let targetValue = StateUtil.getNumberFromState(cmd, state) + const delta = event.options.delta as number + state.storeDelta(cmd, delta) + if (targetValue != undefined) { + targetValue += delta + console.log('targetValue', targetValue) + ActionUtil.runTransition(cmd, 'pan', event, state, transitions, targetValue) + } + }, + subscribe: (event) => { + const src = event.options.src as string + const cmd = ActionUtil.getSendPanoramaCommand(src, getNodeNumber(event, 'src'), getNodeNumber(event, 'dest')) + ensureLoaded(cmd) + }, + }, + [CommonActions.UndoDeltaSendPanorama]: { + name: 'Undo Send Panorama Adjust', + description: 'Undo the panorama adjustment of a send from a channel or aux to a bus.', + options: [ + GetDropdown('From', 'src', [...state.namedChoices.channels, ...state.namedChoices.auxes]), + GetDropdown('To Bus', 'dest', state.namedChoices.busses), + ...FadeDurationChoice, + ], + callback: async (event) => { + const src = event.options.src as string + const cmd = ActionUtil.getSendPanoramaCommand(src, getNodeNumber(event, 'src'), getNodeNumber(event, 'dest')) + let targetValue = StateUtil.getNumberFromState(cmd, state) + const delta = state.restoreDelta(cmd) + if (targetValue != undefined) { + targetValue -= delta + ActionUtil.runTransition(cmd, 'pan', event, state, transitions, targetValue) + } + }, + subscribe: (event) => { + const src = event.options.src as string + const cmd = ActionUtil.getSendPanoramaCommand(src, getNodeNumber(event, 'src'), getNodeNumber(event, 'dest')) + ensureLoaded(cmd) + }, + }, + } + + return actions +} diff --git a/src/actions/index.ts b/src/actions/index.ts new file mode 100644 index 0000000..7636a49 --- /dev/null +++ b/src/actions/index.ts @@ -0,0 +1,22 @@ +import { CompanionActionDefinitions } from '@companion-module/base' +import { createChannelActions } from '../actions/channel.js' +import { GetOtherActions as createOtherActions } from './other.js' +import { createBusActions as createBusActions } from './bus.js' +import { InstanceBaseExt } from '../types.js' +import { WingConfig } from '../config.js' +import { createAuxActions } from './aux.js' +import { createMainActions } from './main.js' +import { createCommonActions } from './common.js' + +export function createActions(self: InstanceBaseExt): CompanionActionDefinitions { + const actions = { + ...createCommonActions(self), + ...createOtherActions(self), + ...createChannelActions(self), + ...createBusActions(self), + ...createAuxActions(self), + ...createMainActions(self), + } + + return actions +} diff --git a/src/actions/main.ts b/src/actions/main.ts new file mode 100644 index 0000000..40f931c --- /dev/null +++ b/src/actions/main.ts @@ -0,0 +1,99 @@ +import { CompanionActionDefinitions } from '@companion-module/base' +import { GetFaderInputField, GetDropdown, GetPanoramaDeltaSlider } from '../choices/common.js' +import { getNodeNumber, runTransition } from './utils.js' +import { CompanionActionWithCallback } from './common.js' +import { MainCommands as Commands } from '../commands/main.js' +import { InstanceBaseExt } from '../types.js' +import { WingConfig } from '../config.js' +import * as ActionUtil from './utils.js' +import { StateUtil } from '../state/index.js' +import { FadeDurationChoice } from '../choices/fades.js' + +export enum MainActions { + MainToMatrixLevel = 'main-to-matrix-level', + MainToMatrixLevelStore = 'main-to-matrix-level-store', + MainToMatrixLevelRestore = 'main-to-matrix-level-restore', + MainToMatrixLevelDelta = 'main-to-matrix-level-delta', + MainToMatrixLevelUndoDelta = 'main-to-matrix-level-undo-delta', +} + +export function createMainActions(self: InstanceBaseExt): CompanionActionDefinitions { + const ensureLoaded = self.ensureLoaded + const state = self.state + const transitions = self.transitions + const actions: { [id in MainActions]: CompanionActionWithCallback | undefined } = { + [MainActions.MainToMatrixLevel]: { + name: 'Set Main to Matrix Level', + options: [ + GetDropdown('From Main', 'bus', state.namedChoices.mains), + GetDropdown('To Matrix', 'matrix', state.namedChoices.matrices), + ...GetFaderInputField('level'), + ], + callback: async (event) => { + const cmd = Commands.MatrixSendLevel(getNodeNumber(event, 'bus'), getNodeNumber(event, 'matrix')) + runTransition(cmd, 'level', event, state, transitions) + }, + subscribe: (event) => { + ensureLoaded(Commands.MatrixSendLevel(getNodeNumber(event, 'bus'), getNodeNumber(event, 'matrix'))) + }, + }, + [MainActions.MainToMatrixLevelStore]: { + name: 'Store Main to Matrix Level', + options: [GetDropdown('Main', 'main', state.namedChoices.mains)], + callback: async (event) => { + const cmd = Commands.MatrixSendLevel(getNodeNumber(event, 'main'), getNodeNumber(event, 'matrix')) + StateUtil.storeValueForCommand(cmd, state) + }, + subscribe: (event) => { + ensureLoaded(Commands.MatrixSendLevel(getNodeNumber(event, 'main'), getNodeNumber(event, 'matrix'))) + }, + }, + [MainActions.MainToMatrixLevelRestore]: { + name: 'Restore Main to Matrix Level', + options: [GetDropdown('Main', 'main', state.namedChoices.mains), ...FadeDurationChoice], + callback: async (event) => { + const cmd = Commands.MatrixSendLevel(getNodeNumber(event, 'main'), getNodeNumber(event, 'matrix')) + const restoreVal = StateUtil.getValueFromKey(cmd, state) + ActionUtil.runTransition(cmd, 'level', event, state, transitions, restoreVal) + }, + subscribe: (event) => { + ensureLoaded(Commands.MatrixSendLevel(getNodeNumber(event, 'main'), getNodeNumber(event, 'matrix'))) + }, + }, + [MainActions.MainToMatrixLevelDelta]: { + name: 'Adjust Main to Matrix Level', + options: [GetDropdown('Main', 'main', state.namedChoices.mains), ...GetPanoramaDeltaSlider('pan', 'Panorama')], + callback: async (event) => { + const cmd = Commands.MatrixSendLevel(getNodeNumber(event, 'main'), getNodeNumber(event, 'matrix')) + let targetValue = StateUtil.getNumberFromState(cmd, state) + const delta = event.options.delta as number + state.storeDelta(cmd, delta) + if (targetValue != undefined) { + targetValue += delta + ActionUtil.runTransition(cmd, 'level', event, state, transitions, targetValue) + } + }, + subscribe: (event) => { + ensureLoaded(Commands.MatrixSendLevel(getNodeNumber(event, 'main'), getNodeNumber(event, 'matrix'))) + }, + }, + [MainActions.MainToMatrixLevelUndoDelta]: { + name: 'Undo Main to Matrix Level Adjust', + options: [GetDropdown('Main', 'main', state.namedChoices.mains), ...FadeDurationChoice], + callback: async (event) => { + const cmd = Commands.MatrixSendLevel(getNodeNumber(event, 'main'), getNodeNumber(event, 'matrix')) + let targetValue = StateUtil.getNumberFromState(cmd, state) + const delta = state.restoreDelta(cmd) + if (targetValue != undefined) { + targetValue -= delta + ActionUtil.runTransition(cmd, 'level', event, state, transitions, targetValue) + } + }, + subscribe: (event) => { + ensureLoaded(Commands.MatrixSendLevel(getNodeNumber(event, 'main'), getNodeNumber(event, 'matrix'))) + }, + }, + } + + return actions +} diff --git a/src/actions/other.ts b/src/actions/other.ts new file mode 100644 index 0000000..971a0e7 --- /dev/null +++ b/src/actions/other.ts @@ -0,0 +1,52 @@ +import { CompanionActionDefinitions } from '@companion-module/base' +import { CompanionActionWithCallback } from './common.js' +import { InstanceBaseExt } from '../types.js' +import { WingConfig } from '../config.js' + +export enum OtherActionId { + SendCommand = 'send-command', + SendCommandWithNumber = 'send-command-with-number', +} + +export function GetOtherActions(self: InstanceBaseExt): CompanionActionDefinitions { + const send = self.sendCommand + + const actions: { [id in OtherActionId]: CompanionActionWithCallback | undefined } = { + [OtherActionId.SendCommand]: { + name: 'Send Command', + options: [ + { + type: 'textinput', + id: 'cmd', + label: 'Command', + }, + ], + callback: async (event) => { + send(event.options.cmd as string) + }, + }, + [OtherActionId.SendCommandWithNumber]: { + name: 'Send Command with Number', + options: [ + { + type: 'textinput', + id: 'cmd', + label: 'Command', + }, + { + type: 'number', + id: 'num', + label: 'Argument', + min: -1000000, + max: 1000000, + default: 0, + }, + ], + callback: async (event) => { + send(event.options.cmd as string, event.options.num as number) + }, + }, + } + + return actions +} diff --git a/src/actions/utils.ts b/src/actions/utils.ts new file mode 100644 index 0000000..9a4e9ba --- /dev/null +++ b/src/actions/utils.ts @@ -0,0 +1,196 @@ +import { WingTransitions } from '../transitions.js' +import { WingState } from '../state/index.js' +import { CompanionActionInfo, CompanionFeedbackInfo } from '@companion-module/base' +import { Easing } from '../easings.js' +import * as StateUtils from '../state/utils.js' +import { ChannelCommands } from '../commands/channel.js' +import { AuxCommands } from '../commands/aux.js' +import { BusCommands } from '../commands/bus.js' +import { MatrixCommands } from '../commands/matrix.js' +import { MainCommands } from '../commands/main.js' + +export function getNodeNumber(action: CompanionActionInfo | CompanionFeedbackInfo, id: string): number { + return action.options[id]?.toString().split('/')[2] as unknown as number +} + +export function getNumber(action: CompanionActionInfo, key: string, defaultValue?: number): number { + const rawVal = action.options[key] + if (defaultValue !== undefined && rawVal === undefined) { + return defaultValue + } + const val = Number(rawVal) + if (isNaN(val)) { + throw new Error(`Invalid option '${key}'`) + } + return val +} + +const getAlgorithm = (action: CompanionActionInfo, key: string): Easing.algorithm | undefined => { + const rawVal = action.options[key] + if (rawVal === undefined) { + return rawVal + } + return rawVal as Easing.algorithm +} + +const getCurve = (action: CompanionActionInfo, key: string): Easing.curve | undefined => { + const rawVal = action.options[key] + if (rawVal === undefined) { + return rawVal + } + return rawVal as Easing.curve +} + +export function getString(action: CompanionActionInfo, key: string, defaultValue?: string): string { + const rawVal = action.options[key] + if (defaultValue !== undefined && rawVal === undefined) { + return defaultValue + } + const val = rawVal as string + return val +} + +export function runTransition( + cmd: string, + valueId: string, + action: CompanionActionInfo, + state: WingState, + transitions: WingTransitions, + targetValue?: number, +): void { + const current = StateUtils.getNumberFromState(cmd, state) + const target = targetValue ?? getNumber(action, valueId) + transitions.run( + cmd, + current as number, + target, + getNumber(action, 'fadeDuration'), + getAlgorithm(action, 'fadeAlgorithm'), + getCurve(action, 'fadeType'), + ) + state.set(cmd, [{ type: 'f', value: target }]) +} + +export function getColorCommand(sel: string, val: number): string { + let cmd = '' + if (sel.startsWith('/ch')) { + cmd = ChannelCommands.Color(val) + } else if (sel.startsWith('/aux')) { + cmd = AuxCommands.Color(val) + } else if (sel.startsWith('/bus')) { + cmd = BusCommands.Color(val) + } else if (sel.startsWith('/mtx')) { + cmd = MatrixCommands.Color(val) + } else if (sel.startsWith('/main')) { + cmd = MainCommands.Color(val) + } + return cmd +} + +export function getNameCommand(sel: string, val: number): string { + let cmd = '' + if (sel.startsWith('/ch')) { + cmd = ChannelCommands.Name(val) + } else if (sel.startsWith('/aux')) { + cmd = AuxCommands.Name(val) + } else if (sel.startsWith('/bus')) { + cmd = BusCommands.Name(val) + } else if (sel.startsWith('/mtx')) { + cmd = MatrixCommands.Name(val) + } else if (sel.startsWith('/main')) { + cmd = MainCommands.Name(val) + } + return cmd +} + +export function getGainCommand(sel: string, val: number): string { + let cmd = '' + if (sel.startsWith('/ch')) { + cmd = ChannelCommands.InputGain(val) + } else if (sel.startsWith('/aux')) { + cmd = AuxCommands.InputGain(val) + } + return cmd +} + +export function getMuteCommand(sel: string, val: number): string { + let cmd = '' + if (sel.startsWith('/ch')) { + cmd = ChannelCommands.Mute(val) + } else if (sel.startsWith('/aux')) { + cmd = AuxCommands.Mute(val) + } else if (sel.startsWith('/bus')) { + cmd = BusCommands.Mute(val) + } else if (sel.startsWith('/mtx')) { + cmd = MatrixCommands.Mute(val) + } else if (sel.startsWith('/main')) { + cmd = MainCommands.Mute(val) + } + return cmd +} + +export function getFaderCommand(sel: string, val: number): string { + let cmd = '' + if (sel.startsWith('/ch')) { + cmd = ChannelCommands.Fader(val) + } else if (sel.startsWith('/aux')) { + cmd = AuxCommands.Fader(val) + } else if (sel.startsWith('/bus')) { + cmd = BusCommands.Fader(val) + } else if (sel.startsWith('/mtx')) { + cmd = MatrixCommands.Fader(val) + } else if (sel.startsWith('/main')) { + cmd = MainCommands.Fader(val) + } + return cmd +} + +export function getPanoramaCommand(sel: string, val: number): string { + let cmd = '' + if (sel.startsWith('/ch')) { + cmd = ChannelCommands.Pan(val) + } else if (sel.startsWith('/aux')) { + cmd = AuxCommands.Pan(val) + } else if (sel.startsWith('/bus')) { + cmd = BusCommands.Pan(val) + } else if (sel.startsWith('/mtx')) { + cmd = MatrixCommands.Pan(val) + } else if (sel.startsWith('/main')) { + cmd = MainCommands.Pan(val) + } + return cmd +} + +export function getSendMuteCommand(sel: string, src: number, dest: number): string { + let cmd = '' + if (sel.startsWith('/ch')) { + cmd = ChannelCommands.SendOn(src, dest) + } else if (sel.startsWith('/aux')) { + cmd = AuxCommands.SendOn(src, dest) + } else if (sel.startsWith('/bus')) { + cmd = BusCommands.SendOn(src, dest) + } + return cmd +} + +export function getSendLevelCommand(sel: string, src: number, dest: number): string { + let cmd = '' + if (sel.startsWith('/ch')) { + cmd = ChannelCommands.SendLevel(src, dest) + } else if (sel.startsWith('/aux')) { + cmd = AuxCommands.SendLevel(src, dest) + } else if (sel.startsWith('/bus')) { + cmd = BusCommands.SendLevel(src, dest) + } + return cmd +} + +export function getSendPanoramaCommand(sel: string, src: number, dest: number): string { + let cmd = '' + if (sel.startsWith('/ch')) { + cmd = ChannelCommands.SendPan(src, dest) + } else if (sel.startsWith('/aux')) { + cmd = AuxCommands.SendPan(src, dest) + } + return cmd +} diff --git a/src/choices/aux.ts b/src/choices/aux.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/choices/bus.ts b/src/choices/bus.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/choices/channel.ts b/src/choices/channel.ts new file mode 100644 index 0000000..33cfa0e --- /dev/null +++ b/src/choices/channel.ts @@ -0,0 +1,56 @@ +import { DropdownChoice } from '@companion-module/base' +import { getIdLabelPair } from '../choices/utils.js' + +export function getFilterModelOptions(): DropdownChoice[] { + return [ + getIdLabelPair('TILT', 'Tilt Filter'), + getIdLabelPair('MAX', 'Maxer Filter'), + getIdLabelPair('AP1', 'All-Pass 90'), + getIdLabelPair('AP2', 'All-Pass 180'), + ] +} + +export function getSendModeChoices(): DropdownChoice[] { + return [getIdLabelPair('PRE', 'Pre-Fade'), getIdLabelPair('POST', 'Post-Fade'), getIdLabelPair('GRP', 'Group')] +} + +export function getProcessOrderChoices(): DropdownChoice[] { + return [ + getIdLabelPair('G', 'Gate'), + getIdLabelPair('E', 'EQ'), + getIdLabelPair('D', 'Dynamics'), + getIdLabelPair('I', 'Insert'), + ] +} + +export function getChannelProcessOrderChoices(): DropdownChoice[] { + return [ + getIdLabelPair('GEDI', 'Gate / EQ / Dynamics / Insert'), + getIdLabelPair('GEID', 'Gate / EQ / Insert / Dynamics'), + getIdLabelPair('GIED', 'Gate / Insert / EQ / Dynamics'), + getIdLabelPair('GIDE', 'Gate / Insert / Dynamics / EQ'), + getIdLabelPair('GDEI', 'Gate / Dynamics / EQ / Insert'), + getIdLabelPair('GDIE', 'Gate / Dynamics / Insert / EQ'), + + getIdLabelPair('EGDI', 'EQ / Gate / Dynamics / Insert'), + getIdLabelPair('EGID', 'EQ / Gate / Insert / Dynamics'), + getIdLabelPair('EIGD', 'EQ / Insert / Gate / Dynamics'), + getIdLabelPair('EIDG', 'EQ / Insert / Dynamics / Gate'), + getIdLabelPair('EDGI', 'EQ / Dynamics / Gate / Insert'), + getIdLabelPair('EDIG', 'EQ / Dynamics / Insert / Gate'), + + getIdLabelPair('IGED', 'Insert / Gate / EQ / Dynamics'), + getIdLabelPair('IGDE', 'Insert / Gate / Dynamics / EQ'), + getIdLabelPair('IEGD', 'Insert / EQ / Gate / Dynamics'), + getIdLabelPair('IEDG', 'Insert / EQ / Dynamics / Gate'), + getIdLabelPair('IDGE', 'Insert / Dynamics / Gate / EQ'), + getIdLabelPair('IDEG', 'Insert / Dynamics / EQ / Gate'), + + getIdLabelPair('DGEI', 'Dynamics / Gate / EQ / Insert'), + getIdLabelPair('DGIE', 'Dynamics / Gate / Insert / EQ'), + getIdLabelPair('DEGI', 'Dynamics / EQ / Gate / Insert'), + getIdLabelPair('DEIG', 'Dynamics / EQ / Insert / Gate'), + getIdLabelPair('DIGE', 'Dynamics / Insert / Gate / EQ'), + getIdLabelPair('DIEG', 'Dynamics / Insert / EQ / Gate'), + ] +} diff --git a/src/choices/common.ts b/src/choices/common.ts new file mode 100644 index 0000000..e047658 --- /dev/null +++ b/src/choices/common.ts @@ -0,0 +1,420 @@ +import { + combineRgb, + CompanionInputFieldDropdown, + CompanionInputFieldNumber, + CompanionInputFieldTextInput, + DropdownChoice, + InputValue, +} from '@companion-module/base' +import { FadeDurationChoice } from './fades.js' +import { getIdLabelPair } from '../choices/utils.js' + +export function GetNumberField( + label: string, + id: string, + min: number, + max: number, + step?: number, + defaultValue?: number, + range?: boolean, + tooltip?: string, +): CompanionInputFieldNumber { + return { + type: 'number', + label: label, + id: id, + default: defaultValue ?? 0, + min: min, + step: step, + max: max, + range: range, + tooltip: tooltip, + } +} + +export function GetTextField( + label: string, + id: string, + defaultValue?: string, + tooltip?: string, +): CompanionInputFieldTextInput { + return { + type: 'textinput', + label: label, + id: id, + default: defaultValue, + tooltip: tooltip, + } +} + +export function GetSlider( + label: string, + id: string, + min: number, + max: number, + step?: number, + defaultValue?: number, + tooltip?: string, +): CompanionInputFieldNumber { + return GetNumberField(label, id, min, max, step, defaultValue, true, tooltip) +} + +export function GetDropdown( + label: string, + id: string, + choices: DropdownChoice[], + defaultChoice?: string, + tooltip?: string, +): CompanionInputFieldDropdown { + return { + type: 'dropdown', + label: label, + id: id, + default: defaultChoice ?? choices[0].id, + choices: choices, + tooltip: tooltip, + } +} + +export function GetMuteDropdown(id: string, label?: string, includeToggle?: boolean): CompanionInputFieldDropdown { + const dropdown = GetDropdown( + label ?? 'Mute', + id, + [getIdLabelPair('0', 'Unmute'), getIdLabelPair('1', 'Mute')], + '1', + 'Select whether to Mute, Unmute or Toggle your selected target', + ) + if (includeToggle == false) return dropdown + + return { ...dropdown, choices: [...dropdown.choices, getIdLabelPair('2', 'Toggle')] } +} + +export function GetPanoramaSlider( + id: string, + name?: string, +): [CompanionInputFieldNumber, CompanionInputFieldNumber, CompanionInputFieldDropdown, CompanionInputFieldDropdown] { + return [ + GetSlider( + name ?? 'Panorama', + id, + -100, + 100, + 1, + 0, + 'Set the panorama of the selected target between -100 (Left) and +100 (Right)', + ), + ...FadeDurationChoice, + ] +} + +export function GetPanoramaDeltaSlider( + id: string, + name?: string, +): [CompanionInputFieldNumber, CompanionInputFieldNumber, CompanionInputFieldDropdown, CompanionInputFieldDropdown] { + return [GetSlider(name ?? 'Panorama', id, -200, 200, 1, 0), ...FadeDurationChoice] +} + +export function GetGainSlider(id: string, name?: string): CompanionInputFieldNumber { + return GetSlider(name ?? 'Gain (dB)', id, -3, 45.5, 0.5, 10, 'Set the input gain of the selected target') +} + +export function GetFaderInputField( + id: string, + name?: string, +): [CompanionInputFieldNumber, CompanionInputFieldNumber, CompanionInputFieldDropdown, CompanionInputFieldDropdown] { + return [ + GetNumberField(name ?? 'Level (dB)', id, -144, 10, 0.1, 0, true, 'Set the fader level of the selected target'), + ...FadeDurationChoice, + ] +} + +export function GetFaderDeltaInputField( + id: string, + name?: string, +): [CompanionInputFieldNumber, CompanionInputFieldNumber, CompanionInputFieldDropdown, CompanionInputFieldDropdown] { + return [GetNumberField(name ?? 'Level (dB)', id, -154, 154, 1, 0, true), ...FadeDurationChoice] +} + +export function GetColorDropdown(id: string, label?: string): CompanionInputFieldDropdown { + return GetDropdown( + label ?? 'Color', + id, + [ + getIdLabelPair('1', 'Gray Blue'), + getIdLabelPair('2', 'Medium Blue'), + getIdLabelPair('3', 'Dark Blue'), + getIdLabelPair('4', 'Turquoise'), + getIdLabelPair('5', 'Green'), + getIdLabelPair('6', 'Olive Green'), + getIdLabelPair('7', 'Yellow'), + getIdLabelPair('8', 'Orange'), + getIdLabelPair('9', 'Red'), + getIdLabelPair('10', 'Coral'), + getIdLabelPair('11', 'Pink'), + getIdLabelPair('12', 'Mauve'), + ], + '1', + 'Set the color of the selected target', + ) +} + +export function getIconChoices(): DropdownChoice[] { + return [ + // General + getIdLabelPair('0', 'Empty'), + getIdLabelPair('1', 'XLR Connector'), + getIdLabelPair('2', 'Jack Connector'), + getIdLabelPair('3', 'Mini Jack Connector'), + getIdLabelPair('4', 'RCA Connector'), + getIdLabelPair('5', 'Fader'), + getIdLabelPair('6', 'FX'), + getIdLabelPair('7', 'Matrix'), + getIdLabelPair('8', 'Bass Clef'), + getIdLabelPair('9', 'Treble Clef'), + getIdLabelPair('10', 'Routing'), + getIdLabelPair('11', 'Bus'), + getIdLabelPair('12', 'Sum'), + getIdLabelPair('13', 'Smile'), + getIdLabelPair('14', 'Wing Logo'), + + // Vocals and Mics + getIdLabelPair('100', 'Microphone'), + getIdLabelPair('101', 'Wireless Microphone'), + getIdLabelPair('102', 'Handheld Microphone'), + getIdLabelPair('103', 'Condenser Microphone'), + getIdLabelPair('104', 'Boundary Microphone'), + getIdLabelPair('105', 'Lapel Microphone'), + getIdLabelPair('106', 'Boundary Microphone (Alternative)'), + getIdLabelPair('107', 'Headset Microphone'), + getIdLabelPair('108', 'Overhead Microphone'), + getIdLabelPair('109', 'Speaker Icon'), + getIdLabelPair('110', 'Studio Microphone'), + getIdLabelPair('111', 'Podcast Microphone'), + getIdLabelPair('112', 'Choir'), + getIdLabelPair('113', 'Female Vocal'), + getIdLabelPair('114', 'Male Vocal'), + + // Drums and Percussions + getIdLabelPair('200', 'Kick In'), + getIdLabelPair('201', 'Kick Out'), + getIdLabelPair('202', 'Snare Top'), + getIdLabelPair('203', 'Snare Bottom'), + getIdLabelPair('204', 'Snare Drum'), + getIdLabelPair('205', 'Hi-Hat'), + getIdLabelPair('206', 'High Tom'), + getIdLabelPair('207', 'Mid Tom'), + getIdLabelPair('208', 'Low Tom'), + getIdLabelPair('209', 'Floor Tom'), + getIdLabelPair('210', 'Drum'), + getIdLabelPair('211', 'Crash Cymbal'), + getIdLabelPair('212', 'Ride Cymbal'), + getIdLabelPair('213', 'Cowbell'), + getIdLabelPair('214', 'Tambourine'), + getIdLabelPair('215', 'Bongos'), + getIdLabelPair('216', 'Conga'), + getIdLabelPair('217', 'Timpani'), + getIdLabelPair('218', 'Cajon'), + getIdLabelPair('219', 'Shaker'), + getIdLabelPair('220', 'Marimba'), + getIdLabelPair('221', 'Hang Drum'), + getIdLabelPair('222', 'Triangle'), + getIdLabelPair('223', 'Drum Machine'), + getIdLabelPair('224', 'Claps'), + + // Strings and Winds + getIdLabelPair('300', 'Electric Guitar 1'), + getIdLabelPair('301', 'Electric Guitar 2'), + getIdLabelPair('302', 'Violin'), + getIdLabelPair('303', 'Oud'), + getIdLabelPair('304', 'Acoustic Guitar'), + getIdLabelPair('305', 'Electric Guitar 3'), + getIdLabelPair('306', 'Electric Guitar 4'), + getIdLabelPair('307', 'Electric Guitar 5'), + getIdLabelPair('308', 'Double Bass'), + getIdLabelPair('309', 'Viola'), + getIdLabelPair('310', 'Cello'), + getIdLabelPair('311', 'Baritone'), + getIdLabelPair('312', 'Saxophone'), + getIdLabelPair('313', 'Trombone'), + getIdLabelPair('314', 'Trumpet'), + getIdLabelPair('315', 'Harp'), + getIdLabelPair('316', 'Organ'), + getIdLabelPair('317', 'Harmonica'), + getIdLabelPair('318', 'Transverse Flute'), + getIdLabelPair('319', 'Clarinet'), + + // Keys + getIdLabelPair('400', 'Grand Piano'), + getIdLabelPair('401', 'Upright Piano'), + getIdLabelPair('402', 'Synthesizer 1'), + getIdLabelPair('403', 'Synthesizer 2'), + getIdLabelPair('404', 'Synthesizer 3'), + getIdLabelPair('405', 'Synthesizer 4'), + getIdLabelPair('406', 'Keytar'), + getIdLabelPair('407', 'Piano Stand'), + getIdLabelPair('408', 'Piano Cross Stand'), + getIdLabelPair('409', 'Hammond Organ'), + + // Speakers + getIdLabelPair('500', 'Full Amplifier'), + getIdLabelPair('501', '2x2 Cabinet'), + getIdLabelPair('502', 'Cabinet'), + getIdLabelPair('503', 'Speaker'), + getIdLabelPair('504', 'Speaker Pair'), + getIdLabelPair('505', 'Speaker with Microphone Set'), + getIdLabelPair('506', 'Subwoofer'), + getIdLabelPair('507', 'Studio Monitor'), + getIdLabelPair('508', 'Studio Monitor 2'), + getIdLabelPair('509', 'Column Speaker'), + getIdLabelPair('510', 'Wall Speaker'), + getIdLabelPair('511', 'Hanging Speaker'), + getIdLabelPair('512', 'Speaker Stand'), + getIdLabelPair('513', 'Speaker Stand Pair'), + getIdLabelPair('514', 'Delay Speakers'), + getIdLabelPair('515', 'Left Monitor'), + getIdLabelPair('516', 'Right Monitor'), + getIdLabelPair('517', 'Monitor'), + getIdLabelPair('518', 'Large Monitor'), + getIdLabelPair('519', 'Large Monitor Pair'), + getIdLabelPair('520', 'Left Wedge Monitor'), + getIdLabelPair('521', 'Right Wedge Monitor'), + getIdLabelPair('522', 'Center Wedge Monitor'), + getIdLabelPair('523', 'Line Array'), + getIdLabelPair('524', 'Ceiling Speaker'), + + // Specials + getIdLabelPair('600', 'Hand'), + getIdLabelPair('601', 'Ear'), + getIdLabelPair('602', 'Headphones'), + getIdLabelPair('603', 'Speaker A'), + getIdLabelPair('604', 'Speaker B'), + getIdLabelPair('605', 'Laptop'), + getIdLabelPair('606', 'MP3 Player'), + getIdLabelPair('607', 'Smartphone'), + getIdLabelPair('608', 'USB Drive'), + getIdLabelPair('609', 'SD Card'), + getIdLabelPair('610', 'CD'), + getIdLabelPair('611', 'Turntable'), + getIdLabelPair('612', 'Reel-to-Reel Player'), + getIdLabelPair('613', 'Cassette Player'), + getIdLabelPair('614', 'Patchbay'), + ] +} + +export enum NumberComparator { + Equal = 'eq', + NotEqual = 'ne', + LessThan = 'lt', + LessThanEqual = 'lte', + GreaterThan = 'gt', + GreaterThanEqual = 'gte', +} + +export function GetNumberComparator(id: string, label?: string): CompanionInputFieldDropdown { + const options = [ + { id: NumberComparator.Equal, label: '=' }, + { id: NumberComparator.NotEqual, label: '!=' }, + { id: NumberComparator.GreaterThan, label: '>' }, + { id: NumberComparator.GreaterThanEqual, label: '>=' }, + { id: NumberComparator.LessThan, label: '<' }, + { id: NumberComparator.LessThanEqual, label: '<=' }, + ] + return { + type: 'dropdown', + label: label ?? 'Function', + id: id, + default: NumberComparator.Equal, + choices: options, + } +} + +export function compareNumber( + target: InputValue | undefined, + comparitor: InputValue | undefined, + currentValue: number, +): boolean { + const targetValue = Number(target) + if (isNaN(targetValue)) { + return false + } + + switch (comparitor) { + case NumberComparator.GreaterThan: + return currentValue > targetValue + case NumberComparator.GreaterThanEqual: + return currentValue >= targetValue + case NumberComparator.LessThan: + return currentValue < targetValue + case NumberComparator.LessThanEqual: + return currentValue <= targetValue + case NumberComparator.NotEqual: + return currentValue != targetValue + default: + return currentValue === targetValue + } +} + +export function getSourceGroupChoices(): DropdownChoice[] { + return [ + getIdLabelPair('OFF', 'Off'), + getIdLabelPair('LCL', 'Local'), + getIdLabelPair('AUX', 'Aux'), + getIdLabelPair('A', 'AES A'), + getIdLabelPair('B', 'AES B'), + getIdLabelPair('C', 'AES C'), + getIdLabelPair('SC', 'StageConnect'), + getIdLabelPair('USB', 'USB Player'), + getIdLabelPair('CRD', 'Expansion Card'), + getIdLabelPair('MOD', 'Module'), + getIdLabelPair('PLAY', 'Player'), + getIdLabelPair('AES', 'AES/EBU'), + getIdLabelPair('USR', 'User'), + getIdLabelPair('OSC', 'Oscillator'), + getIdLabelPair('BUS', 'Bus'), + getIdLabelPair('MAIN', 'Main'), + getIdLabelPair('MTX', 'Matrix'), + ] +} + +export function getTimeFormatChoices(): DropdownChoice[] { + return [ + getIdLabelPair('hhmmss', 'Hours - Minutes - Seconds'), + getIdLabelPair('hhmm', 'Hours - Minutes'), + getIdLabelPair('mmss', 'Minutes - Seconds'), + getIdLabelPair('ss', 'Seconds'), + ] +} + +export function getTriStateColor( + text: string | undefined, + okText?: string, + errorText?: string, + okColor?: number, + errorColor?: number, + thirdColor?: number, +): number { + if (text == (okText ?? 'OK')) { + return okColor ?? combineRgb(0, 255, 0) + } else if (text == (errorText ?? 'ERR')) { + return errorColor ?? combineRgb(255, 0, 0) + } else { + return thirdColor ?? combineRgb(255, 255, 0) + } +} + +export function getTriStateTextColor( + text: string | undefined, + okText?: string, + errorText?: string, + okColor?: number, + errorColor?: number, + thirdColor?: number, +): number { + if (text == (okText ?? 'OK')) { + return okColor ?? combineRgb(255, 255, 255) + } else if (text == (errorText ?? 'ERR')) { + return errorColor ?? combineRgb(255, 255, 255) + } else { + return thirdColor ?? combineRgb(0, 0, 0) + } +} diff --git a/src/choices/eq.ts b/src/choices/eq.ts new file mode 100644 index 0000000..ceaf3b4 --- /dev/null +++ b/src/choices/eq.ts @@ -0,0 +1,98 @@ +import { DropdownChoice, SomeCompanionActionInputField } from '@companion-module/base' +import { getIdLabelPair } from '../choices/utils.js' + +export const EqTypes = { + Standard: { id: 'STD', label: 'Standard EQ' }, + SoulAnalog: { id: 'SOUL', label: 'Soul Analog EQ' }, + Even88: { id: 'E88', label: 'Even 88-Formant EQ' }, + Even84: { id: 'E84', label: 'Even 84 EQ' }, + FocusriteISA: { id: 'F110', label: 'Focusrite ISA 110 EQ' }, + PulsarP1: { id: 'PULSAR', label: 'Pulsar P1a/M5 EQ' }, + Mach: { id: 'MACH', label: 'Mach EQ4' }, +} + +export const EqModelChoice: DropdownChoice[] = [ + getIdLabelPair(EqTypes.Standard.id, EqTypes.Standard.label), + getIdLabelPair(EqTypes.SoulAnalog.id, EqTypes.SoulAnalog.label), + getIdLabelPair(EqTypes.Even88.id, EqTypes.Even88.label), + getIdLabelPair(EqTypes.Even84.id, EqTypes.Even84.label), + getIdLabelPair(EqTypes.FocusriteISA.id, EqTypes.FocusriteISA.label), + getIdLabelPair(EqTypes.PulsarP1.id, EqTypes.PulsarP1.label), + getIdLabelPair(EqTypes.Mach.id, EqTypes.Mach.label), +] + +export function EqModelDropdown(id: string): SomeCompanionActionInputField[] { + return [ + { + type: 'dropdown', + label: 'Model', + id: id, + choices: EqModelChoice, + default: EqTypes.Standard.id, + }, + ] +} + +export function EqParameterDropdown(id: string, modelId: string, bus?: boolean): SomeCompanionActionInputField[] { + // let _bands = [] + // if (EqTypes.Standard.id) { + // _bands = StdEqBandChoices(bus) + // } else if (EqTypes.Even88.id) { + // _bands = Even88EqBandChoices() + // } else if (EqTypes.Even84.id) { + // _bands = Even84EqBandChoices() + // } + // if (_bands.length == 0) {} + + return [ + { + type: 'dropdown', + label: 'Band', + id: id, + choices: StdEqBandChoices(bus), + default: 'l', + isVisible: (opt) => { + // console.log(opt[modelId]?.toString()) + return opt[modelId]?.toString() != 'STD' + }, + }, + ] +} + +function StdEqBandChoices(bus?: boolean): DropdownChoice[] { + const isBus = bus ?? false + if (isBus) { + return [ + getIdLabelPair('l', 'Low'), + getIdLabelPair('1', '1'), + getIdLabelPair('2', '2'), + getIdLabelPair('3', '3'), + getIdLabelPair('4', '4'), + getIdLabelPair('5', '5'), + getIdLabelPair('6', '6'), + getIdLabelPair('h', 'High'), + ] + } else { + return [ + getIdLabelPair('l', 'Low'), + getIdLabelPair('1', '1'), + getIdLabelPair('2', '2'), + getIdLabelPair('3', '3'), + getIdLabelPair('4', '4'), + getIdLabelPair('h', 'High'), + ] + } +} + +// function Even88EqBandChoices(): DropdownChoice[] { +// return [ +// getIdLabelPair('l', 'Low'), +// getIdLabelPair('lm', 'Low-Mid'), +// getIdLabelPair('hm', 'High-Mid'), +// getIdLabelPair('h', 'High'), +// ] +// } + +// function Even84EqBandChoices(): DropdownChoice[] { +// return [getIdLabelPair('l', 'Low'), getIdLabelPair('m', 'Mid'), getIdLabelPair('h', 'High')] +// } diff --git a/src/choices/faderbanks.ts b/src/choices/faderbanks.ts new file mode 100644 index 0000000..b6778fa --- /dev/null +++ b/src/choices/faderbanks.ts @@ -0,0 +1,256 @@ +import { + // CompanionInputFieldNumber, + CompanionOptionValues, + DropdownChoice, + SomeCompanionActionInputField, + // CompanionInputFieldNumber, +} from '@companion-module/base' + +interface LayerConfig { + id: number + label: string +} + +interface BankConfig { + name: string + maxOffset?: number + offsetLayers?: number + layers: LayerConfig[] +} + +export const LayerBankOptions: Record = { + left: { + name: 'Left', + layers: [ + { id: 1, label: 'Channel 1-12' }, + { id: 2, label: 'Channel 13-24' }, + { id: 3, label: 'Channel 25-36' }, + { id: 4, label: 'Channel 36-40 / Aux 1-8' }, + { id: 5, label: 'Bus 1-12' }, + { id: 6, label: 'User 1' }, + { id: 7, label: 'User 2' }, + { id: 10, label: 'Channel 1-8' }, + { id: 11, label: 'Channel 9-16' }, + { id: 12, label: 'Channel 17-24' }, + { id: 13, label: 'Channel 25-32' }, + { id: 14, label: 'Channel 33-40' }, + { id: 15, label: 'Aux 1-8' }, + { id: 16, label: 'Bus 1-8' }, + { id: 17, label: 'Bus 9-16' }, + { id: 18, label: 'Main 1-4' }, + { id: 19, label: 'Matrix 1-8' }, + { id: 20, label: 'DCA 1-8' }, + { id: 21, label: 'DCA 9-16' }, + { id: 22, label: 'Spill' }, + ], + offsetLayers: 7, + maxOffset: 12, + }, + center: { + name: 'Center', + layers: [ + { id: 1, label: 'DCA' }, + { id: 2, label: 'Main / Matrix' }, + { id: 3, label: 'Aux / FX' }, + { id: 4, label: 'Bus / Master' }, + { id: 5, label: 'User 1' }, + { id: 6, label: 'User 2' }, + { id: 10, label: 'Channel 1-8' }, + { id: 11, label: 'Channel 9-16' }, + { id: 12, label: 'Channel 17-24' }, + { id: 13, label: 'Channel 25-32' }, + { id: 14, label: 'Channel 33-40' }, + { id: 15, label: 'Aux 1-8' }, + { id: 16, label: 'Bus 1-8' }, + { id: 17, label: 'Bus 9-16' }, + { id: 18, label: 'Main 1-4' }, + { id: 19, label: 'Matrix 1-8' }, + { id: 20, label: 'DCA 1-8' }, + { id: 21, label: 'DCA 9-16' }, + { id: 22, label: 'Spill' }, + ], + offsetLayers: 6, + maxOffset: 8, + }, + right: { + name: 'Right', + layers: [ + { id: 1, label: 'Main' }, + { id: 2, label: 'DCA' }, + { id: 3, label: 'Channels' }, + { id: 4, label: 'Aux / FX' }, + { id: 5, label: 'Bus / Master' }, + { id: 6, label: 'User 1' }, + { id: 7, label: 'User 2' }, + { id: 10, label: 'Channel 1-4' }, + { id: 11, label: 'Channel 9-12' }, + { id: 12, label: 'Channel 17-20' }, + { id: 13, label: 'Channel 25-38' }, + { id: 14, label: 'Channel 33-36' }, + { id: 15, label: 'Aux 1-4' }, + { id: 16, label: 'Bus 1-4' }, + { id: 17, label: 'Bus 9-12' }, + { id: 18, label: 'Main 1-4' }, + { id: 19, label: 'Matrix 1-8' }, + { id: 20, label: 'DCA 1-4' }, + { id: 21, label: 'DCA 9-12' }, + { id: 22, label: 'Spill' }, + ], + offsetLayers: 7, + maxOffset: 15, + }, +} + +const FaderSpillGroups: DropdownChoice[] = [ + { id: 0, label: 'Off' }, + { id: 1, label: 'DCA 1' }, + { id: 2, label: 'DCA 2' }, + { id: 3, label: 'DCA 3' }, + { id: 4, label: 'DCA 4' }, + { id: 5, label: 'DCA 5' }, + { id: 6, label: 'DCA 6' }, + { id: 7, label: 'DCA 7' }, + { id: 8, label: 'DCA 8' }, + { id: 9, label: 'DCA 9' }, + { id: 10, label: 'DCA 10' }, + { id: 11, label: 'DCA 11' }, + { id: 12, label: 'DCA 12' }, + { id: 13, label: 'DCA 13' }, + { id: 14, label: 'DCA 14' }, + { id: 15, label: 'DCA 15' }, + { id: 16, label: 'DCA 16' }, + { id: 17, label: 'FX 1' }, + { id: 18, label: 'FX 2' }, + { id: 19, label: 'FX 3' }, + { id: 20, label: 'FX 4' }, + { id: 21, label: 'FX 5' }, + { id: 22, label: 'FX 6' }, + { id: 23, label: 'FX 7' }, + { id: 24, label: 'FX 8' }, + { id: 25, label: 'FX 9' }, + { id: 26, label: 'FX 10' }, + { id: 27, label: 'FX 11' }, + { id: 28, label: 'FX 12' }, + { id: 29, label: 'FX 13' }, + { id: 30, label: 'FX 14' }, + { id: 31, label: 'FX 15' }, + { id: 32, label: 'FX 16' }, + { id: 33, label: 'Bus 1' }, + { id: 34, label: 'Bus 2' }, + { id: 35, label: 'Bus 3' }, + { id: 36, label: 'Bus 4' }, + { id: 37, label: 'Bus 5' }, + { id: 38, label: 'Bus 6' }, + { id: 39, label: 'Bus 7' }, + { id: 40, label: 'Bus 8' }, + { id: 41, label: 'Bus 9' }, + { id: 42, label: 'Bus 10' }, + { id: 43, label: 'Bus 11' }, + { id: 44, label: 'Bus 12' }, + { id: 45, label: 'Bus 13' }, + { id: 46, label: 'Bus 14' }, + { id: 47, label: 'Bus 15' }, + { id: 48, label: 'Bus 16' }, + { id: 49, label: 'Matrix 1' }, + { id: 50, label: 'Matrix 2' }, + { id: 51, label: 'Matrix 3' }, + { id: 52, label: 'Matrix 4' }, + { id: 53, label: 'Matrix 5' }, + { id: 54, label: 'Matrix 6' }, + { id: 55, label: 'Matrix 7' }, + { id: 56, label: 'Matrix 8' }, + { id: 57, label: 'Main 1' }, + { id: 58, label: 'Main 2' }, + { id: 59, label: 'Main 3' }, + { id: 60, label: 'Main 4' }, + { id: 61, label: 'Auto X' }, + { id: 62, label: 'Auto Y' }, +] + +// Function to get dropdown choices for fader bank layers +function GetLayerSelectionChoices(bank: string): DropdownChoice[] { + const bankConfig = LayerBankOptions[bank] + if (!bankConfig) { + throw new Error(`Invalid bank: ${bank}`) + } + return bankConfig.layers.map(({ id, label }) => ({ id, label })) +} + +// Function to get offset input field configuration for a specific layer +function GetMaxLayerOffsetForBank(bank: string): number { + const bankConfig = LayerBankOptions[bank] + if (!bankConfig) { + return 0 + } + + return bankConfig.maxOffset ?? 0 +} + +function GetFaderBankSelectionChoices(): DropdownChoice[] { + return Object.keys(LayerBankOptions).map((key) => ({ + id: key, + label: LayerBankOptions[key].name, + })) +} + +export const FaderBankChoice: SomeCompanionActionInputField[] = [ + { + type: 'dropdown', + label: 'Fader Bank', + id: 'bank', + default: 'left', + choices: GetFaderBankSelectionChoices(), + }, + { + type: 'dropdown', + label: 'Bank Layer', + id: 'left', + default: 1, + choices: GetLayerSelectionChoices('left'), + isVisible: (options: CompanionOptionValues): boolean => { + return options.bank != null && (options.bank as string) == 'left' + }, + }, + { + type: 'dropdown', + label: 'Bank Layer', + id: 'center', + default: 1, + choices: GetLayerSelectionChoices('center'), + isVisible: (options: CompanionOptionValues): boolean => { + return options.bank != null && (options.bank as string) == 'center' + }, + }, + { + type: 'dropdown', + label: 'Bank Layer', + id: 'right', + default: 1, + choices: GetLayerSelectionChoices('right'), + isVisible: (options: CompanionOptionValues): boolean => { + return options.bank != null && (options.bank as string) == 'right' + }, + }, + { + type: 'dropdown', + label: 'Spill Group', + id: 'spillgroup', + default: 0, + choices: FaderSpillGroups, + isVisible: (options: CompanionOptionValues): boolean => { + return options.bank != null && options[options.bank as string] == 22 + }, + }, + { + type: 'number', + label: 'Layer Offset', + id: 'offset', + default: 0, + min: 0, + step: 1, + max: GetMaxLayerOffsetForBank('left'), + isVisible: (options: CompanionOptionValues): boolean => { + return options.bank != null && (options.bank as string) == 'left' && (options.layer as number) <= 7 + }, + }, +] diff --git a/src/choices/fades.ts b/src/choices/fades.ts new file mode 100644 index 0000000..3f4d990 --- /dev/null +++ b/src/choices/fades.ts @@ -0,0 +1,59 @@ +import { CompanionInputFieldDropdown, CompanionInputFieldNumber, CompanionOptionValues } from '@companion-module/base' + +export const FadeDurationChoice: [CompanionInputFieldNumber, CompanionInputFieldDropdown, CompanionInputFieldDropdown] = + [ + { + type: 'number', + label: 'Fade Duration (ms)', + id: 'fadeDuration', + default: 0, + min: 0, + step: 10, + max: 60000, + tooltip: + 'Set the desired duration of the transition in milliseconds. Be aware that the minimal temporal resolution is defined by the Fader Update Rate setting of the module.', + }, + { + type: 'dropdown', + label: 'Algorithm', + id: 'fadeAlgorithm', + default: 'linear', + choices: [ + { id: 'linear', label: 'Linear' }, + { id: 'quadratic', label: 'Quadratic' }, + { id: 'cubic', label: 'Cubic' }, + { id: 'quartic', label: 'Quartic' }, + { id: 'quintic', label: 'Quintic' }, + { id: 'sinusoidal', label: 'Sinusoidal' }, + { id: 'exponential', label: 'Exponential' }, + { id: 'circular', label: 'Circular' }, + { id: 'elastic', label: 'Elastic' }, + { id: 'back', label: 'Back' }, + { id: 'bounce', label: 'Bounce' }, + ], + isVisible: (options: CompanionOptionValues): boolean => { + return options.fadeDuration != null && (options.fadeDuration as number) > 0 + }, + tooltip: 'Selec the algorithm with which the fade is performed.', + }, + { + type: 'dropdown', + label: 'Fade type', + id: 'fadeType', + default: 'ease-in-out', + choices: [ + { id: 'ease-in', label: 'Ease-In' }, + { id: 'ease-out', label: 'Ease-Out' }, + { id: 'ease-in-out', label: 'Ease-In-Out' }, + ], + isVisible: (options: CompanionOptionValues): boolean => { + return ( + options.fadeDuration != null && + options.fadeAlgorithm != null && + (options.fadeDuration as number) > 0 && + (options.fadeAlgorithm as string) !== 'linear' + ) + }, + tooltip: 'Select how to ease your algorithm. Easing avoids abrupt changes in value', + }, + ] diff --git a/src/choices/main.ts b/src/choices/main.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/choices/matrix.ts b/src/choices/matrix.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/choices/usbplayer.ts b/src/choices/usbplayer.ts new file mode 100644 index 0000000..9b838f2 --- /dev/null +++ b/src/choices/usbplayer.ts @@ -0,0 +1,21 @@ +import { DropdownChoice } from '@companion-module/base' +import { getIdLabelPair } from '../choices/utils.js' + +export function getUsbPlayerActionChoices(): DropdownChoice[] { + return [ + getIdLabelPair('STOP', 'Stop'), + getIdLabelPair('PLAY', 'Play'), + getIdLabelPair('PAUSE', 'Pause'), + getIdLabelPair('NEXT', 'Next'), + getIdLabelPair('PREV', 'Previous'), + ] +} + +export function getUsbRecorderActionChoices(): DropdownChoice[] { + return [ + getIdLabelPair('STOP', 'Stop'), + getIdLabelPair('REC', 'Record'), + getIdLabelPair('PAUSE', 'Pause'), + getIdLabelPair('NEWFILE', 'New File'), + ] +} diff --git a/src/choices/utils.ts b/src/choices/utils.ts new file mode 100644 index 0000000..1e1bb7e --- /dev/null +++ b/src/choices/utils.ts @@ -0,0 +1,5 @@ +import { DropdownChoice } from '@companion-module/base' + +export function getIdLabelPair(id: string, label?: string): DropdownChoice { + return { id: id, label: label ?? id } +} diff --git a/src/commands/aux.ts b/src/commands/aux.ts new file mode 100644 index 0000000..b752685 --- /dev/null +++ b/src/commands/aux.ts @@ -0,0 +1,134 @@ +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace AuxCommands { + export function Node(aux: number): string { + return `/aux/${aux}` + } + + export function InputNode(aux: number): string { + return `${Node(aux)}/in` + } + + export function InputSetNode(aux: number): string { + return `${InputNode(aux)}/set` + } + + export function InputAutoSourceSwitch(aux: number): string { + return `${InputSetNode(aux)}/srcauto` + } + + export function InputAltSource(aux: number): string { + return `${InputSetNode(aux)}/altsrc` + } + + export function InputInvert(aux: number): string { + return `${InputSetNode(aux)}/inv` + } + + export function InputTrim(aux: number): string { + return `${InputSetNode(aux)}/trim` + } + + export function InputBalance(aux: number): string { + return `${InputSetNode(aux)}/bal` + } + + export function InputGain(aux: number): string { + return `${InputSetNode(aux)}/$g` + } + + export function InputPhantomPower(aux: number): string { + return `${InputSetNode(aux)}/$vph` + } + + export function InputConnectionNode(aux: number): string { + return `${InputNode(aux)}/conn` + } + + export function MainInputConnectionGroup(aux: number): string { + return `${InputConnectionNode(aux)}/grp` + } + + export function MainInputConnectionIndex(aux: number): string { + return `${InputConnectionNode(aux)}/in` + } + + export function AltInputConnectionGroup(aux: number): string { + return `${InputConnectionNode(aux)}/altgrp` + } + + export function AltInputConnectionIndex(aux: number): string { + return `${InputConnectionNode(aux)}/altin` + } + + export function CustomLink(aux: number): string { + return `${Node(aux)}/clink` + } + + export function Color(aux: number): string { + return `${Node(aux)}/col` + } + + export function Name(aux: number): string { + return `${Node(aux)}/name` + } + + export function RealName(aux: number): string { + return `${Node(aux)}/$name` + } + + export function Icon(aux: number): string { + return `${Node(aux)}/icon` + } + + export function ScribbleLight(aux: number): string { + return `${Node(aux)}/led` + } + + export function Mute(aux: number): string { + return `${Node(aux)}/mute` + } + + export function Fader(aux: number): string { + return `${Node(aux)}/fdr` + } + + export function Pan(aux: number): string { + return `${Node(aux)}/pan` + } + + export function Width(aux: number): string { + return `${Node(aux)}/wid` + } + + export function Solo(aux: number): string { + return `${Node(aux)}/$solo` + } + + export function SoloLed(aux: number): string { + return `${Node(aux)}/$sololed` + } + + export function SoloSafe(aux: number): string { + return `${Node(aux)}/solosafe` + } + + export function MonitorMode(aux: number): string { + return `${Node(aux)}/mon` + } + + export function SendNode(aux: number, send: number): string { + return `${Node(aux)}/send/${send}` + } + + export function SendOn(aux: number, send: number): string { + return `${SendNode(aux, send)}/on` + } + + export function SendLevel(aux: number, send: number): string { + return `${SendNode(aux, send)}/lvl` + } + + export function SendPan(aux: number, send: number): string { + return `${SendNode(aux, send)}/pan` + } +} diff --git a/src/commands/bus.ts b/src/commands/bus.ts new file mode 100644 index 0000000..8e93bd5 --- /dev/null +++ b/src/commands/bus.ts @@ -0,0 +1,102 @@ +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace BusCommands { + export function Node(bus: number): string { + return `/bus/${bus}` + } + + export function InputNode(bus: number): string { + return `${Node(bus)}/in` + } + + export function InputSetNode(bus: number): string { + return `${InputNode(bus)}/set` + } + + export function InputInvert(bus: number): string { + return `${InputSetNode(bus)}/inv` + } + + export function InputTrim(bus: number): string { + return `${InputSetNode(bus)}/trim` + } + + export function InputBalance(bus: number): string { + return `${InputSetNode(bus)}/bal` + } + + export function Color(bus: number): string { + return `${Node(bus)}/col` + } + + export function Name(bus: number): string { + return `${Node(bus)}/name` + } + + export function Icon(bus: number): string { + return `${Node(bus)}/icon` + } + + export function ScribbleLight(bus: number): string { + return `${Node(bus)}/led` + } + + export function MonoSwitch(bus: number): string { + return `${Node(bus)}/busmono` + } + + export function Mute(bus: number): string { + return `${Node(bus)}/mute` + } + + export function Fader(bus: number): string { + return `${Node(bus)}/fdr` + } + + export function Pan(bus: number): string { + return `${Node(bus)}/pan` + } + + export function Width(bus: number): string { + return `${Node(bus)}/wid` + } + + export function Solo(bus: number): string { + return `${Node(bus)}/$solo` + } + + export function SoloLed(bus: number): string { + return `${Node(bus)}/$sololed` + } + + export function MonitorMode(bus: number): string { + return `${Node(bus)}/mon` + } + + export function Mode(bus: number): string { + return `${Node(bus)}/busmode` + } + + export function SendNode(bus: number, send: number): string { + return `${Node(bus)}/send/${send}` + } + + export function SendOn(bus: number, send: number): string { + return `${SendNode(bus, send)}/on` + } + + export function SendLevel(bus: number, send: number): string { + return `${SendNode(bus, send)}/lvl` + } + + export function MatrixSendNode(bus: number, matrix: number): string { + return `${Node(bus)}/send/MX${matrix}` + } + + export function MatrixSendOn(bus: number, matrix: number): string { + return `${MatrixSendNode(bus, matrix)}/on` + } + + export function MatrixSendLevel(bus: number, matrix: number): string { + return `${MatrixSendNode(bus, matrix)}/lvl` + } +} diff --git a/src/commands/channel.ts b/src/commands/channel.ts new file mode 100644 index 0000000..1d3185b --- /dev/null +++ b/src/commands/channel.ts @@ -0,0 +1,230 @@ +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace ChannelCommands { + export function Node(channel: number): string { + return `/ch/${channel}` + } + + export function InputNode(channel: number): string { + return `${Node(channel)}/in` + } + + export function InputSetNode(channel: number): string { + return `${InputNode(channel)}/set` + } + + export function InputAutoSourceSwitch(channel: number): string { + return `${InputSetNode(channel)}/srcauto` + } + + export function InputAltSource(channel: number): string { + return `${InputSetNode(channel)}/altsrc` + } + + export function InputInvert(channel: number): string { + return `${InputSetNode(channel)}/inv` + } + + export function InputTrim(channel: number): string { + return `${InputSetNode(channel)}/trim` + } + + export function InputBalance(channel: number): string { + return `${InputSetNode(channel)}/bal` + } + + export function InputDelayMode(channel: number): string { + return `${InputSetNode(channel)}/dlymode` + } + + export function InputGain(channel: number): string { + return `${InputSetNode(channel)}/$g` + } + + export function InputPhantomPower(channel: number): string { + return `${InputSetNode(channel)}/$vph` + } + + export function InputDelay(channel: number): string { + return `${InputSetNode(channel)}/dly` + } + + export function InputDelayOn(channel: number): string { + return `${InputSetNode(channel)}/dlyon` + } + + export function InputConnectionNode(channel: number): string { + return `${InputNode(channel)}/conn` + } + + export function MainInputConnectionGroup(channel: number): string { + return `${InputConnectionNode(channel)}/grp` + } + + export function MainInputConnectionIndex(channel: number): string { + return `${InputConnectionNode(channel)}/in` + } + + export function AltInputConnectionGroup(channel: number): string { + return `${InputConnectionNode(channel)}/altgrp` + } + + export function AltInputConnectionIndex(channel: number): string { + return `${InputConnectionNode(channel)}/altin` + } + + export function FilterNode(channel: number): string { + return `${Node(channel)}/flt` + } + + export function LowCutSwitch(channel: number): string { + return `${FilterNode(channel)}/lc` + } + + export function LowCutFrequency(channel: number): string { + return `${FilterNode(channel)}/lcf` + } + + export function HighCutSwitch(channel: number): string { + return `${FilterNode(channel)}/hc` + } + + export function HighCutFrequency(channel: number): string { + return `${FilterNode(channel)}/hcf` + } + + export function FilterSwitch(channel: number): string { + return `${FilterNode(channel)}/tf` + } + + export function FilterModel(channel: number): string { + return `${FilterNode(channel)}/mdl` + } + + export function FilterTilt(channel: number): string { + return `${FilterNode(channel)}/tilt` + } + + export function CustomLink(channel: number): string { + return `${Node(channel)}/clink` + } + + export function Color(channel: number): string { + return `${Node(channel)}/col` + } + + export function Name(channel: number): string { + return `${Node(channel)}/name` + } + + export function RealName(channel: number): string { + return `${Node(channel)}/$name` + } + + export function Icon(channel: number): string { + return `${Node(channel)}/icon` + } + + export function ScribbleLight(channel: number): string { + return `${Node(channel)}/led` + } + + export function Mute(channel: number): string { + return `${Node(channel)}/mute` + } + + export function Fader(channel: number): string { + return `${Node(channel)}/fdr` + } + + export function Pan(channel: number): string { + return `${Node(channel)}/pan` + } + + export function Width(channel: number): string { + return `${Node(channel)}/wid` + } + + export function Solo(channel: number): string { + return `${Node(channel)}/$solo` + } + + export function SoloLed(channel: number): string { + return `${Node(channel)}/$sololed` + } + + export function SoloSafe(channel: number): string { + return `${Node(channel)}/solosafe` + } + + export function MonitorMode(channel: number): string { + return `${Node(channel)}/mon` + } + + export function ProcessOrder(channel: number): string { + return `${Node(channel)}/proc` + } + + export function PreTap(channel: number): string { + return `${Node(channel)}/ptap` + } + + export function PreSolo(channel: number): string { + return `${Node(channel)}/$presolo` + } + + export function MainNode(channel: number, main: number): string { + return `${Node(channel)}/main/${main}` + } + + export function MainOn(channel: number, main: number): string { + return `${MainNode(channel, main)}/on` + } + + export function MainLevel(channel: number, main: number): string { + return `${MainNode(channel, main)}/lvl` + } + + export function SendNode(channel: number, send: number): string { + return `${Node(channel)}/send/${send}` + } + + export function SendOn(channel: number, send: number): string { + return `${SendNode(channel, send)}/on` + } + + export function SendLevel(channel: number, send: number): string { + return `${SendNode(channel, send)}/lvl` + } + + export function SendPreAlwaysOn(channel: number, send: number): string { + return `${SendNode(channel, send)}/pon` + } + + export function SendIndividualTap(channel: number, send: number): string { + return `${SendNode(channel, send)}/ind` + } + + export function SendMode(channel: number, send: number): string { + return `${SendNode(channel, send)}/mode` + } + + export function SendPanLink(channel: number, send: number): string { + return `${SendNode(channel, send)}/plink` + } + + export function SendPan(channel: number, send: number): string { + return `${SendNode(channel, send)}/pan` + } + + export function SendWidth(channel: number, send: number): string { + return `${SendNode(channel, send)}/wid` + } + + export function EqNode(channel: number): string { + return `${Node(channel)}/eq` + } + + export function EqModel(channel: number): string { + return `${EqNode(channel)}/mdl` + } +} diff --git a/src/commands/config.ts b/src/commands/config.ts new file mode 100644 index 0000000..51a32fd --- /dev/null +++ b/src/commands/config.ts @@ -0,0 +1,192 @@ +import { DropdownChoice } from '@companion-module/base' +import { generateDropdownChoices } from './utils.js' + +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace ConfigurationCommands { + //////////////////////////////////////////////// + // Types + //////////////////////////////////////////////// + export const CLOCK_RATES = [44100, 48000] as const + export type ClockRates = (typeof CLOCK_RATES)[number] + + export const CLOCK_SOURCES = ['INT', 'A', 'B', 'C', 'AES', 'CARD', 'MOD'] as const + export type ClockSource = (typeof CLOCK_SOURCES)[number] + + export const MAIN_LINKS = ['OFF', '2', '2-3', '2-4'] as const + export type MainLink = (typeof MAIN_LINKS)[number] + + export const USB_CONFIGS = ['2/2', '8/8', '16/16', '32/32', '48/48'] as const + export type UsbConfig = (typeof USB_CONFIGS)[number] + + export const SC_CONFIGS = [ + 'AUTO', + '0/32', + '1/31', + '2/30', + '3/29', + '4/28', + '5/27', + '6/26', + '7/25', + '8/24', + '9/23', + '10/22', + '11/21', + '12/20', + '13/19', + '14/18', + '15/17', + '16/16', + '17/15', + '18/14', + '19/13', + '20/12', + '21/11', + '22/10', + '23/9', + '24/8', + '25/7', + '26/6', + '27/5', + '28/4', + '29/3', + '30/2', + '31/1', + '32/0', + ] as const + export type ScConfig = (typeof SC_CONFIGS)[number] + + export const MONITOR_NODES = [1, 2] as const + export type MonitorNode = (typeof MONITOR_NODES)[number] + + export const MONITOR_EQ_BANDS = [1, 2, 3, 4, 5, 6] as const + export type MonitorEqBand = (typeof MONITOR_EQ_BANDS)[number] + + export const MONITOR_SOURCE_OPTIONS = [ + 'OFF', + ...Array.from({ length: 4 }, (_, i) => `MAIN.${i + 1}`), + ...Array.from({ length: 8 }, (_, i) => `MTX.${i + 1}`), + ...Array.from({ length: 16 }, (_, i) => `BUS.${i + 1}`), + ...Array.from({ length: 8 }, (_, i) => `AUX.${i + 1}`), + ] as const + export type MonitorSource = (typeof MONITOR_SOURCE_OPTIONS)[number] + + //////////////////////////////////////////////// + // Dropdown + //////////////////////////////////////////////// + export function getClockRateOptions(): DropdownChoice[] { + return generateDropdownChoices(CLOCK_RATES) + } + + export function getClockSourceOptions(): DropdownChoice[] { + return generateDropdownChoices(CLOCK_SOURCES) + } + + export function getMainLinkOptions(): DropdownChoice[] { + return generateDropdownChoices(MAIN_LINKS) + } + + export function getUsbConfigOptions(): DropdownChoice[] { + return generateDropdownChoices(USB_CONFIGS) + } + + export function getScConfigOptions(): DropdownChoice[] { + return generateDropdownChoices(SC_CONFIGS) + } + + export function getMonitorConfigOptions(): DropdownChoice[] { + return generateDropdownChoices(MONITOR_NODES) + } + + //////////////////////////////////////////////// + // Commands + //////////////////////////////////////////////// + + export function ClockRate(): string { + return '/cfg/clkrate' + } + + export function ClockSource(): string { + return '/cfg/clksrc' + } + + export function MainLink(): string { + return '/cfg/mainlink' + } + + export function DcaGroup(): string { + return '/cfg/dcamgrp' + } + + export function MuteOverride(): string { + return '/cfg/muteovr' + } + + export function StartMute(): string { + return '/cfg/startmute' + } + + export function UsbConfig(): string { + return '/cfg/usbacfg' + } + + export function ScConfig(): string { + return '/cfg/sccfg' + } + + export function MonitorEqGain(node: MonitorNode, band: MonitorEqBand): string { + return `/cfg/mon/${node}/eq/${band}g` + } + + export function MonitorEqFrequency(node: MonitorNode, band: MonitorEqBand): string { + return `/cfg/mon/${node}/eq/${band}f` + } + + export function MonitorEqQ(node: MonitorNode, band: MonitorEqBand): string { + return `/cfg/mon/${node}/eq/${band}q` + } + + export function MonitorEqHighShelfGain(node: MonitorNode): string { + return `/cfg/mon/${node}/eq/hsg` + } + + export function MonitorEqHighShelfFrequency(node: MonitorNode): string { + return `/cfg/mon/${node}/eq/hsf` + } + + export function MonitorLimiterLevel(node: MonitorNode): string { + return `/cfg/mon/${node}/lim` + } + + export function MonitorDelayOn(node: MonitorNode): string { + return `/cfg/mon/${node}/dly/on` + } + + export function MonitorDelayMeters(node: MonitorNode): string { + return `/cfg/mon/${node}/dly/m` + } + + export function MonitorDelayDimLevel(node: MonitorNode): string { + return `/cfg/mon/${node}/dim` + } + + export function MonitorPflDim(node: MonitorNode): string { + return `/cfg/mon/${node}/pfldim` + } + + export function MonitorEqBandSoloTrim(node: MonitorNode): string { + return `/cfg/mon/${node}/eqbdtrim` + } + + export function MonitorSourceLevel(node: MonitorNode): string { + return `/cfg/mon/${node}/srclvl` + } + + export function MonitorSourceMix(node: MonitorNode): string { + return `/cfg/mon/${node}/srcmix` + } + + export function MonitorSource(node: MonitorNode): string { + return `/cfg/mon/${node}/src` + } +} diff --git a/src/commands/index.ts b/src/commands/index.ts new file mode 100644 index 0000000..b1a9f21 --- /dev/null +++ b/src/commands/index.ts @@ -0,0 +1,7 @@ +import { AuxCommands as Aux } from './aux.js' +import { ChannelCommands as Channel } from './channel.js' +import { BusCommands as Bus } from './bus.js' +import { MatrixCommands as Matrix } from './matrix.js' +import { MainCommands as Main } from './main.js' + +export { Aux, Channel, Bus, Matrix, Main } diff --git a/src/commands/main.ts b/src/commands/main.ts new file mode 100644 index 0000000..12a4d55 --- /dev/null +++ b/src/commands/main.ts @@ -0,0 +1,90 @@ +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace MainCommands { + export function Node(main: number): string { + return `/main/${main}` + } + + export function InputNode(main: number): string { + return `${Node(main)}/in` + } + + export function InputSetNode(main: number): string { + return `${InputNode(main)}/set` + } + + export function InputInvert(main: number): string { + return `${InputSetNode(main)}/inv` + } + + export function InputTrim(main: number): string { + return `${InputSetNode(main)}/trim` + } + + export function InputBalance(main: number): string { + return `${InputSetNode(main)}/bal` + } + + export function Color(main: number): string { + return `${Node(main)}/col` + } + + export function Name(main: number): string { + return `${Node(main)}/name` + } + + export function RealName(main: number): string { + return `${Node(main)}/$name` + } + + export function Icon(main: number): string { + return `${Node(main)}/icon` + } + + export function ScribbleLight(main: number): string { + return `${Node(main)}/led` + } + + export function MonoSwitch(main: number): string { + return `${Node(main)}/busmono` + } + + export function Mute(main: number): string { + return `${Node(main)}/mute` + } + + export function Fader(main: number): string { + return `${Node(main)}/fdr` + } + + export function Pan(main: number): string { + return `${Node(main)}/pan` + } + + export function Width(main: number): string { + return `${Node(main)}/wid` + } + + export function Solo(main: number): string { + return `${Node(main)}/$solo` + } + + export function SoloLed(main: number): string { + return `${Node(main)}/$sololed` + } + + export function MonitorMode(main: number): string { + return `${Node(main)}/mon` + } + + export function MatrixSendNode(main: number, matrix: number): string { + return `${Node(main)}/MX${matrix}` + } + + export function MatrixSendMute(main: number, matrix: number): string { + return `${MatrixSendNode(main, matrix)}/on` + } + + export function MatrixSendLevel(main: number, matrix: number): string { + return `${MatrixSendNode(main, matrix)}/lvl` + } +} diff --git a/src/commands/matrix.ts b/src/commands/matrix.ts new file mode 100644 index 0000000..ea25c5c --- /dev/null +++ b/src/commands/matrix.ts @@ -0,0 +1,98 @@ +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace MatrixCommands { + export function Node(matrix: number): string { + return `/mtx/${matrix}` + } + + export function InputNode(matrix: number): string { + return `${Node(matrix)}/in` + } + + export function InputSetNode(matrix: number): string { + return `${InputNode(matrix)}/set` + } + + export function InputInvert(matrix: number): string { + return `${InputSetNode(matrix)}/inv` + } + + export function InputTrim(matrix: number): string { + return `${InputSetNode(matrix)}/trim` + } + + export function InputBalance(matrix: number): string { + return `${InputSetNode(matrix)}/bal` + } + + export function DirectInputNode(matrix: number, direct: number): string { + return `${InputNode(matrix)}/dir/${direct}` + } + + export function DirectInputSwitch(matrix: number, direct: number): string { + return `${DirectInputNode(matrix, direct)}/on` + } + + export function DirectInputLevel(matrix: number, direct: number): string { + return `${DirectInputNode(matrix, direct)}/lvl` + } + + export function DirectInputInvert(matrix: number, direct: number): string { + return `${DirectInputNode(matrix, direct)}/inv` + } + + export function DirectInputTap(matrix: number, direct: number): string { + return `${DirectInputNode(matrix, direct)}/tap` + } + + export function Color(matrix: number): string { + return `${Node(matrix)}/col` + } + + export function Name(matrix: number): string { + return `${Node(matrix)}/name` + } + + export function RealName(matrix: number): string { + return `${Node(matrix)}/$name` + } + + export function Icon(matrix: number): string { + return `${Node(matrix)}/icon` + } + + export function ScribbleLight(matrix: number): string { + return `${Node(matrix)}/led` + } + + export function MonoSwitch(matrix: number): string { + return `${Node(matrix)}/busmono` + } + + export function Mute(matrix: number): string { + return `${Node(matrix)}/mute` + } + + export function Fader(matrix: number): string { + return `${Node(matrix)}/fdr` + } + + export function Pan(matrix: number): string { + return `${Node(matrix)}/pan` + } + + export function Width(matrix: number): string { + return `${Node(matrix)}/wid` + } + + export function Solo(matrix: number): string { + return `${Node(matrix)}/$solo` + } + + export function SoloLed(matrix: number): string { + return `${Node(matrix)}/$sololed` + } + + export function MonitorMode(matrix: number): string { + return `${Node(matrix)}/mon` + } +} diff --git a/src/commands/status.ts b/src/commands/status.ts new file mode 100644 index 0000000..d0da1bd --- /dev/null +++ b/src/commands/status.ts @@ -0,0 +1,38 @@ +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace StatusCommands { + // Status Node + export const Node = (): string => `/$stat` + + // Module Type + export const ModType = (): string => `${Node()}/modtype` + + // AES50 Nodes + export function AesStatus(aes: string): string { + return `${Node()}/${aes}/stat` + } + export function AesDeviceName(aes: string): string { + return `${Node()}/${aes}/dev` + } + + export const ClockLock = (): string => `${Node()}/lock` + export const ClockPPM = (): string => `${Node()}/ppm` + + export const Solo = (): string => `${Node()}/solo` + export const SoloInPlace = (): string => `${Node()}/sip` + + // Real-Time Clock and Time/Date + export const RealTimeClockError = (): string => `${Node()}/rtcerr` + export const ClockTime = (): string => `${Node()}/time` + export const ClockDate = (): string => `${Node()}/date` + + // USB State and Volume Name + export const USBState = (): string => `${Node()}/usbstate` + export const USBVolumeName = (): string => `${Node()}/usbvolname` + + // StageConnect + export const StageConnectStatus = (): string => `${Node()}/sc_stat` + export const StageConnectDevices = (): string => `${Node()}/sc_devices` + export const StageConnectUpstreamCount = (): string => `${Node()}/sc_upcnt` + export const StageConnectDownstreamCount = (): string => `${Node()}/sc_dncnt` + export const StageConnectUpstreamRouting = (): string => `${Node()}/sc_uprout` +} diff --git a/src/commands/usbplayer.ts b/src/commands/usbplayer.ts new file mode 100644 index 0000000..6710e3e --- /dev/null +++ b/src/commands/usbplayer.ts @@ -0,0 +1,118 @@ +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace UsbPlayerCommands { + export function PlayerNode(): string { + return `/play` + } + + export function PlayerActiveState(): string { + return `${PlayerNode()}/$actstate` + } + + export function PlayerActiveFile(): string { + return `${PlayerNode()}/$actfile` + } + + export function PlayerSong(): string { + return `${PlayerNode()}/$song` + } + + export function PlayerAlbum(): string { + return `${PlayerNode()}/$album` + } + + export function PlayerArtist(): string { + return `${PlayerNode()}/$artist` + } + + export function PlayerPosition(): string { + return `${PlayerNode()}/$pos` + } + + export function PlayerTotalTime(): string { + return `${PlayerNode()}/$total` + } + + export function PlayerResolution(): string { + return `${PlayerNode()}/$resolution` + } + + export function PlayerChannels(): string { + return `${PlayerNode()}/$channels` + } + + export function PlayerRate(): string { + return `${PlayerNode()}/$rate` + } + + export function PlayerFormat(): string { + return `${PlayerNode()}/$format` + } + + export function PlayerDirectory(): string { + return `${PlayerNode()}/$dirfile` + } + + export function PlayerDirMode(): string { + return `${PlayerNode()}/$dirmode` + } + + export function PlayerAction(): string { + return `${PlayerNode()}/$action` + } + + export function PlayerPlayAll(): string { + return `${PlayerNode()}/playall` + } + + export function PlayerRepeat(): string { + return `${PlayerNode()}/repeat` + } + + export function PlayerListPosition(): string { + return `${PlayerNode()}/$listpos` + } + + export function PlayerListTotal(): string { + return `${PlayerNode()}/$listlen` + } + + export function PlayerPlaylistNode(): string { + return `${PlayerNode()}/$playlist` + } + + export function PlayerPlaylistItem(index: number): string { + return `${PlayerNode()}/$playlist/${index}` + } + + export function RecorderNode(): string { + return `/rec` + } + + export function RecorderActiveState(): string { + return `${RecorderNode()}/$actstate` + } + + export function RecorderActiveFile(): string { + return `${RecorderNode()}/$actfile` + } + + export function RecorderAction(): string { + return `${RecorderNode()}/$action` + } + + export function RecorderPath(): string { + return `${RecorderNode()}/path` + } + + export function RecorderResolution(): string { + return `${RecorderNode()}/resolution` + } + + export function RecorderChannels(): string { + return `${RecorderNode()}/channels` + } + + export function RecorderTime(): string { + return `${RecorderNode()}/$time` + } +} diff --git a/src/commands/utils.ts b/src/commands/utils.ts new file mode 100644 index 0000000..972d424 --- /dev/null +++ b/src/commands/utils.ts @@ -0,0 +1,17 @@ +import { DropdownChoice, OSCSomeArguments } from '@companion-module/base' + +export function intToOsc(i: number): OSCSomeArguments { + return { type: 'i', value: i } +} + +export function floatToOsc(f: number): OSCSomeArguments { + return { type: 'f', value: f } +} + +export function stringToOsc(s: string): OSCSomeArguments { + return { type: 's', value: s } +} + +export function generateDropdownChoices(values: readonly T[]): DropdownChoice[] { + return values.map((value) => ({ id: value.toString().toLowerCase(), label: value.toString() })) +} diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 0000000..81904e1 --- /dev/null +++ b/src/config.ts @@ -0,0 +1,77 @@ +import { type SomeCompanionConfigField } from '@companion-module/base' +import { WingDeviceDetectorInstance } from './device-detector.js' +// import { ModelChoices, WingModel } from './models/types.js' + +export const fadeUpdateRateDefault = 50 +export const pollUpdateRateDefault = 500 + +export const DeskTypes = [ + { id: 'wing', label: 'Wing' }, + { id: 'compact', label: 'Wing Compact' }, + { id: 'rack', label: 'Wing Rack' }, +] +export interface WingConfig { + host?: string + port?: number + fadeUpdateRate?: number + statusPollUpdateRate?: number + variableUpdateRate?: number +} + +export function GetConfigFields(): SomeCompanionConfigField[] { + return [ + { + type: 'dropdown', + id: 'host', + label: 'Desk IP', + width: 6, + choices: WingDeviceDetectorInstance.listKnown().map((d) => ({ + id: d.address, + label: `${d.address} (${d.deviceName})`, + })), + default: '', + allowCustom: true, + }, + // { + // type: 'dropdown', + // id: 'model', + // label: 'Desk Type', + // width: 6, + // choices: ModelChoices, + // default: WingModel.Full.toString(), + // }, + { + type: 'number', + id: 'fadeUpdateRate', + label: 'Fader Update Rate', + tooltip: + 'Update rate of the faders in milliseconds. A lower values makes the transitions smoother but increases system load.', + width: 5, + min: 20, + max: 60000, + default: fadeUpdateRateDefault, + }, + { + type: 'number', + id: 'statusPollUpdateRate', + label: 'Status Poll Rate', + tooltip: + 'Polling rate of the desk status requests.\nSome values need to be actively requested from the desk, this number sets the interval at which those requests occur.', + width: 5, + min: 20, + max: 60000, + default: pollUpdateRateDefault, + }, + { + type: 'number', + id: 'variableUpdateRate', + label: + 'Rate with which variables are updated.\nDefines how many milliseconds elapse between variable updates. A lower number makes the variables more responsive but may decrease system performance.', + tooltip: 'Update Rate of the Variables', + width: 5, + min: 20, + max: 60000, + default: 100, + }, + ] +} diff --git a/src/device-detector.ts b/src/device-detector.ts new file mode 100644 index 0000000..109d339 --- /dev/null +++ b/src/device-detector.ts @@ -0,0 +1,140 @@ +import osc from 'osc' +import { WingModel } from './models/types.js' + +export interface DeviceInfo { + deviceName: string + address: string + model: WingModel + lastSeen: number +} + +export interface WingDeviceDetector { + subscribe(instanceId: string): void + unsubscribe(instanceId: string): void + listKnown(): DeviceInfo[] +} + +class WingDeviceDetectorImpl implements WingDeviceDetector { + private readonly subscribers = new Set() + private osc?: osc.UDPPort + private knownDevices = new Map() + private queryTimer: NodeJS.Timeout | undefined + + public subscribe(instanceId: string): void { + const startListening = this.subscribers.size === 0 + + this.subscribers.add(instanceId) + + if (startListening) { + this.startListening() + } + } + + public unsubscribe(instanceId: string): void { + if (this.subscribers.delete(instanceId) && this.subscribers.size === 0) { + this.stopListening() + } + } + + public listKnown(): DeviceInfo[] { + return Array.from(this.knownDevices.values()).sort((a, b) => a.deviceName.localeCompare(b.deviceName)) + } + + private startListening(): void { + this.knownDevices.clear() + + if (this.subscribers.size === 0) { + return + } + + this.osc = new osc.UDPPort({ + localAddress: '0.0.0.0', + localPort: 0, + broadcast: true, + metadata: true, + remoteAddress: '255.255.255.255', // broadcast it + remotePort: 2223, + }) + + this.osc.on('error', (_err: Error): void => { + this.stopListening() + this.startListening() + }) + this.osc.on('ready', () => { + if (!this.queryTimer) { + this.queryTimer = setInterval(() => this.sendQuery(), 10000) + } + + this.sendQuery() + }) + + this.osc.on('close' as any, () => { + this.stopListening() + }) + + this.osc.on('message', (message): void => { + const args = message.args as osc.MetaArgument[] + if (!args || args.length === 0 || args[0].type !== 's') { + return + } + + const data = args[0].value.split(',') + + const msg = { + ip: data[1], + name: data[2], + model: data[3], + serial: data[4], + version: data[5], + } + + const info: DeviceInfo = { + address: msg.ip, + deviceName: msg.name, + model: WingModel.Full, + lastSeen: Date.now(), + } + + if (!this.knownDevices.has(info.address)) { + console.log(`Detected new Device: ${JSON.stringify(msg)}`) + } + + this.knownDevices.set(info.address, info) + + // Prune out any not seen for over a minute + for (const [id, data] of Array.from(this.knownDevices.entries())) { + if (data.lastSeen < Date.now() - 60000) { + this.knownDevices.delete(id) + } + } + }) + + this.osc.open() + } + + private stopListening(): void { + if (this.osc) { + try { + this.osc.close() + } catch (_e) { + // Ignore + } + delete this.osc + } + + this.knownDevices.clear() + + if (this.queryTimer) { + clearInterval(this.queryTimer) + delete this.queryTimer + } + } + + private sendQuery(): void { + if (this.osc) { + this.osc.send({ address: '/?', args: [] }) + } + } +} + +export const WingDeviceDetectorInstance: WingDeviceDetector = new WingDeviceDetectorImpl() diff --git a/src/easings.ts b/src/easings.ts new file mode 100644 index 0000000..ba2c22b --- /dev/null +++ b/src/easings.ts @@ -0,0 +1,607 @@ +/* eslint-disable @typescript-eslint/unbound-method */ +/** + * A collection of easing methods defining ease-in ease-out curves from https://github.com/photonstorm/phaser licensed + * under MIT + * + * @class Easing + */ +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace Easing { + export type algorithm = + | 'linear' + | 'quadratic' + | 'cubic' + | 'quartic' + | 'quintic' + | 'sinusoidal' + | 'exponential' + | 'circular' + | 'elastic' + | 'back' + | 'bounce' + + export type curve = 'ease-in' | 'ease-out' | 'ease-in-out' + + export function getEasing(algorithm?: algorithm, curve?: curve): (k: number) => number { + if (algorithm == 'linear' || algorithm == null || curve == null) { + return Linear.None + } + + if (algorithm == 'quadratic' && curve == 'ease-in') { + return Quadratic.In + } + if (algorithm == 'quadratic' && curve == 'ease-out') { + return Quadratic.Out + } + if (algorithm == 'quadratic' && curve == 'ease-in-out') { + return Quadratic.InOut + } + + if (algorithm == 'cubic' && curve == 'ease-in') { + return Cubic.In + } + if (algorithm == 'cubic' && curve == 'ease-out') { + return Cubic.Out + } + if (algorithm == 'cubic' && curve == 'ease-in-out') { + return Cubic.InOut + } + + if (algorithm == 'quartic' && curve == 'ease-in') { + return Quartic.In + } + if (algorithm == 'quartic' && curve == 'ease-out') { + return Quartic.Out + } + if (algorithm == 'quartic' && curve == 'ease-in-out') { + return Quartic.InOut + } + + if (algorithm == 'quintic' && curve == 'ease-in') { + return Quintic.In + } + if (algorithm == 'quintic' && curve == 'ease-out') { + return Quintic.Out + } + if (algorithm == 'quintic' && curve == 'ease-in-out') { + return Quintic.InOut + } + + if (algorithm == 'sinusoidal' && curve == 'ease-in') { + return Sinusoidal.In + } + if (algorithm == 'sinusoidal' && curve == 'ease-out') { + return Sinusoidal.Out + } + if (algorithm == 'sinusoidal' && curve == 'ease-in-out') { + return Sinusoidal.InOut + } + + if (algorithm == 'exponential' && curve == 'ease-in') { + return Exponential.In + } + if (algorithm == 'exponential' && curve == 'ease-out') { + return Exponential.Out + } + if (algorithm == 'exponential' && curve == 'ease-in-out') { + return Exponential.InOut + } + + if (algorithm == 'circular' && curve == 'ease-in') { + return Circular.In + } + if (algorithm == 'circular' && curve == 'ease-out') { + return Circular.Out + } + if (algorithm == 'circular' && curve == 'ease-in-out') { + return Circular.InOut + } + + if (algorithm == 'elastic' && curve == 'ease-in') { + return Elastic.In + } + if (algorithm == 'elastic' && curve == 'ease-out') { + return Elastic.Out + } + if (algorithm == 'elastic' && curve == 'ease-in-out') { + return Elastic.InOut + } + + if (algorithm == 'back' && curve == 'ease-in') { + return Back.In + } + if (algorithm == 'back' && curve == 'ease-out') { + return Back.Out + } + if (algorithm == 'back' && curve == 'ease-in-out') { + return Back.InOut + } + + if (algorithm == 'bounce' && curve == 'ease-in') { + return Bounce.In + } + if (algorithm == 'bounce' && curve == 'ease-out') { + return Bounce.Out + } + if (algorithm == 'bounce' && curve == 'ease-in-out') { + return Bounce.InOut + } + + return Linear.None + } + + /** + * Linear easing. + * + * @class Easing.Linear + */ + export class Linear { + /** + * Ease-in. + * + * @method Easing.Linear#In + * @param {number} k - The value to be tweened. + * @returns {number} k^2. + */ + static None(k: number): number { + return k + } + } + + /** + * Quadratic easing. + * + * @class Easing.Quadratic + */ + export class Quadratic { + /** + * Ease-in. + * + * @method Easing.Quadratic#In + * @param {number} k - The value to be tweened. + * @returns {number} k^2. + */ + static In(k: number): number { + return k * k + } + + /** + * Ease-out. + * + * @method Easing.Quadratic#Out + * @param {number} k - The value to be tweened. + * @returns {number} k* (2-k). + */ + static Out(k: number): number { + return k * (2 - k) + } + + /** + * Ease-in/out. + * + * @method Easing.Quadratic#InOut + * @param {number} k - The value to be tweened. + * @returns {number} The tweened value. + */ + static InOut(k: number): number { + k *= 2 + if (k < 1) return 0.5 * k * k + return -0.5 * (--k * (k - 2) - 1) + } + } + + /** + * Cubic easing. + * + * @class Easing.Cubic + */ + export class Cubic { + /** + * Cubic ease-in. + * + * @method Easing.Cubic#In + * @param {number} k - The value to be tweened. + * @returns {number} The tweened value. + */ + static In(k: number): number { + return k * k * k + } + + /** + * Cubic ease-out. + * + * @method Easing.Cubic#Out + * @param {number} k - The value to be tweened. + * @returns {number} The tweened value. + */ + static Out(k: number): number { + return --k * k * k + 1 + } + + /** + * Cubic ease-in/out. + * + * @method Easing.Cubic#InOut + * @param {number} k - The value to be tweened. + * @returns {number} The tweened value. + */ + static InOut(k: number): number { + k *= 2 + if (k < 1) return 0.5 * k * k * k + return 0.5 * ((k -= 2) * k * k + 2) + } + } + + /** + * Quartic easing. + * + * @class Easing.Quartic + */ + export class Quartic { + /** + * Quartic ease-in. + * + * @method Easing.Quartic#In + * @param {number} k - The value to be tweened. + * @returns {number} The tweened value. + */ + static In(k: number): number { + return k * k * k * k + } + + /** + * Quartic ease-out. + * + * @method Easing.Quartic#Out + * @param {number} k - The value to be tweened. + * @returns {number} The tweened value. + */ + static Out(k: number): number { + return 1 - --k * k * k * k + } + + /** + * Quartic ease-in/out. + * + * @method Easing.Quartic#InOut + * @param {number} k - The value to be tweened. + * @returns {number} The tweened value. + */ + static InOut(k: number): number { + k *= 2 + if (k < 1) return 0.5 * k * k * k * k + return -0.5 * ((k -= 2) * k * k * k - 2) + } + } + + /** + * Quintic easing. + * + * @class Easing.Quintic + */ + export class Quintic { + /** + * Quintic ease-in. + * + * @method Easing.Quintic#In + * @param {number} k - The value to be tweened. + * @returns {number} The tweened value. + */ + static In(k: number): number { + return k * k * k * k * k + } + + /** + * Quintic ease-out. + * + * @method Easing.Quintic#Out + * @param {number} k - The value to be tweened. + * @returns {number} The tweened value. + */ + static Out(k: number): number { + return --k * k * k * k * k + 1 + } + + /** + * Quintic ease-in/out. + * + * @method Easing.Quintic#InOut + * @param {number} k - The value to be tweened. + * @returns {number} The tweened value. + */ + static InOut(k: number): number { + k *= 2 + if (k < 1) return 0.5 * k * k * k * k * k + return 0.5 * ((k -= 2) * k * k * k * k + 2) + } + } + + /** + * Sinusoidal easing. + * + * @class Easing.Sinusoidal + */ + export class Sinusoidal { + /** + * Sinusoidal ease-in. + * + * @method Easing.Sinusoidal#In + * @param {number} k - The value to be tweened. + * @returns {number} The tweened value. + */ + static In(k: number): number { + return 1 - Math.cos((k * Math.PI) / 2) + } + + /** + * Sinusoidal ease-out. + * + * @method Easing.Sinusoidal#Out + * @param {number} k - The value to be tweened. + * @returns {number} The tweened value. + */ + static Out(k: number): number { + return Math.sin((k * Math.PI) / 2) + } + + /** + * Sinusoidal ease-in/out. + * + * @method Easing.Sinusoidal#InOut + * @param {number} k - The value to be tweened. + * @returns {number} The tweened value. + */ + static InOut(k: number): number { + return 0.5 * (1 - Math.cos(Math.PI * k)) + } + } + + /** + * Exponential easing. + * + * @class Easing.Exponential + */ + export class Exponential { + /** + * Exponential ease-in. + * + * @method Easing.Exponential#In + * @param {number} k - The value to be tweened. + * @returns {number} The tweened value. + */ + static In(k: number): number { + return k === 0 ? 0 : Math.pow(1024, k - 1) + } + + /** + * Exponential ease-out. + * + * @method Easing.Exponential#Out + * @param {number} k - The value to be tweened. + * @returns {number} The tweened value. + */ + static Out(k: number): number { + return k === 1 ? 1 : 1 - Math.pow(2, -10 * k) + } + + /** + * Exponential ease-in/out. + * + * @method Easing.Exponential#InOut + * @param {number} k - The value to be tweened. + * @returns {number} The tweened value. + */ + static InOut(k: number): number { + if (k === 0) return 0 + if (k === 1) return 1 + k *= 2 + if (k < 1) return 0.5 * Math.pow(1024, k - 1) + return 0.5 * (-Math.pow(2, -10 * (k - 1)) + 2) + } + } + + /** + * Circular easing. + * + * @class Easing.Circular + */ + export class Circular { + /** + * Circular ease-in. + * + * @method Easing.Circular#In + * @param {number} k - The value to be tweened. + * @returns {number} The tweened value. + */ + static In(k: number): number { + return 1 - Math.sqrt(1 - k * k) + } + + /** + * Circular ease-out. + * + * @method Easing.Circular#Out + * @param {number} k - The value to be tweened. + * @returns {number} The tweened value. + */ + static Out(k: number): number { + return Math.sqrt(1 - --k * k) + } + + /** + * Circular ease-in/out. + * + * @method Easing.Circular#InOut + * @param {number} k - The value to be tweened. + * @returns {number} The tweened value. + */ + static InOut(k: number): number { + k *= 2 + if (k < 1) return -0.5 * (Math.sqrt(1 - k * k) - 1) + return 0.5 * (Math.sqrt(1 - (k -= 2) * k) + 1) + } + } + + /** + * Elastic easing. + * + * @class Easing.Elastic + */ + export class Elastic { + /** + * Elastic ease-in. + * + * @method Easing.Elastic#In + * @param {number} k - The value to be tweened. + * @returns {number} The tweened value. + */ + static In(k: number): number { + let s + let a = 0.1 + const p = 0.4 + if (k === 0) return 0 + if (k === 1) return 1 + if (!a || a < 1) { + a = 1 + s = p / 4 + } else s = (p * Math.asin(1 / a)) / (2 * Math.PI) + return -(a * Math.pow(2, 10 * (k -= 1)) * Math.sin(((k - s) * (2 * Math.PI)) / p)) + } + + /** + * Elastic ease-out. + * + * @method Easing.Elastic#Out + * @param {number} k - The value to be tweened. + * @returns {number} The tweened value. + */ + static Out(k: number): number { + let s + let a = 0.1 + const p = 0.4 + if (k === 0) return 0 + if (k === 1) return 1 + if (!a || a < 1) { + a = 1 + s = p / 4 + } else s = (p * Math.asin(1 / a)) / (2 * Math.PI) + return a * Math.pow(2, -10 * k) * Math.sin(((k - s) * (2 * Math.PI)) / p) + 1 + } + + /** + * Elastic ease-in/out. + * + * @method Easing.Elastic#InOut + * @param {number} k - The value to be tweened. + * @returns {number} The tweened value. + */ + static InOut(k: number): number { + let s + let a = 0.1 + const p = 0.4 + if (k === 0) return 0 + if (k === 1) return 1 + if (!a || a < 1) { + a = 1 + s = p / 4 + } else s = (p * Math.asin(1 / a)) / (2 * Math.PI) + k *= 2 + if (k < 1) return -0.5 * (a * Math.pow(2, 10 * (k -= 1)) * Math.sin(((k - s) * (2 * Math.PI)) / p)) + return a * Math.pow(2, -10 * (k -= 1)) * Math.sin(((k - s) * (2 * Math.PI)) / p) * 0.5 + 1 + } + } + + /** + * Back easing. + * + * @class Easing.Back + */ + export class Back { + /** + * Back ease-in. + * + * @method Easing.Back#In + * @param {number} k - The value to be tweened. + * @returns {number} The tweened value. + */ + static In(k: number): number { + const s = 1.70158 + return k * k * ((s + 1) * k - s) + } + + /** + * Back ease-out. + * + * @method Easing.Back#Out + * @param {number} k - The value to be tweened. + * @returns {number} The tweened value. + */ + static Out(k: number): number { + const s = 1.70158 + return --k * k * ((s + 1) * k + s) + 1 + } + + /** + * Back ease-in/out. + * + * @method Easing.Back#InOut + * @param {number} k - The value to be tweened. + * @returns {number} The tweened value. + */ + static InOut(k: number): number { + const s = 1.70158 * 1.525 + k *= 2 + if (k < 1) return 0.5 * (k * k * ((s + 1) * k - s)) + return 0.5 * ((k -= 2) * k * ((s + 1) * k + s) + 2) + } + } + + /** + * Bounce easing. + * + * @class Easing.Bounce + */ + export class Bounce { + /** + * Bounce ease-in. + * + * @method Easing.Bounce#In + * @param {number} k - The value to be tweened. + * @returns {number} The tweened value. + */ + static In(k: number): number { + return 1 - Bounce.Out(1 - k) + } + + /** + * Bounce ease-out. + * + * @method Easing.Bounce#Out + * @param {number} k - The value to be tweened. + * @returns {number} The tweened value. + */ + static Out(k: number): number { + if (k < 1 / 2.75) { + return 7.5625 * k * k + } else if (k < 2 / 2.75) { + return 7.5625 * (k -= 1.5 / 2.75) * k + 0.75 + } else if (k < 2.5 / 2.75) { + return 7.5625 * (k -= 2.25 / 2.75) * k + 0.9375 + } else { + return 7.5625 * (k -= 2.625 / 2.75) * k + 0.984375 + } + } + + /** + * Bounce ease-in/out. + * + * @method Easing.Bounce#InOut + * @param {number} k - The value to be tweened. + * @returns {number} The tweened value. + */ + static InOut(k: number): number { + if (k < 0.5) return Bounce.In(k * 2) * 0.5 + return Bounce.Out(k * 2 - 1) * 0.5 + 0.5 + } + } +} diff --git a/src/feedbacks.ts b/src/feedbacks.ts new file mode 100644 index 0000000..6edda80 --- /dev/null +++ b/src/feedbacks.ts @@ -0,0 +1,262 @@ +import { WingState, WingSubscriptions } from './state/index.js' +import { InstanceBaseExt } from './types.js' +import { WingConfig } from './config.js' +import { SetRequired } from 'type-fest' // eslint-disable-line n/no-missing-import +import { + combineRgb, + CompanionAdvancedFeedbackDefinition, + CompanionAdvancedFeedbackResult, + CompanionBooleanFeedbackDefinition, + CompanionFeedbackDefinitions, + CompanionFeedbackInfo, +} from '@companion-module/base' +// import { compareNumber, GetDropdownFeedback, GetNumberComparator, GetPanoramaSliderFeedback } from './choices/common.js' +import { + GetDropdown, + GetMuteDropdown, + getTimeFormatChoices, + getTriStateColor, + getTriStateTextColor, +} from './choices/common.js' +import { getNodeNumber } from './actions/utils.js' +import { StateUtil } from './state/index.js' +import { UsbPlayerCommands } from './commands/usbplayer.js' +import * as ActionUtil from './actions/utils.js' +import { getIdLabelPair } from './choices/utils.js' +import { StatusCommands } from './commands/status.js' + +type CompanionFeedbackWithCallback = SetRequired< + CompanionBooleanFeedbackDefinition | CompanionAdvancedFeedbackDefinition, + 'callback' | 'subscribe' | 'unsubscribe' +> + +export enum FeedbackId { + Mute = 'mute', + SendMute = 'send-mute', + AesStatus = 'aes-status', + RecorderTime = 'recorder-time', + RecorderState = 'recorder-state', + PlayerTime = 'player-time', +} + +function subscribeFeedback( + ensureLoaded: (path: string) => void, + subs: WingSubscriptions, + path: string, + event: CompanionFeedbackInfo, +): void { + subs.subscribe(path, event.id, event.feedbackId as FeedbackId) + ensureLoaded(path) +} +function unsubscribeFeedback(subs: WingSubscriptions, path: string, event: CompanionFeedbackInfo): void { + subs.unsubscribe(path, event.id) +} + +export function GetFeedbacksList( + _self: InstanceBaseExt, + state: WingState, + subs: WingSubscriptions, + ensureLoaded: (path: string) => void, +): CompanionFeedbackDefinitions { + const feedbacks: { [id in FeedbackId]: CompanionFeedbackWithCallback | undefined } = { + [FeedbackId.Mute]: { + type: 'boolean', + name: 'Mute', + description: "React to a change in a channel's mute state", + options: [ + GetDropdown('Selection', 'sel', [ + ...state.namedChoices.channels, + ...state.namedChoices.auxes, + ...state.namedChoices.busses, + ...state.namedChoices.matrices, + ...state.namedChoices.mains, + ]), + GetMuteDropdown('mute', 'State', false), + ], + defaultStyle: { + bgcolor: combineRgb(0, 255, 0), + color: combineRgb(0, 0, 0), + }, + callback: (event: CompanionFeedbackInfo): boolean => { + const sel = event.options.sel as string + const cmd = ActionUtil.getMuteCommand(sel, getNodeNumber(event, 'sel')) + const currentValue = StateUtil.getNumberFromState(cmd, state) + return typeof currentValue === 'number' && currentValue == event.options.mute + }, + subscribe: (event): void => { + const sel = event.options.sel as string + const cmd = ActionUtil.getMuteCommand(sel, getNodeNumber(event, 'sel')) + subscribeFeedback(ensureLoaded, subs, cmd, event) + }, + unsubscribe: (event: CompanionFeedbackInfo): void => { + const sel = event.options.sel as string + const cmd = ActionUtil.getMuteCommand(sel, getNodeNumber(event, 'sel')) + unsubscribeFeedback(subs, cmd, event) + }, + }, + [FeedbackId.SendMute]: { + type: 'boolean', + name: 'Send Mute', + description: "React to a change in a channel's send mute state", + options: [ + GetDropdown('From', 'src', [ + ...state.namedChoices.channels, + ...state.namedChoices.auxes, + ...state.namedChoices.busses, + ...state.namedChoices.matrices, + ...state.namedChoices.mains, + ]), + GetDropdown('To Bus', 'dest', state.namedChoices.busses), + GetMuteDropdown('mute', 'State', false), + ], + defaultStyle: { + bgcolor: combineRgb(0, 255, 0), + color: combineRgb(0, 0, 0), + }, + callback: (event: CompanionFeedbackInfo): boolean => { + const sel = event.options.src as string + const cmd = ActionUtil.getSendMuteCommand(sel, getNodeNumber(event, 'src'), getNodeNumber(event, 'dest')) + const currentValue = StateUtil.getNumberFromState(cmd, state) + return typeof currentValue === 'number' && currentValue == event.options.mute + }, + subscribe: (event): void => { + const sel = event.options.src as string + const cmd = ActionUtil.getSendMuteCommand(sel, getNodeNumber(event, 'src'), getNodeNumber(event, 'dest')) + subscribeFeedback(ensureLoaded, subs, cmd, event) + }, + unsubscribe: (event: CompanionFeedbackInfo): void => { + const sel = event.options.src as string + const cmd = ActionUtil.getSendMuteCommand(sel, getNodeNumber(event, 'src'), getNodeNumber(event, 'dest')) + unsubscribeFeedback(subs, cmd, event) + }, + }, + [FeedbackId.AesStatus]: { + type: 'advanced', + name: 'AES Status', + description: 'Status of an AES Connection', + options: [ + GetDropdown('Interface', 'aes', [ + getIdLabelPair('A', 'AES A'), + getIdLabelPair('B', 'AES B'), + getIdLabelPair('C', 'AES C'), + ]), + ], + callback: (event: CompanionFeedbackInfo): CompanionAdvancedFeedbackResult => { + const cmd = StatusCommands.AesStatus(event.options.aes as string) + const val = StateUtil.getStringFromState(cmd, state) + return { + bgcolor: getTriStateColor(val), + color: getTriStateTextColor(val), + } + }, + subscribe: (event): void => { + const cmd = StatusCommands.AesStatus(event.options.aes as string) + subscribeFeedback(ensureLoaded, subs, cmd, event) + }, + unsubscribe: (event: CompanionFeedbackInfo): void => { + const cmd = StatusCommands.AesStatus(event.options.aes as string) + unsubscribeFeedback(subs, cmd, event) + }, + }, + [FeedbackId.RecorderTime]: { + type: 'advanced', + name: 'USB Recorder Time', + description: 'Current Time of the Recording', + options: [GetDropdown('Format', 'format', getTimeFormatChoices())], + callback: (): CompanionAdvancedFeedbackResult => { + const cmd = UsbPlayerCommands.RecorderTime() + const time = StateUtil.getNumberFromState(cmd, state) ?? 'N/A' + if (time) { + if (isNaN(Number(time))) { + return { + text: time as string, + } + } else return {} + } else return {} + }, + subscribe: (event): void => { + const cmd = UsbPlayerCommands.RecorderTime() + subs.subscribePoll(cmd, event.id, event.feedbackId as FeedbackId) + subscribeFeedback(ensureLoaded, subs, cmd, event) + }, + unsubscribe: (event: CompanionFeedbackInfo): void => { + const cmd = UsbPlayerCommands.RecorderTime() + subs.unsubscribePoll(cmd, event.feedbackId as FeedbackId) + unsubscribeFeedback(subs, cmd, event) + }, + }, + [FeedbackId.RecorderState]: { + type: 'advanced', + name: 'USB Recorder State', + description: 'Current State of the USB Recorder', + options: [ + { + type: 'checkbox', + label: 'Display State Text', + id: 'stateText', + default: false, + }, + ], + callback: (event): CompanionAdvancedFeedbackResult => { + const cmd = UsbPlayerCommands.RecorderActiveState() + const recState = StateUtil.getStringFromState(cmd, state) + let textColor = combineRgb(255, 255, 255) + let buttonColor = combineRgb(0, 255, 0) + if (recState == 'REC') { + buttonColor = combineRgb(255, 0, 0) + } else if (recState == 'PAUSE') { + buttonColor = combineRgb(128, 128, 128) + } else if (recState == 'STOP') { + buttonColor = combineRgb(0, 0, 0) + } else { + buttonColor = combineRgb(255, 255, 0) + textColor = combineRgb(0, 0, 0) + } + const result = { + color: textColor, + bgcolor: buttonColor, + } + if (event.options.stateText) { + return { + text: `${recState ?? 'N/A'}`, + ...result, + } + } else { + return result + } + }, + subscribe: (event): void => { + const cmd = UsbPlayerCommands.RecorderActiveState() + subscribeFeedback(ensureLoaded, subs, cmd, event) + }, + unsubscribe: (event: CompanionFeedbackInfo): void => { + const cmd = UsbPlayerCommands.RecorderActiveState() + unsubscribeFeedback(subs, cmd, event) + }, + }, + [FeedbackId.PlayerTime]: { + type: 'advanced', + name: 'USB Player Time', + description: 'Current Time of the USB Player', + options: [GetDropdown('Format', 'format', getTimeFormatChoices())], + callback: (): CompanionAdvancedFeedbackResult => { + const cmd = UsbPlayerCommands.PlayerTotalTime() + return { + text: `${StateUtil.getNumberFromState(cmd, state) ?? 'N/A'}`, + } + }, + subscribe: (event): void => { + const cmd = UsbPlayerCommands.PlayerTotalTime() + subs.subscribePoll(cmd, event.id, event.feedbackId as FeedbackId) + subscribeFeedback(ensureLoaded, subs, cmd, event) + }, + unsubscribe: (event: CompanionFeedbackInfo): void => { + const cmd = UsbPlayerCommands.PlayerTotalTime() + subs.unsubscribePoll(cmd, event.feedbackId as FeedbackId) + unsubscribeFeedback(subs, cmd, event) + }, + }, + } + + return feedbacks +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..ceb6d4c --- /dev/null +++ b/src/index.ts @@ -0,0 +1,432 @@ +import { + InstanceBase, + runEntrypoint, + InstanceStatus, + SomeCompanionConfigField, + OSCSomeArguments, +} from '@companion-module/base' +import PQueue from 'p-queue' +import { InstanceBaseExt } from './types.js' +import { GetConfigFields, WingConfig } from './config.js' +import { UpdateVariables as UpdateAllVariables, UpdateVariableDefinitions } from './variables.js' +import { UpgradeScripts } from './upgrades.js' +import { createActions } from './actions/index.js' +import { FeedbackId, GetFeedbacksList } from './feedbacks.js' +import { WingState, WingSubscriptions } from './state/index.js' +import osc, { OscMessage } from 'osc' +import debounceFn from 'debounce-fn' +import { WingTransitions } from './transitions.js' +import { WingDeviceDetectorInstance } from './device-detector.js' +import { ModelSpec, WingModel } from './models/types.js' +import { getDeskModel } from './models/index.js' +import { GetPresets } from './presets.js' + +export class WingInstance extends InstanceBase implements InstanceBaseExt { + config!: WingConfig + state: WingState + model: ModelSpec + osc: osc.UDPPort + + private WingSubscriptions: WingSubscriptions + + private heartbeatTimer: NodeJS.Timeout | undefined + private reconnectTimer: NodeJS.Timeout | undefined + private syncInterval: NodeJS.Timeout | undefined + private subscribeInterval: NodeJS.Timeout | undefined + private statusUpdateInterval: NodeJS.Timeout | undefined + private variableUpdateInterval: NodeJS.Timeout | undefined + + /** + * Keeps track of sent requests for values that have been sent and not yet answered. + */ + private inFlightRequests: { [path: string]: () => void } = {} + + private readonly debounceUpdateCompanion: () => void + private readonly requestQueue: PQueue = new PQueue({ + concurrency: 100, + timeout: 200, + throwOnTimeout: true, + }) + transitions: WingTransitions + + private readonly messageFeedbacks = new Set() + private readonly debounceMessageFeedbacks: () => void + + private variableMessages: { [path: string]: OscMessage } = {} + + constructor(internal: unknown) { + super(internal) + + this.osc = new osc.UDPPort({}) + this.WingSubscriptions = new WingSubscriptions() + this.model = getDeskModel(WingModel.Full) + this.state = new WingState(this.model) + + this.debounceUpdateCompanion = debounceFn(this.updateCompanionWithState.bind(this), { + wait: 200, + maxWait: 2000, + before: false, + after: true, + }) + + this.debounceMessageFeedbacks = debounceFn( + () => { + const feedbacks = Array.from(this.messageFeedbacks).map((feedback) => feedback.toString()) + this.messageFeedbacks.clear() + this.checkFeedbacks(...feedbacks) + }, + { + wait: 100, + maxWait: 500, + before: true, + after: true, + }, + ) + + this.transitions = new WingTransitions(this) + } + + async init(config: WingConfig): Promise { + this.config = config + + this.transitions.setUpdateRate(this.config.fadeUpdateRate ?? 50) + this.updateStatus(InstanceStatus.Connecting) + this.updateActions() + this.updateFeedbacks() + this.updateVariableDefinitions() + + if (this.config.host !== undefined) { + this.setupOscSocket() + } + + this.variableUpdateInterval = setInterval(() => { + this.UpdateVariables() + }, this.config.variableUpdateRate) + + WingDeviceDetectorInstance.subscribe(this.id) + + this.updateCompanionWithState() + } + + async destroy(): Promise { + if (this.reconnectTimer) { + clearTimeout(this.reconnectTimer) + this.reconnectTimer = undefined + } + if (this.syncInterval) { + clearInterval(this.syncInterval) + this.syncInterval = undefined + } + if (this.subscribeInterval) { + clearInterval(this.subscribeInterval) + this.subscribeInterval = undefined + } + if (this.statusUpdateInterval) { + clearInterval(this.statusUpdateInterval) + this.statusUpdateInterval = undefined + } + if (this.variableUpdateInterval) { + clearInterval(this.variableUpdateInterval) + this.variableUpdateInterval = undefined + } + + WingDeviceDetectorInstance.unsubscribe(this.id) + this.transitions.stopAll() + + if (this.osc) { + try { + this.osc.close() + } catch (_e) { + // Ignore + } + } + } + + async configUpdated(config: WingConfig): Promise { + this.config = config + + this.WingSubscriptions = new WingSubscriptions() + this.state = new WingState(this.model) + + this.transitions.stopAll() + this.transitions.setUpdateRate(this.config.fadeUpdateRate ?? 50) + + if (this.config.host !== undefined) { + this.updateStatus(InstanceStatus.Connecting) + this.setupOscSocket() + this.updateCompanionWithState() + } + + this.variableUpdateInterval = setInterval(() => { + this.UpdateVariables() + }, this.config.variableUpdateRate) + } + + getConfigFields(): SomeCompanionConfigField[] { + return GetConfigFields() + } + + updateActions(): void { + this.setActionDefinitions(createActions(this)) + } + + updateFeedbacks(): void { + this.setFeedbackDefinitions(GetFeedbacksList(this, this.state, this.WingSubscriptions, this.ensureLoaded)) + } + + updateVariableDefinitions(): void { + UpdateVariableDefinitions(this) + } + + private setupOscSocket(): void { + this.updateStatus(InstanceStatus.Connecting) + + if (this.reconnectTimer) { + clearTimeout(this.reconnectTimer) + this.reconnectTimer = undefined + } + if (this.syncInterval) { + clearInterval(this.syncInterval) + this.syncInterval = undefined + } + if (this.statusUpdateInterval) { + clearInterval(this.statusUpdateInterval) + this.statusUpdateInterval = undefined + } + + if (this.osc) { + try { + this.osc.close() + } catch (_e) { + // Ignore + } + } + + this.osc = new osc.UDPPort({ + localAddress: '0.0.0.0', + localPort: 0, + broadcast: true, + metadata: true, + remoteAddress: this.config.host, + remotePort: 2223, + }) + + this.osc.on('error', (err: Error): void => { + this.log('error', `Error: ${err.message}`) + this.updateStatus(InstanceStatus.ConnectionFailure, err.message) + + this.requestQueue.clear() + this.inFlightRequests = {} + + if (this.heartbeatTimer) { + clearInterval(this.heartbeatTimer) + this.heartbeatTimer = undefined + } + if (this.subscribeInterval) { + clearInterval(this.subscribeInterval) + this.subscribeInterval = undefined + } + if (this.statusUpdateInterval) { + clearInterval(this.statusUpdateInterval) + this.statusUpdateInterval = undefined + } + }) + this.osc.on('ready', () => { + this.pulse() + this.heartbeatTimer = setInterval(() => { + this.pulse() + }, 1500) + + this.subscribeForUpdates() + this.subscribeInterval = setInterval(() => { + this.subscribeForUpdates() + }, 9000) + this.statusUpdateInterval = setInterval(() => { + this.requestStatusUpdates() + }, this.config.statusPollUpdateRate ?? 250) + + this.state.requestNames(this.model, this.ensureLoaded) + this.requestQueue.clear() + this.inFlightRequests = {} + + this.updateStatus(InstanceStatus.Connecting) + }) + + this.osc.on('close' as any, () => { + if (this.heartbeatTimer !== undefined) { + clearInterval(this.heartbeatTimer) + this.heartbeatTimer = undefined + } + + if (this.subscribeInterval) { + clearInterval(this.subscribeInterval) + this.subscribeInterval = undefined + } + if (this.statusUpdateInterval) { + clearInterval(this.statusUpdateInterval) + this.statusUpdateInterval = undefined + } + }) + + this.osc.on('message', (message): void => { + this.updateStatus(InstanceStatus.Ok) + const args = message.args as osc.MetaArgument[] + // this.log('debug', `Received ${JSON.stringify(message)}`) + this.state.set(message.address, args) + + if (this.inFlightRequests[message.address]) { + this.log('debug', `Received answer for request ${message.address}`) + this.inFlightRequests[message.address]() + delete this.inFlightRequests[message.address] + } + + // setImmediate(() => { + this.checkFeedbackChanges(message) + + this.variableMessages[message.address] = message + // this.debounceUpdateVariables() + + // }) + }) + + this.osc.open() + } + + private pulse(): void { + try { + this.sendCommand('/WING?') + } catch (_e) { + // Ignore + } + } + private subscribeForUpdates(): void { + if (this.osc) { + try { + this.sendCommand('/*S', undefined) + } catch (_e) { + // Ignore + } + } + } + + private requestStatusUpdates(): void { + this.WingSubscriptions.getPollPaths().forEach((c) => { + this.sendCommand(c) + }) + } + + private checkFeedbackChanges(msg: osc.OscMessage): void { + const toUpdate = this.WingSubscriptions.getFeedbacks(msg.address) + if (toUpdate.length > 0) { + toUpdate.forEach((f) => this.messageFeedbacks.add(f)) + this.debounceMessageFeedbacks() + } + + const channelNameRe = /\/ch\/\d+\/\$name/ + const auxNameRe = /\/aux\/\d+\/\$name/ + const busNameRe = /\/bus\/\d+\/\$name/ + const mtxNameRe = /\/mtx\/\d+\/\$name/ + const mainNameRe = /\/main\/\d+\/\$name/ + if ( + channelNameRe.test(msg.address) || + busNameRe.test(msg.address) || + auxNameRe.test(msg.address) || + mtxNameRe.test(msg.address) || + mainNameRe.test(msg.address) + ) { + // this.log('info', 'Would update now') + + this.debounceUpdateCompanion() + } + } + + private updateCompanionWithState(): void { + this.state.updateNames(this.model) + + this.setPresetDefinitions(GetPresets(this)) + this.setActionDefinitions(createActions(this)) + this.setFeedbackDefinitions(GetFeedbacksList(this, this.state, this.WingSubscriptions, this.ensureLoaded)) + this.checkFeedbacks() + } + + private UpdateVariables(): void { + const messages: OscMessage[] = Object.values(this.variableMessages) + if (messages.length === 0) { + return + } + UpdateAllVariables(this, messages) + this.variableMessages = {} + } + + sendCommand = (cmd: string, argument?: number | string, preferFloat?: boolean): void => { + if (!this.config.host) { + return + } + + let args: OSCSomeArguments = [] + if (typeof argument === 'number') { + if (preferFloat) { + args = { + type: 'f', + value: argument, + } + } else { + if (Number.isInteger(argument)) { + args = { + type: 'i', + value: argument, + } + } else { + args = { + type: 'f', + value: argument, + } + } + } + } else if (typeof argument === 'string') { + args = { + type: 's', + value: argument, + } + } else if (argument == undefined) { + args = [] + } else { + console.error('Unsupported argument type. Command aborted.') + } + const command = { + address: cmd, + args: args, + } + this.osc.send(command) + this.osc.send({ address: cmd, args: [] }) + } + + ensureLoaded = (path: string): void => { + this.requestQueue + .add(async () => { + if (this.inFlightRequests[path]) { + return + } + + if (this.state.get(path)) { + return + } + + this.log('debug', `Requesting ${path}`) + + const p = new Promise((resolve) => { + this.inFlightRequests[path] = resolve + }) + + this.sendCommand(path, undefined) + + await p + }) + .catch((_e: unknown) => { + delete this.inFlightRequests[path] + // this.log('error', `Request failed for ${path}`) + }) + } +} + +runEntrypoint(WingInstance, UpgradeScripts) diff --git a/src/models/compact.ts b/src/models/compact.ts new file mode 100644 index 0000000..74fe311 --- /dev/null +++ b/src/models/compact.ts @@ -0,0 +1,20 @@ +import { WingModel, ModelSpec } from './types.js' + +export const WingCompact: ModelSpec = { + id: WingModel.Compact, + label: 'Compact', + + channels: 40, + auxes: 8, + busses: 16, + matrices: 8, + mains: 4, + + localInputs: 8, + localOutputs: 8, + localAuxIn: 8, + localAuxOut: 8, + aesIns: 2, + aesOut: 2, + stageConnect: 1, +} diff --git a/src/models/full.ts b/src/models/full.ts new file mode 100644 index 0000000..a1d1ac1 --- /dev/null +++ b/src/models/full.ts @@ -0,0 +1,20 @@ +import { WingModel, ModelSpec } from './types.js' + +export const WingFull: ModelSpec = { + id: WingModel.Full, + label: 'Full', + + channels: 40, + auxes: 8, + busses: 16, + matrices: 8, + mains: 4, + + localInputs: 8, + localOutputs: 8, + localAuxIn: 8, + localAuxOut: 8, + aesIns: 2, + aesOut: 2, + stageConnect: 1, +} diff --git a/src/models/index.ts b/src/models/index.ts new file mode 100644 index 0000000..f3d1d5c --- /dev/null +++ b/src/models/index.ts @@ -0,0 +1,18 @@ +import { WingFull } from './full.js' +import { WingCompact } from './compact.js' +import { WingRack } from './rack.js' +import { ModelSpec, WingModel } from './types.js' + +// eslint-disable-file @typescript-eslint/no-unsafe-enum-comparison +export function getDeskModel(id: WingModel): ModelSpec { + switch (id) { + case WingModel.Full: + return WingFull + case WingModel.Compact: + return WingCompact + case WingModel.Rack: + return WingRack + default: + return WingFull + } +} diff --git a/src/models/rack.ts b/src/models/rack.ts new file mode 100644 index 0000000..49b59bf --- /dev/null +++ b/src/models/rack.ts @@ -0,0 +1,20 @@ +import { WingModel, ModelSpec } from './types.js' + +export const WingRack: ModelSpec = { + id: WingModel.Rack, + label: 'Rack', + + channels: 40, + auxes: 8, + busses: 16, + matrices: 8, + mains: 4, + + localInputs: 8, + localOutputs: 8, + localAuxIn: 8, + localAuxOut: 8, + aesIns: 2, + aesOut: 2, + stageConnect: 1, +} diff --git a/src/models/types.ts b/src/models/types.ts new file mode 100644 index 0000000..af164c4 --- /dev/null +++ b/src/models/types.ts @@ -0,0 +1,34 @@ +import { DropdownChoice } from '@companion-module/base' + +export enum WingModel { + Full = 'full', + Compact = 'compact', + Rack = 'rack', +} + +export interface ModelSpec { + id: WingModel + label: string + + channels: number + auxes: number + busses: number + matrices: number + mains: number + + localInputs: number + localOutputs: number + localAuxIn?: number + localAuxOut?: number + aesIns: number + aesOut: number + + stageConnect: number + headphoneOuts?: number +} + +export const ModelChoices: DropdownChoice[] = [ + { id: WingModel.Full.toString(), label: 'Full' }, + { id: WingModel.Compact.toString(), label: 'Compact' }, + { id: WingModel.Rack.toString(), label: 'Rack' }, +] diff --git a/src/presets.ts b/src/presets.ts new file mode 100644 index 0000000..d9c8159 --- /dev/null +++ b/src/presets.ts @@ -0,0 +1,103 @@ +import { combineRgb, CompanionPresetDefinitions, CompanionButtonPresetDefinition } from '@companion-module/base' +import { InstanceBaseExt } from './types.js' +import { WingConfig } from './config.js' +import { CommonActions } from './actions/common.js' +import { FeedbackId } from './feedbacks.js' + +export function GetPresets(_instance: InstanceBaseExt): CompanionPresetDefinitions { + const presets: { + [id: string]: CompanionButtonPresetDefinition | undefined + } = {} + + presets['mute-button'] = { + name: 'Mute Button', + category: 'Channel', + type: 'button', + style: { + text: 'Mute\\n$(wing:ch1_name)', + size: 'auto', + color: combineRgb(255, 255, 255), + bgcolor: combineRgb(0, 0, 0), + }, + options: { + stepAutoProgress: true, + }, + steps: [ + { + down: [ + { + actionId: CommonActions.SetMute, + options: { sel: '/ch/1', mute: 2 }, + }, + ], + up: [], + }, + ], + feedbacks: [ + { + feedbackId: FeedbackId.Mute, + options: { sel: '/ch/1', mute: 1 }, + style: { + color: combineRgb(255, 255, 255), + bgcolor: combineRgb(255, 0, 0), + }, + }, + ], + } + + presets['solo-button'] = { + name: 'Solo Button', + category: 'Channel', + type: 'button', + style: { + text: 'Solo CH1', + size: 'auto', + color: combineRgb(255, 255, 255), + bgcolor: combineRgb(0, 0, 0), + }, + options: { + stepAutoProgress: true, + }, + steps: [ + { + down: [ + { + actionId: CommonActions.DeltaFader, + options: { + sel: '/ch/1', + delta: 3, + fadeDuration: 1000, + fadeAlgorithm: 'quadratic', + fadeType: 'ease-in-out', + }, + }, + { + actionId: CommonActions.StorePanorama, + options: { sel: '/ch/1' }, + }, + { + actionId: CommonActions.SetPanorama, + options: { sel: '/ch/1', pan: 0, fadeDuration: 1000, fadeAlgorithm: 'quadratic', fadeType: 'ease-in-out' }, + }, + ], + up: [], + }, + { + down: [ + { + actionId: CommonActions.UndoDeltaFader, + options: { sel: '/ch/1', fadeDuration: 1000, fadeAlgorithm: 'quadratic', fadeType: 'ease-in-out' }, + }, + { + actionId: CommonActions.RestorePanorama, + options: { sel: '/ch/1', fadeDuration: 1000, fadeAlgorithm: 'quadratic', fadeType: 'ease-in-out' }, + }, + ], + up: [], + }, + ], + feedbacks: [], + } + + return presets +} diff --git a/src/state/index.ts b/src/state/index.ts new file mode 100644 index 0000000..38df7cd --- /dev/null +++ b/src/state/index.ts @@ -0,0 +1,4 @@ +import * as StateUtil from './utils.js' +import { WingState, WingSubscriptions } from './state.js' + +export { WingState, WingSubscriptions, StateUtil } diff --git a/src/state/state.ts b/src/state/state.ts new file mode 100644 index 0000000..b9a95b7 --- /dev/null +++ b/src/state/state.ts @@ -0,0 +1,251 @@ +import osc from 'osc' +import { FeedbackId } from '../feedbacks.js' +import { DropdownChoice } from '@companion-module/base' +import { ModelSpec } from '../models/types.js' +import { getIdLabelPair } from '../choices/utils.js' +import * as Commands from '../commands/index.js' + +type NameChoices = { + channels: DropdownChoice[] + auxes: DropdownChoice[] + busses: DropdownChoice[] + matrices: DropdownChoice[] + mains: DropdownChoice[] +} + +type Names = { + channels: string[] + auxes: string[] + busses: string[] + matrices: string[] + mains: string[] +} +export class WingState implements IStoredChannelSubject { + private readonly data: Map + private readonly pressStorage: Map + private readonly deltaStorage: Map + private storedChannel: number + namedChoices: NameChoices = { + channels: [], + auxes: [], + busses: [], + matrices: [], + mains: [], + } + + names: Names = { + channels: [], + auxes: [], + busses: [], + matrices: [], + mains: [], + } + + constructor(model: ModelSpec) { + this.data = new Map() + this.pressStorage = new Map() + this.deltaStorage = new Map() + this.storedChannel = 1 + this.updateNames(model) + } + + // StoredChannelSubject + private observers: IStoredChannelObserver[] = [] + + attach(observer: IStoredChannelObserver): void { + if (!this.observers.includes(observer)) { + this.observers.push(observer) + } + } + detach(observer: IStoredChannelObserver): void { + const observerIndex = this.observers.indexOf(observer) + if (observerIndex !== -1) { + this.observers.splice(observerIndex, 1) + } + } + + notify(): void { + for (const observer of this.observers) { + observer.storedChannelChanged() + } + } + + public get(path: string): osc.MetaArgument[] | undefined { + // console.log(`Getting ${path}`) + return this.data.get(path) + } + public set(path: string, data: osc.MetaArgument[]): void { + const key = path + if (data[0].value == '-oo') { + data[0] = { type: 'f', value: -140 } + } + // if (data[0].type == 's') { + // const strAsNum = Number(data[0].value) + // console.log(`Setting ${key} to ${strAsNum}`) + // if (strAsNum != undefined) data[0] = { type: 'f', value: strAsNum } + // } + + this.data.set(key, data) + // console.log(`Setting ${key}`) + } + + public setPressValue(path: string, value: number): void { + this.pressStorage.set(path, value) + } + public popPressValue(path: string): number | undefined { + const val = this.pressStorage.get(path) + if (val !== undefined) this.pressStorage.delete(path) + return val + } + + public storeDelta(path: string, delta: number): void { + this.deltaStorage.set(path, delta) + } + + public restoreDelta(path: string): number { + const delta = this.deltaStorage.get(path) + this.deltaStorage.delete(path) + return delta ?? 0 + } + + private getRealName(key: string): string | undefined { + const name = this.get(key)?.[0]?.value as string + return name + } + + private getNameForChoice( + index: number, + id: string, + nameKey: string, + defaultName: string, + short_name: string, + ): DropdownChoice { + const realName = this.getRealName(nameKey) + const label = realName ? `${short_name}${index} - ${realName}` : `${defaultName} ${index}` + return getIdLabelPair(id, label) + } + + public updateNames(model: ModelSpec): void { + this.namedChoices.channels = [] + for (let ch = 1; ch <= model.channels; ch++) { + this.names.channels.push(this.getRealName(Commands.Channel.RealName(ch)) ?? `Channel ${ch}`) + this.namedChoices.channels.push( + this.getNameForChoice(ch, Commands.Channel.Node(ch), Commands.Channel.RealName(ch), 'Channel', 'CH'), + ) + } + + this.namedChoices.auxes = [] + for (let aux = 1; aux <= model.auxes; aux++) { + this.names.channels.push(this.getRealName(Commands.Channel.RealName(aux)) ?? `Aux ${aux}`) + this.namedChoices.auxes.push( + this.getNameForChoice(aux, Commands.Aux.Node(aux), Commands.Aux.RealName(aux), 'Aux', 'A'), + ) + } + + this.namedChoices.busses = [] + for (let bus = 1; bus <= model.busses; bus++) { + this.names.channels.push(this.getRealName(Commands.Channel.RealName(bus)) ?? `Bus ${bus}`) + this.namedChoices.busses.push( + this.getNameForChoice(bus, Commands.Bus.Node(bus), Commands.Bus.Name(bus), 'Bus', 'B'), + ) + } + + this.namedChoices.matrices = [] + for (let matrix = 1; matrix <= model.matrices; matrix++) { + this.names.channels.push(this.getRealName(Commands.Channel.RealName(matrix)) ?? `Matrix ${matrix}`) + this.namedChoices.matrices.push( + this.getNameForChoice(matrix, Commands.Matrix.Node(matrix), Commands.Matrix.RealName(matrix), 'Matrix', 'MX'), + ) + } + + this.namedChoices.mains = [] + for (let main = 1; main <= model.mains; main++) { + this.names.channels.push(this.getRealName(Commands.Channel.RealName(main)) ?? `Main ${main}`) + this.namedChoices.mains.push( + this.getNameForChoice(main, Commands.Main.Node(main), Commands.Main.RealName(main), 'Main', 'M'), + ) + } + } + + public requestNames(model: ModelSpec, ensureLoaded: (path: string) => void): void { + for (let ch = 1; ch <= model.channels; ch++) { + ensureLoaded(Commands.Channel.RealName(ch)) + } + for (let aux = 1; aux <= model.auxes; aux++) { + ensureLoaded(Commands.Aux.RealName(aux)) + } + for (let bus = 1; bus <= model.busses; bus++) { + ensureLoaded(Commands.Bus.Name(bus)) + } + for (let mtx = 1; mtx <= model.matrices; mtx++) { + ensureLoaded(Commands.Matrix.RealName(mtx)) + } + for (let main = 1; main <= model.mains; main++) { + ensureLoaded(Commands.Main.RealName(main)) + } + } + + public setStoredChannel(channel: number): void { + this.storedChannel = channel + this.notify() + } + + public getStoredChannel(): number { + return this.storedChannel + } +} + +export class WingSubscriptions { + private readonly data: Map> + private readonly pollData: Set + + constructor() { + this.data = new Map() + this.pollData = new Set() + } + + public getFeedbacks(path: string): FeedbackId[] { + const entries = this.data.get(path) + if (entries) { + return Array.from(new Set(entries.values())) + } else { + return [] + } + } + public subscribe(path: string, feedbackId: string, type: FeedbackId): void { + let entries = this.data.get(path) + if (!entries) { + entries = new Map() + this.data.set(path, entries) + } + entries.set(feedbackId, type) + } + public unsubscribe(path: string, feedbackId: string): void { + const entries = this.data.get(path) + if (entries) { + entries.delete(feedbackId) + } + } + + public getPollPaths(): string[] { + return Array.from(this.pollData) + } + public subscribePoll(path: string, feedbackId: string, type: FeedbackId): void { + this.subscribe(path, feedbackId, type) + this.pollData.add(path) + } + public unsubscribePoll(path: string, feedbackId: string): void { + this.unsubscribe(path, feedbackId) + this.pollData.delete(path) + } +} + +interface IStoredChannelSubject { + attach(observer: IStoredChannelObserver): void + detach(observer: IStoredChannelObserver): void + notify(): void +} + +export interface IStoredChannelObserver { + storedChannelChanged(): void +} diff --git a/src/state/utils.ts b/src/state/utils.ts new file mode 100644 index 0000000..1df936f --- /dev/null +++ b/src/state/utils.ts @@ -0,0 +1,65 @@ +import { WingState } from './state.js' + +export function getStringFromState(cmd: string, state: WingState): string | undefined { + const currentState = state.get(cmd) + if (!currentState || currentState.length === 0) { + return undefined + } + + const firstState = currentState[0] + + if (firstState.type === 's') { + return firstState.value + } + return undefined +} + +export function storeValueWithKey(cmd: string, state: WingState, value: number | undefined): void { + const key = `${cmd}` + if (value !== undefined) { + state.setPressValue(key, value) + } +} + +export function getValueFromKey(cmd: string, state: WingState): number | undefined { + const key = `${cmd}` + const value = state.popPressValue(key) + return value +} + +export function getNumberFromState(cmd: string, state: WingState): number | undefined { + const currentState = state.get(cmd) + + if (!currentState || currentState.length === 0) { + return undefined + } + + const firstState = currentState[0] + + if (firstState.type === 'f' || firstState.type === 'i') { + return firstState.value + } + + if (firstState.type === 's') { + const numericValue = parseFloat(firstState.value) + return isNaN(numericValue) ? undefined : numericValue + } + + return undefined +} + +export function getBooleanFromState(cmd: string, state: WingState): boolean | undefined { + const val = getNumberFromState(cmd, state) + + if (val) return val > 0 + return undefined +} + +export function storeValueForCommand(cmd: string, state: WingState): void { + const value = getNumberFromState(cmd, state) + storeValueWithKey(cmd, state, value) +} + +export function restoreValueForCommand(cmd: string, state: WingState): number | undefined { + return getValueFromKey(cmd, state) +} diff --git a/src/transitions.ts b/src/transitions.ts new file mode 100644 index 0000000..d90bf7c --- /dev/null +++ b/src/transitions.ts @@ -0,0 +1,96 @@ +import { WingConfig } from './config.js' +import { Easing } from './easings.js' +import { InstanceBaseExt } from './types.js' + +export interface TransitionInfo { + steps: number[] +} + +export class WingTransitions { + private readonly transitions: Map + private readonly instance: InstanceBaseExt + private fadeUpdateRate: number + + private tickInterval: NodeJS.Timeout | undefined + + constructor(instance: InstanceBaseExt) { + this.transitions = new Map() + this.instance = instance + this.fadeUpdateRate = 50 + } + + public setUpdateRate(rate: number): void { + this.fadeUpdateRate = rate + } + + private sendOsc(cmd: string, arg?: string | number): void { + if (this.instance.config.host) { + this.instance.sendCommand(cmd, arg, true) + } + } + + public stopAll(): void { + this.transitions.clear() + + if (this.tickInterval) { + clearInterval(this.tickInterval) + delete this.tickInterval + } + } + + private runTick(): void { + const completedPaths: string[] = [] + for (const [path, info] of this.transitions.entries()) { + const newValue = info.steps.shift() + if (newValue !== undefined) { + this.sendOsc(path, newValue) + } + if (info.steps.length === 0) { + completedPaths.push(path) + } + } + + // Remove any completed transitions + for (const path of completedPaths) { + this.transitions.delete(path) + } + + // If nothing is left, stop the timer + if (this.transitions.size === 0) { + this.stopAll() + } + } + + public run( + path: string, + from: number | undefined, + to: number, + duration: number, + algorithm?: Easing.algorithm, + curve?: Easing.curve, + ): void { + const interval = this.fadeUpdateRate + const stepCount = Math.ceil(duration / interval) + + if (stepCount <= 1 || typeof from !== 'number') { + this.transitions.delete(path) + this.sendOsc(path, to) + } else { + const diff = to - from + const steps: number[] = [] + + const easing = Easing.getEasing(algorithm, curve) + for (let i = 1; i <= stepCount; i++) { + const fraction = easing(i / stepCount) + steps.push(from + diff * fraction) + } + + this.transitions.set(path, { steps }) + + // Start the tick if not already running + if (!this.tickInterval) { + this.tickInterval = setInterval(() => this.runTick(), this.fadeUpdateRate) + } + } + } +} diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..4953acf --- /dev/null +++ b/src/types.ts @@ -0,0 +1,13 @@ +import { InstanceBase } from '@companion-module/base' +import osc from 'osc' +import { WingTransitions } from './transitions.js' +import { WingState } from './state/state.js' + +export interface InstanceBaseExt extends InstanceBase { + config: TConfig + osc: osc.UDPPort + transitions: WingTransitions + state: WingState + sendCommand: (cmd: string, argument?: number | string, preferFloat?: boolean) => void + ensureLoaded: (path: string) => void +} diff --git a/src/upgrades.ts b/src/upgrades.ts new file mode 100644 index 0000000..188f3c4 --- /dev/null +++ b/src/upgrades.ts @@ -0,0 +1,16 @@ +import type { CompanionStaticUpgradeScript } from '@companion-module/base' +import type { WingConfig } from './config.js' + +export const UpgradeScripts: CompanionStaticUpgradeScript[] = [ + /* + * Place your upgrade scripts here + * Remember that once it has been added it cannot be removed! + */ + // function (context, props) { + // return { + // updatedConfig: null, + // updatedActions: [], + // updatedFeedbacks: [], + // } + // }, +] diff --git a/src/variables.ts b/src/variables.ts new file mode 100644 index 0000000..83731b6 --- /dev/null +++ b/src/variables.ts @@ -0,0 +1,227 @@ +import { OscMessage } from 'osc' +import type { WingInstance } from './index.js' +import { OSCMetaArgument } from '@companion-module/base/dist/index.js' // eslint-disable-line n/no-missing-import + +export function UpdateVariableDefinitions(self: WingInstance): void { + const model = self.model + // const state = self.state + + const variables = [] + + variables.push({ variableId: 'desk_ip', name: 'Desk IP Address' }) + variables.push({ variableId: 'desk_name', name: 'Desk Name' }) + + variables.push({ variableId: 'player_time', name: 'USB Player Time' }) + variables.push({ variableId: 'recorder_time', name: 'USB Recorder Time' }) + + for (let ch = 1; ch <= model.channels; ch++) { + variables.push({ + variableId: `ch${ch}_name`, + name: `Channel ${ch} Name`, + }) + variables.push({ + variableId: `ch${ch}_gain`, + name: `Channel ${ch} Gain`, + }) + variables.push({ + variableId: `ch${ch}_level`, + name: `Channel ${ch} Level`, + }) + variables.push({ + variableId: `ch${ch}_pan`, + name: `Channel ${ch} Pan`, + }) + for (let bus = 1; bus <= model.busses; bus++) { + variables.push({ + variableId: `ch${ch}_bus${bus}_level`, + name: `Channel ${ch} to Bus ${bus} Level`, + }) + variables.push({ + variableId: `ch${ch}_bus${bus}_pan`, + name: `Channel ${ch} to Bus ${bus} Pan`, + }) + } + for (let mtx = 1; mtx <= model.matrices; mtx++) { + variables.push({ + variableId: `ch${ch}_mtx${mtx}_level`, + name: `Channel ${ch} to Matrix ${mtx} Level`, + }) + } + } + + for (let aux = 1; aux <= model.auxes; aux++) { + variables.push({ + variableId: `ch${aux}_name`, + name: `Aux ${aux} Name`, + }) + variables.push({ + variableId: `aux${aux}_gain`, + name: `Aux ${aux} Gain`, + }) + variables.push({ + variableId: `aux${aux}_level`, + name: `Aux ${aux} Level`, + }) + variables.push({ + variableId: `aux${aux}_pan`, + name: `Aux ${aux} Pan`, + }) + for (let bus = 1; bus <= model.busses; bus++) { + variables.push({ + variableId: `aux${aux}_bus${bus}_level`, + name: `Aux ${aux} to Bus ${bus} Level`, + }) + variables.push({ + variableId: `aux${aux}_bus${bus}_pan`, + name: `Aux ${aux} to Bus ${bus} Pan`, + }) + } + } + + for (let bus = 1; bus <= model.busses; bus++) { + variables.push({ + variableId: `ch${bus}_name`, + name: `Bus ${bus} Name`, + }) + variables.push({ + variableId: `bus${bus}_level`, + name: `Bus ${bus} Level`, + }) + variables.push({ + variableId: `bus${bus}_pan`, + name: `Bus ${bus} Pan`, + }) + for (let send = 1; send <= model.busses; send++) { + if (bus == send) { + continue + } + variables.push({ + variableId: `bus${bus}_bus${send}_level`, + name: `Bus ${bus} to Bus ${send} Level`, + }) + variables.push({ + variableId: `bus${bus}_bus${send}_pan`, + name: `Bus ${bus} to Bus ${send} Pan`, + }) + } + } + + for (let mtx = 1; mtx <= model.matrices; mtx++) { + variables.push({ + variableId: `ch${mtx}_name`, + name: `Matrix ${mtx} Name`, + }) + variables.push({ + variableId: `mtx${mtx}_level`, + name: `Matrix ${mtx} Level`, + }) + variables.push({ + variableId: `mtx${mtx}_pan`, + name: `Matrix ${mtx} Pan`, + }) + } + + for (let main = 1; main <= model.mains; main++) { + variables.push({ + variableId: `ch${main}_name`, + name: `Main ${main} Name`, + }) + variables.push({ + variableId: `main${main}_level`, + name: `Main ${main} Level`, + }) + variables.push({ + variableId: `main${main}_pan`, + name: `Main ${main} Pan`, + }) + } + + self.setVariableDefinitions(variables) +} + +export function UpdateVariables(self: WingInstance, msgs: OscMessage[]): void { + for (const msg of msgs) { + const path = msg.address + const args = msg.args as OSCMetaArgument[] + + // console.log('Updating variable:', path, args); + + UpdateNameVariables(self, path, args[0]?.value as string) + UpdateFaderVariables(self, path, args[0]?.value as number) + UpdatePanoramaVariables(self, path, args[0]?.value as number) + } +} + +function UpdateNameVariables(self: WingInstance, path: string, value: string): void { + const match = path.match(/\/(\w+)\/(\d+)\/\$name/) + if (!match) { + return + } + + const base = match[1] + const num = match[2] + self.setVariableValues({ [`${base}${num}_name`]: value }) +} + +function UpdateFaderVariables(self: WingInstance, path: string, value: number): void { + // const match = path.match(/^\/(\w+)\/(\w+)(?:\/(\w+)\/(\w+))?/); + const match = path.match(/^\/(\w+)\/(\w+)(?:\/(\w+)\/(\w+))?\/(fdr|lvl|\$fdr|\$lvl)$/) + + if (!match) { + return + } + const source = `${match[1]}${match[2]}` + let destination = null + if (match[3] && match[4]) { + destination = `${match[3]}${match[4]}` + } + + if (destination) { + if (/^send(\d+)$/.test(destination)) { + destination = destination.replace(/^send(\d+)$/, 'bus$1') + } else if (/^sendMX(\d+)$/.test(destination)) { + destination = destination.replace(/^sendMX(\d+)$/, 'mtx$1') + } + } + let varName = null + if (destination) { + varName = `${source}_${destination}_level` + } else { + varName = `${source}_level` + } + const val = Math.round(value * 10) / 10 + if (val > -140) { + self.setVariableValues({ [varName]: val }) + } else { + self.setVariableValues({ [varName]: '-oo' }) + } +} + +function UpdatePanoramaVariables(self: WingInstance, path: string, value: number): void { + const match = path.match(/^\/(\w+)\/(\w+)(?:\/(\w+)\/(\w+))?\/(pan|\$pan)$/) + + if (!match) { + return + } + const source = `${match[1]}${match[2]}` + let destination = null + if (match[3] && match[4]) { + destination = `${match[3]}${match[4]}` + } + + if (destination) { + if (/^send(\d+)$/.test(destination)) { + destination = destination.replace(/^send(\d+)$/, 'bus$1') + } else if (/^sendMX(\d+)$/.test(destination)) { + destination = destination.replace(/^sendMX(\d+)$/, 'mtx$1') + } + } + + let varName = null + if (destination) { + varName = `${source}_${destination}_pan` + } else { + varName = `${source}_pan` + } + self.setVariableValues({ [varName]: Math.round(value) }) +} diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 0000000..df61900 --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,14 @@ +{ + "extends": "@companion-module/tools/tsconfig/node18/recommended", + "include": ["src/**/*.ts"], + "exclude": ["node_modules/**", "src/**/*spec.ts", "src/**/__tests__/*", "src/**/__mocks__/*"], + "compilerOptions": { + "outDir": "./dist", + "baseUrl": "./", + "paths": { + "*": ["./node_modules/*"] + }, + "module": "Node16", + "moduleResolution": "Node16" + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..e3fb9b7 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.build.json", + "include": ["src/**/*.ts"], + "exclude": ["node_modules/**"], + "compilerOptions": { + "types": ["node"] + } +} diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..dd7d5b5 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,3834 @@ +# This file is generated by running "yarn install" inside your project. +# Manual changes might be lost - proceed with caution! + +__metadata: + version: 8 + cacheKey: 10c0 + +"@companion-module/base@npm:~1.11.2": + version: 1.11.3 + resolution: "@companion-module/base@npm:1.11.3" + dependencies: + ajv: "npm:^8.17.1" + colord: "npm:^2.9.3" + ejson: "npm:^2.2.3" + eventemitter3: "npm:^5.0.1" + mimic-fn: "npm:^3.1.0" + nanoid: "npm:^3.3.8" + p-queue: "npm:^6.6.2" + p-timeout: "npm:^4.1.0" + tslib: "npm:^2.8.1" + checksum: 10c0/19acbc544de97ed8f02abf03e1d657085e51f0e92363456a84bdc1975723ba1bdfc0e943ff17444cb4e6a2b588abb794a9c5b48efb4d784be32784362e7c4efd + languageName: node + linkType: hard + +"@companion-module/tools@npm:^2.1.1": + version: 2.1.1 + resolution: "@companion-module/tools@npm:2.1.1" + dependencies: + "@eslint/js": "npm:^9.17.0" + eslint-config-prettier: "npm:^9.1.0" + eslint-plugin-n: "npm:^17.15.0" + eslint-plugin-prettier: "npm:^5.2.1" + find-up: "npm:^7.0.0" + parse-author: "npm:^2.0.0" + tar: "npm:^7.4.3" + webpack: "npm:^5.97.1" + webpack-cli: "npm:^5.1.4" + zx: "npm:^8.2.4" + peerDependencies: + "@companion-module/base": ^1.11.0 + eslint: ^9.0.0 + prettier: ^3.0.0 + typescript-eslint: ^8.2.0 + peerDependenciesMeta: + eslint: + optional: true + prettier: + optional: true + typescript-eslint: + optional: true + bin: + companion-generate-manifest: scripts/generate-manifest.js + companion-module-build: scripts/build.js + companion-module-check: scripts/check.js + checksum: 10c0/d4b5f76f46315fa8bedafc63aa7011986374bd6f96faab2e9726fc8ced8e98835c4b5b6ee5f2e940e46d9a803082b0948bcd84e5c1e5f7eb92f89d1cb155e98a + languageName: node + linkType: hard + +"@discoveryjs/json-ext@npm:^0.5.0": + version: 0.5.7 + resolution: "@discoveryjs/json-ext@npm:0.5.7" + checksum: 10c0/e10f1b02b78e4812646ddf289b7d9f2cb567d336c363b266bd50cd223cf3de7c2c74018d91cd2613041568397ef3a4a2b500aba588c6e5bd78c38374ba68f38c + languageName: node + linkType: hard + +"@eslint-community/eslint-utils@npm:^4.1.2, @eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0, @eslint-community/eslint-utils@npm:^4.4.1": + version: 4.4.1 + resolution: "@eslint-community/eslint-utils@npm:4.4.1" + dependencies: + eslint-visitor-keys: "npm:^3.4.3" + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + checksum: 10c0/2aa0ac2fc50ff3f234408b10900ed4f1a0b19352f21346ad4cc3d83a1271481bdda11097baa45d484dd564c895e0762a27a8240be7a256b3ad47129e96528252 + languageName: node + linkType: hard + +"@eslint-community/regexpp@npm:^4.10.0, @eslint-community/regexpp@npm:^4.11.0, @eslint-community/regexpp@npm:^4.12.1": + version: 4.12.1 + resolution: "@eslint-community/regexpp@npm:4.12.1" + checksum: 10c0/a03d98c246bcb9109aec2c08e4d10c8d010256538dcb3f56610191607214523d4fb1b00aa81df830b6dffb74c5fa0be03642513a289c567949d3e550ca11cdf6 + languageName: node + linkType: hard + +"@eslint/config-array@npm:^0.19.0": + version: 0.19.1 + resolution: "@eslint/config-array@npm:0.19.1" + dependencies: + "@eslint/object-schema": "npm:^2.1.5" + debug: "npm:^4.3.1" + minimatch: "npm:^3.1.2" + checksum: 10c0/43b01f596ddad404473beae5cf95c013d29301c72778d0f5bf8a6699939c8a9a5663dbd723b53c5f476b88b0c694f76ea145d1aa9652230d140fe1161e4a4b49 + languageName: node + linkType: hard + +"@eslint/core@npm:^0.9.0": + version: 0.9.1 + resolution: "@eslint/core@npm:0.9.1" + dependencies: + "@types/json-schema": "npm:^7.0.15" + checksum: 10c0/638104b1b5833a9bbf2329f0c0ddf322e4d6c0410b149477e02cd2b78c04722be90c14b91b8ccdef0d63a2404dff72a17b6b412ce489ea429ae6a8fcb8abff28 + languageName: node + linkType: hard + +"@eslint/eslintrc@npm:^3.2.0": + version: 3.2.0 + resolution: "@eslint/eslintrc@npm:3.2.0" + dependencies: + ajv: "npm:^6.12.4" + debug: "npm:^4.3.2" + espree: "npm:^10.0.1" + globals: "npm:^14.0.0" + ignore: "npm:^5.2.0" + import-fresh: "npm:^3.2.1" + js-yaml: "npm:^4.1.0" + minimatch: "npm:^3.1.2" + strip-json-comments: "npm:^3.1.1" + checksum: 10c0/43867a07ff9884d895d9855edba41acf325ef7664a8df41d957135a81a477ff4df4196f5f74dc3382627e5cc8b7ad6b815c2cea1b58f04a75aced7c43414ab8b + languageName: node + linkType: hard + +"@eslint/js@npm:9.17.0, @eslint/js@npm:^9.17.0": + version: 9.17.0 + resolution: "@eslint/js@npm:9.17.0" + checksum: 10c0/a0fda8657a01c60aa540f95397754267ba640ffb126e011b97fd65c322a94969d161beeaef57c1441c495da2f31167c34bd38209f7c146c7225072378c3a933d + languageName: node + linkType: hard + +"@eslint/object-schema@npm:^2.1.5": + version: 2.1.5 + resolution: "@eslint/object-schema@npm:2.1.5" + checksum: 10c0/5320691ed41ecd09a55aff40ce8e56596b4eb81f3d4d6fe530c50fdd6552d88102d1c1a29d970ae798ce30849752a708772de38ded07a6f25b3da32ebea081d8 + languageName: node + linkType: hard + +"@eslint/plugin-kit@npm:^0.2.3": + version: 0.2.4 + resolution: "@eslint/plugin-kit@npm:0.2.4" + dependencies: + levn: "npm:^0.4.1" + checksum: 10c0/1bcfc0a30b1df891047c1d8b3707833bded12a057ba01757a2a8591fdc8d8fe0dbb8d51d4b0b61b2af4ca1d363057abd7d2fb4799f1706b105734f4d3fa0dbf1 + languageName: node + linkType: hard + +"@humanfs/core@npm:^0.19.1": + version: 0.19.1 + resolution: "@humanfs/core@npm:0.19.1" + checksum: 10c0/aa4e0152171c07879b458d0e8a704b8c3a89a8c0541726c6b65b81e84fd8b7564b5d6c633feadc6598307d34564bd53294b533491424e8e313d7ab6c7bc5dc67 + languageName: node + linkType: hard + +"@humanfs/node@npm:^0.16.6": + version: 0.16.6 + resolution: "@humanfs/node@npm:0.16.6" + dependencies: + "@humanfs/core": "npm:^0.19.1" + "@humanwhocodes/retry": "npm:^0.3.0" + checksum: 10c0/8356359c9f60108ec204cbd249ecd0356667359b2524886b357617c4a7c3b6aace0fd5a369f63747b926a762a88f8a25bc066fa1778508d110195ce7686243e1 + languageName: node + linkType: hard + +"@humanwhocodes/module-importer@npm:^1.0.1": + version: 1.0.1 + resolution: "@humanwhocodes/module-importer@npm:1.0.1" + checksum: 10c0/909b69c3b86d482c26b3359db16e46a32e0fb30bd306a3c176b8313b9e7313dba0f37f519de6aa8b0a1921349e505f259d19475e123182416a506d7f87e7f529 + languageName: node + linkType: hard + +"@humanwhocodes/retry@npm:^0.3.0": + version: 0.3.1 + resolution: "@humanwhocodes/retry@npm:0.3.1" + checksum: 10c0/f0da1282dfb45e8120480b9e2e275e2ac9bbe1cf016d046fdad8e27cc1285c45bb9e711681237944445157b430093412b4446c1ab3fc4bb037861b5904101d3b + languageName: node + linkType: hard + +"@humanwhocodes/retry@npm:^0.4.1": + version: 0.4.1 + resolution: "@humanwhocodes/retry@npm:0.4.1" + checksum: 10c0/be7bb6841c4c01d0b767d9bb1ec1c9359ee61421ce8ba66c249d035c5acdfd080f32d55a5c9e859cdd7868788b8935774f65b2caf24ec0b7bd7bf333791f063b + languageName: node + linkType: hard + +"@isaacs/cliui@npm:^8.0.2": + version: 8.0.2 + resolution: "@isaacs/cliui@npm:8.0.2" + dependencies: + string-width: "npm:^5.1.2" + string-width-cjs: "npm:string-width@^4.2.0" + strip-ansi: "npm:^7.0.1" + strip-ansi-cjs: "npm:strip-ansi@^6.0.1" + wrap-ansi: "npm:^8.1.0" + wrap-ansi-cjs: "npm:wrap-ansi@^7.0.0" + checksum: 10c0/b1bf42535d49f11dc137f18d5e4e63a28c5569de438a221c369483731e9dac9fb797af554e8bf02b6192d1e5eba6e6402cf93900c3d0ac86391d00d04876789e + languageName: node + linkType: hard + +"@isaacs/fs-minipass@npm:^4.0.0": + version: 4.0.1 + resolution: "@isaacs/fs-minipass@npm:4.0.1" + dependencies: + minipass: "npm:^7.0.4" + checksum: 10c0/c25b6dc1598790d5b55c0947a9b7d111cfa92594db5296c3b907e2f533c033666f692a3939eadac17b1c7c40d362d0b0635dc874cbfe3e70db7c2b07cc97a5d2 + languageName: node + linkType: hard + +"@jridgewell/gen-mapping@npm:^0.3.5": + version: 0.3.8 + resolution: "@jridgewell/gen-mapping@npm:0.3.8" + dependencies: + "@jridgewell/set-array": "npm:^1.2.1" + "@jridgewell/sourcemap-codec": "npm:^1.4.10" + "@jridgewell/trace-mapping": "npm:^0.3.24" + checksum: 10c0/c668feaf86c501d7c804904a61c23c67447b2137b813b9ce03eca82cb9d65ac7006d766c218685d76e3d72828279b6ee26c347aa1119dab23fbaf36aed51585a + languageName: node + linkType: hard + +"@jridgewell/resolve-uri@npm:^3.1.0": + version: 3.1.2 + resolution: "@jridgewell/resolve-uri@npm:3.1.2" + checksum: 10c0/d502e6fb516b35032331406d4e962c21fe77cdf1cbdb49c6142bcbd9e30507094b18972778a6e27cbad756209cfe34b1a27729e6fa08a2eb92b33943f680cf1e + languageName: node + linkType: hard + +"@jridgewell/set-array@npm:^1.2.1": + version: 1.2.1 + resolution: "@jridgewell/set-array@npm:1.2.1" + checksum: 10c0/2a5aa7b4b5c3464c895c802d8ae3f3d2b92fcbe84ad12f8d0bfbb1f5ad006717e7577ee1fd2eac00c088abe486c7adb27976f45d2941ff6b0b92b2c3302c60f4 + languageName: node + linkType: hard + +"@jridgewell/source-map@npm:^0.3.3": + version: 0.3.6 + resolution: "@jridgewell/source-map@npm:0.3.6" + dependencies: + "@jridgewell/gen-mapping": "npm:^0.3.5" + "@jridgewell/trace-mapping": "npm:^0.3.25" + checksum: 10c0/6a4ecc713ed246ff8e5bdcc1ef7c49aaa93f7463d948ba5054dda18b02dcc6a055e2828c577bcceee058f302ce1fc95595713d44f5c45e43d459f88d267f2f04 + languageName: node + linkType: hard + +"@jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.14": + version: 1.5.0 + resolution: "@jridgewell/sourcemap-codec@npm:1.5.0" + checksum: 10c0/2eb864f276eb1096c3c11da3e9bb518f6d9fc0023c78344cdc037abadc725172c70314bdb360f2d4b7bffec7f5d657ce006816bc5d4ecb35e61b66132db00c18 + languageName: node + linkType: hard + +"@jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25": + version: 0.3.25 + resolution: "@jridgewell/trace-mapping@npm:0.3.25" + dependencies: + "@jridgewell/resolve-uri": "npm:^3.1.0" + "@jridgewell/sourcemap-codec": "npm:^1.4.14" + checksum: 10c0/3d1ce6ebc69df9682a5a8896b414c6537e428a1d68b02fcc8363b04284a8ca0df04d0ee3013132252ab14f2527bc13bea6526a912ecb5658f0e39fd2860b4df4 + languageName: node + linkType: hard + +"@nodelib/fs.scandir@npm:2.1.5": + version: 2.1.5 + resolution: "@nodelib/fs.scandir@npm:2.1.5" + dependencies: + "@nodelib/fs.stat": "npm:2.0.5" + run-parallel: "npm:^1.1.9" + checksum: 10c0/732c3b6d1b1e967440e65f284bd06e5821fedf10a1bea9ed2bb75956ea1f30e08c44d3def9d6a230666574edbaf136f8cfd319c14fd1f87c66e6a44449afb2eb + languageName: node + linkType: hard + +"@nodelib/fs.stat@npm:2.0.5, @nodelib/fs.stat@npm:^2.0.2": + version: 2.0.5 + resolution: "@nodelib/fs.stat@npm:2.0.5" + checksum: 10c0/88dafe5e3e29a388b07264680dc996c17f4bda48d163a9d4f5c1112979f0ce8ec72aa7116122c350b4e7976bc5566dc3ddb579be1ceaacc727872eb4ed93926d + languageName: node + linkType: hard + +"@nodelib/fs.walk@npm:^1.2.3": + version: 1.2.8 + resolution: "@nodelib/fs.walk@npm:1.2.8" + dependencies: + "@nodelib/fs.scandir": "npm:2.1.5" + fastq: "npm:^1.6.0" + checksum: 10c0/db9de047c3bb9b51f9335a7bb46f4fcfb6829fb628318c12115fbaf7d369bfce71c15b103d1fc3b464812d936220ee9bc1c8f762d032c9f6be9acc99249095b1 + languageName: node + linkType: hard + +"@npmcli/agent@npm:^3.0.0": + version: 3.0.0 + resolution: "@npmcli/agent@npm:3.0.0" + dependencies: + agent-base: "npm:^7.1.0" + http-proxy-agent: "npm:^7.0.0" + https-proxy-agent: "npm:^7.0.1" + lru-cache: "npm:^10.0.1" + socks-proxy-agent: "npm:^8.0.3" + checksum: 10c0/efe37b982f30740ee77696a80c196912c274ecd2cb243bc6ae7053a50c733ce0f6c09fda085145f33ecf453be19654acca74b69e81eaad4c90f00ccffe2f9271 + languageName: node + linkType: hard + +"@npmcli/fs@npm:^4.0.0": + version: 4.0.0 + resolution: "@npmcli/fs@npm:4.0.0" + dependencies: + semver: "npm:^7.3.5" + checksum: 10c0/c90935d5ce670c87b6b14fab04a965a3b8137e585f8b2a6257263bd7f97756dd736cb165bb470e5156a9e718ecd99413dccc54b1138c1a46d6ec7cf325982fe5 + languageName: node + linkType: hard + +"@pkgjs/parseargs@npm:^0.11.0": + version: 0.11.0 + resolution: "@pkgjs/parseargs@npm:0.11.0" + checksum: 10c0/5bd7576bb1b38a47a7fc7b51ac9f38748e772beebc56200450c4a817d712232b8f1d3ef70532c80840243c657d491cf6a6be1e3a214cff907645819fdc34aadd + languageName: node + linkType: hard + +"@pkgr/core@npm:^0.1.0": + version: 0.1.1 + resolution: "@pkgr/core@npm:0.1.1" + checksum: 10c0/3f7536bc7f57320ab2cf96f8973664bef624710c403357429fbf680a5c3b4843c1dbd389bb43daa6b1f6f1f007bb082f5abcb76bb2b5dc9f421647743b71d3d8 + languageName: node + linkType: hard + +"@serialport/binding-mock@npm:10.2.2": + version: 10.2.2 + resolution: "@serialport/binding-mock@npm:10.2.2" + dependencies: + "@serialport/bindings-interface": "npm:^1.2.1" + debug: "npm:^4.3.3" + checksum: 10c0/ba0b26f0e449b32b77315cf57d377318d4ffdaa84d6aaa1efdc48f35b99ade513d2a37f672443163446f1feeb664d5d518f05ea5c5c90d935b87a92fee36b2ba + languageName: node + linkType: hard + +"@serialport/bindings-cpp@npm:12.0.1": + version: 12.0.1 + resolution: "@serialport/bindings-cpp@npm:12.0.1" + dependencies: + "@serialport/bindings-interface": "npm:1.2.2" + "@serialport/parser-readline": "npm:11.0.0" + debug: "npm:4.3.4" + node-addon-api: "npm:7.0.0" + node-gyp: "npm:latest" + node-gyp-build: "npm:4.6.0" + checksum: 10c0/cd64221e8b5a612cf6916843b56e6c5aacdc16d2de4c123354638466c9f57155db77152351a344a8f7e0c25279bfdb30c44a5ed77c00cdaa0a7b782b25e042e0 + languageName: node + linkType: hard + +"@serialport/bindings-interface@npm:1.2.2, @serialport/bindings-interface@npm:^1.2.1": + version: 1.2.2 + resolution: "@serialport/bindings-interface@npm:1.2.2" + checksum: 10c0/e0fb8deaee585573c4151dcb13cc2dc471fc9906a16d8b595fd9aed27e263b8b2e1ba94b0eda814f2ddab7d4143ad7d504b5d2034e5607d8c1d77b4b3dcfc369 + languageName: node + linkType: hard + +"@serialport/parser-byte-length@npm:12.0.0": + version: 12.0.0 + resolution: "@serialport/parser-byte-length@npm:12.0.0" + checksum: 10c0/8dbc2515c83c9ecbaa1f6fbd20c04a13d49f440e4cbcf248a67c728ff28643fca24cea749f12b12231ce14397be124cd893eb2b21ced551982acb795b7a09398 + languageName: node + linkType: hard + +"@serialport/parser-cctalk@npm:12.0.0": + version: 12.0.0 + resolution: "@serialport/parser-cctalk@npm:12.0.0" + checksum: 10c0/9abf7342f9199feb3c222269bb8f47e7a722c747242df32529dc94dabe4e9caf591b0827caaf82ef5143aaf6103e63e9ec2f5f3e28657f08040991cdd67825ee + languageName: node + linkType: hard + +"@serialport/parser-delimiter@npm:11.0.0": + version: 11.0.0 + resolution: "@serialport/parser-delimiter@npm:11.0.0" + checksum: 10c0/59ddd9603396e40f145e8087050596fa02b0e64f52972f328a54b3260b0582c7b4107dbbb0fb8db2f9ea6d0db72237c6a262992649f1e2aa706924a490079380 + languageName: node + linkType: hard + +"@serialport/parser-delimiter@npm:12.0.0": + version: 12.0.0 + resolution: "@serialport/parser-delimiter@npm:12.0.0" + checksum: 10c0/b7c558cd300d7a137b9cf5eb7617d2598fa2cdc2f9feefea79667a6fef37f36aa08c7c7849413f361802801acfd2a81a4fe13fe6672801a65b5ee5e1d5625578 + languageName: node + linkType: hard + +"@serialport/parser-inter-byte-timeout@npm:12.0.0": + version: 12.0.0 + resolution: "@serialport/parser-inter-byte-timeout@npm:12.0.0" + checksum: 10c0/a6454d96310c471fd99cb613b7e4dd3bb26aa32036f739ac584491c2bbbb9cf15e3799e6d129dc74e0db008df209c06002b7b344d66d7aae251ae78547040fc1 + languageName: node + linkType: hard + +"@serialport/parser-packet-length@npm:12.0.0": + version: 12.0.0 + resolution: "@serialport/parser-packet-length@npm:12.0.0" + checksum: 10c0/2f1a8b22d6e4af4c42f8dcab0edf4bece4b879b75fb703d77ea2dbaf0ecf61ac0ed943529c33cfb74f77257cd07991337b6eaade16be782713836dc664982647 + languageName: node + linkType: hard + +"@serialport/parser-readline@npm:11.0.0": + version: 11.0.0 + resolution: "@serialport/parser-readline@npm:11.0.0" + dependencies: + "@serialport/parser-delimiter": "npm:11.0.0" + checksum: 10c0/20cf031cd6abef27721303444c1348fcc4bd1976a4557810c4b57ccc356a84cb7de6351613fe2a54eef20030ca23b345fc1077dcdf979548bca9483cf45ad5d5 + languageName: node + linkType: hard + +"@serialport/parser-readline@npm:12.0.0": + version: 12.0.0 + resolution: "@serialport/parser-readline@npm:12.0.0" + dependencies: + "@serialport/parser-delimiter": "npm:12.0.0" + checksum: 10c0/765b8e89f40c2e3e4516027009ac65fc01f475dc2d111a4d50bdeac2fd5a9653e9222c9cce3abd305d45d555ec9c6a8764b38cf214a90be6073751d6456889e4 + languageName: node + linkType: hard + +"@serialport/parser-ready@npm:12.0.0": + version: 12.0.0 + resolution: "@serialport/parser-ready@npm:12.0.0" + checksum: 10c0/6fcfb505c3eebaf50d9df7b27934b217dac626f08a0446393ae2d575ffa41deefc8c27cfd46dd079add215c859616127e69f1f1aff2a6905aaaa9609d11b269e + languageName: node + linkType: hard + +"@serialport/parser-regex@npm:12.0.0": + version: 12.0.0 + resolution: "@serialport/parser-regex@npm:12.0.0" + checksum: 10c0/6ec242235bf67a9c359b1164a553ee4ad683519326c989a88b499b8a872ce07a843556b2d1efe7d091bf3976c8b2458c399dd1c57efeee4d0ba49291365eda30 + languageName: node + linkType: hard + +"@serialport/parser-slip-encoder@npm:12.0.0": + version: 12.0.0 + resolution: "@serialport/parser-slip-encoder@npm:12.0.0" + checksum: 10c0/e1619a79d175f208d6e808920fb8fe011405e0d544459088d461be497b8a4933b47db8e6e4133f3b16562f563216c312d1e8f37c8629dcca8221ee39b7049e37 + languageName: node + linkType: hard + +"@serialport/parser-spacepacket@npm:12.0.0": + version: 12.0.0 + resolution: "@serialport/parser-spacepacket@npm:12.0.0" + checksum: 10c0/e4ee26dc00f16abd1e7b17f999a0e335c6b963c8f00083bf11c8feb8baad891602dca91a614e4d12f7b3805e1cf829ea497aca022b8f0e02b8cad235e21955ef + languageName: node + linkType: hard + +"@serialport/stream@npm:12.0.0": + version: 12.0.0 + resolution: "@serialport/stream@npm:12.0.0" + dependencies: + "@serialport/bindings-interface": "npm:1.2.2" + debug: "npm:4.3.4" + checksum: 10c0/5edd07425c8801f8ea50d1601663a4c39eb67cc984c0898b8176aa7e86f76ffe4400da7ff624f222182a66f87694c2915ecb416113b038a0e5ce82306308ef6e + languageName: node + linkType: hard + +"@types/eslint-scope@npm:^3.7.7": + version: 3.7.7 + resolution: "@types/eslint-scope@npm:3.7.7" + dependencies: + "@types/eslint": "npm:*" + "@types/estree": "npm:*" + checksum: 10c0/a0ecbdf2f03912679440550817ff77ef39a30fa8bfdacaf6372b88b1f931828aec392f52283240f0d648cf3055c5ddc564544a626bcf245f3d09fcb099ebe3cc + languageName: node + linkType: hard + +"@types/eslint@npm:*": + version: 9.6.1 + resolution: "@types/eslint@npm:9.6.1" + dependencies: + "@types/estree": "npm:*" + "@types/json-schema": "npm:*" + checksum: 10c0/69ba24fee600d1e4c5abe0df086c1a4d798abf13792d8cfab912d76817fe1a894359a1518557d21237fbaf6eda93c5ab9309143dee4c59ef54336d1b3570420e + languageName: node + linkType: hard + +"@types/estree@npm:*, @types/estree@npm:^1.0.6": + version: 1.0.6 + resolution: "@types/estree@npm:1.0.6" + checksum: 10c0/cdfd751f6f9065442cd40957c07fd80361c962869aa853c1c2fd03e101af8b9389d8ff4955a43a6fcfa223dd387a089937f95be0f3eec21ca527039fd2d9859a + languageName: node + linkType: hard + +"@types/fs-extra@npm:>=11": + version: 11.0.4 + resolution: "@types/fs-extra@npm:11.0.4" + dependencies: + "@types/jsonfile": "npm:*" + "@types/node": "npm:*" + checksum: 10c0/9e34f9b24ea464f3c0b18c3f8a82aefc36dc524cc720fc2b886e5465abc66486ff4e439ea3fb2c0acebf91f6d3f74e514f9983b1f02d4243706bdbb7511796ad + languageName: node + linkType: hard + +"@types/json-schema@npm:*, @types/json-schema@npm:^7.0.15, @types/json-schema@npm:^7.0.8, @types/json-schema@npm:^7.0.9": + version: 7.0.15 + resolution: "@types/json-schema@npm:7.0.15" + checksum: 10c0/a996a745e6c5d60292f36731dd41341339d4eeed8180bb09226e5c8d23759067692b1d88e5d91d72ee83dfc00d3aca8e7bd43ea120516c17922cbcb7c3e252db + languageName: node + linkType: hard + +"@types/jsonfile@npm:*": + version: 6.1.4 + resolution: "@types/jsonfile@npm:6.1.4" + dependencies: + "@types/node": "npm:*" + checksum: 10c0/b12d068b021e4078f6ac4441353965769be87acf15326173e2aea9f3bf8ead41bd0ad29421df5bbeb0123ec3fc02eb0a734481d52903704a1454a1845896b9eb + languageName: node + linkType: hard + +"@types/node@npm:*, @types/node@npm:>=20, @types/node@npm:^22.10.2": + version: 22.10.2 + resolution: "@types/node@npm:22.10.2" + dependencies: + undici-types: "npm:~6.20.0" + checksum: 10c0/2c7b71a040f1ef5320938eca8ebc946e6905caa9bbf3d5665d9b3774a8d15ea9fab1582b849a6d28c7fc80756a62c5666bc66b69f42f4d5dafd1ccb193cdb4ac + languageName: node + linkType: hard + +"@typescript-eslint/eslint-plugin@npm:8.18.2": + version: 8.18.2 + resolution: "@typescript-eslint/eslint-plugin@npm:8.18.2" + dependencies: + "@eslint-community/regexpp": "npm:^4.10.0" + "@typescript-eslint/scope-manager": "npm:8.18.2" + "@typescript-eslint/type-utils": "npm:8.18.2" + "@typescript-eslint/utils": "npm:8.18.2" + "@typescript-eslint/visitor-keys": "npm:8.18.2" + graphemer: "npm:^1.4.0" + ignore: "npm:^5.3.1" + natural-compare: "npm:^1.4.0" + ts-api-utils: "npm:^1.3.0" + peerDependencies: + "@typescript-eslint/parser": ^8.0.0 || ^8.0.0-alpha.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <5.8.0" + checksum: 10c0/ce854835a12747cd8efea5b70921e1a80b62af2a2d311b09343862a6af225b821a6729784547d37eb5f8eb286d1f086f41f305445adc3a054e37cc8c71561ccd + languageName: node + linkType: hard + +"@typescript-eslint/parser@npm:8.18.2": + version: 8.18.2 + resolution: "@typescript-eslint/parser@npm:8.18.2" + dependencies: + "@typescript-eslint/scope-manager": "npm:8.18.2" + "@typescript-eslint/types": "npm:8.18.2" + "@typescript-eslint/typescript-estree": "npm:8.18.2" + "@typescript-eslint/visitor-keys": "npm:8.18.2" + debug: "npm:^4.3.4" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <5.8.0" + checksum: 10c0/ea28130e0a2733e3e40708ddfbb7b6522d9644e49cae2c3dc3faddd7ac7e7f73ed9775f19463ca0deca55edb52f5d9d522c206bb2a14fe3c9c6eef03d144b41f + languageName: node + linkType: hard + +"@typescript-eslint/scope-manager@npm:8.18.2": + version: 8.18.2 + resolution: "@typescript-eslint/scope-manager@npm:8.18.2" + dependencies: + "@typescript-eslint/types": "npm:8.18.2" + "@typescript-eslint/visitor-keys": "npm:8.18.2" + checksum: 10c0/2c05f5361e84d687555717bfb15988d5c11601c1094edeaafc8db5c961359982d7aeb192d775d348ab65ac43c5a6c968f3e8503ee1e6bf875aca27588907139f + languageName: node + linkType: hard + +"@typescript-eslint/type-utils@npm:8.18.2": + version: 8.18.2 + resolution: "@typescript-eslint/type-utils@npm:8.18.2" + dependencies: + "@typescript-eslint/typescript-estree": "npm:8.18.2" + "@typescript-eslint/utils": "npm:8.18.2" + debug: "npm:^4.3.4" + ts-api-utils: "npm:^1.3.0" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <5.8.0" + checksum: 10c0/0441ca33f7381abae559e188bd7b2844159806e8bf5ab8d6f6d9b3a7a6bf9f9d0babf8452e83565da0e9841f656b25f44fd96f40bda1006c934535e37a997c6a + languageName: node + linkType: hard + +"@typescript-eslint/types@npm:8.18.2": + version: 8.18.2 + resolution: "@typescript-eslint/types@npm:8.18.2" + checksum: 10c0/4abf252671dd7c3a5c9b7ae2f523d91b04d937dbb601f3bc0182c234d50e4958be67248c1bb37833584ff0128844243145753614c7e80615b6cd6813f0713872 + languageName: node + linkType: hard + +"@typescript-eslint/typescript-estree@npm:8.18.2": + version: 8.18.2 + resolution: "@typescript-eslint/typescript-estree@npm:8.18.2" + dependencies: + "@typescript-eslint/types": "npm:8.18.2" + "@typescript-eslint/visitor-keys": "npm:8.18.2" + debug: "npm:^4.3.4" + fast-glob: "npm:^3.3.2" + is-glob: "npm:^4.0.3" + minimatch: "npm:^9.0.4" + semver: "npm:^7.6.0" + ts-api-utils: "npm:^1.3.0" + peerDependencies: + typescript: ">=4.8.4 <5.8.0" + checksum: 10c0/648296d6c95d80d37bdb5ee6662554af425ff85f1c4805ea344234a1c386c91a36b05cddf52c80264912b29693d3e1b9a45d84414a3aee1393ace2d0babc9e95 + languageName: node + linkType: hard + +"@typescript-eslint/utils@npm:8.18.2": + version: 8.18.2 + resolution: "@typescript-eslint/utils@npm:8.18.2" + dependencies: + "@eslint-community/eslint-utils": "npm:^4.4.0" + "@typescript-eslint/scope-manager": "npm:8.18.2" + "@typescript-eslint/types": "npm:8.18.2" + "@typescript-eslint/typescript-estree": "npm:8.18.2" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <5.8.0" + checksum: 10c0/1cb86e2e4f4e29cbaebe4272c15d98f6193b1476f65dd028d77bf4fd09e715b01d82619509c466b95056148db8d3e04f0a3ef27dc2f034a7c7ab4b2d429e58bb + languageName: node + linkType: hard + +"@typescript-eslint/visitor-keys@npm:8.18.2": + version: 8.18.2 + resolution: "@typescript-eslint/visitor-keys@npm:8.18.2" + dependencies: + "@typescript-eslint/types": "npm:8.18.2" + eslint-visitor-keys: "npm:^4.2.0" + checksum: 10c0/b8fe05bc3bafa7930d6671c2e1807ae47788060eb573e6a000c9597690dfaff6a4eb9f6f934719a18bae631d238ca32847510aeecc61032170e58ab45244e869 + languageName: node + linkType: hard + +"@webassemblyjs/ast@npm:1.14.1, @webassemblyjs/ast@npm:^1.14.1": + version: 1.14.1 + resolution: "@webassemblyjs/ast@npm:1.14.1" + dependencies: + "@webassemblyjs/helper-numbers": "npm:1.13.2" + "@webassemblyjs/helper-wasm-bytecode": "npm:1.13.2" + checksum: 10c0/67a59be8ed50ddd33fbb2e09daa5193ac215bf7f40a9371be9a0d9797a114d0d1196316d2f3943efdb923a3d809175e1563a3cb80c814fb8edccd1e77494972b + languageName: node + linkType: hard + +"@webassemblyjs/floating-point-hex-parser@npm:1.13.2": + version: 1.13.2 + resolution: "@webassemblyjs/floating-point-hex-parser@npm:1.13.2" + checksum: 10c0/0e88bdb8b50507d9938be64df0867f00396b55eba9df7d3546eb5dc0ca64d62e06f8d881ec4a6153f2127d0f4c11d102b6e7d17aec2f26bb5ff95a5e60652412 + languageName: node + linkType: hard + +"@webassemblyjs/helper-api-error@npm:1.13.2": + version: 1.13.2 + resolution: "@webassemblyjs/helper-api-error@npm:1.13.2" + checksum: 10c0/31be497f996ed30aae4c08cac3cce50c8dcd5b29660383c0155fce1753804fc55d47fcba74e10141c7dd2899033164e117b3bcfcda23a6b043e4ded4f1003dfb + languageName: node + linkType: hard + +"@webassemblyjs/helper-buffer@npm:1.14.1": + version: 1.14.1 + resolution: "@webassemblyjs/helper-buffer@npm:1.14.1" + checksum: 10c0/0d54105dc373c0fe6287f1091e41e3a02e36cdc05e8cf8533cdc16c59ff05a646355415893449d3768cda588af451c274f13263300a251dc11a575bc4c9bd210 + languageName: node + linkType: hard + +"@webassemblyjs/helper-numbers@npm:1.13.2": + version: 1.13.2 + resolution: "@webassemblyjs/helper-numbers@npm:1.13.2" + dependencies: + "@webassemblyjs/floating-point-hex-parser": "npm:1.13.2" + "@webassemblyjs/helper-api-error": "npm:1.13.2" + "@xtuc/long": "npm:4.2.2" + checksum: 10c0/9c46852f31b234a8fb5a5a9d3f027bc542392a0d4de32f1a9c0075d5e8684aa073cb5929b56df565500b3f9cc0a2ab983b650314295b9bf208d1a1651bfc825a + languageName: node + linkType: hard + +"@webassemblyjs/helper-wasm-bytecode@npm:1.13.2": + version: 1.13.2 + resolution: "@webassemblyjs/helper-wasm-bytecode@npm:1.13.2" + checksum: 10c0/c4355d14f369b30cf3cbdd3acfafc7d0488e086be6d578e3c9780bd1b512932352246be96e034e2a7fcfba4f540ec813352f312bfcbbfe5bcfbf694f82ccc682 + languageName: node + linkType: hard + +"@webassemblyjs/helper-wasm-section@npm:1.14.1": + version: 1.14.1 + resolution: "@webassemblyjs/helper-wasm-section@npm:1.14.1" + dependencies: + "@webassemblyjs/ast": "npm:1.14.1" + "@webassemblyjs/helper-buffer": "npm:1.14.1" + "@webassemblyjs/helper-wasm-bytecode": "npm:1.13.2" + "@webassemblyjs/wasm-gen": "npm:1.14.1" + checksum: 10c0/1f9b33731c3c6dbac3a9c483269562fa00d1b6a4e7133217f40e83e975e636fd0f8736e53abd9a47b06b66082ecc976c7384391ab0a68e12d509ea4e4b948d64 + languageName: node + linkType: hard + +"@webassemblyjs/ieee754@npm:1.13.2": + version: 1.13.2 + resolution: "@webassemblyjs/ieee754@npm:1.13.2" + dependencies: + "@xtuc/ieee754": "npm:^1.2.0" + checksum: 10c0/2e732ca78c6fbae3c9b112f4915d85caecdab285c0b337954b180460290ccd0fb00d2b1dc4bb69df3504abead5191e0d28d0d17dfd6c9d2f30acac8c4961c8a7 + languageName: node + linkType: hard + +"@webassemblyjs/leb128@npm:1.13.2": + version: 1.13.2 + resolution: "@webassemblyjs/leb128@npm:1.13.2" + dependencies: + "@xtuc/long": "npm:4.2.2" + checksum: 10c0/dad5ef9e383c8ab523ce432dfd80098384bf01c45f70eb179d594f85ce5db2f80fa8c9cba03adafd85684e6d6310f0d3969a882538975989919329ac4c984659 + languageName: node + linkType: hard + +"@webassemblyjs/utf8@npm:1.13.2": + version: 1.13.2 + resolution: "@webassemblyjs/utf8@npm:1.13.2" + checksum: 10c0/d3fac9130b0e3e5a1a7f2886124a278e9323827c87a2b971e6d0da22a2ba1278ac9f66a4f2e363ecd9fac8da42e6941b22df061a119e5c0335f81006de9ee799 + languageName: node + linkType: hard + +"@webassemblyjs/wasm-edit@npm:^1.14.1": + version: 1.14.1 + resolution: "@webassemblyjs/wasm-edit@npm:1.14.1" + dependencies: + "@webassemblyjs/ast": "npm:1.14.1" + "@webassemblyjs/helper-buffer": "npm:1.14.1" + "@webassemblyjs/helper-wasm-bytecode": "npm:1.13.2" + "@webassemblyjs/helper-wasm-section": "npm:1.14.1" + "@webassemblyjs/wasm-gen": "npm:1.14.1" + "@webassemblyjs/wasm-opt": "npm:1.14.1" + "@webassemblyjs/wasm-parser": "npm:1.14.1" + "@webassemblyjs/wast-printer": "npm:1.14.1" + checksum: 10c0/5ac4781086a2ca4b320bdbfd965a209655fe8a208ca38d89197148f8597e587c9a2c94fb6bd6f1a7dbd4527c49c6844fcdc2af981f8d793a97bf63a016aa86d2 + languageName: node + linkType: hard + +"@webassemblyjs/wasm-gen@npm:1.14.1": + version: 1.14.1 + resolution: "@webassemblyjs/wasm-gen@npm:1.14.1" + dependencies: + "@webassemblyjs/ast": "npm:1.14.1" + "@webassemblyjs/helper-wasm-bytecode": "npm:1.13.2" + "@webassemblyjs/ieee754": "npm:1.13.2" + "@webassemblyjs/leb128": "npm:1.13.2" + "@webassemblyjs/utf8": "npm:1.13.2" + checksum: 10c0/d678810d7f3f8fecb2e2bdadfb9afad2ec1d2bc79f59e4711ab49c81cec578371e22732d4966f59067abe5fba8e9c54923b57060a729d28d408e608beef67b10 + languageName: node + linkType: hard + +"@webassemblyjs/wasm-opt@npm:1.14.1": + version: 1.14.1 + resolution: "@webassemblyjs/wasm-opt@npm:1.14.1" + dependencies: + "@webassemblyjs/ast": "npm:1.14.1" + "@webassemblyjs/helper-buffer": "npm:1.14.1" + "@webassemblyjs/wasm-gen": "npm:1.14.1" + "@webassemblyjs/wasm-parser": "npm:1.14.1" + checksum: 10c0/515bfb15277ee99ba6b11d2232ddbf22aed32aad6d0956fe8a0a0a004a1b5a3a277a71d9a3a38365d0538ac40d1b7b7243b1a244ad6cd6dece1c1bb2eb5de7ee + languageName: node + linkType: hard + +"@webassemblyjs/wasm-parser@npm:1.14.1, @webassemblyjs/wasm-parser@npm:^1.14.1": + version: 1.14.1 + resolution: "@webassemblyjs/wasm-parser@npm:1.14.1" + dependencies: + "@webassemblyjs/ast": "npm:1.14.1" + "@webassemblyjs/helper-api-error": "npm:1.13.2" + "@webassemblyjs/helper-wasm-bytecode": "npm:1.13.2" + "@webassemblyjs/ieee754": "npm:1.13.2" + "@webassemblyjs/leb128": "npm:1.13.2" + "@webassemblyjs/utf8": "npm:1.13.2" + checksum: 10c0/95427b9e5addbd0f647939bd28e3e06b8deefdbdadcf892385b5edc70091bf9b92fa5faac3fce8333554437c5d85835afef8c8a7d9d27ab6ba01ffab954db8c6 + languageName: node + linkType: hard + +"@webassemblyjs/wast-printer@npm:1.14.1": + version: 1.14.1 + resolution: "@webassemblyjs/wast-printer@npm:1.14.1" + dependencies: + "@webassemblyjs/ast": "npm:1.14.1" + "@xtuc/long": "npm:4.2.2" + checksum: 10c0/8d7768608996a052545251e896eac079c98e0401842af8dd4de78fba8d90bd505efb6c537e909cd6dae96e09db3fa2e765a6f26492553a675da56e2db51f9d24 + languageName: node + linkType: hard + +"@webpack-cli/configtest@npm:^2.1.1": + version: 2.1.1 + resolution: "@webpack-cli/configtest@npm:2.1.1" + peerDependencies: + webpack: 5.x.x + webpack-cli: 5.x.x + checksum: 10c0/a8da1f15702cb289807da99235ed95326ed7dabeb1a36ca59bd3a5dbe6adcc946a9a2767936050fc4d5ed14efab0e5b5a641dfe8e3d862c36caa5791ac12759d + languageName: node + linkType: hard + +"@webpack-cli/info@npm:^2.0.2": + version: 2.0.2 + resolution: "@webpack-cli/info@npm:2.0.2" + peerDependencies: + webpack: 5.x.x + webpack-cli: 5.x.x + checksum: 10c0/ca88a35604dc9aedac7c26e8f6793c5039dc1eea2b12a85fbfd669a5f21ecf9cf169d7fd157ea366a62666e3fa05b776306a96742ac61a9868f44fdce6b40f7d + languageName: node + linkType: hard + +"@webpack-cli/serve@npm:^2.0.5": + version: 2.0.5 + resolution: "@webpack-cli/serve@npm:2.0.5" + peerDependencies: + webpack: 5.x.x + webpack-cli: 5.x.x + peerDependenciesMeta: + webpack-dev-server: + optional: true + checksum: 10c0/36079d34971ff99a58b66b13f4184dcdd8617853c48cccdbc3f9ab7ea9e5d4fcf504e873c298ea7aa15e0b51ad2c4aee4d7a70bd7d9364e60f57b0eb93ca15fc + languageName: node + linkType: hard + +"@xtuc/ieee754@npm:^1.2.0": + version: 1.2.0 + resolution: "@xtuc/ieee754@npm:1.2.0" + checksum: 10c0/a8565d29d135039bd99ae4b2220d3e167d22cf53f867e491ed479b3f84f895742d0097f935b19aab90265a23d5d46711e4204f14c479ae3637fbf06c4666882f + languageName: node + linkType: hard + +"@xtuc/long@npm:4.2.2": + version: 4.2.2 + resolution: "@xtuc/long@npm:4.2.2" + checksum: 10c0/8582cbc69c79ad2d31568c412129bf23d2b1210a1dfb60c82d5a1df93334da4ee51f3057051658569e2c196d8dc33bc05ae6b974a711d0d16e801e1d0647ccd1 + languageName: node + linkType: hard + +"abbrev@npm:^2.0.0": + version: 2.0.0 + resolution: "abbrev@npm:2.0.0" + checksum: 10c0/f742a5a107473946f426c691c08daba61a1d15942616f300b5d32fd735be88fef5cba24201757b6c407fd564555fb48c751cfa33519b2605c8a7aadd22baf372 + languageName: node + linkType: hard + +"acorn-jsx@npm:^5.3.2": + version: 5.3.2 + resolution: "acorn-jsx@npm:5.3.2" + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + checksum: 10c0/4c54868fbef3b8d58927d5e33f0a4de35f59012fe7b12cf9dfbb345fb8f46607709e1c4431be869a23fb63c151033d84c4198fa9f79385cec34fcb1dd53974c1 + languageName: node + linkType: hard + +"acorn@npm:^8.14.0, acorn@npm:^8.8.2": + version: 8.14.0 + resolution: "acorn@npm:8.14.0" + bin: + acorn: bin/acorn + checksum: 10c0/6d4ee461a7734b2f48836ee0fbb752903606e576cc100eb49340295129ca0b452f3ba91ddd4424a1d4406a98adfb2ebb6bd0ff4c49d7a0930c10e462719bbfd7 + languageName: node + linkType: hard + +"agent-base@npm:^7.1.0, agent-base@npm:^7.1.2": + version: 7.1.3 + resolution: "agent-base@npm:7.1.3" + checksum: 10c0/6192b580c5b1d8fb399b9c62bf8343d76654c2dd62afcb9a52b2cf44a8b6ace1e3b704d3fe3547d91555c857d3df02603341ff2cb961b9cfe2b12f9f3c38ee11 + languageName: node + linkType: hard + +"ajv-formats@npm:^2.1.1": + version: 2.1.1 + resolution: "ajv-formats@npm:2.1.1" + dependencies: + ajv: "npm:^8.0.0" + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + checksum: 10c0/e43ba22e91b6a48d96224b83d260d3a3a561b42d391f8d3c6d2c1559f9aa5b253bfb306bc94bbeca1d967c014e15a6efe9a207309e95b3eaae07fcbcdc2af662 + languageName: node + linkType: hard + +"ajv-keywords@npm:^3.5.2": + version: 3.5.2 + resolution: "ajv-keywords@npm:3.5.2" + peerDependencies: + ajv: ^6.9.1 + checksum: 10c0/0c57a47cbd656e8cdfd99d7c2264de5868918ffa207c8d7a72a7f63379d4333254b2ba03d69e3c035e996a3fd3eb6d5725d7a1597cca10694296e32510546360 + languageName: node + linkType: hard + +"ajv-keywords@npm:^5.1.0": + version: 5.1.0 + resolution: "ajv-keywords@npm:5.1.0" + dependencies: + fast-deep-equal: "npm:^3.1.3" + peerDependencies: + ajv: ^8.8.2 + checksum: 10c0/18bec51f0171b83123ba1d8883c126e60c6f420cef885250898bf77a8d3e65e3bfb9e8564f497e30bdbe762a83e0d144a36931328616a973ee669dc74d4a9590 + languageName: node + linkType: hard + +"ajv@npm:^6.12.4, ajv@npm:^6.12.5": + version: 6.12.6 + resolution: "ajv@npm:6.12.6" + dependencies: + fast-deep-equal: "npm:^3.1.1" + fast-json-stable-stringify: "npm:^2.0.0" + json-schema-traverse: "npm:^0.4.1" + uri-js: "npm:^4.2.2" + checksum: 10c0/41e23642cbe545889245b9d2a45854ebba51cda6c778ebced9649420d9205f2efb39cb43dbc41e358409223b1ea43303ae4839db682c848b891e4811da1a5a71 + languageName: node + linkType: hard + +"ajv@npm:^8.0.0, ajv@npm:^8.17.1, ajv@npm:^8.9.0": + version: 8.17.1 + resolution: "ajv@npm:8.17.1" + dependencies: + fast-deep-equal: "npm:^3.1.3" + fast-uri: "npm:^3.0.1" + json-schema-traverse: "npm:^1.0.0" + require-from-string: "npm:^2.0.2" + checksum: 10c0/ec3ba10a573c6b60f94639ffc53526275917a2df6810e4ab5a6b959d87459f9ef3f00d5e7865b82677cb7d21590355b34da14d1d0b9c32d75f95a187e76fff35 + languageName: node + linkType: hard + +"ansi-escapes@npm:^7.0.0": + version: 7.0.0 + resolution: "ansi-escapes@npm:7.0.0" + dependencies: + environment: "npm:^1.0.0" + checksum: 10c0/86e51e36fabef18c9c004af0a280573e828900641cea35134a124d2715e0c5a473494ab4ce396614505da77638ae290ff72dd8002d9747d2ee53f5d6bbe336be + languageName: node + linkType: hard + +"ansi-regex@npm:^5.0.1": + version: 5.0.1 + resolution: "ansi-regex@npm:5.0.1" + checksum: 10c0/9a64bb8627b434ba9327b60c027742e5d17ac69277960d041898596271d992d4d52ba7267a63ca10232e29f6107fc8a835f6ce8d719b88c5f8493f8254813737 + languageName: node + linkType: hard + +"ansi-regex@npm:^6.0.1": + version: 6.1.0 + resolution: "ansi-regex@npm:6.1.0" + checksum: 10c0/a91daeddd54746338478eef88af3439a7edf30f8e23196e2d6ed182da9add559c601266dbef01c2efa46a958ad6f1f8b176799657616c702b5b02e799e7fd8dc + languageName: node + linkType: hard + +"ansi-styles@npm:^4.0.0, ansi-styles@npm:^4.1.0": + version: 4.3.0 + resolution: "ansi-styles@npm:4.3.0" + dependencies: + color-convert: "npm:^2.0.1" + checksum: 10c0/895a23929da416f2bd3de7e9cb4eabd340949328ab85ddd6e484a637d8f6820d485f53933446f5291c3b760cbc488beb8e88573dd0f9c7daf83dccc8fe81b041 + languageName: node + linkType: hard + +"ansi-styles@npm:^6.0.0, ansi-styles@npm:^6.1.0, ansi-styles@npm:^6.2.1": + version: 6.2.1 + resolution: "ansi-styles@npm:6.2.1" + checksum: 10c0/5d1ec38c123984bcedd996eac680d548f31828bd679a66db2bdf11844634dde55fec3efa9c6bb1d89056a5e79c1ac540c4c784d592ea1d25028a92227d2f2d5c + languageName: node + linkType: hard + +"argparse@npm:^2.0.1": + version: 2.0.1 + resolution: "argparse@npm:2.0.1" + checksum: 10c0/c5640c2d89045371c7cedd6a70212a04e360fd34d6edeae32f6952c63949e3525ea77dbec0289d8213a99bbaeab5abfa860b5c12cf88a2e6cf8106e90dd27a7e + languageName: node + linkType: hard + +"author-regex@npm:^1.0.0": + version: 1.0.0 + resolution: "author-regex@npm:1.0.0" + checksum: 10c0/3f3a5ad6660be010bd5b979fac180f435bd9615e81db2b1cdac081eb3f639461f6c3927ced956e377a5c91cc789e3de3a3e900e296c1423971043c8fd8be0b73 + languageName: node + linkType: hard + +"balanced-match@npm:^1.0.0": + version: 1.0.2 + resolution: "balanced-match@npm:1.0.2" + checksum: 10c0/9308baf0a7e4838a82bbfd11e01b1cb0f0cf2893bc1676c27c2a8c0e70cbae1c59120c3268517a8ae7fb6376b4639ef81ca22582611dbee4ed28df945134aaee + languageName: node + linkType: hard + +"brace-expansion@npm:^1.1.7": + version: 1.1.11 + resolution: "brace-expansion@npm:1.1.11" + dependencies: + balanced-match: "npm:^1.0.0" + concat-map: "npm:0.0.1" + checksum: 10c0/695a56cd058096a7cb71fb09d9d6a7070113c7be516699ed361317aca2ec169f618e28b8af352e02ab4233fb54eb0168460a40dc320bab0034b36ab59aaad668 + languageName: node + linkType: hard + +"brace-expansion@npm:^2.0.1": + version: 2.0.1 + resolution: "brace-expansion@npm:2.0.1" + dependencies: + balanced-match: "npm:^1.0.0" + checksum: 10c0/b358f2fe060e2d7a87aa015979ecea07f3c37d4018f8d6deb5bd4c229ad3a0384fe6029bb76cd8be63c81e516ee52d1a0673edbe2023d53a5191732ae3c3e49f + languageName: node + linkType: hard + +"braces@npm:^3.0.3": + version: 3.0.3 + resolution: "braces@npm:3.0.3" + dependencies: + fill-range: "npm:^7.1.1" + checksum: 10c0/7c6dfd30c338d2997ba77500539227b9d1f85e388a5f43220865201e407e076783d0881f2d297b9f80951b4c957fcf0b51c1d2d24227631643c3f7c284b0aa04 + languageName: node + linkType: hard + +"browserslist@npm:^4.24.0": + version: 4.24.3 + resolution: "browserslist@npm:4.24.3" + dependencies: + caniuse-lite: "npm:^1.0.30001688" + electron-to-chromium: "npm:^1.5.73" + node-releases: "npm:^2.0.19" + update-browserslist-db: "npm:^1.1.1" + bin: + browserslist: cli.js + checksum: 10c0/bab261ef7b6e1656a719a9fa31240ae7ce4d5ba68e479f6b11e348d819346ab4c0ff6f4821f43adcc9c193a734b186775a83b37979e70a69d182965909fe569a + languageName: node + linkType: hard + +"buffer-from@npm:^1.0.0": + version: 1.1.2 + resolution: "buffer-from@npm:1.1.2" + checksum: 10c0/124fff9d66d691a86d3b062eff4663fe437a9d9ee4b47b1b9e97f5a5d14f6d5399345db80f796827be7c95e70a8e765dd404b7c3ff3b3324f98e9b0c8826cc34 + languageName: node + linkType: hard + +"cacache@npm:^19.0.1": + version: 19.0.1 + resolution: "cacache@npm:19.0.1" + dependencies: + "@npmcli/fs": "npm:^4.0.0" + fs-minipass: "npm:^3.0.0" + glob: "npm:^10.2.2" + lru-cache: "npm:^10.0.1" + minipass: "npm:^7.0.3" + minipass-collect: "npm:^2.0.1" + minipass-flush: "npm:^1.0.5" + minipass-pipeline: "npm:^1.2.4" + p-map: "npm:^7.0.2" + ssri: "npm:^12.0.0" + tar: "npm:^7.4.3" + unique-filename: "npm:^4.0.0" + checksum: 10c0/01f2134e1bd7d3ab68be851df96c8d63b492b1853b67f2eecb2c37bb682d37cb70bb858a16f2f0554d3c0071be6dfe21456a1ff6fa4b7eed996570d6a25ffe9c + languageName: node + linkType: hard + +"callsites@npm:^3.0.0": + version: 3.1.0 + resolution: "callsites@npm:3.1.0" + checksum: 10c0/fff92277400eb06c3079f9e74f3af120db9f8ea03bad0e84d9aede54bbe2d44a56cccb5f6cf12211f93f52306df87077ecec5b712794c5a9b5dac6d615a3f301 + languageName: node + linkType: hard + +"caniuse-lite@npm:^1.0.30001688": + version: 1.0.30001690 + resolution: "caniuse-lite@npm:1.0.30001690" + checksum: 10c0/646bd469032afa90400a84dec30a2b00a6eda62c03ead358117e3f884cda8aacec02ec058a6dbee5eaf9714f83e483b9b0eb4fb42febb8076569f5ca40f1d347 + languageName: node + linkType: hard + +"chalk@npm:^4.0.0": + version: 4.1.2 + resolution: "chalk@npm:4.1.2" + dependencies: + ansi-styles: "npm:^4.1.0" + supports-color: "npm:^7.1.0" + checksum: 10c0/4a3fef5cc34975c898ffe77141450f679721df9dde00f6c304353fa9c8b571929123b26a0e4617bde5018977eb655b31970c297b91b63ee83bb82aeb04666880 + languageName: node + linkType: hard + +"chalk@npm:~5.4.1": + version: 5.4.1 + resolution: "chalk@npm:5.4.1" + checksum: 10c0/b23e88132c702f4855ca6d25cb5538b1114343e41472d5263ee8a37cccfccd9c4216d111e1097c6a27830407a1dc81fecdf2a56f2c63033d4dbbd88c10b0dcef + languageName: node + linkType: hard + +"chownr@npm:^3.0.0": + version: 3.0.0 + resolution: "chownr@npm:3.0.0" + checksum: 10c0/43925b87700f7e3893296c8e9c56cc58f926411cce3a6e5898136daaf08f08b9a8eb76d37d3267e707d0dcc17aed2e2ebdf5848c0c3ce95cf910a919935c1b10 + languageName: node + linkType: hard + +"chrome-trace-event@npm:^1.0.2": + version: 1.0.4 + resolution: "chrome-trace-event@npm:1.0.4" + checksum: 10c0/3058da7a5f4934b87cf6a90ef5fb68ebc5f7d06f143ed5a4650208e5d7acae47bc03ec844b29fbf5ba7e46e8daa6acecc878f7983a4f4bb7271593da91e61ff5 + languageName: node + linkType: hard + +"cli-cursor@npm:^5.0.0": + version: 5.0.0 + resolution: "cli-cursor@npm:5.0.0" + dependencies: + restore-cursor: "npm:^5.0.0" + checksum: 10c0/7ec62f69b79f6734ab209a3e4dbdc8af7422d44d360a7cb1efa8a0887bbe466a6e625650c466fe4359aee44dbe2dc0b6994b583d40a05d0808a5cb193641d220 + languageName: node + linkType: hard + +"cli-truncate@npm:^4.0.0": + version: 4.0.0 + resolution: "cli-truncate@npm:4.0.0" + dependencies: + slice-ansi: "npm:^5.0.0" + string-width: "npm:^7.0.0" + checksum: 10c0/d7f0b73e3d9b88cb496e6c086df7410b541b56a43d18ade6a573c9c18bd001b1c3fba1ad578f741a4218fdc794d042385f8ac02c25e1c295a2d8b9f3cb86eb4c + languageName: node + linkType: hard + +"clone-deep@npm:^4.0.1": + version: 4.0.1 + resolution: "clone-deep@npm:4.0.1" + dependencies: + is-plain-object: "npm:^2.0.4" + kind-of: "npm:^6.0.2" + shallow-clone: "npm:^3.0.0" + checksum: 10c0/637753615aa24adf0f2d505947a1bb75e63964309034a1cf56ba4b1f30af155201edd38d26ffe26911adaae267a3c138b344a4947d39f5fc1b6d6108125aa758 + languageName: node + linkType: hard + +"color-convert@npm:^2.0.1": + version: 2.0.1 + resolution: "color-convert@npm:2.0.1" + dependencies: + color-name: "npm:~1.1.4" + checksum: 10c0/37e1150172f2e311fe1b2df62c6293a342ee7380da7b9cfdba67ea539909afbd74da27033208d01d6d5cfc65ee7868a22e18d7e7648e004425441c0f8a15a7d7 + languageName: node + linkType: hard + +"color-name@npm:~1.1.4": + version: 1.1.4 + resolution: "color-name@npm:1.1.4" + checksum: 10c0/a1a3f914156960902f46f7f56bc62effc6c94e84b2cae157a526b1c1f74b677a47ec602bf68a61abfa2b42d15b7c5651c6dbe72a43af720bc588dff885b10f95 + languageName: node + linkType: hard + +"colord@npm:^2.9.3": + version: 2.9.3 + resolution: "colord@npm:2.9.3" + checksum: 10c0/9699e956894d8996b28c686afe8988720785f476f59335c80ce852ded76ab3ebe252703aec53d9bef54f6219aea6b960fb3d9a8300058a1d0c0d4026460cd110 + languageName: node + linkType: hard + +"colorette@npm:^2.0.14, colorette@npm:^2.0.20": + version: 2.0.20 + resolution: "colorette@npm:2.0.20" + checksum: 10c0/e94116ff33b0ff56f3b83b9ace895e5bf87c2a7a47b3401b8c3f3226e050d5ef76cf4072fb3325f9dc24d1698f9b730baf4e05eeaf861d74a1883073f4c98a40 + languageName: node + linkType: hard + +"commander@npm:^10.0.1": + version: 10.0.1 + resolution: "commander@npm:10.0.1" + checksum: 10c0/53f33d8927758a911094adadda4b2cbac111a5b377d8706700587650fd8f45b0bbe336de4b5c3fe47fd61f420a3d9bd452b6e0e6e5600a7e74d7bf0174f6efe3 + languageName: node + linkType: hard + +"commander@npm:^2.20.0": + version: 2.20.3 + resolution: "commander@npm:2.20.3" + checksum: 10c0/74c781a5248c2402a0a3e966a0a2bba3c054aad144f5c023364be83265e796b20565aa9feff624132ff629aa64e16999fa40a743c10c12f7c61e96a794b99288 + languageName: node + linkType: hard + +"commander@npm:~12.1.0": + version: 12.1.0 + resolution: "commander@npm:12.1.0" + checksum: 10c0/6e1996680c083b3b897bfc1cfe1c58dfbcd9842fd43e1aaf8a795fbc237f65efcc860a3ef457b318e73f29a4f4a28f6403c3d653d021d960e4632dd45bde54a9 + languageName: node + linkType: hard + +"concat-map@npm:0.0.1": + version: 0.0.1 + resolution: "concat-map@npm:0.0.1" + checksum: 10c0/c996b1cfdf95b6c90fee4dae37e332c8b6eb7d106430c17d538034c0ad9a1630cb194d2ab37293b1bdd4d779494beee7786d586a50bd9376fd6f7bcc2bd4c98f + languageName: node + linkType: hard + +"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.3, cross-spawn@npm:^7.0.6": + version: 7.0.6 + resolution: "cross-spawn@npm:7.0.6" + dependencies: + path-key: "npm:^3.1.0" + shebang-command: "npm:^2.0.0" + which: "npm:^2.0.1" + checksum: 10c0/053ea8b2135caff68a9e81470e845613e374e7309a47731e81639de3eaeb90c3d01af0e0b44d2ab9d50b43467223b88567dfeb3262db942dc063b9976718ffc1 + languageName: node + linkType: hard + +"debounce-fn@npm:^6.0.0": + version: 6.0.0 + resolution: "debounce-fn@npm:6.0.0" + dependencies: + mimic-function: "npm:^5.0.0" + checksum: 10c0/ff5c48a7d644e292a653fd602db340b701bddba3973da7f64c6f25bbd9ab0fde9058567fdbe7efc72553561f3393f285413818d58f08614e73e0e85319f1da6e + languageName: node + linkType: hard + +"debug@npm:4, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4, debug@npm:~4.4.0": + version: 4.4.0 + resolution: "debug@npm:4.4.0" + dependencies: + ms: "npm:^2.1.3" + peerDependenciesMeta: + supports-color: + optional: true + checksum: 10c0/db94f1a182bf886f57b4755f85b3a74c39b5114b9377b7ab375dc2cfa3454f09490cc6c30f829df3fc8042bc8b8995f6567ce5cd96f3bc3688bd24027197d9de + languageName: node + linkType: hard + +"debug@npm:4.3.4": + version: 4.3.4 + resolution: "debug@npm:4.3.4" + dependencies: + ms: "npm:2.1.2" + peerDependenciesMeta: + supports-color: + optional: true + checksum: 10c0/cedbec45298dd5c501d01b92b119cd3faebe5438c3917ff11ae1bff86a6c722930ac9c8659792824013168ba6db7c4668225d845c633fbdafbbf902a6389f736 + languageName: node + linkType: hard + +"deep-is@npm:^0.1.3": + version: 0.1.4 + resolution: "deep-is@npm:0.1.4" + checksum: 10c0/7f0ee496e0dff14a573dc6127f14c95061b448b87b995fc96c017ce0a1e66af1675e73f1d6064407975bc4ea6ab679497a29fff7b5b9c4e99cb10797c1ad0b4c + languageName: node + linkType: hard + +"eastasianwidth@npm:^0.2.0": + version: 0.2.0 + resolution: "eastasianwidth@npm:0.2.0" + checksum: 10c0/26f364ebcdb6395f95124fda411f63137a4bfb5d3a06453f7f23dfe52502905bd84e0488172e0f9ec295fdc45f05c23d5d91baf16bd26f0fe9acd777a188dc39 + languageName: node + linkType: hard + +"ejson@npm:^2.2.3": + version: 2.2.3 + resolution: "ejson@npm:2.2.3" + checksum: 10c0/648ea347f5e57441b7b9341adc6de244445b6da1d0e7747ea7a083f906299b92e4c44fc29e6de0b240d8fa4a73159e85f9367780d7af2ecbc50aae8a4e4961ae + languageName: node + linkType: hard + +"electron-to-chromium@npm:^1.5.73": + version: 1.5.76 + resolution: "electron-to-chromium@npm:1.5.76" + checksum: 10c0/5a977be9fd5810769a7b4eae0e4b41b6beca65f2b3f3b7442819f6c93366d767d183cfbf408714f944a9bf3aa304f8c9ab9d0cdfd8e878ab8f2cbb61f8b22acd + languageName: node + linkType: hard + +"emoji-regex@npm:^10.3.0": + version: 10.4.0 + resolution: "emoji-regex@npm:10.4.0" + checksum: 10c0/a3fcedfc58bfcce21a05a5f36a529d81e88d602100145fcca3dc6f795e3c8acc4fc18fe773fbf9b6d6e9371205edb3afa2668ec3473fa2aa7fd47d2a9d46482d + languageName: node + linkType: hard + +"emoji-regex@npm:^8.0.0": + version: 8.0.0 + resolution: "emoji-regex@npm:8.0.0" + checksum: 10c0/b6053ad39951c4cf338f9092d7bfba448cdfd46fe6a2a034700b149ac9ffbc137e361cbd3c442297f86bed2e5f7576c1b54cc0a6bf8ef5106cc62f496af35010 + languageName: node + linkType: hard + +"emoji-regex@npm:^9.2.2": + version: 9.2.2 + resolution: "emoji-regex@npm:9.2.2" + checksum: 10c0/af014e759a72064cf66e6e694a7fc6b0ed3d8db680427b021a89727689671cefe9d04151b2cad51dbaf85d5ba790d061cd167f1cf32eb7b281f6368b3c181639 + languageName: node + linkType: hard + +"encoding@npm:^0.1.13": + version: 0.1.13 + resolution: "encoding@npm:0.1.13" + dependencies: + iconv-lite: "npm:^0.6.2" + checksum: 10c0/36d938712ff00fe1f4bac88b43bcffb5930c1efa57bbcdca9d67e1d9d6c57cfb1200fb01efe0f3109b2ce99b231f90779532814a81370a1bd3274a0f58585039 + languageName: node + linkType: hard + +"enhanced-resolve@npm:^5.17.1": + version: 5.18.0 + resolution: "enhanced-resolve@npm:5.18.0" + dependencies: + graceful-fs: "npm:^4.2.4" + tapable: "npm:^2.2.0" + checksum: 10c0/5fcc264a6040754ab5b349628cac2bb5f89cee475cbe340804e657a5b9565f70e6aafb338d5895554eb0ced9f66c50f38a255274a0591dcb64ee17c549c459ce + languageName: node + linkType: hard + +"env-paths@npm:^2.2.0": + version: 2.2.1 + resolution: "env-paths@npm:2.2.1" + checksum: 10c0/285325677bf00e30845e330eec32894f5105529db97496ee3f598478e50f008c5352a41a30e5e72ec9de8a542b5a570b85699cd63bd2bc646dbcb9f311d83bc4 + languageName: node + linkType: hard + +"envinfo@npm:^7.7.3": + version: 7.14.0 + resolution: "envinfo@npm:7.14.0" + bin: + envinfo: dist/cli.js + checksum: 10c0/059a031eee101e056bd9cc5cbfe25c2fab433fe1780e86cf0a82d24a000c6931e327da6a8ffb3dce528a24f83f256e7efc0b36813113eff8fdc6839018efe327 + languageName: node + linkType: hard + +"environment@npm:^1.0.0": + version: 1.1.0 + resolution: "environment@npm:1.1.0" + checksum: 10c0/fb26434b0b581ab397039e51ff3c92b34924a98b2039dcb47e41b7bca577b9dbf134a8eadb364415c74464b682e2d3afe1a4c0eb9873dc44ea814c5d3103331d + languageName: node + linkType: hard + +"err-code@npm:^2.0.2": + version: 2.0.3 + resolution: "err-code@npm:2.0.3" + checksum: 10c0/b642f7b4dd4a376e954947550a3065a9ece6733ab8e51ad80db727aaae0817c2e99b02a97a3d6cecc648a97848305e728289cf312d09af395403a90c9d4d8a66 + languageName: node + linkType: hard + +"es-module-lexer@npm:^1.2.1": + version: 1.6.0 + resolution: "es-module-lexer@npm:1.6.0" + checksum: 10c0/667309454411c0b95c476025929881e71400d74a746ffa1ff4cb450bd87f8e33e8eef7854d68e401895039ac0bac64e7809acbebb6253e055dd49ea9e3ea9212 + languageName: node + linkType: hard + +"escalade@npm:^3.2.0": + version: 3.2.0 + resolution: "escalade@npm:3.2.0" + checksum: 10c0/ced4dd3a78e15897ed3be74e635110bbf3b08877b0a41be50dcb325ee0e0b5f65fc2d50e9845194d7c4633f327e2e1c6cce00a71b617c5673df0374201d67f65 + languageName: node + linkType: hard + +"escape-string-regexp@npm:^4.0.0": + version: 4.0.0 + resolution: "escape-string-regexp@npm:4.0.0" + checksum: 10c0/9497d4dd307d845bd7f75180d8188bb17ea8c151c1edbf6b6717c100e104d629dc2dfb687686181b0f4b7d732c7dfdc4d5e7a8ff72de1b0ca283a75bbb3a9cd9 + languageName: node + linkType: hard + +"eslint-compat-utils@npm:^0.5.1": + version: 0.5.1 + resolution: "eslint-compat-utils@npm:0.5.1" + dependencies: + semver: "npm:^7.5.4" + peerDependencies: + eslint: ">=6.0.0" + checksum: 10c0/325e815205fab70ebcd379f6d4b5d44c7d791bb8dfe0c9888233f30ebabd9418422595b53a781b946c768d9244d858540e5e6129a6b3dd6d606f467d599edc6c + languageName: node + linkType: hard + +"eslint-config-prettier@npm:^9.1.0": + version: 9.1.0 + resolution: "eslint-config-prettier@npm:9.1.0" + peerDependencies: + eslint: ">=7.0.0" + bin: + eslint-config-prettier: bin/cli.js + checksum: 10c0/6d332694b36bc9ac6fdb18d3ca2f6ac42afa2ad61f0493e89226950a7091e38981b66bac2b47ba39d15b73fff2cd32c78b850a9cf9eed9ca9a96bfb2f3a2f10d + languageName: node + linkType: hard + +"eslint-plugin-es-x@npm:^7.8.0": + version: 7.8.0 + resolution: "eslint-plugin-es-x@npm:7.8.0" + dependencies: + "@eslint-community/eslint-utils": "npm:^4.1.2" + "@eslint-community/regexpp": "npm:^4.11.0" + eslint-compat-utils: "npm:^0.5.1" + peerDependencies: + eslint: ">=8" + checksum: 10c0/002fda8c029bc5da41e24e7ac11654062831d675fc4f5f20d0de460e24bf1e05cd559000678ef3e46c48641190f4fc07ae3d57aa5e8b085ef5f67e5f63742614 + languageName: node + linkType: hard + +"eslint-plugin-n@npm:^17.15.0": + version: 17.15.1 + resolution: "eslint-plugin-n@npm:17.15.1" + dependencies: + "@eslint-community/eslint-utils": "npm:^4.4.1" + enhanced-resolve: "npm:^5.17.1" + eslint-plugin-es-x: "npm:^7.8.0" + get-tsconfig: "npm:^4.8.1" + globals: "npm:^15.11.0" + ignore: "npm:^5.3.2" + minimatch: "npm:^9.0.5" + semver: "npm:^7.6.3" + peerDependencies: + eslint: ">=8.23.0" + checksum: 10c0/0b52ffed0b80d74977e1157b4c0cc79efcdf81ea35d2997bdbf02f3d41f428f52ccb7fb3a08cf02e6fed8ae1bf4708d69fdf496e75b8b2bd3e671029d89ccc6c + languageName: node + linkType: hard + +"eslint-plugin-prettier@npm:^5.2.1": + version: 5.2.1 + resolution: "eslint-plugin-prettier@npm:5.2.1" + dependencies: + prettier-linter-helpers: "npm:^1.0.0" + synckit: "npm:^0.9.1" + peerDependencies: + "@types/eslint": ">=8.0.0" + eslint: ">=8.0.0" + eslint-config-prettier: "*" + prettier: ">=3.0.0" + peerDependenciesMeta: + "@types/eslint": + optional: true + eslint-config-prettier: + optional: true + checksum: 10c0/4bc8bbaf5bb556c9c501dcdff369137763c49ccaf544f9fa91400360ed5e3a3f1234ab59690e06beca5b1b7e6f6356978cdd3b02af6aba3edea2ffe69ca6e8b2 + languageName: node + linkType: hard + +"eslint-scope@npm:5.1.1": + version: 5.1.1 + resolution: "eslint-scope@npm:5.1.1" + dependencies: + esrecurse: "npm:^4.3.0" + estraverse: "npm:^4.1.1" + checksum: 10c0/d30ef9dc1c1cbdece34db1539a4933fe3f9b14e1ffb27ecc85987902ee663ad7c9473bbd49a9a03195a373741e62e2f807c4938992e019b511993d163450e70a + languageName: node + linkType: hard + +"eslint-scope@npm:^8.2.0": + version: 8.2.0 + resolution: "eslint-scope@npm:8.2.0" + dependencies: + esrecurse: "npm:^4.3.0" + estraverse: "npm:^5.2.0" + checksum: 10c0/8d2d58e2136d548ac7e0099b1a90d9fab56f990d86eb518de1247a7066d38c908be2f3df477a79cf60d70b30ba18735d6c6e70e9914dca2ee515a729975d70d6 + languageName: node + linkType: hard + +"eslint-visitor-keys@npm:^3.4.3": + version: 3.4.3 + resolution: "eslint-visitor-keys@npm:3.4.3" + checksum: 10c0/92708e882c0a5ffd88c23c0b404ac1628cf20104a108c745f240a13c332a11aac54f49a22d5762efbffc18ecbc9a580d1b7ad034bf5f3cc3307e5cbff2ec9820 + languageName: node + linkType: hard + +"eslint-visitor-keys@npm:^4.2.0": + version: 4.2.0 + resolution: "eslint-visitor-keys@npm:4.2.0" + checksum: 10c0/2ed81c663b147ca6f578312919483eb040295bbab759e5a371953456c636c5b49a559883e2677112453728d66293c0a4c90ab11cab3428cf02a0236d2e738269 + languageName: node + linkType: hard + +"eslint@npm:^9.17.0": + version: 9.17.0 + resolution: "eslint@npm:9.17.0" + dependencies: + "@eslint-community/eslint-utils": "npm:^4.2.0" + "@eslint-community/regexpp": "npm:^4.12.1" + "@eslint/config-array": "npm:^0.19.0" + "@eslint/core": "npm:^0.9.0" + "@eslint/eslintrc": "npm:^3.2.0" + "@eslint/js": "npm:9.17.0" + "@eslint/plugin-kit": "npm:^0.2.3" + "@humanfs/node": "npm:^0.16.6" + "@humanwhocodes/module-importer": "npm:^1.0.1" + "@humanwhocodes/retry": "npm:^0.4.1" + "@types/estree": "npm:^1.0.6" + "@types/json-schema": "npm:^7.0.15" + ajv: "npm:^6.12.4" + chalk: "npm:^4.0.0" + cross-spawn: "npm:^7.0.6" + debug: "npm:^4.3.2" + escape-string-regexp: "npm:^4.0.0" + eslint-scope: "npm:^8.2.0" + eslint-visitor-keys: "npm:^4.2.0" + espree: "npm:^10.3.0" + esquery: "npm:^1.5.0" + esutils: "npm:^2.0.2" + fast-deep-equal: "npm:^3.1.3" + file-entry-cache: "npm:^8.0.0" + find-up: "npm:^5.0.0" + glob-parent: "npm:^6.0.2" + ignore: "npm:^5.2.0" + imurmurhash: "npm:^0.1.4" + is-glob: "npm:^4.0.0" + json-stable-stringify-without-jsonify: "npm:^1.0.1" + lodash.merge: "npm:^4.6.2" + minimatch: "npm:^3.1.2" + natural-compare: "npm:^1.4.0" + optionator: "npm:^0.9.3" + peerDependencies: + jiti: "*" + peerDependenciesMeta: + jiti: + optional: true + bin: + eslint: bin/eslint.js + checksum: 10c0/9edd8dd782b4ae2eb00a158ed4708194835d4494d75545fa63a51f020ed17f865c49b4ae1914a2ecbc7fdb262bd8059e811aeef9f0bae63dced9d3293be1bbdd + languageName: node + linkType: hard + +"espree@npm:^10.0.1, espree@npm:^10.3.0": + version: 10.3.0 + resolution: "espree@npm:10.3.0" + dependencies: + acorn: "npm:^8.14.0" + acorn-jsx: "npm:^5.3.2" + eslint-visitor-keys: "npm:^4.2.0" + checksum: 10c0/272beeaca70d0a1a047d61baff64db04664a33d7cfb5d144f84bc8a5c6194c6c8ebe9cc594093ca53add88baa23e59b01e69e8a0160ab32eac570482e165c462 + languageName: node + linkType: hard + +"esquery@npm:^1.5.0": + version: 1.6.0 + resolution: "esquery@npm:1.6.0" + dependencies: + estraverse: "npm:^5.1.0" + checksum: 10c0/cb9065ec605f9da7a76ca6dadb0619dfb611e37a81e318732977d90fab50a256b95fee2d925fba7c2f3f0523aa16f91587246693bc09bc34d5a59575fe6e93d2 + languageName: node + linkType: hard + +"esrecurse@npm:^4.3.0": + version: 4.3.0 + resolution: "esrecurse@npm:4.3.0" + dependencies: + estraverse: "npm:^5.2.0" + checksum: 10c0/81a37116d1408ded88ada45b9fb16dbd26fba3aadc369ce50fcaf82a0bac12772ebd7b24cd7b91fc66786bf2c1ac7b5f196bc990a473efff972f5cb338877cf5 + languageName: node + linkType: hard + +"estraverse@npm:^4.1.1": + version: 4.3.0 + resolution: "estraverse@npm:4.3.0" + checksum: 10c0/9cb46463ef8a8a4905d3708a652d60122a0c20bb58dec7e0e12ab0e7235123d74214fc0141d743c381813e1b992767e2708194f6f6e0f9fd00c1b4e0887b8b6d + languageName: node + linkType: hard + +"estraverse@npm:^5.1.0, estraverse@npm:^5.2.0": + version: 5.3.0 + resolution: "estraverse@npm:5.3.0" + checksum: 10c0/1ff9447b96263dec95d6d67431c5e0771eb9776427421260a3e2f0fdd5d6bd4f8e37a7338f5ad2880c9f143450c9b1e4fc2069060724570a49cf9cf0312bd107 + languageName: node + linkType: hard + +"esutils@npm:^2.0.2": + version: 2.0.3 + resolution: "esutils@npm:2.0.3" + checksum: 10c0/9a2fe69a41bfdade834ba7c42de4723c97ec776e40656919c62cbd13607c45e127a003f05f724a1ea55e5029a4cf2de444b13009f2af71271e42d93a637137c7 + languageName: node + linkType: hard + +"eventemitter3@npm:^4.0.4": + version: 4.0.7 + resolution: "eventemitter3@npm:4.0.7" + checksum: 10c0/5f6d97cbcbac47be798e6355e3a7639a84ee1f7d9b199a07017f1d2f1e2fe236004d14fa5dfaeba661f94ea57805385e326236a6debbc7145c8877fbc0297c6b + languageName: node + linkType: hard + +"eventemitter3@npm:^5.0.1": + version: 5.0.1 + resolution: "eventemitter3@npm:5.0.1" + checksum: 10c0/4ba5c00c506e6c786b4d6262cfbce90ddc14c10d4667e5c83ae993c9de88aa856033994dd2b35b83e8dc1170e224e66a319fa80adc4c32adcd2379bbc75da814 + languageName: node + linkType: hard + +"events@npm:^3.2.0": + version: 3.3.0 + resolution: "events@npm:3.3.0" + checksum: 10c0/d6b6f2adbccbcda74ddbab52ed07db727ef52e31a61ed26db9feb7dc62af7fc8e060defa65e5f8af9449b86b52cc1a1f6a79f2eafcf4e62add2b7a1fa4a432f6 + languageName: node + linkType: hard + +"execa@npm:~8.0.1": + version: 8.0.1 + resolution: "execa@npm:8.0.1" + dependencies: + cross-spawn: "npm:^7.0.3" + get-stream: "npm:^8.0.1" + human-signals: "npm:^5.0.0" + is-stream: "npm:^3.0.0" + merge-stream: "npm:^2.0.0" + npm-run-path: "npm:^5.1.0" + onetime: "npm:^6.0.0" + signal-exit: "npm:^4.1.0" + strip-final-newline: "npm:^3.0.0" + checksum: 10c0/2c52d8775f5bf103ce8eec9c7ab3059909ba350a5164744e9947ed14a53f51687c040a250bda833f906d1283aa8803975b84e6c8f7a7c42f99dc8ef80250d1af + languageName: node + linkType: hard + +"exponential-backoff@npm:^3.1.1": + version: 3.1.1 + resolution: "exponential-backoff@npm:3.1.1" + checksum: 10c0/160456d2d647e6019640bd07111634d8c353038d9fa40176afb7cd49b0548bdae83b56d05e907c2cce2300b81cae35d800ef92fefb9d0208e190fa3b7d6bb579 + languageName: node + linkType: hard + +"fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3": + version: 3.1.3 + resolution: "fast-deep-equal@npm:3.1.3" + checksum: 10c0/40dedc862eb8992c54579c66d914635afbec43350afbbe991235fdcb4e3a8d5af1b23ae7e79bef7d4882d0ecee06c3197488026998fb19f72dc95acff1d1b1d0 + languageName: node + linkType: hard + +"fast-diff@npm:^1.1.2": + version: 1.3.0 + resolution: "fast-diff@npm:1.3.0" + checksum: 10c0/5c19af237edb5d5effda008c891a18a585f74bf12953be57923f17a3a4d0979565fc64dbc73b9e20926b9d895f5b690c618cbb969af0cf022e3222471220ad29 + languageName: node + linkType: hard + +"fast-glob@npm:^3.3.2": + version: 3.3.2 + resolution: "fast-glob@npm:3.3.2" + dependencies: + "@nodelib/fs.stat": "npm:^2.0.2" + "@nodelib/fs.walk": "npm:^1.2.3" + glob-parent: "npm:^5.1.2" + merge2: "npm:^1.3.0" + micromatch: "npm:^4.0.4" + checksum: 10c0/42baad7b9cd40b63e42039132bde27ca2cb3a4950d0a0f9abe4639ea1aa9d3e3b40f98b1fe31cbc0cc17b664c9ea7447d911a152fa34ec5b72977b125a6fc845 + languageName: node + linkType: hard + +"fast-json-stable-stringify@npm:^2.0.0": + version: 2.1.0 + resolution: "fast-json-stable-stringify@npm:2.1.0" + checksum: 10c0/7f081eb0b8a64e0057b3bb03f974b3ef00135fbf36c1c710895cd9300f13c94ba809bb3a81cf4e1b03f6e5285610a61abbd7602d0652de423144dfee5a389c9b + languageName: node + linkType: hard + +"fast-levenshtein@npm:^2.0.6": + version: 2.0.6 + resolution: "fast-levenshtein@npm:2.0.6" + checksum: 10c0/111972b37338bcb88f7d9e2c5907862c280ebf4234433b95bc611e518d192ccb2d38119c4ac86e26b668d75f7f3894f4ff5c4982899afced7ca78633b08287c4 + languageName: node + linkType: hard + +"fast-uri@npm:^3.0.1": + version: 3.0.3 + resolution: "fast-uri@npm:3.0.3" + checksum: 10c0/4b2c5ce681a062425eae4f15cdc8fc151fd310b2f69b1f96680677820a8b49c3cd6e80661a406e19d50f0c40a3f8bffdd458791baf66f4a879d80be28e10a320 + languageName: node + linkType: hard + +"fastest-levenshtein@npm:^1.0.12": + version: 1.0.16 + resolution: "fastest-levenshtein@npm:1.0.16" + checksum: 10c0/7e3d8ae812a7f4fdf8cad18e9cde436a39addf266a5986f653ea0d81e0de0900f50c0f27c6d5aff3f686bcb48acbd45be115ae2216f36a6a13a7dbbf5cad878b + languageName: node + linkType: hard + +"fastq@npm:^1.6.0": + version: 1.18.0 + resolution: "fastq@npm:1.18.0" + dependencies: + reusify: "npm:^1.0.4" + checksum: 10c0/7be87ecc41762adbddf558d24182f50a4b1a3ef3ee807d33b7623da7aee5faecdcc94fce5aa13fe91df93e269f383232bbcdb2dc5338cd1826503d6063221f36 + languageName: node + linkType: hard + +"file-entry-cache@npm:^8.0.0": + version: 8.0.0 + resolution: "file-entry-cache@npm:8.0.0" + dependencies: + flat-cache: "npm:^4.0.0" + checksum: 10c0/9e2b5938b1cd9b6d7e3612bdc533afd4ac17b2fc646569e9a8abbf2eb48e5eb8e316bc38815a3ef6a1b456f4107f0d0f055a614ca613e75db6bf9ff4d72c1638 + languageName: node + linkType: hard + +"fill-range@npm:^7.1.1": + version: 7.1.1 + resolution: "fill-range@npm:7.1.1" + dependencies: + to-regex-range: "npm:^5.0.1" + checksum: 10c0/b75b691bbe065472f38824f694c2f7449d7f5004aa950426a2c28f0306c60db9b880c0b0e4ed819997ffb882d1da02cfcfc819bddc94d71627f5269682edf018 + languageName: node + linkType: hard + +"find-up@npm:^4.0.0": + version: 4.1.0 + resolution: "find-up@npm:4.1.0" + dependencies: + locate-path: "npm:^5.0.0" + path-exists: "npm:^4.0.0" + checksum: 10c0/0406ee89ebeefa2d507feb07ec366bebd8a6167ae74aa4e34fb4c4abd06cf782a3ce26ae4194d70706f72182841733f00551c209fe575cb00bd92104056e78c1 + languageName: node + linkType: hard + +"find-up@npm:^5.0.0": + version: 5.0.0 + resolution: "find-up@npm:5.0.0" + dependencies: + locate-path: "npm:^6.0.0" + path-exists: "npm:^4.0.0" + checksum: 10c0/062c5a83a9c02f53cdd6d175a37ecf8f87ea5bbff1fdfb828f04bfa021441bc7583e8ebc0872a4c1baab96221fb8a8a275a19809fb93fbc40bd69ec35634069a + languageName: node + linkType: hard + +"find-up@npm:^7.0.0": + version: 7.0.0 + resolution: "find-up@npm:7.0.0" + dependencies: + locate-path: "npm:^7.2.0" + path-exists: "npm:^5.0.0" + unicorn-magic: "npm:^0.1.0" + checksum: 10c0/e6ee3e6154560bc0ab3bc3b7d1348b31513f9bdf49a5dd2e952495427d559fa48cdf33953e85a309a323898b43fa1bfbc8b80c880dfc16068384783034030008 + languageName: node + linkType: hard + +"flat-cache@npm:^4.0.0": + version: 4.0.1 + resolution: "flat-cache@npm:4.0.1" + dependencies: + flatted: "npm:^3.2.9" + keyv: "npm:^4.5.4" + checksum: 10c0/2c59d93e9faa2523e4fda6b4ada749bed432cfa28c8e251f33b25795e426a1c6dbada777afb1f74fcfff33934fdbdea921ee738fcc33e71adc9d6eca984a1cfc + languageName: node + linkType: hard + +"flat@npm:^5.0.2": + version: 5.0.2 + resolution: "flat@npm:5.0.2" + bin: + flat: cli.js + checksum: 10c0/f178b13482f0cd80c7fede05f4d10585b1f2fdebf26e12edc138e32d3150c6ea6482b7f12813a1091143bad52bb6d3596bca51a162257a21163c0ff438baa5fe + languageName: node + linkType: hard + +"flatted@npm:^3.2.9": + version: 3.3.2 + resolution: "flatted@npm:3.3.2" + checksum: 10c0/24cc735e74d593b6c767fe04f2ef369abe15b62f6906158079b9874bdb3ee5ae7110bb75042e70cd3f99d409d766f357caf78d5ecee9780206f5fdc5edbad334 + languageName: node + linkType: hard + +"foreground-child@npm:^3.1.0": + version: 3.3.0 + resolution: "foreground-child@npm:3.3.0" + dependencies: + cross-spawn: "npm:^7.0.0" + signal-exit: "npm:^4.0.1" + checksum: 10c0/028f1d41000553fcfa6c4bb5c372963bf3d9bf0b1f25a87d1a6253014343fb69dfb1b42d9625d7cf44c8ba429940f3d0ff718b62105d4d4a4f6ef8ca0a53faa2 + languageName: node + linkType: hard + +"fs-minipass@npm:^3.0.0": + version: 3.0.3 + resolution: "fs-minipass@npm:3.0.3" + dependencies: + minipass: "npm:^7.0.3" + checksum: 10c0/63e80da2ff9b621e2cb1596abcb9207f1cf82b968b116ccd7b959e3323144cce7fb141462200971c38bbf2ecca51695069db45265705bed09a7cd93ae5b89f94 + languageName: node + linkType: hard + +"function-bind@npm:^1.1.2": + version: 1.1.2 + resolution: "function-bind@npm:1.1.2" + checksum: 10c0/d8680ee1e5fcd4c197e4ac33b2b4dce03c71f4d91717292785703db200f5c21f977c568d28061226f9b5900cbcd2c84463646134fd5337e7925e0942bc3f46d5 + languageName: node + linkType: hard + +"get-east-asian-width@npm:^1.0.0": + version: 1.3.0 + resolution: "get-east-asian-width@npm:1.3.0" + checksum: 10c0/1a049ba697e0f9a4d5514c4623781c5246982bdb61082da6b5ae6c33d838e52ce6726407df285cdbb27ec1908b333cf2820989bd3e986e37bb20979437fdf34b + languageName: node + linkType: hard + +"get-stream@npm:^8.0.1": + version: 8.0.1 + resolution: "get-stream@npm:8.0.1" + checksum: 10c0/5c2181e98202b9dae0bb4a849979291043e5892eb40312b47f0c22b9414fc9b28a3b6063d2375705eb24abc41ecf97894d9a51f64ff021511b504477b27b4290 + languageName: node + linkType: hard + +"get-tsconfig@npm:^4.8.1": + version: 4.8.1 + resolution: "get-tsconfig@npm:4.8.1" + dependencies: + resolve-pkg-maps: "npm:^1.0.0" + checksum: 10c0/536ee85d202f604f4b5fb6be81bcd6e6d9a96846811e83e9acc6de4a04fb49506edea0e1b8cf1d5ee7af33e469916ec2809d4c5445ab8ae015a7a51fbd1572f9 + languageName: node + linkType: hard + +"glob-parent@npm:^5.1.2": + version: 5.1.2 + resolution: "glob-parent@npm:5.1.2" + dependencies: + is-glob: "npm:^4.0.1" + checksum: 10c0/cab87638e2112bee3f839ef5f6e0765057163d39c66be8ec1602f3823da4692297ad4e972de876ea17c44d652978638d2fd583c6713d0eb6591706825020c9ee + languageName: node + linkType: hard + +"glob-parent@npm:^6.0.2": + version: 6.0.2 + resolution: "glob-parent@npm:6.0.2" + dependencies: + is-glob: "npm:^4.0.3" + checksum: 10c0/317034d88654730230b3f43bb7ad4f7c90257a426e872ea0bf157473ac61c99bf5d205fad8f0185f989be8d2fa6d3c7dce1645d99d545b6ea9089c39f838e7f8 + languageName: node + linkType: hard + +"glob-to-regexp@npm:^0.4.1": + version: 0.4.1 + resolution: "glob-to-regexp@npm:0.4.1" + checksum: 10c0/0486925072d7a916f052842772b61c3e86247f0a80cc0deb9b5a3e8a1a9faad5b04fb6f58986a09f34d3e96cd2a22a24b7e9882fb1cf904c31e9a310de96c429 + languageName: node + linkType: hard + +"glob@npm:^10.2.2, glob@npm:^10.3.10, glob@npm:^10.3.7": + version: 10.4.5 + resolution: "glob@npm:10.4.5" + dependencies: + foreground-child: "npm:^3.1.0" + jackspeak: "npm:^3.1.2" + minimatch: "npm:^9.0.4" + minipass: "npm:^7.1.2" + package-json-from-dist: "npm:^1.0.0" + path-scurry: "npm:^1.11.1" + bin: + glob: dist/esm/bin.mjs + checksum: 10c0/19a9759ea77b8e3ca0a43c2f07ecddc2ad46216b786bb8f993c445aee80d345925a21e5280c7b7c6c59e860a0154b84e4b2b60321fea92cd3c56b4a7489f160e + languageName: node + linkType: hard + +"glob@npm:^11.0.0": + version: 11.0.0 + resolution: "glob@npm:11.0.0" + dependencies: + foreground-child: "npm:^3.1.0" + jackspeak: "npm:^4.0.1" + minimatch: "npm:^10.0.0" + minipass: "npm:^7.1.2" + package-json-from-dist: "npm:^1.0.0" + path-scurry: "npm:^2.0.0" + bin: + glob: dist/esm/bin.mjs + checksum: 10c0/419866015d8795258a8ac51de5b9d1a99c72634fc3ead93338e4da388e89773ab21681e494eac0fbc4250b003451ca3110bb4f1c9393d15d14466270094fdb4e + languageName: node + linkType: hard + +"globals@npm:^14.0.0": + version: 14.0.0 + resolution: "globals@npm:14.0.0" + checksum: 10c0/b96ff42620c9231ad468d4c58ff42afee7777ee1c963013ff8aabe095a451d0ceeb8dcd8ef4cbd64d2538cef45f787a78ba3a9574f4a634438963e334471302d + languageName: node + linkType: hard + +"globals@npm:^15.11.0": + version: 15.14.0 + resolution: "globals@npm:15.14.0" + checksum: 10c0/039deb8648bd373b7940c15df9f96ab7508fe92b31bbd39cbd1c1a740bd26db12457aa3e5d211553b234f30e9b1db2fee3683012f543a01a6942c9062857facb + languageName: node + linkType: hard + +"graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.2.11, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6": + version: 4.2.11 + resolution: "graceful-fs@npm:4.2.11" + checksum: 10c0/386d011a553e02bc594ac2ca0bd6d9e4c22d7fa8cfbfc448a6d148c59ea881b092db9dbe3547ae4b88e55f1b01f7c4a2ecc53b310c042793e63aa44cf6c257f2 + languageName: node + linkType: hard + +"graphemer@npm:^1.4.0": + version: 1.4.0 + resolution: "graphemer@npm:1.4.0" + checksum: 10c0/e951259d8cd2e0d196c72ec711add7115d42eb9a8146c8eeda5b8d3ac91e5dd816b9cd68920726d9fd4490368e7ed86e9c423f40db87e2d8dfafa00fa17c3a31 + languageName: node + linkType: hard + +"has-flag@npm:^4.0.0": + version: 4.0.0 + resolution: "has-flag@npm:4.0.0" + checksum: 10c0/2e789c61b7888d66993e14e8331449e525ef42aac53c627cc53d1c3334e768bcb6abdc4f5f0de1478a25beec6f0bd62c7549058b7ac53e924040d4f301f02fd1 + languageName: node + linkType: hard + +"hasown@npm:^2.0.2": + version: 2.0.2 + resolution: "hasown@npm:2.0.2" + dependencies: + function-bind: "npm:^1.1.2" + checksum: 10c0/3769d434703b8ac66b209a4cca0737519925bbdb61dd887f93a16372b14694c63ff4e797686d87c90f08168e81082248b9b028bad60d4da9e0d1148766f56eb9 + languageName: node + linkType: hard + +"http-cache-semantics@npm:^4.1.1": + version: 4.1.1 + resolution: "http-cache-semantics@npm:4.1.1" + checksum: 10c0/ce1319b8a382eb3cbb4a37c19f6bfe14e5bb5be3d09079e885e8c513ab2d3cd9214902f8a31c9dc4e37022633ceabfc2d697405deeaf1b8f3552bb4ed996fdfc + languageName: node + linkType: hard + +"http-proxy-agent@npm:^7.0.0": + version: 7.0.2 + resolution: "http-proxy-agent@npm:7.0.2" + dependencies: + agent-base: "npm:^7.1.0" + debug: "npm:^4.3.4" + checksum: 10c0/4207b06a4580fb85dd6dff521f0abf6db517489e70863dca1a0291daa7f2d3d2d6015a57bd702af068ea5cf9f1f6ff72314f5f5b4228d299c0904135d2aef921 + languageName: node + linkType: hard + +"https-proxy-agent@npm:^7.0.1": + version: 7.0.6 + resolution: "https-proxy-agent@npm:7.0.6" + dependencies: + agent-base: "npm:^7.1.2" + debug: "npm:4" + checksum: 10c0/f729219bc735edb621fa30e6e84e60ee5d00802b8247aac0d7b79b0bd6d4b3294737a337b93b86a0bd9e68099d031858a39260c976dc14cdbba238ba1f8779ac + languageName: node + linkType: hard + +"human-signals@npm:^5.0.0": + version: 5.0.0 + resolution: "human-signals@npm:5.0.0" + checksum: 10c0/5a9359073fe17a8b58e5a085e9a39a950366d9f00217c4ff5878bd312e09d80f460536ea6a3f260b5943a01fe55c158d1cea3fc7bee3d0520aeef04f6d915c82 + languageName: node + linkType: hard + +"husky@npm:^9.1.7": + version: 9.1.7 + resolution: "husky@npm:9.1.7" + bin: + husky: bin.js + checksum: 10c0/35bb110a71086c48906aa7cd3ed4913fb913823715359d65e32e0b964cb1e255593b0ae8014a5005c66a68e6fa66c38dcfa8056dbbdfb8b0187c0ffe7ee3a58f + languageName: node + linkType: hard + +"iconv-lite@npm:^0.6.2": + version: 0.6.3 + resolution: "iconv-lite@npm:0.6.3" + dependencies: + safer-buffer: "npm:>= 2.1.2 < 3.0.0" + checksum: 10c0/98102bc66b33fcf5ac044099d1257ba0b7ad5e3ccd3221f34dd508ab4070edff183276221684e1e0555b145fce0850c9f7d2b60a9fcac50fbb4ea0d6e845a3b1 + languageName: node + linkType: hard + +"ignore@npm:^5.2.0, ignore@npm:^5.3.1, ignore@npm:^5.3.2": + version: 5.3.2 + resolution: "ignore@npm:5.3.2" + checksum: 10c0/f9f652c957983634ded1e7f02da3b559a0d4cc210fca3792cb67f1b153623c9c42efdc1c4121af171e295444459fc4a9201101fb041b1104a3c000bccb188337 + languageName: node + linkType: hard + +"import-fresh@npm:^3.2.1": + version: 3.3.0 + resolution: "import-fresh@npm:3.3.0" + dependencies: + parent-module: "npm:^1.0.0" + resolve-from: "npm:^4.0.0" + checksum: 10c0/7f882953aa6b740d1f0e384d0547158bc86efbf2eea0f1483b8900a6f65c5a5123c2cf09b0d542cc419d0b98a759ecaeb394237e97ea427f2da221dc3cd80cc3 + languageName: node + linkType: hard + +"import-local@npm:^3.0.2": + version: 3.2.0 + resolution: "import-local@npm:3.2.0" + dependencies: + pkg-dir: "npm:^4.2.0" + resolve-cwd: "npm:^3.0.0" + bin: + import-local-fixture: fixtures/cli.js + checksum: 10c0/94cd6367a672b7e0cb026970c85b76902d2710a64896fa6de93bd5c571dd03b228c5759308959de205083e3b1c61e799f019c9e36ee8e9c523b993e1057f0433 + languageName: node + linkType: hard + +"imurmurhash@npm:^0.1.4": + version: 0.1.4 + resolution: "imurmurhash@npm:0.1.4" + checksum: 10c0/8b51313850dd33605c6c9d3fd9638b714f4c4c40250cff658209f30d40da60f78992fb2df5dabee4acf589a6a82bbc79ad5486550754bd9ec4e3fc0d4a57d6a6 + languageName: node + linkType: hard + +"interpret@npm:^3.1.1": + version: 3.1.1 + resolution: "interpret@npm:3.1.1" + checksum: 10c0/6f3c4d0aa6ec1b43a8862375588a249e3c917739895cbe67fe12f0a76260ea632af51e8e2431b50fbcd0145356dc28ca147be08dbe6a523739fd55c0f91dc2a5 + languageName: node + linkType: hard + +"ip-address@npm:^9.0.5": + version: 9.0.5 + resolution: "ip-address@npm:9.0.5" + dependencies: + jsbn: "npm:1.1.0" + sprintf-js: "npm:^1.1.3" + checksum: 10c0/331cd07fafcb3b24100613e4b53e1a2b4feab11e671e655d46dc09ee233da5011284d09ca40c4ecbdfe1d0004f462958675c224a804259f2f78d2465a87824bc + languageName: node + linkType: hard + +"is-core-module@npm:^2.16.0": + version: 2.16.1 + resolution: "is-core-module@npm:2.16.1" + dependencies: + hasown: "npm:^2.0.2" + checksum: 10c0/898443c14780a577e807618aaae2b6f745c8538eca5c7bc11388a3f2dc6de82b9902bcc7eb74f07be672b11bbe82dd6a6edded44a00cb3d8f933d0459905eedd + languageName: node + linkType: hard + +"is-extglob@npm:^2.1.1": + version: 2.1.1 + resolution: "is-extglob@npm:2.1.1" + checksum: 10c0/5487da35691fbc339700bbb2730430b07777a3c21b9ebaecb3072512dfd7b4ba78ac2381a87e8d78d20ea08affb3f1971b4af629173a6bf435ff8a4c47747912 + languageName: node + linkType: hard + +"is-fullwidth-code-point@npm:^3.0.0": + version: 3.0.0 + resolution: "is-fullwidth-code-point@npm:3.0.0" + checksum: 10c0/bb11d825e049f38e04c06373a8d72782eee0205bda9d908cc550ccb3c59b99d750ff9537982e01733c1c94a58e35400661f57042158ff5e8f3e90cf936daf0fc + languageName: node + linkType: hard + +"is-fullwidth-code-point@npm:^4.0.0": + version: 4.0.0 + resolution: "is-fullwidth-code-point@npm:4.0.0" + checksum: 10c0/df2a717e813567db0f659c306d61f2f804d480752526886954a2a3e2246c7745fd07a52b5fecf2b68caf0a6c79dcdace6166fdf29cc76ed9975cc334f0a018b8 + languageName: node + linkType: hard + +"is-fullwidth-code-point@npm:^5.0.0": + version: 5.0.0 + resolution: "is-fullwidth-code-point@npm:5.0.0" + dependencies: + get-east-asian-width: "npm:^1.0.0" + checksum: 10c0/cd591b27d43d76b05fa65ed03eddce57a16e1eca0b7797ff7255de97019bcaf0219acfc0c4f7af13319e13541f2a53c0ace476f442b13267b9a6a7568f2b65c8 + languageName: node + linkType: hard + +"is-glob@npm:^4.0.0, is-glob@npm:^4.0.1, is-glob@npm:^4.0.3": + version: 4.0.3 + resolution: "is-glob@npm:4.0.3" + dependencies: + is-extglob: "npm:^2.1.1" + checksum: 10c0/17fb4014e22be3bbecea9b2e3a76e9e34ff645466be702f1693e8f1ee1adac84710d0be0bd9f967d6354036fd51ab7c2741d954d6e91dae6bb69714de92c197a + languageName: node + linkType: hard + +"is-number@npm:^7.0.0": + version: 7.0.0 + resolution: "is-number@npm:7.0.0" + checksum: 10c0/b4686d0d3053146095ccd45346461bc8e53b80aeb7671cc52a4de02dbbf7dc0d1d2a986e2fe4ae206984b4d34ef37e8b795ebc4f4295c978373e6575e295d811 + languageName: node + linkType: hard + +"is-plain-object@npm:^2.0.4": + version: 2.0.4 + resolution: "is-plain-object@npm:2.0.4" + dependencies: + isobject: "npm:^3.0.1" + checksum: 10c0/f050fdd5203d9c81e8c4df1b3ff461c4bc64e8b5ca383bcdde46131361d0a678e80bcf00b5257646f6c636197629644d53bd8e2375aea633de09a82d57e942f4 + languageName: node + linkType: hard + +"is-stream@npm:^3.0.0": + version: 3.0.0 + resolution: "is-stream@npm:3.0.0" + checksum: 10c0/eb2f7127af02ee9aa2a0237b730e47ac2de0d4e76a4a905a50a11557f2339df5765eaea4ceb8029f1efa978586abe776908720bfcb1900c20c6ec5145f6f29d8 + languageName: node + linkType: hard + +"isexe@npm:^2.0.0": + version: 2.0.0 + resolution: "isexe@npm:2.0.0" + checksum: 10c0/228cfa503fadc2c31596ab06ed6aa82c9976eec2bfd83397e7eaf06d0ccf42cd1dfd6743bf9aeb01aebd4156d009994c5f76ea898d2832c1fe342da923ca457d + languageName: node + linkType: hard + +"isexe@npm:^3.1.1": + version: 3.1.1 + resolution: "isexe@npm:3.1.1" + checksum: 10c0/9ec257654093443eb0a528a9c8cbba9c0ca7616ccb40abd6dde7202734d96bb86e4ac0d764f0f8cd965856aacbff2f4ce23e730dc19dfb41e3b0d865ca6fdcc7 + languageName: node + linkType: hard + +"isobject@npm:^3.0.1": + version: 3.0.1 + resolution: "isobject@npm:3.0.1" + checksum: 10c0/03344f5064a82f099a0cd1a8a407f4c0d20b7b8485e8e816c39f249e9416b06c322e8dec5b842b6bb8a06de0af9cb48e7bc1b5352f0fadc2f0abac033db3d4db + languageName: node + linkType: hard + +"jackspeak@npm:^3.1.2": + version: 3.4.3 + resolution: "jackspeak@npm:3.4.3" + dependencies: + "@isaacs/cliui": "npm:^8.0.2" + "@pkgjs/parseargs": "npm:^0.11.0" + dependenciesMeta: + "@pkgjs/parseargs": + optional: true + checksum: 10c0/6acc10d139eaefdbe04d2f679e6191b3abf073f111edf10b1de5302c97ec93fffeb2fdd8681ed17f16268aa9dd4f8c588ed9d1d3bffbbfa6e8bf897cbb3149b9 + languageName: node + linkType: hard + +"jackspeak@npm:^4.0.1": + version: 4.0.2 + resolution: "jackspeak@npm:4.0.2" + dependencies: + "@isaacs/cliui": "npm:^8.0.2" + checksum: 10c0/b26039d11c0163a95b1e58851b9ac453cce64ad6d1eb98a00b303ad5eeb761b29d33c9419d1e16c016d3f7151c8edf7df223e6cf93a1907655fd95d6ce85c0de + languageName: node + linkType: hard + +"jest-worker@npm:^27.4.5": + version: 27.5.1 + resolution: "jest-worker@npm:27.5.1" + dependencies: + "@types/node": "npm:*" + merge-stream: "npm:^2.0.0" + supports-color: "npm:^8.0.0" + checksum: 10c0/8c4737ffd03887b3c6768e4cc3ca0269c0336c1e4b1b120943958ddb035ed2a0fc6acab6dc99631720a3720af4e708ff84fb45382ad1e83c27946adf3623969b + languageName: node + linkType: hard + +"js-yaml@npm:^4.1.0": + version: 4.1.0 + resolution: "js-yaml@npm:4.1.0" + dependencies: + argparse: "npm:^2.0.1" + bin: + js-yaml: bin/js-yaml.js + checksum: 10c0/184a24b4eaacfce40ad9074c64fd42ac83cf74d8c8cd137718d456ced75051229e5061b8633c3366b8aada17945a7a356b337828c19da92b51ae62126575018f + languageName: node + linkType: hard + +"jsbn@npm:1.1.0": + version: 1.1.0 + resolution: "jsbn@npm:1.1.0" + checksum: 10c0/4f907fb78d7b712e11dea8c165fe0921f81a657d3443dde75359ed52eb2b5d33ce6773d97985a089f09a65edd80b11cb75c767b57ba47391fee4c969f7215c96 + languageName: node + linkType: hard + +"json-buffer@npm:3.0.1": + version: 3.0.1 + resolution: "json-buffer@npm:3.0.1" + checksum: 10c0/0d1c91569d9588e7eef2b49b59851f297f3ab93c7b35c7c221e288099322be6b562767d11e4821da500f3219542b9afd2e54c5dc573107c1126ed1080f8e96d7 + languageName: node + linkType: hard + +"json-parse-even-better-errors@npm:^2.3.1": + version: 2.3.1 + resolution: "json-parse-even-better-errors@npm:2.3.1" + checksum: 10c0/140932564c8f0b88455432e0f33c4cb4086b8868e37524e07e723f4eaedb9425bdc2bafd71bd1d9765bd15fd1e2d126972bc83990f55c467168c228c24d665f3 + languageName: node + linkType: hard + +"json-schema-traverse@npm:^0.4.1": + version: 0.4.1 + resolution: "json-schema-traverse@npm:0.4.1" + checksum: 10c0/108fa90d4cc6f08243aedc6da16c408daf81793bf903e9fd5ab21983cda433d5d2da49e40711da016289465ec2e62e0324dcdfbc06275a607fe3233fde4942ce + languageName: node + linkType: hard + +"json-schema-traverse@npm:^1.0.0": + version: 1.0.0 + resolution: "json-schema-traverse@npm:1.0.0" + checksum: 10c0/71e30015d7f3d6dc1c316d6298047c8ef98a06d31ad064919976583eb61e1018a60a0067338f0f79cabc00d84af3fcc489bd48ce8a46ea165d9541ba17fb30c6 + languageName: node + linkType: hard + +"json-stable-stringify-without-jsonify@npm:^1.0.1": + version: 1.0.1 + resolution: "json-stable-stringify-without-jsonify@npm:1.0.1" + checksum: 10c0/cb168b61fd4de83e58d09aaa6425ef71001bae30d260e2c57e7d09a5fd82223e2f22a042dedaab8db23b7d9ae46854b08bb1f91675a8be11c5cffebef5fb66a5 + languageName: node + linkType: hard + +"keyv@npm:^4.5.4": + version: 4.5.4 + resolution: "keyv@npm:4.5.4" + dependencies: + json-buffer: "npm:3.0.1" + checksum: 10c0/aa52f3c5e18e16bb6324876bb8b59dd02acf782a4b789c7b2ae21107fab95fab3890ed448d4f8dba80ce05391eeac4bfabb4f02a20221342982f806fa2cf271e + languageName: node + linkType: hard + +"kind-of@npm:^6.0.2": + version: 6.0.3 + resolution: "kind-of@npm:6.0.3" + checksum: 10c0/61cdff9623dabf3568b6445e93e31376bee1cdb93f8ba7033d86022c2a9b1791a1d9510e026e6465ebd701a6dd2f7b0808483ad8838341ac52f003f512e0b4c4 + languageName: node + linkType: hard + +"levn@npm:^0.4.1": + version: 0.4.1 + resolution: "levn@npm:0.4.1" + dependencies: + prelude-ls: "npm:^1.2.1" + type-check: "npm:~0.4.0" + checksum: 10c0/effb03cad7c89dfa5bd4f6989364bfc79994c2042ec5966cb9b95990e2edee5cd8969ddf42616a0373ac49fac1403437deaf6e9050fbbaa3546093a59b9ac94e + languageName: node + linkType: hard + +"lilconfig@npm:~3.1.3": + version: 3.1.3 + resolution: "lilconfig@npm:3.1.3" + checksum: 10c0/f5604e7240c5c275743561442fbc5abf2a84ad94da0f5adc71d25e31fa8483048de3dcedcb7a44112a942fed305fd75841cdf6c9681c7f640c63f1049e9a5dcc + languageName: node + linkType: hard + +"lint-staged@npm:^15.2.11": + version: 15.3.0 + resolution: "lint-staged@npm:15.3.0" + dependencies: + chalk: "npm:~5.4.1" + commander: "npm:~12.1.0" + debug: "npm:~4.4.0" + execa: "npm:~8.0.1" + lilconfig: "npm:~3.1.3" + listr2: "npm:~8.2.5" + micromatch: "npm:~4.0.8" + pidtree: "npm:~0.6.0" + string-argv: "npm:~0.3.2" + yaml: "npm:~2.6.1" + bin: + lint-staged: bin/lint-staged.js + checksum: 10c0/1ddf9488c523c0b65c85b755428d4ad74fac3aa6ccb2e28e9bff5b8d86503158fe241d20d5433a11146872050b43580644901a5ef4c924b1ad7017c224a07339 + languageName: node + linkType: hard + +"listr2@npm:~8.2.5": + version: 8.2.5 + resolution: "listr2@npm:8.2.5" + dependencies: + cli-truncate: "npm:^4.0.0" + colorette: "npm:^2.0.20" + eventemitter3: "npm:^5.0.1" + log-update: "npm:^6.1.0" + rfdc: "npm:^1.4.1" + wrap-ansi: "npm:^9.0.0" + checksum: 10c0/f5a9599514b00c27d7eb32d1117c83c61394b2a985ec20e542c798bf91cf42b19340215701522736f5b7b42f557e544afeadec47866e35e5d4f268f552729671 + languageName: node + linkType: hard + +"loader-runner@npm:^4.2.0": + version: 4.3.0 + resolution: "loader-runner@npm:4.3.0" + checksum: 10c0/a44d78aae0907a72f73966fe8b82d1439c8c485238bd5a864b1b9a2a3257832effa858790241e6b37876b5446a78889adf2fcc8dd897ce54c089ecc0a0ce0bf0 + languageName: node + linkType: hard + +"locate-path@npm:^5.0.0": + version: 5.0.0 + resolution: "locate-path@npm:5.0.0" + dependencies: + p-locate: "npm:^4.1.0" + checksum: 10c0/33a1c5247e87e022f9713e6213a744557a3e9ec32c5d0b5efb10aa3a38177615bf90221a5592674857039c1a0fd2063b82f285702d37b792d973e9e72ace6c59 + languageName: node + linkType: hard + +"locate-path@npm:^6.0.0": + version: 6.0.0 + resolution: "locate-path@npm:6.0.0" + dependencies: + p-locate: "npm:^5.0.0" + checksum: 10c0/d3972ab70dfe58ce620e64265f90162d247e87159b6126b01314dd67be43d50e96a50b517bce2d9452a79409c7614054c277b5232377de50416564a77ac7aad3 + languageName: node + linkType: hard + +"locate-path@npm:^7.2.0": + version: 7.2.0 + resolution: "locate-path@npm:7.2.0" + dependencies: + p-locate: "npm:^6.0.0" + checksum: 10c0/139e8a7fe11cfbd7f20db03923cacfa5db9e14fa14887ea121345597472b4a63c1a42a8a5187defeeff6acf98fd568da7382aa39682d38f0af27433953a97751 + languageName: node + linkType: hard + +"lodash.merge@npm:^4.6.2": + version: 4.6.2 + resolution: "lodash.merge@npm:4.6.2" + checksum: 10c0/402fa16a1edd7538de5b5903a90228aa48eb5533986ba7fa26606a49db2572bf414ff73a2c9f5d5fd36b31c46a5d5c7e1527749c07cbcf965ccff5fbdf32c506 + languageName: node + linkType: hard + +"log-update@npm:^6.1.0": + version: 6.1.0 + resolution: "log-update@npm:6.1.0" + dependencies: + ansi-escapes: "npm:^7.0.0" + cli-cursor: "npm:^5.0.0" + slice-ansi: "npm:^7.1.0" + strip-ansi: "npm:^7.1.0" + wrap-ansi: "npm:^9.0.0" + checksum: 10c0/4b350c0a83d7753fea34dcac6cd797d1dc9603291565de009baa4aa91c0447eab0d3815a05c8ec9ac04fdfffb43c82adcdb03ec1fceafd8518e1a8c1cff4ff89 + languageName: node + linkType: hard + +"long@npm:4.0.0": + version: 4.0.0 + resolution: "long@npm:4.0.0" + checksum: 10c0/50a6417d15b06104dbe4e3d4a667c39b137f130a9108ea8752b352a4cfae047531a3ac351c181792f3f8768fe17cca6b0f406674a541a86fb638aaac560d83ed + languageName: node + linkType: hard + +"lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0": + version: 10.4.3 + resolution: "lru-cache@npm:10.4.3" + checksum: 10c0/ebd04fbca961e6c1d6c0af3799adcc966a1babe798f685bb84e6599266599cd95d94630b10262f5424539bc4640107e8a33aa28585374abf561d30d16f4b39fb + languageName: node + linkType: hard + +"lru-cache@npm:^11.0.0": + version: 11.0.2 + resolution: "lru-cache@npm:11.0.2" + checksum: 10c0/c993b8e06ead0b24b969c1dbb5b301716aed66e320e9014a80012f5febe280b438f28ff50046b2c55ff404e889351ccb332ff91f8dd175a21f5eae80e3fb155f + languageName: node + linkType: hard + +"make-fetch-happen@npm:^14.0.3": + version: 14.0.3 + resolution: "make-fetch-happen@npm:14.0.3" + dependencies: + "@npmcli/agent": "npm:^3.0.0" + cacache: "npm:^19.0.1" + http-cache-semantics: "npm:^4.1.1" + minipass: "npm:^7.0.2" + minipass-fetch: "npm:^4.0.0" + minipass-flush: "npm:^1.0.5" + minipass-pipeline: "npm:^1.2.4" + negotiator: "npm:^1.0.0" + proc-log: "npm:^5.0.0" + promise-retry: "npm:^2.0.1" + ssri: "npm:^12.0.0" + checksum: 10c0/c40efb5e5296e7feb8e37155bde8eb70bc57d731b1f7d90e35a092fde403d7697c56fb49334d92d330d6f1ca29a98142036d6480a12681133a0a1453164cb2f0 + languageName: node + linkType: hard + +"merge-stream@npm:^2.0.0": + version: 2.0.0 + resolution: "merge-stream@npm:2.0.0" + checksum: 10c0/867fdbb30a6d58b011449b8885601ec1690c3e41c759ecd5a9d609094f7aed0096c37823ff4a7190ef0b8f22cc86beb7049196ff68c016e3b3c671d0dac91ce5 + languageName: node + linkType: hard + +"merge2@npm:^1.3.0": + version: 1.4.1 + resolution: "merge2@npm:1.4.1" + checksum: 10c0/254a8a4605b58f450308fc474c82ac9a094848081bf4c06778200207820e5193726dc563a0d2c16468810516a5c97d9d3ea0ca6585d23c58ccfff2403e8dbbeb + languageName: node + linkType: hard + +"micromatch@npm:^4.0.4, micromatch@npm:~4.0.8": + version: 4.0.8 + resolution: "micromatch@npm:4.0.8" + dependencies: + braces: "npm:^3.0.3" + picomatch: "npm:^2.3.1" + checksum: 10c0/166fa6eb926b9553f32ef81f5f531d27b4ce7da60e5baf8c021d043b27a388fb95e46a8038d5045877881e673f8134122b59624d5cecbd16eb50a42e7a6b5ca8 + languageName: node + linkType: hard + +"mime-db@npm:1.52.0": + version: 1.52.0 + resolution: "mime-db@npm:1.52.0" + checksum: 10c0/0557a01deebf45ac5f5777fe7740b2a5c309c6d62d40ceab4e23da9f821899ce7a900b7ac8157d4548ddbb7beffe9abc621250e6d182b0397ec7f10c7b91a5aa + languageName: node + linkType: hard + +"mime-types@npm:^2.1.27": + version: 2.1.35 + resolution: "mime-types@npm:2.1.35" + dependencies: + mime-db: "npm:1.52.0" + checksum: 10c0/82fb07ec56d8ff1fc999a84f2f217aa46cb6ed1033fefaabd5785b9a974ed225c90dc72fff460259e66b95b73648596dbcc50d51ed69cdf464af2d237d3149b2 + languageName: node + linkType: hard + +"mimic-fn@npm:^3.1.0": + version: 3.1.0 + resolution: "mimic-fn@npm:3.1.0" + checksum: 10c0/a07cdd8ed6490c2dff5b11f889b245d9556b80f5a653a552a651d17cff5a2d156e632d235106c2369f00cccef4071704589574cf3601bc1b1400a1f620dff067 + languageName: node + linkType: hard + +"mimic-fn@npm:^4.0.0": + version: 4.0.0 + resolution: "mimic-fn@npm:4.0.0" + checksum: 10c0/de9cc32be9996fd941e512248338e43407f63f6d497abe8441fa33447d922e927de54d4cc3c1a3c6d652857acd770389d5a3823f311a744132760ce2be15ccbf + languageName: node + linkType: hard + +"mimic-function@npm:^5.0.0": + version: 5.0.1 + resolution: "mimic-function@npm:5.0.1" + checksum: 10c0/f3d9464dd1816ecf6bdf2aec6ba32c0728022039d992f178237d8e289b48764fee4131319e72eedd4f7f094e22ded0af836c3187a7edc4595d28dd74368fd81d + languageName: node + linkType: hard + +"minimatch@npm:^10.0.0": + version: 10.0.1 + resolution: "minimatch@npm:10.0.1" + dependencies: + brace-expansion: "npm:^2.0.1" + checksum: 10c0/e6c29a81fe83e1877ad51348306be2e8aeca18c88fdee7a99df44322314279e15799e41d7cb274e4e8bb0b451a3bc622d6182e157dfa1717d6cda75e9cd8cd5d + languageName: node + linkType: hard + +"minimatch@npm:^3.1.2": + version: 3.1.2 + resolution: "minimatch@npm:3.1.2" + dependencies: + brace-expansion: "npm:^1.1.7" + checksum: 10c0/0262810a8fc2e72cca45d6fd86bd349eee435eb95ac6aa45c9ea2180e7ee875ef44c32b55b5973ceabe95ea12682f6e3725cbb63d7a2d1da3ae1163c8b210311 + languageName: node + linkType: hard + +"minimatch@npm:^9.0.4, minimatch@npm:^9.0.5": + version: 9.0.5 + resolution: "minimatch@npm:9.0.5" + dependencies: + brace-expansion: "npm:^2.0.1" + checksum: 10c0/de96cf5e35bdf0eab3e2c853522f98ffbe9a36c37797778d2665231ec1f20a9447a7e567cb640901f89e4daaa95ae5d70c65a9e8aa2bb0019b6facbc3c0575ed + languageName: node + linkType: hard + +"minipass-collect@npm:^2.0.1": + version: 2.0.1 + resolution: "minipass-collect@npm:2.0.1" + dependencies: + minipass: "npm:^7.0.3" + checksum: 10c0/5167e73f62bb74cc5019594709c77e6a742051a647fe9499abf03c71dca75515b7959d67a764bdc4f8b361cf897fbf25e2d9869ee039203ed45240f48b9aa06e + languageName: node + linkType: hard + +"minipass-fetch@npm:^4.0.0": + version: 4.0.0 + resolution: "minipass-fetch@npm:4.0.0" + dependencies: + encoding: "npm:^0.1.13" + minipass: "npm:^7.0.3" + minipass-sized: "npm:^1.0.3" + minizlib: "npm:^3.0.1" + dependenciesMeta: + encoding: + optional: true + checksum: 10c0/7fa30ce7c373fb6f94c086b374fff1589fd7e78451855d2d06c2e2d9df936d131e73e952163063016592ed3081444bd8d1ea608533313b0149156ce23311da4b + languageName: node + linkType: hard + +"minipass-flush@npm:^1.0.5": + version: 1.0.5 + resolution: "minipass-flush@npm:1.0.5" + dependencies: + minipass: "npm:^3.0.0" + checksum: 10c0/2a51b63feb799d2bb34669205eee7c0eaf9dce01883261a5b77410c9408aa447e478efd191b4de6fc1101e796ff5892f8443ef20d9544385819093dbb32d36bd + languageName: node + linkType: hard + +"minipass-pipeline@npm:^1.2.4": + version: 1.2.4 + resolution: "minipass-pipeline@npm:1.2.4" + dependencies: + minipass: "npm:^3.0.0" + checksum: 10c0/cbda57cea20b140b797505dc2cac71581a70b3247b84480c1fed5ca5ba46c25ecc25f68bfc9e6dcb1a6e9017dab5c7ada5eab73ad4f0a49d84e35093e0c643f2 + languageName: node + linkType: hard + +"minipass-sized@npm:^1.0.3": + version: 1.0.3 + resolution: "minipass-sized@npm:1.0.3" + dependencies: + minipass: "npm:^3.0.0" + checksum: 10c0/298f124753efdc745cfe0f2bdfdd81ba25b9f4e753ca4a2066eb17c821f25d48acea607dfc997633ee5bf7b6dfffb4eee4f2051eb168663f0b99fad2fa4829cb + languageName: node + linkType: hard + +"minipass@npm:^3.0.0": + version: 3.3.6 + resolution: "minipass@npm:3.3.6" + dependencies: + yallist: "npm:^4.0.0" + checksum: 10c0/a114746943afa1dbbca8249e706d1d38b85ed1298b530f5808ce51f8e9e941962e2a5ad2e00eae7dd21d8a4aae6586a66d4216d1a259385e9d0358f0c1eba16c + languageName: node + linkType: hard + +"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.0.4, minipass@npm:^7.1.2": + version: 7.1.2 + resolution: "minipass@npm:7.1.2" + checksum: 10c0/b0fd20bb9fb56e5fa9a8bfac539e8915ae07430a619e4b86ff71f5fc757ef3924b23b2c4230393af1eda647ed3d75739e4e0acb250a6b1eb277cf7f8fe449557 + languageName: node + linkType: hard + +"minizlib@npm:^3.0.1": + version: 3.0.1 + resolution: "minizlib@npm:3.0.1" + dependencies: + minipass: "npm:^7.0.4" + rimraf: "npm:^5.0.5" + checksum: 10c0/82f8bf70da8af656909a8ee299d7ed3b3372636749d29e105f97f20e88971be31f5ed7642f2e898f00283b68b701cc01307401cdc209b0efc5dd3818220e5093 + languageName: node + linkType: hard + +"mkdirp@npm:^3.0.1": + version: 3.0.1 + resolution: "mkdirp@npm:3.0.1" + bin: + mkdirp: dist/cjs/src/bin.js + checksum: 10c0/9f2b975e9246351f5e3a40dcfac99fcd0baa31fbfab615fe059fb11e51f10e4803c63de1f384c54d656e4db31d000e4767e9ef076a22e12a641357602e31d57d + languageName: node + linkType: hard + +"ms@npm:2.1.2": + version: 2.1.2 + resolution: "ms@npm:2.1.2" + checksum: 10c0/a437714e2f90dbf881b5191d35a6db792efbca5badf112f87b9e1c712aace4b4b9b742dd6537f3edf90fd6f684de897cec230abde57e87883766712ddda297cc + languageName: node + linkType: hard + +"ms@npm:^2.1.3": + version: 2.1.3 + resolution: "ms@npm:2.1.3" + checksum: 10c0/d924b57e7312b3b63ad21fc5b3dc0af5e78d61a1fc7cfb5457edaf26326bf62be5307cc87ffb6862ef1c2b33b0233cdb5d4f01c4c958cc0d660948b65a287a48 + languageName: node + linkType: hard + +"nanoid@npm:^3.3.8": + version: 3.3.8 + resolution: "nanoid@npm:3.3.8" + bin: + nanoid: bin/nanoid.cjs + checksum: 10c0/4b1bb29f6cfebf3be3bc4ad1f1296fb0a10a3043a79f34fbffe75d1621b4318319211cd420549459018ea3592f0d2f159247a6f874911d6d26eaaadda2478120 + languageName: node + linkType: hard + +"natural-compare@npm:^1.4.0": + version: 1.4.0 + resolution: "natural-compare@npm:1.4.0" + checksum: 10c0/f5f9a7974bfb28a91afafa254b197f0f22c684d4a1731763dda960d2c8e375b36c7d690e0d9dc8fba774c537af14a7e979129bca23d88d052fbeb9466955e447 + languageName: node + linkType: hard + +"negotiator@npm:^1.0.0": + version: 1.0.0 + resolution: "negotiator@npm:1.0.0" + checksum: 10c0/4c559dd52669ea48e1914f9d634227c561221dd54734070791f999c52ed0ff36e437b2e07d5c1f6e32909fc625fe46491c16e4a8f0572567d4dd15c3a4fda04b + languageName: node + linkType: hard + +"neo-async@npm:^2.6.2": + version: 2.6.2 + resolution: "neo-async@npm:2.6.2" + checksum: 10c0/c2f5a604a54a8ec5438a342e1f356dff4bc33ccccdb6dc668d94fe8e5eccfc9d2c2eea6064b0967a767ba63b33763f51ccf2cd2441b461a7322656c1f06b3f5d + languageName: node + linkType: hard + +"node-addon-api@npm:7.0.0": + version: 7.0.0 + resolution: "node-addon-api@npm:7.0.0" + dependencies: + node-gyp: "npm:latest" + checksum: 10c0/3d5a15ee434e122b345e614db122a63f30194c298104c3d8a0fa9f68707abb278af27b45222602456a131890a59b4a92291ff5b4b7938ff282168e9ad1bf7103 + languageName: node + linkType: hard + +"node-gyp-build@npm:4.6.0": + version: 4.6.0 + resolution: "node-gyp-build@npm:4.6.0" + bin: + node-gyp-build: bin.js + node-gyp-build-optional: optional.js + node-gyp-build-test: build-test.js + checksum: 10c0/147add65942acd3cf641d11d9becd030128c7298a5b4aec4ebf3ad4afcc3d0298ad2562afba3e7b2bf70160c5e2e82235e3bc043ff9c52dc68bdd36c856764fe + languageName: node + linkType: hard + +"node-gyp@npm:latest": + version: 11.0.0 + resolution: "node-gyp@npm:11.0.0" + dependencies: + env-paths: "npm:^2.2.0" + exponential-backoff: "npm:^3.1.1" + glob: "npm:^10.3.10" + graceful-fs: "npm:^4.2.6" + make-fetch-happen: "npm:^14.0.3" + nopt: "npm:^8.0.0" + proc-log: "npm:^5.0.0" + semver: "npm:^7.3.5" + tar: "npm:^7.4.3" + which: "npm:^5.0.0" + bin: + node-gyp: bin/node-gyp.js + checksum: 10c0/a3b885bbee2d271f1def32ba2e30ffcf4562a3db33af06b8b365e053153e2dd2051b9945783c3c8e852d26a0f20f65b251c7e83361623383a99635c0280ee573 + languageName: node + linkType: hard + +"node-releases@npm:^2.0.19": + version: 2.0.19 + resolution: "node-releases@npm:2.0.19" + checksum: 10c0/52a0dbd25ccf545892670d1551690fe0facb6a471e15f2cfa1b20142a5b255b3aa254af5f59d6ecb69c2bec7390bc643c43aa63b13bf5e64b6075952e716b1aa + languageName: node + linkType: hard + +"nopt@npm:^8.0.0": + version: 8.0.0 + resolution: "nopt@npm:8.0.0" + dependencies: + abbrev: "npm:^2.0.0" + bin: + nopt: bin/nopt.js + checksum: 10c0/19cb986f79abaca2d0f0b560021da7b32ee6fcc3de48f3eaeb0c324d36755c17754f886a754c091f01f740c17caf7d6aea8237b7fbaf39f476ae5e30a249f18f + languageName: node + linkType: hard + +"npm-run-path@npm:^5.1.0": + version: 5.3.0 + resolution: "npm-run-path@npm:5.3.0" + dependencies: + path-key: "npm:^4.0.0" + checksum: 10c0/124df74820c40c2eb9a8612a254ea1d557ddfab1581c3e751f825e3e366d9f00b0d76a3c94ecd8398e7f3eee193018622677e95816e8491f0797b21e30b2deba + languageName: node + linkType: hard + +"onetime@npm:^6.0.0": + version: 6.0.0 + resolution: "onetime@npm:6.0.0" + dependencies: + mimic-fn: "npm:^4.0.0" + checksum: 10c0/4eef7c6abfef697dd4479345a4100c382d73c149d2d56170a54a07418c50816937ad09500e1ed1e79d235989d073a9bade8557122aee24f0576ecde0f392bb6c + languageName: node + linkType: hard + +"onetime@npm:^7.0.0": + version: 7.0.0 + resolution: "onetime@npm:7.0.0" + dependencies: + mimic-function: "npm:^5.0.0" + checksum: 10c0/5cb9179d74b63f52a196a2e7037ba2b9a893245a5532d3f44360012005c9cadb60851d56716ebff18a6f47129dab7168022445df47c2aff3b276d92585ed1221 + languageName: node + linkType: hard + +"optionator@npm:^0.9.3": + version: 0.9.4 + resolution: "optionator@npm:0.9.4" + dependencies: + deep-is: "npm:^0.1.3" + fast-levenshtein: "npm:^2.0.6" + levn: "npm:^0.4.1" + prelude-ls: "npm:^1.2.1" + type-check: "npm:^0.4.0" + word-wrap: "npm:^1.2.5" + checksum: 10c0/4afb687a059ee65b61df74dfe87d8d6815cd6883cb8b3d5883a910df72d0f5d029821f37025e4bccf4048873dbdb09acc6d303d27b8f76b1a80dd5a7d5334675 + languageName: node + linkType: hard + +"osc@npm:^2.4.5": + version: 2.4.5 + resolution: "osc@npm:2.4.5" + dependencies: + long: "npm:4.0.0" + serialport: "npm:12.0.0" + slip: "npm:1.0.2" + wolfy87-eventemitter: "npm:5.2.9" + ws: "npm:8.18.0" + dependenciesMeta: + serialport: + optional: true + checksum: 10c0/745be9a71bb2d784046b08db5a255634835851c635d3ed97c26949b823b970ded55efc0a832a706e488ee3829ea17a0edbce8eb002223cbe0bc30c5d4f701e84 + languageName: node + linkType: hard + +"p-finally@npm:^1.0.0": + version: 1.0.0 + resolution: "p-finally@npm:1.0.0" + checksum: 10c0/6b8552339a71fe7bd424d01d8451eea92d379a711fc62f6b2fe64cad8a472c7259a236c9a22b4733abca0b5666ad503cb497792a0478c5af31ded793d00937e7 + languageName: node + linkType: hard + +"p-limit@npm:^2.2.0": + version: 2.3.0 + resolution: "p-limit@npm:2.3.0" + dependencies: + p-try: "npm:^2.0.0" + checksum: 10c0/8da01ac53efe6a627080fafc127c873da40c18d87b3f5d5492d465bb85ec7207e153948df6b9cbaeb130be70152f874229b8242ee2be84c0794082510af97f12 + languageName: node + linkType: hard + +"p-limit@npm:^3.0.2": + version: 3.1.0 + resolution: "p-limit@npm:3.1.0" + dependencies: + yocto-queue: "npm:^0.1.0" + checksum: 10c0/9db675949dbdc9c3763c89e748d0ef8bdad0afbb24d49ceaf4c46c02c77d30db4e0652ed36d0a0a7a95154335fab810d95c86153105bb73b3a90448e2bb14e1a + languageName: node + linkType: hard + +"p-limit@npm:^4.0.0": + version: 4.0.0 + resolution: "p-limit@npm:4.0.0" + dependencies: + yocto-queue: "npm:^1.0.0" + checksum: 10c0/a56af34a77f8df2ff61ddfb29431044557fcbcb7642d5a3233143ebba805fc7306ac1d448de724352861cb99de934bc9ab74f0d16fe6a5460bdbdf938de875ad + languageName: node + linkType: hard + +"p-locate@npm:^4.1.0": + version: 4.1.0 + resolution: "p-locate@npm:4.1.0" + dependencies: + p-limit: "npm:^2.2.0" + checksum: 10c0/1b476ad69ad7f6059744f343b26d51ce091508935c1dbb80c4e0a2f397ffce0ca3a1f9f5cd3c7ce19d7929a09719d5c65fe70d8ee289c3f267cd36f2881813e9 + languageName: node + linkType: hard + +"p-locate@npm:^5.0.0": + version: 5.0.0 + resolution: "p-locate@npm:5.0.0" + dependencies: + p-limit: "npm:^3.0.2" + checksum: 10c0/2290d627ab7903b8b70d11d384fee714b797f6040d9278932754a6860845c4d3190603a0772a663c8cb5a7b21d1b16acb3a6487ebcafa9773094edc3dfe6009a + languageName: node + linkType: hard + +"p-locate@npm:^6.0.0": + version: 6.0.0 + resolution: "p-locate@npm:6.0.0" + dependencies: + p-limit: "npm:^4.0.0" + checksum: 10c0/d72fa2f41adce59c198270aa4d3c832536c87a1806e0f69dffb7c1a7ca998fb053915ca833d90f166a8c082d3859eabfed95f01698a3214c20df6bb8de046312 + languageName: node + linkType: hard + +"p-map@npm:^7.0.2": + version: 7.0.3 + resolution: "p-map@npm:7.0.3" + checksum: 10c0/46091610da2b38ce47bcd1d8b4835a6fa4e832848a6682cf1652bc93915770f4617afc844c10a77d1b3e56d2472bb2d5622353fa3ead01a7f42b04fc8e744a5c + languageName: node + linkType: hard + +"p-queue@npm:^6.6.2": + version: 6.6.2 + resolution: "p-queue@npm:6.6.2" + dependencies: + eventemitter3: "npm:^4.0.4" + p-timeout: "npm:^3.2.0" + checksum: 10c0/5739ecf5806bbeadf8e463793d5e3004d08bb3f6177bd1a44a005da8fd81bb90f80e4633e1fb6f1dfd35ee663a5c0229abe26aebb36f547ad5a858347c7b0d3e + languageName: node + linkType: hard + +"p-queue@npm:^8.0.1": + version: 8.0.1 + resolution: "p-queue@npm:8.0.1" + dependencies: + eventemitter3: "npm:^5.0.1" + p-timeout: "npm:^6.1.2" + checksum: 10c0/fe185bc8bbd32d17a5f6dba090077b1bb326b008b4ec9b0646c57a32a6984035aa8ece909a6d0de7f6c4640296dc288197f430e7394cdc76a26d862339494616 + languageName: node + linkType: hard + +"p-timeout@npm:^3.2.0": + version: 3.2.0 + resolution: "p-timeout@npm:3.2.0" + dependencies: + p-finally: "npm:^1.0.0" + checksum: 10c0/524b393711a6ba8e1d48137c5924749f29c93d70b671e6db761afa784726572ca06149c715632da8f70c090073afb2af1c05730303f915604fd38ee207b70a61 + languageName: node + linkType: hard + +"p-timeout@npm:^4.1.0": + version: 4.1.0 + resolution: "p-timeout@npm:4.1.0" + checksum: 10c0/25aaf13ae9ebfff4ab45591f6647f3bee1ecede073c367175fb0157c27efb170cfb51259a4d2e9118d2ca595453bc885f086ad0ca7476d1c26cee3d13a7c9f6d + languageName: node + linkType: hard + +"p-timeout@npm:^6.1.2": + version: 6.1.3 + resolution: "p-timeout@npm:6.1.3" + checksum: 10c0/6dcd1efc1a18afac08dd4f8e09797bbe635110e597d27026b478f884b867616871499427643a6b2e11f0404b2936d17db69da2b5e58d5fe99e1fac80a53f0250 + languageName: node + linkType: hard + +"p-try@npm:^2.0.0": + version: 2.2.0 + resolution: "p-try@npm:2.2.0" + checksum: 10c0/c36c19907734c904b16994e6535b02c36c2224d433e01a2f1ab777237f4d86e6289fd5fd464850491e940379d4606ed850c03e0f9ab600b0ebddb511312e177f + languageName: node + linkType: hard + +"package-json-from-dist@npm:^1.0.0": + version: 1.0.1 + resolution: "package-json-from-dist@npm:1.0.1" + checksum: 10c0/62ba2785eb655fec084a257af34dbe24292ab74516d6aecef97ef72d4897310bc6898f6c85b5cd22770eaa1ce60d55a0230e150fb6a966e3ecd6c511e23d164b + languageName: node + linkType: hard + +"parent-module@npm:^1.0.0": + version: 1.0.1 + resolution: "parent-module@npm:1.0.1" + dependencies: + callsites: "npm:^3.0.0" + checksum: 10c0/c63d6e80000d4babd11978e0d3fee386ca7752a02b035fd2435960ffaa7219dc42146f07069fb65e6e8bf1caef89daf9af7535a39bddf354d78bf50d8294f556 + languageName: node + linkType: hard + +"parse-author@npm:^2.0.0": + version: 2.0.0 + resolution: "parse-author@npm:2.0.0" + dependencies: + author-regex: "npm:^1.0.0" + checksum: 10c0/8b4c19523588a4271c89f64e8167be7c80b4765059c865d38a996c37d080c7e5123e3e2d07d47290891628ad27f4dfb692e3b61156c52fcc0af6cdce3ed57afd + languageName: node + linkType: hard + +"path-exists@npm:^4.0.0": + version: 4.0.0 + resolution: "path-exists@npm:4.0.0" + checksum: 10c0/8c0bd3f5238188197dc78dced15207a4716c51cc4e3624c44fc97acf69558f5ebb9a2afff486fe1b4ee148e0c133e96c5e11a9aa5c48a3006e3467da070e5e1b + languageName: node + linkType: hard + +"path-exists@npm:^5.0.0": + version: 5.0.0 + resolution: "path-exists@npm:5.0.0" + checksum: 10c0/b170f3060b31604cde93eefdb7392b89d832dfbc1bed717c9718cbe0f230c1669b7e75f87e19901da2250b84d092989a0f9e44d2ef41deb09aa3ad28e691a40a + languageName: node + linkType: hard + +"path-key@npm:^3.1.0": + version: 3.1.1 + resolution: "path-key@npm:3.1.1" + checksum: 10c0/748c43efd5a569c039d7a00a03b58eecd1d75f3999f5a28303d75f521288df4823bc057d8784eb72358b2895a05f29a070bc9f1f17d28226cc4e62494cc58c4c + languageName: node + linkType: hard + +"path-key@npm:^4.0.0": + version: 4.0.0 + resolution: "path-key@npm:4.0.0" + checksum: 10c0/794efeef32863a65ac312f3c0b0a99f921f3e827ff63afa5cb09a377e202c262b671f7b3832a4e64731003fa94af0263713962d317b9887bd1e0c48a342efba3 + languageName: node + linkType: hard + +"path-parse@npm:^1.0.7": + version: 1.0.7 + resolution: "path-parse@npm:1.0.7" + checksum: 10c0/11ce261f9d294cc7a58d6a574b7f1b935842355ec66fba3c3fd79e0f036462eaf07d0aa95bb74ff432f9afef97ce1926c720988c6a7451d8a584930ae7de86e1 + languageName: node + linkType: hard + +"path-scurry@npm:^1.11.1": + version: 1.11.1 + resolution: "path-scurry@npm:1.11.1" + dependencies: + lru-cache: "npm:^10.2.0" + minipass: "npm:^5.0.0 || ^6.0.2 || ^7.0.0" + checksum: 10c0/32a13711a2a505616ae1cc1b5076801e453e7aae6ac40ab55b388bb91b9d0547a52f5aaceff710ea400205f18691120d4431e520afbe4266b836fadede15872d + languageName: node + linkType: hard + +"path-scurry@npm:^2.0.0": + version: 2.0.0 + resolution: "path-scurry@npm:2.0.0" + dependencies: + lru-cache: "npm:^11.0.0" + minipass: "npm:^7.1.2" + checksum: 10c0/3da4adedaa8e7ef8d6dc4f35a0ff8f05a9b4d8365f2b28047752b62d4c1ad73eec21e37b1579ef2d075920157856a3b52ae8309c480a6f1a8bbe06ff8e52b33c + languageName: node + linkType: hard + +"picocolors@npm:^1.1.0": + version: 1.1.1 + resolution: "picocolors@npm:1.1.1" + checksum: 10c0/e2e3e8170ab9d7c7421969adaa7e1b31434f789afb9b3f115f6b96d91945041ac3ceb02e9ec6fe6510ff036bcc0bf91e69a1772edc0b707e12b19c0f2d6bcf58 + languageName: node + linkType: hard + +"picomatch@npm:^2.3.1": + version: 2.3.1 + resolution: "picomatch@npm:2.3.1" + checksum: 10c0/26c02b8d06f03206fc2ab8d16f19960f2ff9e81a658f831ecb656d8f17d9edc799e8364b1f4a7873e89d9702dff96204be0fa26fe4181f6843f040f819dac4be + languageName: node + linkType: hard + +"pidtree@npm:~0.6.0": + version: 0.6.0 + resolution: "pidtree@npm:0.6.0" + bin: + pidtree: bin/pidtree.js + checksum: 10c0/0829ec4e9209e230f74ebf4265f5ccc9ebfb488334b525cb13f86ff801dca44b362c41252cd43ae4d7653a10a5c6ab3be39d2c79064d6895e0d78dc50a5ed6e9 + languageName: node + linkType: hard + +"pkg-dir@npm:^4.2.0": + version: 4.2.0 + resolution: "pkg-dir@npm:4.2.0" + dependencies: + find-up: "npm:^4.0.0" + checksum: 10c0/c56bda7769e04907a88423feb320babaed0711af8c436ce3e56763ab1021ba107c7b0cafb11cde7529f669cfc22bffcaebffb573645cbd63842ea9fb17cd7728 + languageName: node + linkType: hard + +"prelude-ls@npm:^1.2.1": + version: 1.2.1 + resolution: "prelude-ls@npm:1.2.1" + checksum: 10c0/b00d617431e7886c520a6f498a2e14c75ec58f6d93ba48c3b639cf241b54232d90daa05d83a9e9b9fef6baa63cb7e1e4602c2372fea5bc169668401eb127d0cd + languageName: node + linkType: hard + +"prettier-linter-helpers@npm:^1.0.0": + version: 1.0.0 + resolution: "prettier-linter-helpers@npm:1.0.0" + dependencies: + fast-diff: "npm:^1.1.2" + checksum: 10c0/81e0027d731b7b3697ccd2129470ed9913ecb111e4ec175a12f0fcfab0096516373bf0af2fef132af50cafb0a905b74ff57996d615f59512bb9ac7378fcc64ab + languageName: node + linkType: hard + +"prettier@npm:^3.4.2": + version: 3.4.2 + resolution: "prettier@npm:3.4.2" + bin: + prettier: bin/prettier.cjs + checksum: 10c0/99e076a26ed0aba4ebc043880d0f08bbb8c59a4c6641cdee6cdadf2205bdd87aa1d7823f50c3aea41e015e99878d37c58d7b5f0e663bba0ef047f94e36b96446 + languageName: node + linkType: hard + +"proc-log@npm:^5.0.0": + version: 5.0.0 + resolution: "proc-log@npm:5.0.0" + checksum: 10c0/bbe5edb944b0ad63387a1d5b1911ae93e05ce8d0f60de1035b218cdcceedfe39dbd2c697853355b70f1a090f8f58fe90da487c85216bf9671f9499d1a897e9e3 + languageName: node + linkType: hard + +"promise-retry@npm:^2.0.1": + version: 2.0.1 + resolution: "promise-retry@npm:2.0.1" + dependencies: + err-code: "npm:^2.0.2" + retry: "npm:^0.12.0" + checksum: 10c0/9c7045a1a2928094b5b9b15336dcd2a7b1c052f674550df63cc3f36cd44028e5080448175b6f6ca32b642de81150f5e7b1a98b728f15cb069f2dd60ac2616b96 + languageName: node + linkType: hard + +"punycode@npm:^2.1.0": + version: 2.3.1 + resolution: "punycode@npm:2.3.1" + checksum: 10c0/14f76a8206bc3464f794fb2e3d3cc665ae416c01893ad7a02b23766eb07159144ee612ad67af5e84fa4479ccfe67678c4feb126b0485651b302babf66f04f9e9 + languageName: node + linkType: hard + +"queue-microtask@npm:^1.2.2": + version: 1.2.3 + resolution: "queue-microtask@npm:1.2.3" + checksum: 10c0/900a93d3cdae3acd7d16f642c29a642aea32c2026446151f0778c62ac089d4b8e6c986811076e1ae180a694cedf077d453a11b58ff0a865629a4f82ab558e102 + languageName: node + linkType: hard + +"randombytes@npm:^2.1.0": + version: 2.1.0 + resolution: "randombytes@npm:2.1.0" + dependencies: + safe-buffer: "npm:^5.1.0" + checksum: 10c0/50395efda7a8c94f5dffab564f9ff89736064d32addf0cc7e8bf5e4166f09f8ded7a0849ca6c2d2a59478f7d90f78f20d8048bca3cdf8be09d8e8a10790388f3 + languageName: node + linkType: hard + +"rechoir@npm:^0.8.0": + version: 0.8.0 + resolution: "rechoir@npm:0.8.0" + dependencies: + resolve: "npm:^1.20.0" + checksum: 10c0/1a30074124a22abbd5d44d802dac26407fa72a0a95f162aa5504ba8246bc5452f8b1a027b154d9bdbabcd8764920ff9333d934c46a8f17479c8912e92332f3ff + languageName: node + linkType: hard + +"require-from-string@npm:^2.0.2": + version: 2.0.2 + resolution: "require-from-string@npm:2.0.2" + checksum: 10c0/aaa267e0c5b022fc5fd4eef49d8285086b15f2a1c54b28240fdf03599cbd9c26049fee3eab894f2e1f6ca65e513b030a7c264201e3f005601e80c49fb2937ce2 + languageName: node + linkType: hard + +"resolve-cwd@npm:^3.0.0": + version: 3.0.0 + resolution: "resolve-cwd@npm:3.0.0" + dependencies: + resolve-from: "npm:^5.0.0" + checksum: 10c0/e608a3ebd15356264653c32d7ecbc8fd702f94c6703ea4ac2fb81d9c359180cba0ae2e6b71faa446631ed6145454d5a56b227efc33a2d40638ac13f8beb20ee4 + languageName: node + linkType: hard + +"resolve-from@npm:^4.0.0": + version: 4.0.0 + resolution: "resolve-from@npm:4.0.0" + checksum: 10c0/8408eec31a3112ef96e3746c37be7d64020cda07c03a920f5024e77290a218ea758b26ca9529fd7b1ad283947f34b2291c1c0f6aa0ed34acfdda9c6014c8d190 + languageName: node + linkType: hard + +"resolve-from@npm:^5.0.0": + version: 5.0.0 + resolution: "resolve-from@npm:5.0.0" + checksum: 10c0/b21cb7f1fb746de8107b9febab60095187781137fd803e6a59a76d421444b1531b641bba5857f5dc011974d8a5c635d61cec49e6bd3b7fc20e01f0fafc4efbf2 + languageName: node + linkType: hard + +"resolve-pkg-maps@npm:^1.0.0": + version: 1.0.0 + resolution: "resolve-pkg-maps@npm:1.0.0" + checksum: 10c0/fb8f7bbe2ca281a73b7ef423a1cbc786fb244bd7a95cbe5c3fba25b27d327150beca8ba02f622baea65919a57e061eb5005204daa5f93ed590d9b77463a567ab + languageName: node + linkType: hard + +"resolve@npm:^1.20.0": + version: 1.22.10 + resolution: "resolve@npm:1.22.10" + dependencies: + is-core-module: "npm:^2.16.0" + path-parse: "npm:^1.0.7" + supports-preserve-symlinks-flag: "npm:^1.0.0" + bin: + resolve: bin/resolve + checksum: 10c0/8967e1f4e2cc40f79b7e080b4582b9a8c5ee36ffb46041dccb20e6461161adf69f843b43067b4a375de926a2cd669157e29a29578191def399dd5ef89a1b5203 + languageName: node + linkType: hard + +"resolve@patch:resolve@npm%3A^1.20.0#optional!builtin": + version: 1.22.10 + resolution: "resolve@patch:resolve@npm%3A1.22.10#optional!builtin::version=1.22.10&hash=c3c19d" + dependencies: + is-core-module: "npm:^2.16.0" + path-parse: "npm:^1.0.7" + supports-preserve-symlinks-flag: "npm:^1.0.0" + bin: + resolve: bin/resolve + checksum: 10c0/52a4e505bbfc7925ac8f4cd91fd8c4e096b6a89728b9f46861d3b405ac9a1ccf4dcbf8befb4e89a2e11370dacd0160918163885cbc669369590f2f31f4c58939 + languageName: node + linkType: hard + +"restore-cursor@npm:^5.0.0": + version: 5.1.0 + resolution: "restore-cursor@npm:5.1.0" + dependencies: + onetime: "npm:^7.0.0" + signal-exit: "npm:^4.1.0" + checksum: 10c0/c2ba89131eea791d1b25205bdfdc86699767e2b88dee2a590b1a6caa51737deac8bad0260a5ded2f7c074b7db2f3a626bcf1fcf3cdf35974cbeea5e2e6764f60 + languageName: node + linkType: hard + +"retry@npm:^0.12.0": + version: 0.12.0 + resolution: "retry@npm:0.12.0" + checksum: 10c0/59933e8501727ba13ad73ef4a04d5280b3717fd650408460c987392efe9d7be2040778ed8ebe933c5cbd63da3dcc37919c141ef8af0a54a6e4fca5a2af177bfe + languageName: node + linkType: hard + +"reusify@npm:^1.0.4": + version: 1.0.4 + resolution: "reusify@npm:1.0.4" + checksum: 10c0/c19ef26e4e188f408922c46f7ff480d38e8dfc55d448310dfb518736b23ed2c4f547fb64a6ed5bdba92cd7e7ddc889d36ff78f794816d5e71498d645ef476107 + languageName: node + linkType: hard + +"rfdc@npm:^1.4.1": + version: 1.4.1 + resolution: "rfdc@npm:1.4.1" + checksum: 10c0/4614e4292356cafade0b6031527eea9bc90f2372a22c012313be1dcc69a3b90c7338158b414539be863fa95bfcb2ddcd0587be696841af4e6679d85e62c060c7 + languageName: node + linkType: hard + +"rimraf@npm:^5.0.5": + version: 5.0.10 + resolution: "rimraf@npm:5.0.10" + dependencies: + glob: "npm:^10.3.7" + bin: + rimraf: dist/esm/bin.mjs + checksum: 10c0/7da4fd0e15118ee05b918359462cfa1e7fe4b1228c7765195a45b55576e8c15b95db513b8466ec89129666f4af45ad978a3057a02139afba1a63512a2d9644cc + languageName: node + linkType: hard + +"rimraf@npm:^6.0.1": + version: 6.0.1 + resolution: "rimraf@npm:6.0.1" + dependencies: + glob: "npm:^11.0.0" + package-json-from-dist: "npm:^1.0.0" + bin: + rimraf: dist/esm/bin.mjs + checksum: 10c0/b30b6b072771f0d1e73b4ca5f37bb2944ee09375be9db5f558fcd3310000d29dfcfa93cf7734d75295ad5a7486dc8e40f63089ced1722a664539ffc0c3ece8c6 + languageName: node + linkType: hard + +"run-parallel@npm:^1.1.9": + version: 1.2.0 + resolution: "run-parallel@npm:1.2.0" + dependencies: + queue-microtask: "npm:^1.2.2" + checksum: 10c0/200b5ab25b5b8b7113f9901bfe3afc347e19bb7475b267d55ad0eb86a62a46d77510cb0f232507c9e5d497ebda569a08a9867d0d14f57a82ad5564d991588b39 + languageName: node + linkType: hard + +"safe-buffer@npm:^5.1.0": + version: 5.2.1 + resolution: "safe-buffer@npm:5.2.1" + checksum: 10c0/6501914237c0a86e9675d4e51d89ca3c21ffd6a31642efeba25ad65720bce6921c9e7e974e5be91a786b25aa058b5303285d3c15dbabf983a919f5f630d349f3 + languageName: node + linkType: hard + +"safer-buffer@npm:>= 2.1.2 < 3.0.0": + version: 2.1.2 + resolution: "safer-buffer@npm:2.1.2" + checksum: 10c0/7e3c8b2e88a1841c9671094bbaeebd94448111dd90a81a1f606f3f67708a6ec57763b3b47f06da09fc6054193e0e6709e77325415dc8422b04497a8070fa02d4 + languageName: node + linkType: hard + +"schema-utils@npm:^3.2.0": + version: 3.3.0 + resolution: "schema-utils@npm:3.3.0" + dependencies: + "@types/json-schema": "npm:^7.0.8" + ajv: "npm:^6.12.5" + ajv-keywords: "npm:^3.5.2" + checksum: 10c0/fafdbde91ad8aa1316bc543d4b61e65ea86970aebbfb750bfb6d8a6c287a23e415e0e926c2498696b242f63af1aab8e585252637fabe811fd37b604351da6500 + languageName: node + linkType: hard + +"schema-utils@npm:^4.3.0": + version: 4.3.0 + resolution: "schema-utils@npm:4.3.0" + dependencies: + "@types/json-schema": "npm:^7.0.9" + ajv: "npm:^8.9.0" + ajv-formats: "npm:^2.1.1" + ajv-keywords: "npm:^5.1.0" + checksum: 10c0/c23f0fa73ef71a01d4a2bb7af4c91e0d356ec640e071aa2d06ea5e67f042962bb7ac7c29a60a295bb0125878801bc3209197a2b8a833dd25bd38e37c3ed21427 + languageName: node + linkType: hard + +"semver@npm:^7.3.5, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.6.3": + version: 7.6.3 + resolution: "semver@npm:7.6.3" + bin: + semver: bin/semver.js + checksum: 10c0/88f33e148b210c153873cb08cfe1e281d518aaa9a666d4d148add6560db5cd3c582f3a08ccb91f38d5f379ead256da9931234ed122057f40bb5766e65e58adaf + languageName: node + linkType: hard + +"serialize-javascript@npm:^6.0.2": + version: 6.0.2 + resolution: "serialize-javascript@npm:6.0.2" + dependencies: + randombytes: "npm:^2.1.0" + checksum: 10c0/2dd09ef4b65a1289ba24a788b1423a035581bef60817bea1f01eda8e3bda623f86357665fe7ac1b50f6d4f583f97db9615b3f07b2a2e8cbcb75033965f771dd2 + languageName: node + linkType: hard + +"serialport@npm:12.0.0": + version: 12.0.0 + resolution: "serialport@npm:12.0.0" + dependencies: + "@serialport/binding-mock": "npm:10.2.2" + "@serialport/bindings-cpp": "npm:12.0.1" + "@serialport/parser-byte-length": "npm:12.0.0" + "@serialport/parser-cctalk": "npm:12.0.0" + "@serialport/parser-delimiter": "npm:12.0.0" + "@serialport/parser-inter-byte-timeout": "npm:12.0.0" + "@serialport/parser-packet-length": "npm:12.0.0" + "@serialport/parser-readline": "npm:12.0.0" + "@serialport/parser-ready": "npm:12.0.0" + "@serialport/parser-regex": "npm:12.0.0" + "@serialport/parser-slip-encoder": "npm:12.0.0" + "@serialport/parser-spacepacket": "npm:12.0.0" + "@serialport/stream": "npm:12.0.0" + debug: "npm:4.3.4" + checksum: 10c0/ad282898055947dd89ebb7ac7433d2961b0bba311a1c7009f2eda1aac9bfd5b1f59e50b30df8e1d3c20c323f05aefd134028bdab1b8f9dde3cbc46b32e9b8765 + languageName: node + linkType: hard + +"shallow-clone@npm:^3.0.0": + version: 3.0.1 + resolution: "shallow-clone@npm:3.0.1" + dependencies: + kind-of: "npm:^6.0.2" + checksum: 10c0/7bab09613a1b9f480c85a9823aebec533015579fa055ba6634aa56ba1f984380670eaf33b8217502931872aa1401c9fcadaa15f9f604d631536df475b05bcf1e + languageName: node + linkType: hard + +"shebang-command@npm:^2.0.0": + version: 2.0.0 + resolution: "shebang-command@npm:2.0.0" + dependencies: + shebang-regex: "npm:^3.0.0" + checksum: 10c0/a41692e7d89a553ef21d324a5cceb5f686d1f3c040759c50aab69688634688c5c327f26f3ecf7001ebfd78c01f3c7c0a11a7c8bfd0a8bc9f6240d4f40b224e4e + languageName: node + linkType: hard + +"shebang-regex@npm:^3.0.0": + version: 3.0.0 + resolution: "shebang-regex@npm:3.0.0" + checksum: 10c0/1dbed0726dd0e1152a92696c76c7f06084eb32a90f0528d11acd764043aacf76994b2fb30aa1291a21bd019d6699164d048286309a278855ee7bec06cf6fb690 + languageName: node + linkType: hard + +"signal-exit@npm:^4.0.1, signal-exit@npm:^4.1.0": + version: 4.1.0 + resolution: "signal-exit@npm:4.1.0" + checksum: 10c0/41602dce540e46d599edba9d9860193398d135f7ff72cab629db5171516cfae628d21e7bfccde1bbfdf11c48726bc2a6d1a8fb8701125852fbfda7cf19c6aa83 + languageName: node + linkType: hard + +"slice-ansi@npm:^5.0.0": + version: 5.0.0 + resolution: "slice-ansi@npm:5.0.0" + dependencies: + ansi-styles: "npm:^6.0.0" + is-fullwidth-code-point: "npm:^4.0.0" + checksum: 10c0/2d4d40b2a9d5cf4e8caae3f698fe24ae31a4d778701724f578e984dcb485ec8c49f0c04dab59c401821e80fcdfe89cace9c66693b0244e40ec485d72e543914f + languageName: node + linkType: hard + +"slice-ansi@npm:^7.1.0": + version: 7.1.0 + resolution: "slice-ansi@npm:7.1.0" + dependencies: + ansi-styles: "npm:^6.2.1" + is-fullwidth-code-point: "npm:^5.0.0" + checksum: 10c0/631c971d4abf56cf880f034d43fcc44ff883624867bf11ecbd538c47343911d734a4656d7bc02362b40b89d765652a7f935595441e519b59e2ad3f4d5d6fe7ca + languageName: node + linkType: hard + +"slip@npm:1.0.2": + version: 1.0.2 + resolution: "slip@npm:1.0.2" + checksum: 10c0/f1a235ec2960c9c769c149dd6f0c21b74c7abf7f1f8b249cbe1370bae75e20f60faee25b4a951407cedd355ebab17038323f5fc800f6bab188877472566fe81a + languageName: node + linkType: hard + +"smart-buffer@npm:^4.2.0": + version: 4.2.0 + resolution: "smart-buffer@npm:4.2.0" + checksum: 10c0/a16775323e1404dd43fabafe7460be13a471e021637bc7889468eb45ce6a6b207261f454e4e530a19500cc962c4cc5348583520843b363f4193cee5c00e1e539 + languageName: node + linkType: hard + +"socks-proxy-agent@npm:^8.0.3": + version: 8.0.5 + resolution: "socks-proxy-agent@npm:8.0.5" + dependencies: + agent-base: "npm:^7.1.2" + debug: "npm:^4.3.4" + socks: "npm:^2.8.3" + checksum: 10c0/5d2c6cecba6821389aabf18728325730504bf9bb1d9e342e7987a5d13badd7a98838cc9a55b8ed3cb866ad37cc23e1086f09c4d72d93105ce9dfe76330e9d2a6 + languageName: node + linkType: hard + +"socks@npm:^2.8.3": + version: 2.8.3 + resolution: "socks@npm:2.8.3" + dependencies: + ip-address: "npm:^9.0.5" + smart-buffer: "npm:^4.2.0" + checksum: 10c0/d54a52bf9325165770b674a67241143a3d8b4e4c8884560c4e0e078aace2a728dffc7f70150660f51b85797c4e1a3b82f9b7aa25e0a0ceae1a243365da5c51a7 + languageName: node + linkType: hard + +"source-map-support@npm:~0.5.20": + version: 0.5.21 + resolution: "source-map-support@npm:0.5.21" + dependencies: + buffer-from: "npm:^1.0.0" + source-map: "npm:^0.6.0" + checksum: 10c0/9ee09942f415e0f721d6daad3917ec1516af746a8120bba7bb56278707a37f1eb8642bde456e98454b8a885023af81a16e646869975f06afc1a711fb90484e7d + languageName: node + linkType: hard + +"source-map@npm:^0.6.0": + version: 0.6.1 + resolution: "source-map@npm:0.6.1" + checksum: 10c0/ab55398007c5e5532957cb0beee2368529618ac0ab372d789806f5718123cc4367d57de3904b4e6a4170eb5a0b0f41373066d02ca0735a0c4d75c7d328d3e011 + languageName: node + linkType: hard + +"sprintf-js@npm:^1.1.3": + version: 1.1.3 + resolution: "sprintf-js@npm:1.1.3" + checksum: 10c0/09270dc4f30d479e666aee820eacd9e464215cdff53848b443964202bf4051490538e5dd1b42e1a65cf7296916ca17640aebf63dae9812749c7542ee5f288dec + languageName: node + linkType: hard + +"ssri@npm:^12.0.0": + version: 12.0.0 + resolution: "ssri@npm:12.0.0" + dependencies: + minipass: "npm:^7.0.3" + checksum: 10c0/caddd5f544b2006e88fa6b0124d8d7b28208b83c72d7672d5ade44d794525d23b540f3396108c4eb9280dcb7c01f0bef50682f5b4b2c34291f7c5e211fd1417d + languageName: node + linkType: hard + +"string-argv@npm:~0.3.2": + version: 0.3.2 + resolution: "string-argv@npm:0.3.2" + checksum: 10c0/75c02a83759ad1722e040b86823909d9a2fc75d15dd71ec4b537c3560746e33b5f5a07f7332d1e3f88319909f82190843aa2f0a0d8c8d591ec08e93d5b8dec82 + languageName: node + linkType: hard + +"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0": + version: 4.2.3 + resolution: "string-width@npm:4.2.3" + dependencies: + emoji-regex: "npm:^8.0.0" + is-fullwidth-code-point: "npm:^3.0.0" + strip-ansi: "npm:^6.0.1" + checksum: 10c0/1e525e92e5eae0afd7454086eed9c818ee84374bb80328fc41217ae72ff5f065ef1c9d7f72da41de40c75fa8bb3dee63d92373fd492c84260a552c636392a47b + languageName: node + linkType: hard + +"string-width@npm:^5.0.1, string-width@npm:^5.1.2": + version: 5.1.2 + resolution: "string-width@npm:5.1.2" + dependencies: + eastasianwidth: "npm:^0.2.0" + emoji-regex: "npm:^9.2.2" + strip-ansi: "npm:^7.0.1" + checksum: 10c0/ab9c4264443d35b8b923cbdd513a089a60de339216d3b0ed3be3ba57d6880e1a192b70ae17225f764d7adbf5994e9bb8df253a944736c15a0240eff553c678ca + languageName: node + linkType: hard + +"string-width@npm:^7.0.0": + version: 7.2.0 + resolution: "string-width@npm:7.2.0" + dependencies: + emoji-regex: "npm:^10.3.0" + get-east-asian-width: "npm:^1.0.0" + strip-ansi: "npm:^7.1.0" + checksum: 10c0/eb0430dd43f3199c7a46dcbf7a0b34539c76fe3aa62763d0b0655acdcbdf360b3f66f3d58ca25ba0205f42ea3491fa00f09426d3b7d3040e506878fc7664c9b9 + languageName: node + linkType: hard + +"strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": + version: 6.0.1 + resolution: "strip-ansi@npm:6.0.1" + dependencies: + ansi-regex: "npm:^5.0.1" + checksum: 10c0/1ae5f212a126fe5b167707f716942490e3933085a5ff6c008ab97ab2f272c8025d3aa218b7bd6ab25729ca20cc81cddb252102f8751e13482a5199e873680952 + languageName: node + linkType: hard + +"strip-ansi@npm:^7.0.1, strip-ansi@npm:^7.1.0": + version: 7.1.0 + resolution: "strip-ansi@npm:7.1.0" + dependencies: + ansi-regex: "npm:^6.0.1" + checksum: 10c0/a198c3762e8832505328cbf9e8c8381de14a4fa50a4f9b2160138158ea88c0f5549fb50cb13c651c3088f47e63a108b34622ec18c0499b6c8c3a5ddf6b305ac4 + languageName: node + linkType: hard + +"strip-final-newline@npm:^3.0.0": + version: 3.0.0 + resolution: "strip-final-newline@npm:3.0.0" + checksum: 10c0/a771a17901427bac6293fd416db7577e2bc1c34a19d38351e9d5478c3c415f523f391003b42ed475f27e33a78233035df183525395f731d3bfb8cdcbd4da08ce + languageName: node + linkType: hard + +"strip-json-comments@npm:^3.1.1": + version: 3.1.1 + resolution: "strip-json-comments@npm:3.1.1" + checksum: 10c0/9681a6257b925a7fa0f285851c0e613cc934a50661fa7bb41ca9cbbff89686bb4a0ee366e6ecedc4daafd01e83eee0720111ab294366fe7c185e935475ebcecd + languageName: node + linkType: hard + +"supports-color@npm:^7.1.0": + version: 7.2.0 + resolution: "supports-color@npm:7.2.0" + dependencies: + has-flag: "npm:^4.0.0" + checksum: 10c0/afb4c88521b8b136b5f5f95160c98dee7243dc79d5432db7efc27efb219385bbc7d9427398e43dd6cc730a0f87d5085ce1652af7efbe391327bc0a7d0f7fc124 + languageName: node + linkType: hard + +"supports-color@npm:^8.0.0": + version: 8.1.1 + resolution: "supports-color@npm:8.1.1" + dependencies: + has-flag: "npm:^4.0.0" + checksum: 10c0/ea1d3c275dd604c974670f63943ed9bd83623edc102430c05adb8efc56ba492746b6e95386e7831b872ec3807fd89dd8eb43f735195f37b5ec343e4234cc7e89 + languageName: node + linkType: hard + +"supports-preserve-symlinks-flag@npm:^1.0.0": + version: 1.0.0 + resolution: "supports-preserve-symlinks-flag@npm:1.0.0" + checksum: 10c0/6c4032340701a9950865f7ae8ef38578d8d7053f5e10518076e6554a9381fa91bd9c6850193695c141f32b21f979c985db07265a758867bac95de05f7d8aeb39 + languageName: node + linkType: hard + +"synckit@npm:^0.9.1": + version: 0.9.2 + resolution: "synckit@npm:0.9.2" + dependencies: + "@pkgr/core": "npm:^0.1.0" + tslib: "npm:^2.6.2" + checksum: 10c0/e0c262817444e5b872708adb6f5ad37951ba33f6b2d1d4477d45db1f57573a784618ceed5e6614e0225db330632b1f6b95bb74d21e4d013e45ad4bde03d0cb59 + languageName: node + linkType: hard + +"tapable@npm:^2.1.1, tapable@npm:^2.2.0": + version: 2.2.1 + resolution: "tapable@npm:2.2.1" + checksum: 10c0/bc40e6efe1e554d075469cedaba69a30eeb373552aaf41caeaaa45bf56ffacc2674261b106245bd566b35d8f3329b52d838e851ee0a852120acae26e622925c9 + languageName: node + linkType: hard + +"tar@npm:^7.4.3": + version: 7.4.3 + resolution: "tar@npm:7.4.3" + dependencies: + "@isaacs/fs-minipass": "npm:^4.0.0" + chownr: "npm:^3.0.0" + minipass: "npm:^7.1.2" + minizlib: "npm:^3.0.1" + mkdirp: "npm:^3.0.1" + yallist: "npm:^5.0.0" + checksum: 10c0/d4679609bb2a9b48eeaf84632b6d844128d2412b95b6de07d53d8ee8baf4ca0857c9331dfa510390a0727b550fd543d4d1a10995ad86cdf078423fbb8d99831d + languageName: node + linkType: hard + +"terser-webpack-plugin@npm:^5.3.10": + version: 5.3.11 + resolution: "terser-webpack-plugin@npm:5.3.11" + dependencies: + "@jridgewell/trace-mapping": "npm:^0.3.25" + jest-worker: "npm:^27.4.5" + schema-utils: "npm:^4.3.0" + serialize-javascript: "npm:^6.0.2" + terser: "npm:^5.31.1" + peerDependencies: + webpack: ^5.1.0 + peerDependenciesMeta: + "@swc/core": + optional: true + esbuild: + optional: true + uglify-js: + optional: true + checksum: 10c0/4794274f445dc589f4c113c75a55ce51364ccf09bfe8a545cdb462e3f752bf300ea91f072fa28bbed291bbae03274da06fe4eca180e784fb8a43646aa7dbcaef + languageName: node + linkType: hard + +"terser@npm:^5.31.1": + version: 5.37.0 + resolution: "terser@npm:5.37.0" + dependencies: + "@jridgewell/source-map": "npm:^0.3.3" + acorn: "npm:^8.8.2" + commander: "npm:^2.20.0" + source-map-support: "npm:~0.5.20" + bin: + terser: bin/terser + checksum: 10c0/ff0dc79b0a0da821e7f5bf7a047eab6d04e70e88b62339a0f1d71117db3310e255f5c00738fa3b391f56c3571f800a00047720261ba04ced0241c1f9922199f4 + languageName: node + linkType: hard + +"to-regex-range@npm:^5.0.1": + version: 5.0.1 + resolution: "to-regex-range@npm:5.0.1" + dependencies: + is-number: "npm:^7.0.0" + checksum: 10c0/487988b0a19c654ff3e1961b87f471702e708fa8a8dd02a298ef16da7206692e8552a0250e8b3e8759270f62e9d8314616f6da274734d3b558b1fc7b7724e892 + languageName: node + linkType: hard + +"ts-api-utils@npm:^1.3.0": + version: 1.4.3 + resolution: "ts-api-utils@npm:1.4.3" + peerDependencies: + typescript: ">=4.2.0" + checksum: 10c0/e65dc6e7e8141140c23e1dc94984bf995d4f6801919c71d6dc27cf0cd51b100a91ffcfe5217626193e5bea9d46831e8586febdc7e172df3f1091a7384299e23a + languageName: node + linkType: hard + +"tslib@npm:^2.6.2, tslib@npm:^2.8.1": + version: 2.8.1 + resolution: "tslib@npm:2.8.1" + checksum: 10c0/9c4759110a19c53f992d9aae23aac5ced636e99887b51b9e61def52611732872ff7668757d4e4c61f19691e36f4da981cd9485e869b4a7408d689f6bf1f14e62 + languageName: node + linkType: hard + +"type-check@npm:^0.4.0, type-check@npm:~0.4.0": + version: 0.4.0 + resolution: "type-check@npm:0.4.0" + dependencies: + prelude-ls: "npm:^1.2.1" + checksum: 10c0/7b3fd0ed43891e2080bf0c5c504b418fbb3e5c7b9708d3d015037ba2e6323a28152ec163bcb65212741fa5d2022e3075ac3c76440dbd344c9035f818e8ecee58 + languageName: node + linkType: hard + +"type-fest@npm:^4.31.0": + version: 4.31.0 + resolution: "type-fest@npm:4.31.0" + checksum: 10c0/a5bb69e3b0f82e068af8c645ac3d50b1fa5c588ebc83735a6add4ef6dacf277bb3605801f66c72c069af20120ee7387a3ae6dd84e12c152f5982784c710b4051 + languageName: node + linkType: hard + +"typescript-eslint@npm:^8.18.1": + version: 8.18.2 + resolution: "typescript-eslint@npm:8.18.2" + dependencies: + "@typescript-eslint/eslint-plugin": "npm:8.18.2" + "@typescript-eslint/parser": "npm:8.18.2" + "@typescript-eslint/utils": "npm:8.18.2" + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: ">=4.8.4 <5.8.0" + checksum: 10c0/30a0314a2484bcbe286fc6eda55784d9954605c7e60ddd35281da90c6fcb75a40bd3abd84617814dff4e1504d762234407c99153fdd812dce712cef11bbb9b3f + languageName: node + linkType: hard + +"typescript@npm:~5.5.4": + version: 5.5.4 + resolution: "typescript@npm:5.5.4" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/422be60f89e661eab29ac488c974b6cc0a660fb2228003b297c3d10c32c90f3bcffc1009b43876a082515a3c376b1eefcce823d6e78982e6878408b9a923199c + languageName: node + linkType: hard + +"typescript@patch:typescript@npm%3A~5.5.4#optional!builtin": + version: 5.5.4 + resolution: "typescript@patch:typescript@npm%3A5.5.4#optional!builtin::version=5.5.4&hash=379a07" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/73409d7b9196a5a1217b3aaad929bf76294d3ce7d6e9766dd880ece296ee91cf7d7db6b16c6c6c630ee5096eccde726c0ef17c7dfa52b01a243e57ae1f09ef07 + languageName: node + linkType: hard + +"undici-types@npm:~6.20.0": + version: 6.20.0 + resolution: "undici-types@npm:6.20.0" + checksum: 10c0/68e659a98898d6a836a9a59e6adf14a5d799707f5ea629433e025ac90d239f75e408e2e5ff086afc3cace26f8b26ee52155293564593fbb4a2f666af57fc59bf + languageName: node + linkType: hard + +"unicorn-magic@npm:^0.1.0": + version: 0.1.0 + resolution: "unicorn-magic@npm:0.1.0" + checksum: 10c0/e4ed0de05b0a05e735c7d8a2930881e5efcfc3ec897204d5d33e7e6247f4c31eac92e383a15d9a6bccb7319b4271ee4bea946e211bf14951fec6ff2cbbb66a92 + languageName: node + linkType: hard + +"unique-filename@npm:^4.0.0": + version: 4.0.0 + resolution: "unique-filename@npm:4.0.0" + dependencies: + unique-slug: "npm:^5.0.0" + checksum: 10c0/38ae681cceb1408ea0587b6b01e29b00eee3c84baee1e41fd5c16b9ed443b80fba90c40e0ba69627e30855570a34ba8b06702d4a35035d4b5e198bf5a64c9ddc + languageName: node + linkType: hard + +"unique-slug@npm:^5.0.0": + version: 5.0.0 + resolution: "unique-slug@npm:5.0.0" + dependencies: + imurmurhash: "npm:^0.1.4" + checksum: 10c0/d324c5a44887bd7e105ce800fcf7533d43f29c48757ac410afd42975de82cc38ea2035c0483f4de82d186691bf3208ef35c644f73aa2b1b20b8e651be5afd293 + languageName: node + linkType: hard + +"update-browserslist-db@npm:^1.1.1": + version: 1.1.1 + resolution: "update-browserslist-db@npm:1.1.1" + dependencies: + escalade: "npm:^3.2.0" + picocolors: "npm:^1.1.0" + peerDependencies: + browserslist: ">= 4.21.0" + bin: + update-browserslist-db: cli.js + checksum: 10c0/536a2979adda2b4be81b07e311bd2f3ad5e978690987956bc5f514130ad50cac87cd22c710b686d79731e00fbee8ef43efe5fcd72baa241045209195d43dcc80 + languageName: node + linkType: hard + +"uri-js@npm:^4.2.2": + version: 4.4.1 + resolution: "uri-js@npm:4.4.1" + dependencies: + punycode: "npm:^2.1.0" + checksum: 10c0/4ef57b45aa820d7ac6496e9208559986c665e49447cb072744c13b66925a362d96dd5a46c4530a6b8e203e5db5fe849369444440cb22ecfc26c679359e5dfa3c + languageName: node + linkType: hard + +"watchpack@npm:^2.4.1": + version: 2.4.2 + resolution: "watchpack@npm:2.4.2" + dependencies: + glob-to-regexp: "npm:^0.4.1" + graceful-fs: "npm:^4.1.2" + checksum: 10c0/ec60a5f0e9efaeca0102fd9126346b3b2d523e01c34030d3fddf5813a7125765121ebdc2552981136dcd2c852deb1af0b39340f2fcc235f292db5399d0283577 + languageName: node + linkType: hard + +"webpack-cli@npm:^5.1.4": + version: 5.1.4 + resolution: "webpack-cli@npm:5.1.4" + dependencies: + "@discoveryjs/json-ext": "npm:^0.5.0" + "@webpack-cli/configtest": "npm:^2.1.1" + "@webpack-cli/info": "npm:^2.0.2" + "@webpack-cli/serve": "npm:^2.0.5" + colorette: "npm:^2.0.14" + commander: "npm:^10.0.1" + cross-spawn: "npm:^7.0.3" + envinfo: "npm:^7.7.3" + fastest-levenshtein: "npm:^1.0.12" + import-local: "npm:^3.0.2" + interpret: "npm:^3.1.1" + rechoir: "npm:^0.8.0" + webpack-merge: "npm:^5.7.3" + peerDependencies: + webpack: 5.x.x + peerDependenciesMeta: + "@webpack-cli/generators": + optional: true + webpack-bundle-analyzer: + optional: true + webpack-dev-server: + optional: true + bin: + webpack-cli: bin/cli.js + checksum: 10c0/4266909ae5e2e662c8790ac286e965b2c7fd5a4a2f07f48e28576234c9a5f631847ccddc18e1b3281c7b4be04a7ff4717d2636033a322dde13ac995fd0d9de10 + languageName: node + linkType: hard + +"webpack-merge@npm:^5.7.3": + version: 5.10.0 + resolution: "webpack-merge@npm:5.10.0" + dependencies: + clone-deep: "npm:^4.0.1" + flat: "npm:^5.0.2" + wildcard: "npm:^2.0.0" + checksum: 10c0/b607c84cabaf74689f965420051a55a08722d897bdd6c29cb0b2263b451c090f962d41ecf8c9bf56b0ab3de56e65476ace0a8ecda4f4a4663684243d90e0512b + languageName: node + linkType: hard + +"webpack-sources@npm:^3.2.3": + version: 3.2.3 + resolution: "webpack-sources@npm:3.2.3" + checksum: 10c0/2ef63d77c4fad39de4a6db17323d75eb92897b32674e97d76f0a1e87c003882fc038571266ad0ef581ac734cbe20952912aaa26155f1905e96ce251adbb1eb4e + languageName: node + linkType: hard + +"webpack@npm:^5.97.1": + version: 5.97.1 + resolution: "webpack@npm:5.97.1" + dependencies: + "@types/eslint-scope": "npm:^3.7.7" + "@types/estree": "npm:^1.0.6" + "@webassemblyjs/ast": "npm:^1.14.1" + "@webassemblyjs/wasm-edit": "npm:^1.14.1" + "@webassemblyjs/wasm-parser": "npm:^1.14.1" + acorn: "npm:^8.14.0" + browserslist: "npm:^4.24.0" + chrome-trace-event: "npm:^1.0.2" + enhanced-resolve: "npm:^5.17.1" + es-module-lexer: "npm:^1.2.1" + eslint-scope: "npm:5.1.1" + events: "npm:^3.2.0" + glob-to-regexp: "npm:^0.4.1" + graceful-fs: "npm:^4.2.11" + json-parse-even-better-errors: "npm:^2.3.1" + loader-runner: "npm:^4.2.0" + mime-types: "npm:^2.1.27" + neo-async: "npm:^2.6.2" + schema-utils: "npm:^3.2.0" + tapable: "npm:^2.1.1" + terser-webpack-plugin: "npm:^5.3.10" + watchpack: "npm:^2.4.1" + webpack-sources: "npm:^3.2.3" + peerDependenciesMeta: + webpack-cli: + optional: true + bin: + webpack: bin/webpack.js + checksum: 10c0/a12d3dc882ca582075f2c4bd88840be8307427245c90a8a0e0b372d73560df13fcf25a61625c9e7edc964981d16b5a8323640562eb48347cf9dd2f8bd1b39d35 + languageName: node + linkType: hard + +"which@npm:^2.0.1": + version: 2.0.2 + resolution: "which@npm:2.0.2" + dependencies: + isexe: "npm:^2.0.0" + bin: + node-which: ./bin/node-which + checksum: 10c0/66522872a768b60c2a65a57e8ad184e5372f5b6a9ca6d5f033d4b0dc98aff63995655a7503b9c0a2598936f532120e81dd8cc155e2e92ed662a2b9377cc4374f + languageName: node + linkType: hard + +"which@npm:^5.0.0": + version: 5.0.0 + resolution: "which@npm:5.0.0" + dependencies: + isexe: "npm:^3.1.1" + bin: + node-which: bin/which.js + checksum: 10c0/e556e4cd8b7dbf5df52408c9a9dd5ac6518c8c5267c8953f5b0564073c66ed5bf9503b14d876d0e9c7844d4db9725fb0dcf45d6e911e17e26ab363dc3965ae7b + languageName: node + linkType: hard + +"wildcard@npm:^2.0.0": + version: 2.0.1 + resolution: "wildcard@npm:2.0.1" + checksum: 10c0/08f70cd97dd9a20aea280847a1fe8148e17cae7d231640e41eb26d2388697cbe65b67fd9e68715251c39b080c5ae4f76d71a9a69fa101d897273efdfb1b58bf7 + languageName: node + linkType: hard + +"wing-companion@workspace:.": + version: 0.0.0-use.local + resolution: "wing-companion@workspace:." + dependencies: + "@companion-module/base": "npm:~1.11.2" + "@companion-module/tools": "npm:^2.1.1" + "@types/node": "npm:^22.10.2" + debounce-fn: "npm:^6.0.0" + eslint: "npm:^9.17.0" + husky: "npm:^9.1.7" + lint-staged: "npm:^15.2.11" + osc: "npm:^2.4.5" + p-queue: "npm:^8.0.1" + prettier: "npm:^3.4.2" + rimraf: "npm:^6.0.1" + type-fest: "npm:^4.31.0" + typescript: "npm:~5.5.4" + typescript-eslint: "npm:^8.18.1" + languageName: unknown + linkType: soft + +"wolfy87-eventemitter@npm:5.2.9": + version: 5.2.9 + resolution: "wolfy87-eventemitter@npm:5.2.9" + checksum: 10c0/dd218e3eb5fe2bbb7894d19b857ee7df1b711cadb6dfa177392d42b71522979d1883489057d13cf7af6785835b9807ab1f821d3dbf79ee9691ba27e72ad91c4d + languageName: node + linkType: hard + +"word-wrap@npm:^1.2.5": + version: 1.2.5 + resolution: "word-wrap@npm:1.2.5" + checksum: 10c0/e0e4a1ca27599c92a6ca4c32260e8a92e8a44f4ef6ef93f803f8ed823f486e0889fc0b93be4db59c8d51b3064951d25e43d434e95dc8c960cc3a63d65d00ba20 + languageName: node + linkType: hard + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version: 7.0.0 + resolution: "wrap-ansi@npm:7.0.0" + dependencies: + ansi-styles: "npm:^4.0.0" + string-width: "npm:^4.1.0" + strip-ansi: "npm:^6.0.0" + checksum: 10c0/d15fc12c11e4cbc4044a552129ebc75ee3f57aa9c1958373a4db0292d72282f54373b536103987a4a7594db1ef6a4f10acf92978f79b98c49306a4b58c77d4da + languageName: node + linkType: hard + +"wrap-ansi@npm:^8.1.0": + version: 8.1.0 + resolution: "wrap-ansi@npm:8.1.0" + dependencies: + ansi-styles: "npm:^6.1.0" + string-width: "npm:^5.0.1" + strip-ansi: "npm:^7.0.1" + checksum: 10c0/138ff58a41d2f877eae87e3282c0630fc2789012fc1af4d6bd626eeb9a2f9a65ca92005e6e69a75c7b85a68479fe7443c7dbe1eb8fbaa681a4491364b7c55c60 + languageName: node + linkType: hard + +"wrap-ansi@npm:^9.0.0": + version: 9.0.0 + resolution: "wrap-ansi@npm:9.0.0" + dependencies: + ansi-styles: "npm:^6.2.1" + string-width: "npm:^7.0.0" + strip-ansi: "npm:^7.1.0" + checksum: 10c0/a139b818da9573677548dd463bd626a5a5286271211eb6e4e82f34a4f643191d74e6d4a9bb0a3c26ec90e6f904f679e0569674ac099ea12378a8b98e20706066 + languageName: node + linkType: hard + +"ws@npm:8.18.0": + version: 8.18.0 + resolution: "ws@npm:8.18.0" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 10c0/25eb33aff17edcb90721ed6b0eb250976328533ad3cd1a28a274bd263682e7296a6591ff1436d6cbc50fa67463158b062f9d1122013b361cec99a05f84680e06 + languageName: node + linkType: hard + +"yallist@npm:^4.0.0": + version: 4.0.0 + resolution: "yallist@npm:4.0.0" + checksum: 10c0/2286b5e8dbfe22204ab66e2ef5cc9bbb1e55dfc873bbe0d568aa943eb255d131890dfd5bf243637273d31119b870f49c18fcde2c6ffbb7a7a092b870dc90625a + languageName: node + linkType: hard + +"yallist@npm:^5.0.0": + version: 5.0.0 + resolution: "yallist@npm:5.0.0" + checksum: 10c0/a499c81ce6d4a1d260d4ea0f6d49ab4da09681e32c3f0472dee16667ed69d01dae63a3b81745a24bd78476ec4fcf856114cb4896ace738e01da34b2c42235416 + languageName: node + linkType: hard + +"yaml@npm:~2.6.1": + version: 2.6.1 + resolution: "yaml@npm:2.6.1" + bin: + yaml: bin.mjs + checksum: 10c0/aebf07f61c72b38c74d2b60c3a3ccf89ee4da45bcd94b2bfb7899ba07a5257625a7c9f717c65a6fc511563d48001e01deb1d9e55f0133f3e2edf86039c8c1be7 + languageName: node + linkType: hard + +"yocto-queue@npm:^0.1.0": + version: 0.1.0 + resolution: "yocto-queue@npm:0.1.0" + checksum: 10c0/dceb44c28578b31641e13695d200d34ec4ab3966a5729814d5445b194933c096b7ced71494ce53a0e8820685d1d010df8b2422e5bf2cdea7e469d97ffbea306f + languageName: node + linkType: hard + +"yocto-queue@npm:^1.0.0": + version: 1.1.1 + resolution: "yocto-queue@npm:1.1.1" + checksum: 10c0/cb287fe5e6acfa82690acb43c283de34e945c571a78a939774f6eaba7c285bacdf6c90fbc16ce530060863984c906d2b4c6ceb069c94d1e0a06d5f2b458e2a92 + languageName: node + linkType: hard + +"zx@npm:^8.2.4": + version: 8.3.0 + resolution: "zx@npm:8.3.0" + dependencies: + "@types/fs-extra": "npm:>=11" + "@types/node": "npm:>=20" + dependenciesMeta: + "@types/fs-extra": + optional: true + "@types/node": + optional: true + bin: + zx: build/cli.js + checksum: 10c0/ee40ad833fb5fd58e578be3d4fc811c268a3ad3f4063902c46497d329bf9fc631b3545fb56e98416c9b4cae4e9a6e9a02a99425e43691f3b42ade9e4a8399646 + languageName: node + linkType: hard