From fd09914a0fd38209c3fee84ec399444e4b7dd432 Mon Sep 17 00:00:00 2001 From: Michael Kochell <6913320+mickmister@users.noreply.github.com> Date: Sat, 19 Apr 2025 10:26:02 -0400 Subject: [PATCH 1/7] initial implementation --- .github/workflows/jamscribe_release.yml | 112 ++ .gitignore | 2 + .npmrc | 2 + package.json | 23 + pnpm-lock.yaml | 1303 +++++++++++++++++++++++ src/index.tsx | 1 + src/jamscribe_module.tsx | 172 +++ src/services/recorder.ts | 198 ++++ 8 files changed, 1813 insertions(+) create mode 100644 .github/workflows/jamscribe_release.yml create mode 100644 .gitignore create mode 100644 .npmrc create mode 100644 package.json create mode 100644 pnpm-lock.yaml create mode 100644 src/index.tsx create mode 100644 src/jamscribe_module.tsx create mode 100644 src/services/recorder.ts diff --git a/.github/workflows/jamscribe_release.yml b/.github/workflows/jamscribe_release.yml new file mode 100644 index 0000000..43a2fc9 --- /dev/null +++ b/.github/workflows/jamscribe_release.yml @@ -0,0 +1,112 @@ +name: JamScribe release + +on: + push: + branches: + - "*" + workflow_dispatch: + inputs: + ref: + description: 'Ref to use in application repo' + required: false + default: 'main' + # repository_dispatch: + # types: [ build-remote ] + +env: + TARGET_BRANCH: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.ref != '' && github.event.inputs.ref || github.event_name == 'repository_dispatch' && github.event.client_payload.sha != '' && github.event.client_payload.sha || 'main' }} + +jobs: + release-jamscribe: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Checkout code + uses: actions/checkout@v3 + with: + repository: ${{ secrets.REPO }} + token: ${{ secrets.REPO_TOKEN }} + ref: ${{ env.TARGET_BRANCH }} + + - name: Install zipcmp + run: sudo apt-get update && sudo apt-get install -y zipcmp + + - name: Checkout songdrive-releases repo into `.github/composite-actions` + uses: actions/checkout@v3 + with: + repository: jamtools/songdrive-releases + path: .github/composite-actions + ref: ${{ github.sha }} + + - name: Run build composite action + uses: ./.github/composite-actions/.github/actions/build_app + with: + entrypoint: src/index.tsx + + - name: Create dist.zip + run: | + cd dist + zip -r ../dist.zip . + + - name: Find latest matching release by tag prefix + id: find_release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + PREFIX="vjamscribe-" + latest=$(gh release list --repo "${{ github.repository }}" --limit 100 --json tagName,publishedAt \ + | jq -r --arg prefix "$PREFIX" ' + map(select(.tagName | startswith($prefix))) + | sort_by(.publishedAt) + | last + | .tagName[1:] + ') + echo "latest_tag=$latest" >> $GITHUB_OUTPUT + + - name: Download artifact from latest release + if: steps.find_release.outputs.latest_tag != '' + uses: blauqs/actions-download-asset@master + with: + repo: ${{ github.repository }} + version: ${{ steps.find_release.outputs.latest_tag }} + file: dist.zip + out: previous_release.zip + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Compare ZIP files using zipcmp + id: compare_zips + run: | + if zipcmp dist.zip previous_release.zip; then + echo "match=true" >> $GITHUB_OUTPUT + else + echo "match=false" >> $GITHUB_OUTPUT + fi + + - name: Set version number + id: version + run: | + if [ -n "${{ steps.find_release.outputs.latest_tag }}" ]; then + # Extract number from the tag and increment + current=$(echo "${{ steps.find_release.outputs.latest_tag }}" | grep -o '[0-9]\+') + echo "number=$((current + 1))" >> $GITHUB_OUTPUT + else + echo "number=1" >> $GITHUB_OUTPUT + fi + + - name: Create new release + if: steps.compare_zips.outputs.match == 'false' + uses: ncipollo/release-action@v1 + with: + tag: vjamscribe-${{ steps.version.outputs.number }} + name: JamScribe v${{ steps.version.outputs.number }} + body: Automated release + artifacts: dist.zip + token: ${{ secrets.GITHUB_TOKEN }} + makeLatest: true + + - name: Upload build artifact + uses: actions/upload-artifact@v4 + with: + name: jamscribe-dist + path: dist.zip diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f06235c --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +dist diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..21c6c7a --- /dev/null +++ b/.npmrc @@ -0,0 +1,2 @@ +node-linker=hoisted +strict-peer-dependencies=false diff --git a/package.json b/package.json new file mode 100644 index 0000000..390bace --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "jamscribe", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "dev": "sb dev src/index.tsx", + "build": "sb build src/index.tsx" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "@jamtools/core": "0.15.0-rc19", + "hono": "4.7.6", + "react": "^19.1.0", + "springboard": "0.15.0-rc19", + "springboard-cli": "0.15.0-rc19" + }, + "devDependencies": { + "@types/react": "^19.1.2" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..c38e05b --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,1303 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@jamtools/core': + specifier: 0.15.0-rc19 + version: 0.15.0-rc19(@tonejs/midi@2.0.28)(springboard@0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2)) + hono: + specifier: 4.7.6 + version: 4.7.6 + react: + specifier: ^19.1.0 + version: 19.1.0 + springboard: + specifier: 0.15.0-rc19 + version: 0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2) + springboard-cli: + specifier: 0.15.0-rc19 + version: 0.15.0-rc19(@springboardjs/platforms-browser@0.15.0-rc19(react-router-dom@6.30.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(springboard@0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2)))(@springboardjs/platforms-node@0.15.0-rc19(isomorphic-ws@4.0.1(ws@8.18.1))(springboard@0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2))(ws@8.18.1))(springboard-server@0.15.0-rc19(@springboardjs/data-storage@0.15.0-rc19(kysely@0.28.1))(@trpc/server@10.45.2)(hono@4.7.6)(springboard@0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2)))(springboard@0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2)) + devDependencies: + '@types/react': + specifier: ^19.1.2 + version: 19.1.2 + +packages: + + '@babel/runtime@7.27.0': + resolution: {integrity: sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==} + engines: {node: '>=6.9.0'} + + '@esbuild/aix-ppc64@0.23.1': + resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.23.1': + resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.23.1': + resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.23.1': + resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.23.1': + resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.23.1': + resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.23.1': + resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.23.1': + resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.23.1': + resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.23.1': + resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.23.1': + resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.23.1': + resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.23.1': + resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.23.1': + resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.23.1': + resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.23.1': + resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.23.1': + resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.23.1': + resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.23.1': + resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.23.1': + resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.23.1': + resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.23.1': + resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.23.1': + resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.23.1': + resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@hono/node-server@1.14.1': + resolution: {integrity: sha512-vmbuM+HPinjWzPe7FFPWMMQMsbKE9gDPhaH0FFdqbGpkT5lp++tcWDTxwBl5EgS5y6JVgIaCdjeHRfQ4XRBRjQ==} + engines: {node: '>=18.14.1'} + peerDependencies: + hono: ^4 + + '@hono/node-ws@1.1.1': + resolution: {integrity: sha512-iFJrAw5GuBTstehBzLY2FyW5rRlXmO3Uwpijpm4Liv75owNP/UjZe3KExsLuEK4w+u+xhvHqOoQUyEKWUvyghw==} + engines: {node: '>=18.14.1'} + peerDependencies: + '@hono/node-server': ^1.11.1 + hono: ^4.6.0 + + '@hono/trpc-server@0.3.4': + resolution: {integrity: sha512-xFOPjUPnII70FgicDzOJy1ufIoBTu8eF578zGiDOrYOrYN8CJe140s9buzuPkX+SwJRYK8LjEBHywqZtxdm8aA==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@trpc/server': ^10.10.0 || >11.0.0-rc + hono: '>=4.*' + + '@jamtools/core@0.15.0-rc19': + resolution: {integrity: sha512-JAq7BcZZess6uBoCVSNPUVXT3VFXrEkSk313i+hMIxTt+K41anewmVlvkGnghkyJf/4DUZxH0bGhZFy/7jn3NQ==} + peerDependencies: + '@tonejs/midi': ^2.0.0 + springboard: 0.15.0-rc19 + + '@julusian/midi@3.6.1': + resolution: {integrity: sha512-sC6tTMAMZsHOQILAv/R0On5tKKhzBQUjdyYWzh9l0UQeNry12CFIyRWK1Mep5xCHWCTUB0w4gxngpciA5PgN/Q==} + engines: {node: '>=14.15'} + + '@remix-run/router@1.23.0': + resolution: {integrity: sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==} + engines: {node: '>=14.0.0'} + + '@springboardjs/data-storage@0.15.0-rc19': + resolution: {integrity: sha512-HW5W9Y5rZ8B4JUcQoO+Xg9973UAOi/qTnzmWbhA1KJohBRHzw8ffhkCKxqLFtVrVy2owE2L0oo0IZgUhFC3/pA==} + peerDependencies: + kysely: '>= 0.24.0' + + '@springboardjs/platforms-browser@0.15.0-rc19': + resolution: {integrity: sha512-R2inBKRjEdBNTdocuhND9RJdv810QIStXeSj2K8pNnTWMPFaBEdOL6Mf2hRLu7a1lyYuy/JuFoCsqWDcqsjK7g==} + peerDependencies: + react-router-dom: ^6 + springboard: 0.15.0-rc19 + + '@springboardjs/platforms-node@0.15.0-rc19': + resolution: {integrity: sha512-a/sftaPQ1cZ/nnfN1X81TCrXPLbssUQJvT+6Jqa0/M7UnjY4G0TjTYEwTUu38MqnpH9m8pZovGtOTWBVHsioZA==} + peerDependencies: + isomorphic-ws: ^4.0.1 + springboard: 0.15.0-rc19 + ws: ^8.18.0 + + '@tonejs/midi@2.0.28': + resolution: {integrity: sha512-RII6YpInPsOZ5t3Si/20QKpNqB1lZ2OCFJSOzJxz38YdY/3zqDr3uaml4JuCWkdixuPqP1/TBnXzhQ39csyoVg==} + + '@trpc/client@10.45.2': + resolution: {integrity: sha512-ykALM5kYWTLn1zYuUOZ2cPWlVfrXhc18HzBDyRhoPYN0jey4iQHEFSEowfnhg1RvYnrAVjNBgHNeSAXjrDbGwg==} + peerDependencies: + '@trpc/server': 10.45.2 + + '@trpc/server@10.45.2': + resolution: {integrity: sha512-wOrSThNNE4HUnuhJG6PfDRp4L2009KDVxsd+2VYH8ro6o/7/jwYZ8Uu5j+VaW+mOmc8EHerHzGcdbGNQSAUPgg==} + + '@types/react@19.1.2': + resolution: {integrity: sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw==} + + '@types/webmidi@2.1.0': + resolution: {integrity: sha512-k898MjEUSHB+6rSeCPQk/kLgie0wgWZ2t78GlWj86HbTQ+XmtbBafYg5LNjn8bVHfItEhPGZPf579Xfc6keV6w==} + + adsr@1.0.1: + resolution: {integrity: sha512-thr9LK4jxApOzBA33IWOA83bXJFbyfbeozpHXyrMQOIhUni198uRxXqDhobW0S/51iokqty2Yz2WbLZbE6tntQ==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + array-flatten@3.0.0: + resolution: {integrity: sha512-zPMVc3ZYlGLNk4mpK1NzP2wg0ml9t7fUgDsayR5Y5rSzxQilzR9FGu/EH2jQOcKSAeAfWeylyW8juy3OkWRvNA==} + + audio-loader@0.5.0: + resolution: {integrity: sha512-mEoYRjZhqkBSen/X9i2PNosqvafEsur8bI5MNoPr0wsJu9Nzlul3Yv1elYeMPsXxTxYhXLY8AZlScBvaK4mydg==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + better-sqlite3@11.9.1: + resolution: {integrity: sha512-Ba0KR+Fzxh2jDRhdg6TSH0SJGzb8C0aBY4hR8w8madIdIzzC6Y1+kx5qR6eS1Z+Gy20h6ZU28aeyg0z1VIrShQ==} + + bindings@1.5.0: + resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chownr@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + + concurrently@9.1.2: + resolution: {integrity: sha512-H9MWcoPsYddwbOGM6difjVwVZHl63nwMEwDJG/L7VGtuaJhb12h2caPG2tVPWs7emuYix252iGfqOyrz1GczTQ==} + engines: {node: '>=18'} + hasBin: true + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + + deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + + detect-libc@2.0.3: + resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} + engines: {node: '>=8'} + + dexie@4.0.11: + resolution: {integrity: sha512-SOKO002EqlvBYYKQSew3iymBoN2EQ4BDw/3yprjh7kAfFzjBYkaMNa/pZvcA7HSWlcKSQb9XhPe3wKyQ0x4A8A==} + + djipevents@2.0.7: + resolution: {integrity: sha512-KNFYaU85imxOCKOUsIR70Iz9E19r96/X7LSH+u0tSoZdpWcBdzoqtTsU+wuLhc6GMpSFob+KInkZAbfKi01Bjg==} + + easymidi@3.1.0: + resolution: {integrity: sha512-bxEwfPysM1L+SO/qwHaYu9dvTxw2QHFjGV9EMzqGQJbhEP2MupKpg6eJMkj+uoXN0Ep1JhVPLbNLPmt3UkZRTw==} + engines: {node: '>=14.15'} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + end-of-stream@1.4.4: + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + + esbuild@0.23.1: + resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + expand-template@2.0.3: + resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} + engines: {node: '>=6'} + + file-uri-to-path@1.0.0: + resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + + fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + github-from-package@0.0.0: + resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + hono@4.7.6: + resolution: {integrity: sha512-564rVzELU+9BRqqx5k8sT2NFwGD3I3Vifdb6P7CmM6FiarOSY+fDC+6B+k9wcCb86ReoayteZP2ki0cRLN1jbw==} + engines: {node: '>=16.9.0'} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + immer@10.1.1: + resolution: {integrity: sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + isomorphic-ws@4.0.1: + resolution: {integrity: sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==} + peerDependencies: + ws: '*' + + jazz-midi@1.7.9: + resolution: {integrity: sha512-c8c4BBgwxdsIr1iVm53nadCrtH7BUlnX3V95ciK/gbvXN/ndE5+POskBalXgqlc/r9p2XUbdLTrgrC6fou5p9w==} + engines: {node: '>=10.0.0'} + + json-rpc-2.0@1.7.0: + resolution: {integrity: sha512-asnLgC1qD5ytP+fvBP8uL0rvj+l8P6iYICbzZ8dVxCpESffVjzA7KkYkbKCIbavs7cllwH1ZUaNtJwphdeRqpg==} + + jzz@1.9.3: + resolution: {integrity: sha512-DXQ7xXuJzC4YxNbQZy9pfHC/DOXM6IHgceTXnkhk1/+lU/qDk8W9xUaNpgCH6t1QDQ9roz7FSVtFc0/JhYQFxg==} + + kysely@0.28.1: + resolution: {integrity: sha512-umkhsHB0y2JvI83DUtuYYTfvr063xTHTcr/k1z0E2Bg39zGiBcdDvlbP79YMcItn55pKQtD120sZmKa9jhCVtw==} + engines: {node: '>=18.0.0'} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + midi-file@1.2.4: + resolution: {integrity: sha512-B5SnBC6i2bwJIXTY9MElIydJwAmnKx+r5eJ1jknTLetzLflEl0GWveuBB6ACrQpecSRkOB6fhTx1PwXk2BVxnA==} + + midimessage@1.0.5: + resolution: {integrity: sha512-MPJ2tDupFOfZB5/PLp8fri1IS4fd9hPj0Bio//FBhWRQ+TsJA7/49CF1aJyraDxa0Jq8zMHAwrwXl2GINvLvgw==} + + mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + + napi-build-utils@2.0.0: + resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} + + node-abi@3.74.0: + resolution: {integrity: sha512-c5XK0MjkGBrQPGYG24GBADZud0NCbznxNx0ZkS+ebUTrmV1qTDxPxSL8zEAPURXSbLRWVexxmP4986BziahL5w==} + engines: {node: '>=10'} + + node-addon-api@6.1.0: + resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==} + + note-parser@1.1.0: + resolution: {integrity: sha512-YTqWQBsRp40EFrEznnkGtmx68gcgOQ8CdoBspqGBA3G1/4mJwIYbDe/vuNpX3oGX2DhP7b1dBgTmj7p3Zr0P1Q==} + + note-parser@2.0.1: + resolution: {integrity: sha512-w9o6Fv46y3NsFxeezTZSmftBtUM/ypme6iZWVrTJvvsD5RN+w0XNDePWtfreNrZFL3jSjBFhadPoXb+pJO4UdA==} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + pg-cloudflare@1.1.1: + resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==} + + pg-connection-string@2.7.0: + resolution: {integrity: sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==} + + pg-int8@1.0.1: + resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} + engines: {node: '>=4.0.0'} + + pg-pool@3.8.0: + resolution: {integrity: sha512-VBw3jiVm6ZOdLBTIcXLNdSotb6Iy3uOCwDGFAksZCXmi10nyRvnP2v3jl4d+IsLYRyXf6o9hIm/ZtUzlByNUdw==} + peerDependencies: + pg: '>=8.0' + + pg-protocol@1.8.0: + resolution: {integrity: sha512-jvuYlEkL03NRvOoyoRktBK7+qU5kOvlAwvmrH8sr3wbLrOdVWsRxQfz8mMy9sZFsqJ1hEWNfdWKI4SAmoL+j7g==} + + pg-types@2.2.0: + resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} + engines: {node: '>=4'} + + pg@8.14.1: + resolution: {integrity: sha512-0TdbqfjwIun9Fm/r89oB7RFQ0bLgduAhiIqIXOsyKoiC/L54DbuAAzIEN/9Op0f1Po9X7iCPXGoa/Ah+2aI8Xw==} + engines: {node: '>= 8.0.0'} + peerDependencies: + pg-native: '>=3.0.1' + peerDependenciesMeta: + pg-native: + optional: true + + pgpass@1.0.5: + resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} + + pkg-prebuilds@1.0.0: + resolution: {integrity: sha512-D9wlkXZCmjxj2kBHTw3fGSyjoahr33breGBoJcoezpi7ouYS59DJVOHMZ+dgqacSrZiJo4qtkXxLQTE+BqXJmQ==} + engines: {node: '>= 14.15.0'} + hasBin: true + + postgres-array@2.0.0: + resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} + engines: {node: '>=4'} + + postgres-bytea@1.0.0: + resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} + engines: {node: '>=0.10.0'} + + postgres-date@1.0.7: + resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} + engines: {node: '>=0.10.0'} + + postgres-interval@1.2.0: + resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} + engines: {node: '>=0.10.0'} + + prebuild-install@7.1.3: + resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} + engines: {node: '>=10'} + hasBin: true + + pump@3.0.2: + resolution: {integrity: sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==} + + rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + + react-dom@19.1.0: + resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==} + peerDependencies: + react: ^19.1.0 + + react-router-dom@6.30.0: + resolution: {integrity: sha512-x30B78HV5tFk8ex0ITwzC9TTZMua4jGyA9IUlH1JLQYQTFyxr/ZxwOJq7evg1JX1qGVUcvhsmQSKdPncQrjTgA==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: '>=16.8' + react-dom: '>=16.8' + + react-router@6.30.0: + resolution: {integrity: sha512-D3X8FyH9nBcTSHGdEKurK7r8OYE1kKFn3d/CF+CoxbSHkxU7o37+Uh7eAHRXr6k2tSExXYO++07PeXJtA/dEhQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: '>=16.8' + + react@19.1.0: + resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==} + engines: {node: '>=0.10.0'} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + reconnecting-websocket@4.4.0: + resolution: {integrity: sha512-D2E33ceRPga0NvTDhJmphEgJ7FUYF0v4lr1ki0csq06OdlxKfugGzN0dSkxM/NfqCxYELK4KcaTOUOjTV6Dcng==} + + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + rxjs@7.8.2: + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + sample-player@0.5.5: + resolution: {integrity: sha512-VQ9pXPJ1m/eTH8QK6OQ8Dn/HSVToNyY9w9vnv+y/yjkJeRm87tJ/gBEm66jItfSLhKe6VG1DfX8+oT+Mg7QUpg==} + + scheduler@0.26.0: + resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} + + semver@7.7.1: + resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} + engines: {node: '>=10'} + hasBin: true + + shell-quote@1.8.2: + resolution: {integrity: sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==} + engines: {node: '>= 0.4'} + + simple-concat@1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + + simple-get@4.0.1: + resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + + soundfont-player@0.12.0: + resolution: {integrity: sha512-8BJIsAt7h1PK3thSZDgF6zecgGhYkK74JnZO8WRZi3h34qG6H/DYlnv7cpRvL7Q9C8N6qld4Qwj7nJsX1gYjEA==} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + springboard-cli@0.15.0-rc19: + resolution: {integrity: sha512-zCQh2QDEWZsF0PjHnsLRqLdObPXwJ8BnMSQzjhPsdBa5D9c1Lx348e5FLo9TdVU241df699nVzvUXTUhBWnHJQ==} + hasBin: true + peerDependencies: + '@springboardjs/platforms-browser': 0.15.0-rc19 + '@springboardjs/platforms-node': 0.15.0-rc19 + springboard: 0.15.0-rc19 + springboard-server: 0.15.0-rc19 + + springboard-server@0.15.0-rc19: + resolution: {integrity: sha512-/P0ImAOfI4dz8thrhBBMYv5LSu5Af2no+D9fsGtFwBo3byGc6dOH3mTRsZ6pzosKu1A35CXN+m0qCys2/5qd9A==} + peerDependencies: + '@springboardjs/data-storage': 0.15.0-rc19 + hono: ^4.6.7 + springboard: 0.15.0-rc19 + + springboard@0.15.0-rc19: + resolution: {integrity: sha512-LDRLoCiFo9fTS/j8/9t5LPg5GlMzFlxW5CpyMHIXcl/OaehqFhagQ1GIy8gEo13SZIvEeVm5Fnwhjr8N2eKrGg==} + peerDependencies: + '@trpc/client': ^10.45.2 + json-rpc-2.0: ^1.7.0 + react: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + rxjs: ^7.0.0 + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + tar-fs@2.1.2: + resolution: {integrity: sha512-EsaAXwxmx8UB7FRKqeozqEPop69DXcmYwTQwXvyAPF352HJsPdkVhvTaDPYqfNgruveJIJy3TA2l+2zj8LJIJA==} + + tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tunnel-agent@0.6.0: + resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} + hasBin: true + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + webmidi@3.1.12: + resolution: {integrity: sha512-X1lACggXm2BxuAPdx5wleh8S2kygduHbtR2ti5sGhivLkX6Muv/sLAYmPuIaNLOUddyxr71+3tsq8m5dKXoT4A==} + engines: {node: '>=8.5'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + ws@8.18.1: + resolution: {integrity: sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + zod@3.24.3: + resolution: {integrity: sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==} + +snapshots: + + '@babel/runtime@7.27.0': + dependencies: + regenerator-runtime: 0.14.1 + + '@esbuild/aix-ppc64@0.23.1': + optional: true + + '@esbuild/android-arm64@0.23.1': + optional: true + + '@esbuild/android-arm@0.23.1': + optional: true + + '@esbuild/android-x64@0.23.1': + optional: true + + '@esbuild/darwin-arm64@0.23.1': + optional: true + + '@esbuild/darwin-x64@0.23.1': + optional: true + + '@esbuild/freebsd-arm64@0.23.1': + optional: true + + '@esbuild/freebsd-x64@0.23.1': + optional: true + + '@esbuild/linux-arm64@0.23.1': + optional: true + + '@esbuild/linux-arm@0.23.1': + optional: true + + '@esbuild/linux-ia32@0.23.1': + optional: true + + '@esbuild/linux-loong64@0.23.1': + optional: true + + '@esbuild/linux-mips64el@0.23.1': + optional: true + + '@esbuild/linux-ppc64@0.23.1': + optional: true + + '@esbuild/linux-riscv64@0.23.1': + optional: true + + '@esbuild/linux-s390x@0.23.1': + optional: true + + '@esbuild/linux-x64@0.23.1': + optional: true + + '@esbuild/netbsd-x64@0.23.1': + optional: true + + '@esbuild/openbsd-arm64@0.23.1': + optional: true + + '@esbuild/openbsd-x64@0.23.1': + optional: true + + '@esbuild/sunos-x64@0.23.1': + optional: true + + '@esbuild/win32-arm64@0.23.1': + optional: true + + '@esbuild/win32-ia32@0.23.1': + optional: true + + '@esbuild/win32-x64@0.23.1': + optional: true + + '@hono/node-server@1.14.1(hono@4.7.6)': + dependencies: + hono: 4.7.6 + + '@hono/node-ws@1.1.1(@hono/node-server@1.14.1(hono@4.7.6))(hono@4.7.6)': + dependencies: + '@hono/node-server': 1.14.1(hono@4.7.6) + hono: 4.7.6 + ws: 8.18.1 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@hono/trpc-server@0.3.4(@trpc/server@10.45.2)(hono@4.7.6)': + dependencies: + '@trpc/server': 10.45.2 + hono: 4.7.6 + + '@jamtools/core@0.15.0-rc19(@tonejs/midi@2.0.28)(springboard@0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2))': + dependencies: + '@tonejs/midi': 2.0.28 + easymidi: 3.1.0 + midi-file: 1.2.4 + soundfont-player: 0.12.0 + springboard: 0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2) + webmidi: 3.1.12 + + '@julusian/midi@3.6.1': + dependencies: + node-addon-api: 6.1.0 + pkg-prebuilds: 1.0.0 + + '@remix-run/router@1.23.0': {} + + '@springboardjs/data-storage@0.15.0-rc19(kysely@0.28.1)': + dependencies: + '@trpc/client': 10.45.2(@trpc/server@10.45.2) + '@trpc/server': 10.45.2 + better-sqlite3: 11.9.1 + kysely: 0.28.1 + pg: 8.14.1 + zod: 3.24.3 + transitivePeerDependencies: + - pg-native + + '@springboardjs/platforms-browser@0.15.0-rc19(react-router-dom@6.30.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(springboard@0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2))': + dependencies: + react-router-dom: 6.30.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + springboard: 0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2) + + '@springboardjs/platforms-node@0.15.0-rc19(isomorphic-ws@4.0.1(ws@8.18.1))(springboard@0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2))(ws@8.18.1)': + dependencies: + isomorphic-ws: 4.0.1(ws@8.18.1) + springboard: 0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2) + ws: 8.18.1 + + '@tonejs/midi@2.0.28': + dependencies: + array-flatten: 3.0.0 + midi-file: 1.2.4 + + '@trpc/client@10.45.2(@trpc/server@10.45.2)': + dependencies: + '@trpc/server': 10.45.2 + + '@trpc/server@10.45.2': {} + + '@types/react@19.1.2': + dependencies: + csstype: 3.1.3 + + '@types/webmidi@2.1.0': + optional: true + + adsr@1.0.1: {} + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + array-flatten@3.0.0: {} + + audio-loader@0.5.0: {} + + base64-js@1.5.1: {} + + better-sqlite3@11.9.1: + dependencies: + bindings: 1.5.0 + prebuild-install: 7.1.3 + + bindings@1.5.0: + dependencies: + file-uri-to-path: 1.0.0 + + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chownr@1.1.4: {} + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + commander@12.1.0: {} + + concurrently@9.1.2: + dependencies: + chalk: 4.1.2 + lodash: 4.17.21 + rxjs: 7.8.2 + shell-quote: 1.8.2 + supports-color: 8.1.1 + tree-kill: 1.2.2 + yargs: 17.7.2 + + csstype@3.1.3: {} + + decompress-response@6.0.0: + dependencies: + mimic-response: 3.1.0 + + deep-extend@0.6.0: {} + + detect-libc@2.0.3: {} + + dexie@4.0.11: {} + + djipevents@2.0.7: + dependencies: + '@babel/runtime': 7.27.0 + + easymidi@3.1.0: + dependencies: + '@julusian/midi': 3.6.1 + + emoji-regex@8.0.0: {} + + end-of-stream@1.4.4: + dependencies: + once: 1.4.0 + + esbuild@0.23.1: + optionalDependencies: + '@esbuild/aix-ppc64': 0.23.1 + '@esbuild/android-arm': 0.23.1 + '@esbuild/android-arm64': 0.23.1 + '@esbuild/android-x64': 0.23.1 + '@esbuild/darwin-arm64': 0.23.1 + '@esbuild/darwin-x64': 0.23.1 + '@esbuild/freebsd-arm64': 0.23.1 + '@esbuild/freebsd-x64': 0.23.1 + '@esbuild/linux-arm': 0.23.1 + '@esbuild/linux-arm64': 0.23.1 + '@esbuild/linux-ia32': 0.23.1 + '@esbuild/linux-loong64': 0.23.1 + '@esbuild/linux-mips64el': 0.23.1 + '@esbuild/linux-ppc64': 0.23.1 + '@esbuild/linux-riscv64': 0.23.1 + '@esbuild/linux-s390x': 0.23.1 + '@esbuild/linux-x64': 0.23.1 + '@esbuild/netbsd-x64': 0.23.1 + '@esbuild/openbsd-arm64': 0.23.1 + '@esbuild/openbsd-x64': 0.23.1 + '@esbuild/sunos-x64': 0.23.1 + '@esbuild/win32-arm64': 0.23.1 + '@esbuild/win32-ia32': 0.23.1 + '@esbuild/win32-x64': 0.23.1 + + escalade@3.2.0: {} + + expand-template@2.0.3: {} + + file-uri-to-path@1.0.0: {} + + fs-constants@1.0.0: {} + + get-caller-file@2.0.5: {} + + github-from-package@0.0.0: {} + + has-flag@4.0.0: {} + + hono@4.7.6: {} + + ieee754@1.2.1: {} + + immer@10.1.1: {} + + inherits@2.0.4: {} + + ini@1.3.8: {} + + is-fullwidth-code-point@3.0.0: {} + + isomorphic-ws@4.0.1(ws@8.18.1): + dependencies: + ws: 8.18.1 + + jazz-midi@1.7.9: + optional: true + + json-rpc-2.0@1.7.0: {} + + jzz@1.9.3: + dependencies: + '@types/webmidi': 2.1.0 + jazz-midi: 1.7.9 + optional: true + + kysely@0.28.1: {} + + lodash@4.17.21: {} + + midi-file@1.2.4: {} + + midimessage@1.0.5: {} + + mimic-response@3.1.0: {} + + minimist@1.2.8: {} + + mkdirp-classic@0.5.3: {} + + napi-build-utils@2.0.0: {} + + node-abi@3.74.0: + dependencies: + semver: 7.7.1 + + node-addon-api@6.1.0: {} + + note-parser@1.1.0: {} + + note-parser@2.0.1: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + pg-cloudflare@1.1.1: + optional: true + + pg-connection-string@2.7.0: {} + + pg-int8@1.0.1: {} + + pg-pool@3.8.0(pg@8.14.1): + dependencies: + pg: 8.14.1 + + pg-protocol@1.8.0: {} + + pg-types@2.2.0: + dependencies: + pg-int8: 1.0.1 + postgres-array: 2.0.0 + postgres-bytea: 1.0.0 + postgres-date: 1.0.7 + postgres-interval: 1.2.0 + + pg@8.14.1: + dependencies: + pg-connection-string: 2.7.0 + pg-pool: 3.8.0(pg@8.14.1) + pg-protocol: 1.8.0 + pg-types: 2.2.0 + pgpass: 1.0.5 + optionalDependencies: + pg-cloudflare: 1.1.1 + + pgpass@1.0.5: + dependencies: + split2: 4.2.0 + + pkg-prebuilds@1.0.0: + dependencies: + yargs: 17.7.2 + + postgres-array@2.0.0: {} + + postgres-bytea@1.0.0: {} + + postgres-date@1.0.7: {} + + postgres-interval@1.2.0: + dependencies: + xtend: 4.0.2 + + prebuild-install@7.1.3: + dependencies: + detect-libc: 2.0.3 + expand-template: 2.0.3 + github-from-package: 0.0.0 + minimist: 1.2.8 + mkdirp-classic: 0.5.3 + napi-build-utils: 2.0.0 + node-abi: 3.74.0 + pump: 3.0.2 + rc: 1.2.8 + simple-get: 4.0.1 + tar-fs: 2.1.2 + tunnel-agent: 0.6.0 + + pump@3.0.2: + dependencies: + end-of-stream: 1.4.4 + once: 1.4.0 + + rc@1.2.8: + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + + react-dom@19.1.0(react@19.1.0): + dependencies: + react: 19.1.0 + scheduler: 0.26.0 + + react-router-dom@6.30.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + '@remix-run/router': 1.23.0 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-router: 6.30.0(react@19.1.0) + + react-router@6.30.0(react@19.1.0): + dependencies: + '@remix-run/router': 1.23.0 + react: 19.1.0 + + react@19.1.0: {} + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + reconnecting-websocket@4.4.0: {} + + regenerator-runtime@0.14.1: {} + + require-directory@2.1.1: {} + + rxjs@7.8.2: + dependencies: + tslib: 2.8.1 + + safe-buffer@5.2.1: {} + + sample-player@0.5.5: + dependencies: + adsr: 1.0.1 + midimessage: 1.0.5 + note-parser: 1.1.0 + + scheduler@0.26.0: {} + + semver@7.7.1: {} + + shell-quote@1.8.2: {} + + simple-concat@1.0.1: {} + + simple-get@4.0.1: + dependencies: + decompress-response: 6.0.0 + once: 1.4.0 + simple-concat: 1.0.1 + + soundfont-player@0.12.0: + dependencies: + audio-loader: 0.5.0 + note-parser: 2.0.1 + sample-player: 0.5.5 + + split2@4.2.0: {} + + springboard-cli@0.15.0-rc19(@springboardjs/platforms-browser@0.15.0-rc19(react-router-dom@6.30.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(springboard@0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2)))(@springboardjs/platforms-node@0.15.0-rc19(isomorphic-ws@4.0.1(ws@8.18.1))(springboard@0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2))(ws@8.18.1))(springboard-server@0.15.0-rc19(@springboardjs/data-storage@0.15.0-rc19(kysely@0.28.1))(@trpc/server@10.45.2)(hono@4.7.6)(springboard@0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2)))(springboard@0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2)): + dependencies: + '@springboardjs/platforms-browser': 0.15.0-rc19(react-router-dom@6.30.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(springboard@0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2)) + '@springboardjs/platforms-node': 0.15.0-rc19(isomorphic-ws@4.0.1(ws@8.18.1))(springboard@0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2))(ws@8.18.1) + commander: 12.1.0 + concurrently: 9.1.2 + esbuild: 0.23.1 + springboard: 0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2) + springboard-server: 0.15.0-rc19(@springboardjs/data-storage@0.15.0-rc19(kysely@0.28.1))(@trpc/server@10.45.2)(hono@4.7.6)(springboard@0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2)) + typescript: 5.8.3 + + springboard-server@0.15.0-rc19(@springboardjs/data-storage@0.15.0-rc19(kysely@0.28.1))(@trpc/server@10.45.2)(hono@4.7.6)(springboard@0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2)): + dependencies: + '@hono/node-server': 1.14.1(hono@4.7.6) + '@hono/node-ws': 1.1.1(@hono/node-server@1.14.1(hono@4.7.6))(hono@4.7.6) + '@hono/trpc-server': 0.3.4(@trpc/server@10.45.2)(hono@4.7.6) + '@springboardjs/data-storage': 0.15.0-rc19(kysely@0.28.1) + hono: 4.7.6 + springboard: 0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2) + transitivePeerDependencies: + - '@trpc/server' + - bufferutil + - utf-8-validate + + springboard@0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2): + dependencies: + '@trpc/client': 10.45.2(@trpc/server@10.45.2) + dexie: 4.0.11 + immer: 10.1.1 + json-rpc-2.0: 1.7.0 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + reconnecting-websocket: 4.4.0 + rxjs: 7.8.2 + ws: 8.18.1 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-json-comments@2.0.1: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + tar-fs@2.1.2: + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.2 + tar-stream: 2.2.0 + + tar-stream@2.2.0: + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.4 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + + tree-kill@1.2.2: {} + + tslib@2.8.1: {} + + tunnel-agent@0.6.0: + dependencies: + safe-buffer: 5.2.1 + + typescript@5.8.3: {} + + util-deprecate@1.0.2: {} + + webmidi@3.1.12: + dependencies: + djipevents: 2.0.7 + optionalDependencies: + jzz: 1.9.3 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrappy@1.0.2: {} + + ws@8.18.1: {} + + xtend@4.0.2: {} + + y18n@5.0.8: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + zod@3.24.3: {} diff --git a/src/index.tsx b/src/index.tsx new file mode 100644 index 0000000..287962d --- /dev/null +++ b/src/index.tsx @@ -0,0 +1 @@ +import './jamscribe_module'; diff --git a/src/jamscribe_module.tsx b/src/jamscribe_module.tsx new file mode 100644 index 0000000..23e3979 --- /dev/null +++ b/src/jamscribe_module.tsx @@ -0,0 +1,172 @@ +// TODO: useState isn't working for some reason +import React from 'react'; + +import springboard from 'springboard'; + +import '@jamtools/core/modules/io/io_module'; +import 'springboard/modules/files/files_module'; + +import type {FileSaver, RecordingConfig} from './services/recorder'; + +let fileSaver: FileSaver | undefined; + +// @platform "node" +import fs from 'node:fs'; +fileSaver = { + writeFile: async (fileName, buffer) => { + if (!fs.existsSync('./midi_files')) { + fs.mkdirSync('midi_files') + } + + const content = buffer.toString(); + await fs.promises.writeFile(fileName, content); + }, +}; +// @platform end + +import {MidiRecorderImpl} from './services/recorder'; + +type DraftedFile = { + name: string; + buffer: Buffer; +} + +const initialRecordingConfig: RecordingConfig = { + inactivityTimeLimitSeconds: 60, +}; + +springboard.registerModule('JamScribe', {}, async (moduleAPI) => { + // @platform "node" + await moduleAPI.getModule('io').ensureListening(); + // @platform end + + const recordingConfig = await moduleAPI.statesAPI.createPersistentState('recordingConfig', initialRecordingConfig); + const draftRecordingConfig = await moduleAPI.statesAPI.createSharedState('draftRecordingConfig', recordingConfig.getState()); + + const logMessages = await moduleAPI.statesAPI.createSharedState('logMessages', []); + const draftedFiles = await moduleAPI.statesAPI.createSharedState('draftedFiles', []); + + const changeDraftInactivityTimeLimit = moduleAPI.createAction('changeDraftInactivityTimeLimit', {}, async ({limit}: {limit: number}) => { + draftRecordingConfig.setState(c => ({...c, inactivityTimeLimitSeconds: limit})); + }); + + const submitInactivityTimeLimit = moduleAPI.createAction('submitInactivityTimeLimit', {}, async () => { + recordingConfig.setState(c => ({...c, inactivityTimeLimitSeconds: draftRecordingConfig.getState().inactivityTimeLimitSeconds})); + }); + + moduleAPI.registerRoute('/', {}, () => ( +
changeDraftInactivityTimeLimit({limit})} + submitInactivityTimeLimitChange={() => submitInactivityTimeLimit({})} + /> + )); + + // bail out if this is a presentation-only client + if (!moduleAPI.deps.core.isMaestro()) { + return; + } + + // default implementation of file saver + if (!fileSaver) { + fileSaver = { + writeFile: (fileName, buffer) => { + const filesModule = moduleAPI.deps.module.moduleRegistry.getModule('Files'); + const file = new File([ + new Blob([buffer.toString()]) + ], fileName); + + filesModule.uploadFile(file); + }, + }; + } + + const log = (msg: string) => { + console.log(msg); + logMessages.setState(logs => { + return [...logs, msg] + }); + } + + const ioModule = moduleAPI.deps.module.moduleRegistry.getModule('io'); + + ioModule.midiDeviceStatusSubject.subscribe(device => { + const msg = `Device '${device.name}' ${device.status}`; + log(msg); + }); + + const recorder = new MidiRecorderImpl(ioModule.midiInputSubject, {log}, fileSaver, recordingConfig); + recorder.initialize(); +}); + +type MainProps = { + logs: string[]; + availableFiles: DraftedFile[]; + + recordingConfig: RecordingConfig; + + draftInactivityTimeLimit: number; + onDraftInactivityTimeLimitChange: (newLimit: number) => void; + submitInactivityTimeLimitChange: () => void; +} + +const Main = ({ + logs, + availableFiles, + recordingConfig, + draftInactivityTimeLimit, + onDraftInactivityTimeLimitChange, + submitInactivityTimeLimitChange +}: MainProps) => { + return ( + <> +
+ + Config + +
+                    {JSON.stringify(recordingConfig, null, 2)}
+                
+ onDraftInactivityTimeLimitChange(parseInt(e.target.value))} + /> + +
+
+ Files +
    + {availableFiles.map(file => ( +
  • { + + }} + > + {file.name} +
  • + ))} +
+
+
+
    + {logs.map((msg, i) => ( +
  • + {msg} +
  • + ))} +
+
+ + ); +} diff --git a/src/services/recorder.ts b/src/services/recorder.ts new file mode 100644 index 0000000..f44c07a --- /dev/null +++ b/src/services/recorder.ts @@ -0,0 +1,198 @@ +import {writeMidi, MidiData} from 'midi-file'; +import {Buffer} from 'buffer'; +import {Subject} from 'rxjs'; + +import {MidiEventFull} from '@jamtools/core/modules/macro_module/macro_module_types'; +import {StateSupervisor} from 'springboard/services/states/shared_state_service'; + +const sendPushNotification = (data: {title: string, data: {url: string}}) => { + +}; + +// const FIVE_SECONDS = 1000 * 5; +const TICKS_PER_BEAT = 480; // Standard MIDI timing resolution +const BPM = 120; // Default BPM + +type LoggedMidiEvent = { + event: MidiEventFull; + time: number; +}; + +type Logger = { + log: (msg: string) => void; +}; + +export type FileSaver = { + writeFile: (fileName: string, buffer: Buffer) => void; +} + +export type RecordingConfig = { + inactivityTimeLimitSeconds: number; +} + +export class MidiRecorderImpl { + private deviceActivity: {[deviceName: string]: boolean} = {}; + private deviceTimeouts: {[deviceName: string]: NodeJS.Timeout | undefined} = {}; + private recordedEvents: {[deviceName: string]: LoggedMidiEvent[]} = {}; + // private INACTIVITY_LIMIT = FIVE_SECONDS; + + constructor(private onInputEvent: Subject, private logger: Logger, private fileSaver: FileSaver, private recordingConfigState: StateSupervisor) { } + + public initialize = () => { + this.onInputEvent.subscribe(this.handleMidiEvent); + }; + + private handleMidiEvent = (midiEventFull: MidiEventFull) => { + const deviceName = midiEventFull.deviceInfo.name; + const event = midiEventFull.event; + const time = performance.now(); + + this.deviceActivity[deviceName] = true; + + // Store the event in memory + if (!this.recordedEvents[deviceName]?.length) { + this.logger.log(`Started recording ${deviceName}. Will stop after ${this.getInactivityLimit() / 1000} seconds`); + this.recordedEvents[deviceName] = []; + this.notifyUserOfStartRecording(); + } + this.recordedEvents[deviceName].push({event: midiEventFull, time}); + + this.resetDeviceInactivityTimerForDevice(deviceName); + }; + + // Stop recording and save all recorded MIDI events to a file + private stopRecordingForAllDevices = () => { + this.logger.log('Stopping all recordings due to inactivity...'); + Object.keys(this.recordedEvents).forEach((deviceName) => { + this.saveRecordedMidiToFile(deviceName); + + // Clear events after saving + this.recordedEvents[deviceName] = []; + }); + }; + + private getInactivityLimit = () => { + return this.recordingConfigState.getState().inactivityTimeLimitSeconds * 1000; + } + + private resetDeviceInactivityTimerForDevice = (deviceName: string) => { + if (this.deviceTimeouts[deviceName]) { + clearTimeout(this.deviceTimeouts[deviceName]); + } + + this.deviceTimeouts[deviceName] = setTimeout(() => { + this.logger.log(`Device ${deviceName} is now inactive.`); + this.deviceActivity[deviceName] = false; + + const allInactive = Object.values(this.deviceActivity).every(isActive => !isActive); + if (allInactive) { + this.stopRecordingForAllDevices(); + } + }, this.getInactivityLimit()); + }; + + private generateFilename = (deviceName: string): string => { + const timestamp = new Date().toISOString(); + const filename = `./midi_files/${deviceName}_${timestamp}_recording.mid`; + return filename; + }; + + private saveRecordedMidiToFile = (deviceName: string) => { + const midiEvents = this.recordedEvents[deviceName]; + if (!midiEvents || midiEvents.length === 0) { + this.logger.log(`No events recorded for device: ${deviceName}`); + return; + } + + // Convert the stored events to a MIDI file structure + const midiData: MidiData = { + header: { + format: 1, + numTracks: 1, + ticksPerBeat: TICKS_PER_BEAT, + }, + tracks: [[]], + }; + + let previousTime = midiEvents[0].time; // Set the initial time + midiEvents.forEach(({event, time}) => { + const deltaTime = this.calculateDeltaTime(previousTime, time); + previousTime = time; + + const midiTrackEvent = this.convertMidiEventToMidiFileFormat(event, deltaTime); + + if (midiTrackEvent) { + midiData.tracks[0].push(midiTrackEvent); + } + }); + + const midiFilePath = this.generateFilename(deviceName); + + // Write the MIDI file to disk + try { + const outputBuffer = Buffer.from(writeMidi(midiData)); + this.fileSaver.writeFile(midiFilePath, outputBuffer); + this.logger.log(`MIDI file saved for device: ${deviceName} at ${midiFilePath}`); + this.notifyUserOfNewRecordedSession(); + } catch (error) { + this.logger.log(`Error while saving MIDI file for ${deviceName}: ${(error as Error).message}`); + } + }; + + private notifyUserOfStartRecording = () => { + sendPushNotification({ + title: 'Started recording', + data: { + url: 'http://jamscribe.local:1337', + } + }); + }; + + private notifyUserOfNewRecordedSession = () => { + sendPushNotification({ + title: 'Stopped recording', + data: { + url: 'http://jamscribe.local:1337', + } + }); + }; + + // Convert the event to a format that `midi-file` expects + private convertMidiEventToMidiFileFormat = (event: MidiEventFull, deltaTime: number): MidiData['tracks'][0][0] | null => { + if (event.event.type === 'noteon') { + return { + deltaTime, + type: 'noteOn', + noteNumber: event.event.number, + velocity: event.event.velocity || 64, + channel: event.event.channel, + }; + } + if (event.event.type === 'noteoff') { + return { + deltaTime, + type: 'noteOff', + noteNumber: event.event.number, + velocity: 0, + channel: event.event.channel, + }; + } + if (event.event.type === 'cc') { + return { + deltaTime, + type: 'controller', + controllerType: event.event.number, + value: event.event.value!, + channel: event.event.channel, + }; + } + + return null; + }; + + private calculateDeltaTime = (previousTime: number, currentTime: number): number => { + const msPerBeat = (60 / BPM) * 1000; + const msDifference = currentTime - previousTime; + return Math.round((msDifference / msPerBeat) * TICKS_PER_BEAT); + }; +} From 84e3bd6ce35275814b884d4a2a13af82799dfad1 Mon Sep 17 00:00:00 2001 From: Michael Kochell <6913320+mickmister@users.noreply.github.com> Date: Sat, 19 Apr 2025 10:27:17 -0400 Subject: [PATCH 2/7] fix workflow --- .github/workflows/jamscribe_release.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/jamscribe_release.yml b/.github/workflows/jamscribe_release.yml index 43a2fc9..f6fc1d7 100644 --- a/.github/workflows/jamscribe_release.yml +++ b/.github/workflows/jamscribe_release.yml @@ -24,10 +24,6 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v3 - with: - repository: ${{ secrets.REPO }} - token: ${{ secrets.REPO_TOKEN }} - ref: ${{ env.TARGET_BRANCH }} - name: Install zipcmp run: sudo apt-get update && sudo apt-get install -y zipcmp From 19171a04a164664b6e283cd5427f5b9d139ddb17 Mon Sep 17 00:00:00 2001 From: Michael Kochell <6913320+mickmister@users.noreply.github.com> Date: Sat, 19 Apr 2025 10:30:25 -0400 Subject: [PATCH 3/7] fix again --- .github/workflows/jamscribe_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/jamscribe_release.yml b/.github/workflows/jamscribe_release.yml index f6fc1d7..8dbc8a7 100644 --- a/.github/workflows/jamscribe_release.yml +++ b/.github/workflows/jamscribe_release.yml @@ -33,7 +33,7 @@ jobs: with: repository: jamtools/songdrive-releases path: .github/composite-actions - ref: ${{ github.sha }} + ref: dev - name: Run build composite action uses: ./.github/composite-actions/.github/actions/build_app From 1448880a8aa8ef75c3869cdeefa94097a6e1ba0c Mon Sep 17 00:00:00 2001 From: Michael Kochell <6913320+mickmister@users.noreply.github.com> Date: Sat, 19 Apr 2025 10:35:47 -0400 Subject: [PATCH 4/7] fix workflow to support creating first release in repo --- .github/workflows/jamscribe_release.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/jamscribe_release.yml b/.github/workflows/jamscribe_release.yml index 8dbc8a7..41b90d0 100644 --- a/.github/workflows/jamscribe_release.yml +++ b/.github/workflows/jamscribe_release.yml @@ -56,12 +56,12 @@ jobs: map(select(.tagName | startswith($prefix))) | sort_by(.publishedAt) | last - | .tagName[1:] + | if . == null then "none" else .tagName[1:] end ') echo "latest_tag=$latest" >> $GITHUB_OUTPUT - name: Download artifact from latest release - if: steps.find_release.outputs.latest_tag != '' + if: steps.find_release.outputs.latest_tag != 'none' uses: blauqs/actions-download-asset@master with: repo: ${{ github.repository }} @@ -72,6 +72,7 @@ jobs: - name: Compare ZIP files using zipcmp id: compare_zips + if: steps.find_release.outputs.latest_tag != 'none' run: | if zipcmp dist.zip previous_release.zip; then echo "match=true" >> $GITHUB_OUTPUT @@ -82,20 +83,20 @@ jobs: - name: Set version number id: version run: | - if [ -n "${{ steps.find_release.outputs.latest_tag }}" ]; then + if "${{ steps.find_release.outputs.latest_tag }}" != 'none'; then # Extract number from the tag and increment current=$(echo "${{ steps.find_release.outputs.latest_tag }}" | grep -o '[0-9]\+') - echo "number=$((current + 1))" >> $GITHUB_OUTPUT + echo "new_release_number=$((current + 1))" >> $GITHUB_OUTPUT else - echo "number=1" >> $GITHUB_OUTPUT + echo "new_release_number=1" >> $GITHUB_OUTPUT fi - name: Create new release if: steps.compare_zips.outputs.match == 'false' uses: ncipollo/release-action@v1 with: - tag: vjamscribe-${{ steps.version.outputs.number }} - name: JamScribe v${{ steps.version.outputs.number }} + tag: vjamscribe-${{ steps.version.outputs.new_release_number }} + name: JamScribe v${{ steps.version.outputs.new_release_number }} body: Automated release artifacts: dist.zip token: ${{ secrets.GITHUB_TOKEN }} From 229d862cb5e33619dd851d1a3e905ace616b11b4 Mon Sep 17 00:00:00 2001 From: Michael Kochell <6913320+mickmister@users.noreply.github.com> Date: Sat, 19 Apr 2025 10:37:56 -0400 Subject: [PATCH 5/7] fix bash if statement --- .github/workflows/jamscribe_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/jamscribe_release.yml b/.github/workflows/jamscribe_release.yml index 41b90d0..34fa059 100644 --- a/.github/workflows/jamscribe_release.yml +++ b/.github/workflows/jamscribe_release.yml @@ -83,7 +83,7 @@ jobs: - name: Set version number id: version run: | - if "${{ steps.find_release.outputs.latest_tag }}" != 'none'; then + if [[ "${{ steps.find_release.outputs.latest_tag }}" != "none" ]]; then # Extract number from the tag and increment current=$(echo "${{ steps.find_release.outputs.latest_tag }}" | grep -o '[0-9]\+') echo "new_release_number=$((current + 1))" >> $GITHUB_OUTPUT From 1f63ee70aba9a16cc529c2ee4b9e2acd3f2107c1 Mon Sep 17 00:00:00 2001 From: Michael Kochell <6913320+mickmister@users.noreply.github.com> Date: Sat, 19 Apr 2025 10:40:19 -0400 Subject: [PATCH 6/7] fix release compare --- .github/workflows/jamscribe_release.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/jamscribe_release.yml b/.github/workflows/jamscribe_release.yml index 34fa059..333e335 100644 --- a/.github/workflows/jamscribe_release.yml +++ b/.github/workflows/jamscribe_release.yml @@ -72,12 +72,15 @@ jobs: - name: Compare ZIP files using zipcmp id: compare_zips - if: steps.find_release.outputs.latest_tag != 'none' run: | - if zipcmp dist.zip previous_release.zip; then - echo "match=true" >> $GITHUB_OUTPUT - else + if [[ "${{ steps.find_release.outputs.latest_tag }}" == "none" ]]; then echo "match=false" >> $GITHUB_OUTPUT + else + if zipcmp dist.zip previous_release.zip; then + echo "match=true" >> $GITHUB_OUTPUT + else + echo "match=false" >> $GITHUB_OUTPUT + fi fi - name: Set version number From 52c7855cb01633b6feababe4fefbb44d11f6c58b Mon Sep 17 00:00:00 2001 From: Michael Kochell <6913320+mickmister@users.noreply.github.com> Date: Sat, 19 Apr 2025 11:52:37 -0400 Subject: [PATCH 7/7] upgrade @jamtools/core to space out midi device discovery --- package.json | 10 +++-- pnpm-lock.yaml | 102 +++++++++++++++++++++++++++---------------------- 2 files changed, 64 insertions(+), 48 deletions(-) diff --git a/package.json b/package.json index 390bace..c290144 100644 --- a/package.json +++ b/package.json @@ -11,11 +11,15 @@ "license": "ISC", "description": "", "dependencies": { - "@jamtools/core": "0.15.0-rc19", + "@jamtools/core": "0.15.0-rc20", + "@springboardjs/data-storage": "0.15.0-rc20", + "@springboardjs/platforms-browser": "0.15.0-rc20", + "@springboardjs/platforms-node": "0.15.0-rc20", "hono": "4.7.6", "react": "^19.1.0", - "springboard": "0.15.0-rc19", - "springboard-cli": "0.15.0-rc19" + "springboard": "0.15.0-rc20", + "springboard-cli": "0.15.0-rc20", + "springboard-server": "0.15.0-rc20" }, "devDependencies": { "@types/react": "^19.1.2" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c38e05b..e4f4b53 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,8 +9,17 @@ importers: .: dependencies: '@jamtools/core': - specifier: 0.15.0-rc19 - version: 0.15.0-rc19(@tonejs/midi@2.0.28)(springboard@0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2)) + specifier: 0.15.0-rc20 + version: 0.15.0-rc20(@tonejs/midi@2.0.28)(springboard@0.15.0-rc20(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2)) + '@springboardjs/data-storage': + specifier: 0.15.0-rc20 + version: 0.15.0-rc20(kysely@0.28.1) + '@springboardjs/platforms-browser': + specifier: 0.15.0-rc20 + version: 0.15.0-rc20(react-router-dom@6.30.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(springboard@0.15.0-rc20(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2)) + '@springboardjs/platforms-node': + specifier: 0.15.0-rc20 + version: 0.15.0-rc20(isomorphic-ws@4.0.1(ws@8.18.1))(springboard@0.15.0-rc20(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2))(ws@8.18.1) hono: specifier: 4.7.6 version: 4.7.6 @@ -18,11 +27,14 @@ importers: specifier: ^19.1.0 version: 19.1.0 springboard: - specifier: 0.15.0-rc19 - version: 0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2) + specifier: 0.15.0-rc20 + version: 0.15.0-rc20(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2) springboard-cli: - specifier: 0.15.0-rc19 - version: 0.15.0-rc19(@springboardjs/platforms-browser@0.15.0-rc19(react-router-dom@6.30.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(springboard@0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2)))(@springboardjs/platforms-node@0.15.0-rc19(isomorphic-ws@4.0.1(ws@8.18.1))(springboard@0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2))(ws@8.18.1))(springboard-server@0.15.0-rc19(@springboardjs/data-storage@0.15.0-rc19(kysely@0.28.1))(@trpc/server@10.45.2)(hono@4.7.6)(springboard@0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2)))(springboard@0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2)) + specifier: 0.15.0-rc20 + version: 0.15.0-rc20(@springboardjs/platforms-browser@0.15.0-rc20(react-router-dom@6.30.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(springboard@0.15.0-rc20(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2)))(@springboardjs/platforms-node@0.15.0-rc20(isomorphic-ws@4.0.1(ws@8.18.1))(springboard@0.15.0-rc20(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2))(ws@8.18.1))(springboard-server@0.15.0-rc20(@springboardjs/data-storage@0.15.0-rc20(kysely@0.28.1))(@trpc/server@10.45.2)(hono@4.7.6)(springboard@0.15.0-rc20(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2)))(springboard@0.15.0-rc20(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2)) + springboard-server: + specifier: 0.15.0-rc20 + version: 0.15.0-rc20(@springboardjs/data-storage@0.15.0-rc20(kysely@0.28.1))(@trpc/server@10.45.2)(hono@4.7.6)(springboard@0.15.0-rc20(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2)) devDependencies: '@types/react': specifier: ^19.1.2 @@ -198,11 +210,11 @@ packages: '@trpc/server': ^10.10.0 || >11.0.0-rc hono: '>=4.*' - '@jamtools/core@0.15.0-rc19': - resolution: {integrity: sha512-JAq7BcZZess6uBoCVSNPUVXT3VFXrEkSk313i+hMIxTt+K41anewmVlvkGnghkyJf/4DUZxH0bGhZFy/7jn3NQ==} + '@jamtools/core@0.15.0-rc20': + resolution: {integrity: sha512-ZfpSYOnCQnwS4AUUljF9dMFmcaKArGwNbpGYitkfZ5tUIJdMMNhVxC47YSpA0MFftl/Kk2cZGOabFHPYw/GKIQ==} peerDependencies: '@tonejs/midi': ^2.0.0 - springboard: 0.15.0-rc19 + springboard: 0.15.0-rc20 '@julusian/midi@3.6.1': resolution: {integrity: sha512-sC6tTMAMZsHOQILAv/R0On5tKKhzBQUjdyYWzh9l0UQeNry12CFIyRWK1Mep5xCHWCTUB0w4gxngpciA5PgN/Q==} @@ -212,22 +224,22 @@ packages: resolution: {integrity: sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==} engines: {node: '>=14.0.0'} - '@springboardjs/data-storage@0.15.0-rc19': - resolution: {integrity: sha512-HW5W9Y5rZ8B4JUcQoO+Xg9973UAOi/qTnzmWbhA1KJohBRHzw8ffhkCKxqLFtVrVy2owE2L0oo0IZgUhFC3/pA==} + '@springboardjs/data-storage@0.15.0-rc20': + resolution: {integrity: sha512-ff7kd/NgieXQj/15644NTVvWz904oNXIpJ7BSrjlBnmsRGDRHrt9YrCB3v03Y1NXCn/3GsM4qiMO13MEDCcq1Q==} peerDependencies: kysely: '>= 0.24.0' - '@springboardjs/platforms-browser@0.15.0-rc19': - resolution: {integrity: sha512-R2inBKRjEdBNTdocuhND9RJdv810QIStXeSj2K8pNnTWMPFaBEdOL6Mf2hRLu7a1lyYuy/JuFoCsqWDcqsjK7g==} + '@springboardjs/platforms-browser@0.15.0-rc20': + resolution: {integrity: sha512-6EcpEeEcgTYCbvXmiUZTvm/gL7hq23js9bA1tlWi9RmHnH5kIQSUACSuJwF87Yu4o+amOSMrD4yXZBg73l2e9g==} peerDependencies: react-router-dom: ^6 - springboard: 0.15.0-rc19 + springboard: 0.15.0-rc20 - '@springboardjs/platforms-node@0.15.0-rc19': - resolution: {integrity: sha512-a/sftaPQ1cZ/nnfN1X81TCrXPLbssUQJvT+6Jqa0/M7UnjY4G0TjTYEwTUu38MqnpH9m8pZovGtOTWBVHsioZA==} + '@springboardjs/platforms-node@0.15.0-rc20': + resolution: {integrity: sha512-gEeC8HIWHPTH48nMVXaLJDXi1cWPc9o5ZDEA+v3dsob2/5YPQ4+JLXoIDPWKO7hqKDlbeSiiUC3LwJjHdq514A==} peerDependencies: isomorphic-ws: ^4.0.1 - springboard: 0.15.0-rc19 + springboard: 0.15.0-rc20 ws: ^8.18.0 '@tonejs/midi@2.0.28': @@ -581,24 +593,24 @@ packages: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} - springboard-cli@0.15.0-rc19: - resolution: {integrity: sha512-zCQh2QDEWZsF0PjHnsLRqLdObPXwJ8BnMSQzjhPsdBa5D9c1Lx348e5FLo9TdVU241df699nVzvUXTUhBWnHJQ==} + springboard-cli@0.15.0-rc20: + resolution: {integrity: sha512-yfO+YVR8RuMfSOWfawnRVq4ep+j+DpGf53Ul+cJx8DlgN0GqcAdMAMJalALiFiTExN2SAYHCUK/F+WnYNnhtJQ==} hasBin: true peerDependencies: - '@springboardjs/platforms-browser': 0.15.0-rc19 - '@springboardjs/platforms-node': 0.15.0-rc19 - springboard: 0.15.0-rc19 - springboard-server: 0.15.0-rc19 + '@springboardjs/platforms-browser': 0.15.0-rc20 + '@springboardjs/platforms-node': 0.15.0-rc20 + springboard: 0.15.0-rc20 + springboard-server: 0.15.0-rc20 - springboard-server@0.15.0-rc19: - resolution: {integrity: sha512-/P0ImAOfI4dz8thrhBBMYv5LSu5Af2no+D9fsGtFwBo3byGc6dOH3mTRsZ6pzosKu1A35CXN+m0qCys2/5qd9A==} + springboard-server@0.15.0-rc20: + resolution: {integrity: sha512-sEbT8bXwY5fF9/DmWBU6l+zrbwOSNPa+Mr+cdjHfbkgc1Tij/Zxl7O9f6o00HIQNA+kBrQrAuxy5G/LzHv4rGg==} peerDependencies: - '@springboardjs/data-storage': 0.15.0-rc19 + '@springboardjs/data-storage': 0.15.0-rc20 hono: ^4.6.7 - springboard: 0.15.0-rc19 + springboard: 0.15.0-rc20 - springboard@0.15.0-rc19: - resolution: {integrity: sha512-LDRLoCiFo9fTS/j8/9t5LPg5GlMzFlxW5CpyMHIXcl/OaehqFhagQ1GIy8gEo13SZIvEeVm5Fnwhjr8N2eKrGg==} + springboard@0.15.0-rc20: + resolution: {integrity: sha512-Hy1wu2Mk8b+3hyRPppy/hiwRF9HHQ7qeOjW2l6MS7AKrD7Dt2HygkgaH5sXZySE8N9cJQodj1WkBPpvxMuI/Cw==} peerDependencies: '@trpc/client': ^10.45.2 json-rpc-2.0: ^1.7.0 @@ -792,13 +804,13 @@ snapshots: '@trpc/server': 10.45.2 hono: 4.7.6 - '@jamtools/core@0.15.0-rc19(@tonejs/midi@2.0.28)(springboard@0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2))': + '@jamtools/core@0.15.0-rc20(@tonejs/midi@2.0.28)(springboard@0.15.0-rc20(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2))': dependencies: '@tonejs/midi': 2.0.28 easymidi: 3.1.0 midi-file: 1.2.4 soundfont-player: 0.12.0 - springboard: 0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2) + springboard: 0.15.0-rc20(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2) webmidi: 3.1.12 '@julusian/midi@3.6.1': @@ -808,7 +820,7 @@ snapshots: '@remix-run/router@1.23.0': {} - '@springboardjs/data-storage@0.15.0-rc19(kysely@0.28.1)': + '@springboardjs/data-storage@0.15.0-rc20(kysely@0.28.1)': dependencies: '@trpc/client': 10.45.2(@trpc/server@10.45.2) '@trpc/server': 10.45.2 @@ -819,15 +831,15 @@ snapshots: transitivePeerDependencies: - pg-native - '@springboardjs/platforms-browser@0.15.0-rc19(react-router-dom@6.30.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(springboard@0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2))': + '@springboardjs/platforms-browser@0.15.0-rc20(react-router-dom@6.30.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(springboard@0.15.0-rc20(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2))': dependencies: react-router-dom: 6.30.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - springboard: 0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2) + springboard: 0.15.0-rc20(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2) - '@springboardjs/platforms-node@0.15.0-rc19(isomorphic-ws@4.0.1(ws@8.18.1))(springboard@0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2))(ws@8.18.1)': + '@springboardjs/platforms-node@0.15.0-rc20(isomorphic-ws@4.0.1(ws@8.18.1))(springboard@0.15.0-rc20(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2))(ws@8.18.1)': dependencies: isomorphic-ws: 4.0.1(ws@8.18.1) - springboard: 0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2) + springboard: 0.15.0-rc20(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2) ws: 8.18.1 '@tonejs/midi@2.0.28': @@ -1178,31 +1190,31 @@ snapshots: split2@4.2.0: {} - springboard-cli@0.15.0-rc19(@springboardjs/platforms-browser@0.15.0-rc19(react-router-dom@6.30.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(springboard@0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2)))(@springboardjs/platforms-node@0.15.0-rc19(isomorphic-ws@4.0.1(ws@8.18.1))(springboard@0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2))(ws@8.18.1))(springboard-server@0.15.0-rc19(@springboardjs/data-storage@0.15.0-rc19(kysely@0.28.1))(@trpc/server@10.45.2)(hono@4.7.6)(springboard@0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2)))(springboard@0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2)): + springboard-cli@0.15.0-rc20(@springboardjs/platforms-browser@0.15.0-rc20(react-router-dom@6.30.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(springboard@0.15.0-rc20(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2)))(@springboardjs/platforms-node@0.15.0-rc20(isomorphic-ws@4.0.1(ws@8.18.1))(springboard@0.15.0-rc20(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2))(ws@8.18.1))(springboard-server@0.15.0-rc20(@springboardjs/data-storage@0.15.0-rc20(kysely@0.28.1))(@trpc/server@10.45.2)(hono@4.7.6)(springboard@0.15.0-rc20(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2)))(springboard@0.15.0-rc20(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2)): dependencies: - '@springboardjs/platforms-browser': 0.15.0-rc19(react-router-dom@6.30.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(springboard@0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2)) - '@springboardjs/platforms-node': 0.15.0-rc19(isomorphic-ws@4.0.1(ws@8.18.1))(springboard@0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2))(ws@8.18.1) + '@springboardjs/platforms-browser': 0.15.0-rc20(react-router-dom@6.30.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(springboard@0.15.0-rc20(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2)) + '@springboardjs/platforms-node': 0.15.0-rc20(isomorphic-ws@4.0.1(ws@8.18.1))(springboard@0.15.0-rc20(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2))(ws@8.18.1) commander: 12.1.0 concurrently: 9.1.2 esbuild: 0.23.1 - springboard: 0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2) - springboard-server: 0.15.0-rc19(@springboardjs/data-storage@0.15.0-rc19(kysely@0.28.1))(@trpc/server@10.45.2)(hono@4.7.6)(springboard@0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2)) + springboard: 0.15.0-rc20(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2) + springboard-server: 0.15.0-rc20(@springboardjs/data-storage@0.15.0-rc20(kysely@0.28.1))(@trpc/server@10.45.2)(hono@4.7.6)(springboard@0.15.0-rc20(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2)) typescript: 5.8.3 - springboard-server@0.15.0-rc19(@springboardjs/data-storage@0.15.0-rc19(kysely@0.28.1))(@trpc/server@10.45.2)(hono@4.7.6)(springboard@0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2)): + springboard-server@0.15.0-rc20(@springboardjs/data-storage@0.15.0-rc20(kysely@0.28.1))(@trpc/server@10.45.2)(hono@4.7.6)(springboard@0.15.0-rc20(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2)): dependencies: '@hono/node-server': 1.14.1(hono@4.7.6) '@hono/node-ws': 1.1.1(@hono/node-server@1.14.1(hono@4.7.6))(hono@4.7.6) '@hono/trpc-server': 0.3.4(@trpc/server@10.45.2)(hono@4.7.6) - '@springboardjs/data-storage': 0.15.0-rc19(kysely@0.28.1) + '@springboardjs/data-storage': 0.15.0-rc20(kysely@0.28.1) hono: 4.7.6 - springboard: 0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2) + springboard: 0.15.0-rc20(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2) transitivePeerDependencies: - '@trpc/server' - bufferutil - utf-8-validate - springboard@0.15.0-rc19(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2): + springboard@0.15.0-rc20(@trpc/client@10.45.2(@trpc/server@10.45.2))(json-rpc-2.0@1.7.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(rxjs@7.8.2): dependencies: '@trpc/client': 10.45.2(@trpc/server@10.45.2) dexie: 4.0.11