diff --git a/.github/workflows/deploy_gh_pages.yml b/.github/workflows/deploy_gh_pages.yml
new file mode 100644
index 0000000..96b44ca
--- /dev/null
+++ b/.github/workflows/deploy_gh_pages.yml
@@ -0,0 +1,51 @@
+name: Deploy to GitHub Pages
+
+on:
+ push:
+ branches:
+ - gh-deploy
+
+jobs:
+ build_site:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Install Node.js
+ uses: actions/setup-node@v3
+ with:
+ node-version: 18
+ cache: npm
+
+ - name: Install dependencies
+ run: npm install
+
+ - name: build
+ env:
+ BASE_PATH: '/${{ github.event.repository.name }}'
+ run: |
+ npm run build
+
+ - name: Upload Artifacts
+ uses: actions/upload-pages-artifact@v2
+ with:
+ # this should match the `pages` option in your adapter-static options
+ path: 'build/'
+
+ deploy:
+ needs: build_site
+ runs-on: ubuntu-latest
+
+ permissions:
+ pages: write
+ id-token: write
+
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+
+ steps:
+ - name: Deploy
+ id: deployment
+ uses: actions/deploy-pages@v2
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..6635cf5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+.DS_Store
+node_modules
+/build
+/.svelte-kit
+/package
+.env
+.env.*
+!.env.example
+vite.config.js.timestamp-*
+vite.config.ts.timestamp-*
diff --git a/.npmrc b/.npmrc
new file mode 100644
index 0000000..b6f27f1
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1 @@
+engine-strict=true
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 0000000..cc41cea
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,4 @@
+# Ignore files for PNPM, NPM and YARN
+pnpm-lock.yaml
+package-lock.json
+yarn.lock
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 0000000..9573023
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,8 @@
+{
+ "useTabs": true,
+ "singleQuote": true,
+ "trailingComma": "none",
+ "printWidth": 100,
+ "plugins": ["prettier-plugin-svelte"],
+ "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
+}
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..8e5ff78
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License Copyright (c) 2024 flo-bit
+
+Permission is hereby granted, free of
+charge, to any person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the
+following conditions:
+
+The above copyright notice and this permission notice
+(including the next paragraph) shall be included in all copies or substantial
+portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
+EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e99b347
--- /dev/null
+++ b/README.md
@@ -0,0 +1,126 @@
+# sveltekit openai realtime api
+
+this is a sveltekit port of the [openai-realtime-console](https://github.com/openai/openai-realtime-console) repository.
+
+it allows you to easily use the openai realtime api in your sveltekit project.
+
+work in progress, but it should be functional.
+
+## how to use
+
+> [!WARNING]
+> this is the setup for the client side only version of the realtime api, you will need to use a relay server to use this in production.
+> see further down for more information on how to use a relay server.
+
+1. copy the `src/lib/realtime/` folder from this repository into your sveltekit projects `src/lib/` folder
+
+2. install the dependency:
+
+```bash
+$ npm i openai/openai-realtime-api-beta
+```
+
+2. import the `Realtime` component into your svelte file and use it e.g. like so:
+
+```svelte
+
+
+{#if apiKey}
+
+{/if}
+```
+
+see `src/routes/+page.svelte` for a full example.
+
+## relay server
+
+for production use, you will need to use a relay server to use the realtime api.
+
+1. add `OPENAI_API_KEY` to your `.env` file
+
+2. tell the Realtime component to use the relay server:
+
+```svelte
+
+```
+
+then you have two options:
+
+### run the relay server with your sveltekit server
+
+change your `vite.config.ts` to this:
+
+```ts
+import { realtimeWebSocketServer } from './src/lib/realtime/realtime_server';
+import { sveltekit } from '@sveltejs/kit/vite';
+import { defineConfig } from 'vite';
+
+export default defineConfig({
+ plugins: [sveltekit(), realtimeWebSocketServer]
+});
+```
+
+### run the relay server independently
+
+- copy the `relay-server/` folder (identical to the code in the
+ [openai-realtime-console](https://github.com/openai/openai-realtime-console) repository)
+ from this repository into your project
+
+- install the dependencies:
+
+```bash
+npm i dotenv openai/openai-realtime-api-beta
+```
+
+- run the relay server:
+
+```bash
+$ node relay-server/index.js
+```
+
+- add the relay server url to your Realtime component:
+
+```
+
+```
+
+## todos
+
+- [ ] add tests
+- [ ] add more documentation
+- [ ] show waveforms
+- [ ] tool calling
+- [ ] update ui
+
+## license
+
+MIT
diff --git a/eslint.config.js b/eslint.config.js
new file mode 100644
index 0000000..a351fa9
--- /dev/null
+++ b/eslint.config.js
@@ -0,0 +1,33 @@
+import js from '@eslint/js';
+import ts from 'typescript-eslint';
+import svelte from 'eslint-plugin-svelte';
+import prettier from 'eslint-config-prettier';
+import globals from 'globals';
+
+/** @type {import('eslint').Linter.FlatConfig[]} */
+export default [
+ js.configs.recommended,
+ ...ts.configs.recommended,
+ ...svelte.configs['flat/recommended'],
+ prettier,
+ ...svelte.configs['flat/prettier'],
+ {
+ languageOptions: {
+ globals: {
+ ...globals.browser,
+ ...globals.node
+ }
+ }
+ },
+ {
+ files: ['**/*.svelte'],
+ languageOptions: {
+ parserOptions: {
+ parser: ts.parser
+ }
+ }
+ },
+ {
+ ignores: ['build/', '.svelte-kit/', 'dist/']
+ }
+];
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..7b6c940
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,4085 @@
+{
+ "name": "visualization",
+ "version": "0.0.1",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "visualization",
+ "version": "0.0.1",
+ "dependencies": {
+ "@openai/realtime-api-beta": "github:openai/openai-realtime-api-beta",
+ "@sveltejs/adapter-static": "^3.0.5",
+ "dotenv": "^16.4.5"
+ },
+ "devDependencies": {
+ "@sveltejs/adapter-auto": "^3.0.0",
+ "@sveltejs/adapter-node": "^5.2.5",
+ "@sveltejs/kit": "^2.0.0",
+ "@sveltejs/vite-plugin-svelte": "^3.0.0",
+ "@types/eslint": "^8.56.7",
+ "@types/ws": "^8.5.12",
+ "autoprefixer": "^10.4.19",
+ "eslint": "^9.0.0",
+ "eslint-config-prettier": "^9.1.0",
+ "eslint-plugin-svelte": "^2.36.0",
+ "globals": "^15.0.0",
+ "postcss": "^8.4.38",
+ "prettier": "^3.1.1",
+ "prettier-plugin-svelte": "^3.1.2",
+ "svelte": "^4.2.7",
+ "svelte-check": "^3.6.0",
+ "tailwindcss": "^3.4.3",
+ "tslib": "^2.4.1",
+ "typescript": "^5.0.0",
+ "typescript-eslint": "^8.0.0-alpha.20",
+ "vite": "^5.0.3"
+ }
+ },
+ "node_modules/@alloc/quick-lru": {
+ "version": "5.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@ampproject/remapping": {
+ "version": "2.3.0",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz",
+ "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==",
+ "cpu": [
+ "arm"
+ ],
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz",
+ "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz",
+ "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.20.2",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz",
+ "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz",
+ "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz",
+ "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz",
+ "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==",
+ "cpu": [
+ "arm"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz",
+ "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz",
+ "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==",
+ "cpu": [
+ "ia32"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz",
+ "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz",
+ "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==",
+ "cpu": [
+ "mips64el"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz",
+ "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz",
+ "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz",
+ "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz",
+ "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz",
+ "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz",
+ "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz",
+ "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz",
+ "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz",
+ "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz",
+ "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==",
+ "cpu": [
+ "x64"
+ ],
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.4.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.10.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/config-array": {
+ "version": "0.15.1",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.3",
+ "debug": "^4.3.1",
+ "minimatch": "^3.0.5"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/globals": {
+ "version": "14.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "9.4.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.3",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.3.0",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@isaacs/cliui": {
+ "version": "8.0.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^5.1.2",
+ "string-width-cjs": "npm:string-width@^4.2.0",
+ "strip-ansi": "^7.0.1",
+ "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+ "wrap-ansi": "^8.1.0",
+ "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/ansi-regex": {
+ "version": "6.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.5",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/set-array": "^1.2.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/set-array": {
+ "version": "1.2.1",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.4.15",
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.25",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@openai/realtime-api-beta": {
+ "version": "0.0.0",
+ "resolved": "git+ssh://git@github.com/openai/openai-realtime-api-beta.git#37fefd76b66624aeb828865f2fc9e3204b2b11e0",
+ "license": "MIT",
+ "dependencies": {
+ "ws": "^8.18.0"
+ }
+ },
+ "node_modules/@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@polka/url": {
+ "version": "1.0.0-next.25",
+ "license": "MIT"
+ },
+ "node_modules/@rollup/plugin-commonjs": {
+ "version": "28.0.0",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.0.tgz",
+ "integrity": "sha512-BJcu+a+Mpq476DMXG+hevgPSl56bkUoi88dKT8t3RyUp8kGuOh+2bU8Gs7zXDlu+fyZggnJ+iOBGrb/O1SorYg==",
+ "dev": true,
+ "dependencies": {
+ "@rollup/pluginutils": "^5.0.1",
+ "commondir": "^1.0.1",
+ "estree-walker": "^2.0.2",
+ "fdir": "^6.1.1",
+ "is-reference": "1.2.1",
+ "magic-string": "^0.30.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=16.0.0 || 14 >= 14.17"
+ },
+ "peerDependencies": {
+ "rollup": "^2.68.0||^3.0.0||^4.0.0"
+ },
+ "peerDependenciesMeta": {
+ "rollup": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rollup/plugin-commonjs/node_modules/estree-walker": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+ "dev": true
+ },
+ "node_modules/@rollup/plugin-commonjs/node_modules/is-reference": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
+ "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree": "*"
+ }
+ },
+ "node_modules/@rollup/plugin-commonjs/node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/@rollup/plugin-json": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz",
+ "integrity": "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==",
+ "dev": true,
+ "dependencies": {
+ "@rollup/pluginutils": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
+ },
+ "peerDependenciesMeta": {
+ "rollup": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rollup/plugin-node-resolve": {
+ "version": "15.3.0",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.3.0.tgz",
+ "integrity": "sha512-9eO5McEICxMzJpDW9OnMYSv4Sta3hmt7VtBFz5zR9273suNOydOyq/FrGeGy+KsTRFm8w0SLVhzig2ILFT63Ag==",
+ "dev": true,
+ "dependencies": {
+ "@rollup/pluginutils": "^5.0.1",
+ "@types/resolve": "1.20.2",
+ "deepmerge": "^4.2.2",
+ "is-module": "^1.0.0",
+ "resolve": "^1.22.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^2.78.0||^3.0.0||^4.0.0"
+ },
+ "peerDependenciesMeta": {
+ "rollup": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rollup/pluginutils": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.2.tgz",
+ "integrity": "sha512-/FIdS3PyZ39bjZlwqFnWqCOVnW7o963LtKMwQOD0NhQqw22gSr2YY1afu3FxRip4ZCZNsD5jq6Aaz6QV3D/Njw==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "estree-walker": "^2.0.2",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
+ },
+ "peerDependenciesMeta": {
+ "rollup": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rollup/pluginutils/node_modules/estree-walker": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+ "dev": true
+ },
+ "node_modules/@rollup/pluginutils/node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.18.0",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@sveltejs/adapter-auto": {
+ "version": "3.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "import-meta-resolve": "^4.1.0"
+ },
+ "peerDependencies": {
+ "@sveltejs/kit": "^2.0.0"
+ }
+ },
+ "node_modules/@sveltejs/adapter-node": {
+ "version": "5.2.5",
+ "resolved": "https://registry.npmjs.org/@sveltejs/adapter-node/-/adapter-node-5.2.5.tgz",
+ "integrity": "sha512-FVeysFqeIlKFpDF1Oj38gby34f6uA9FuXnV330Z0RHmSyOR9JzJs70/nFKy1Ue3fWtf7S0RemOrP66Vr9Jcmew==",
+ "dev": true,
+ "dependencies": {
+ "@rollup/plugin-commonjs": "^28.0.0",
+ "@rollup/plugin-json": "^6.1.0",
+ "@rollup/plugin-node-resolve": "^15.3.0",
+ "rollup": "^4.9.5"
+ },
+ "peerDependencies": {
+ "@sveltejs/kit": "^2.4.0"
+ }
+ },
+ "node_modules/@sveltejs/adapter-static": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.5.tgz",
+ "integrity": "sha512-kFJR7RxeB6FBvrKZWAEzIALatgy11ISaaZbcPup8JdWUdrmmfUHHTJ738YHJTEfnCiiXi6aX8Q6ePY7tnSMD6Q==",
+ "peerDependencies": {
+ "@sveltejs/kit": "^2.0.0"
+ }
+ },
+ "node_modules/@sveltejs/kit": {
+ "version": "2.5.10",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/cookie": "^0.6.0",
+ "cookie": "^0.6.0",
+ "devalue": "^5.0.0",
+ "esm-env": "^1.0.0",
+ "import-meta-resolve": "^4.1.0",
+ "kleur": "^4.1.5",
+ "magic-string": "^0.30.5",
+ "mrmime": "^2.0.0",
+ "sade": "^1.8.1",
+ "set-cookie-parser": "^2.6.0",
+ "sirv": "^2.0.4",
+ "tiny-glob": "^0.2.9"
+ },
+ "bin": {
+ "svelte-kit": "svelte-kit.js"
+ },
+ "engines": {
+ "node": ">=18.13"
+ },
+ "peerDependencies": {
+ "@sveltejs/vite-plugin-svelte": "^3.0.0",
+ "svelte": "^4.0.0 || ^5.0.0-next.0",
+ "vite": "^5.0.3"
+ }
+ },
+ "node_modules/@sveltejs/vite-plugin-svelte": {
+ "version": "3.1.1",
+ "license": "MIT",
+ "dependencies": {
+ "@sveltejs/vite-plugin-svelte-inspector": "^2.1.0",
+ "debug": "^4.3.4",
+ "deepmerge": "^4.3.1",
+ "kleur": "^4.1.5",
+ "magic-string": "^0.30.10",
+ "svelte-hmr": "^0.16.0",
+ "vitefu": "^0.2.5"
+ },
+ "engines": {
+ "node": "^18.0.0 || >=20"
+ },
+ "peerDependencies": {
+ "svelte": "^4.0.0 || ^5.0.0-next.0",
+ "vite": "^5.0.0"
+ }
+ },
+ "node_modules/@sveltejs/vite-plugin-svelte-inspector": {
+ "version": "2.1.0",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.0.0 || >=20"
+ },
+ "peerDependencies": {
+ "@sveltejs/vite-plugin-svelte": "^3.0.0",
+ "svelte": "^4.0.0 || ^5.0.0-next.0",
+ "vite": "^5.0.0"
+ }
+ },
+ "node_modules/@types/cookie": {
+ "version": "0.6.0",
+ "license": "MIT"
+ },
+ "node_modules/@types/eslint": {
+ "version": "8.56.10",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "*",
+ "@types/json-schema": "*"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.5",
+ "license": "MIT"
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "22.7.4",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.4.tgz",
+ "integrity": "sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg==",
+ "devOptional": true,
+ "dependencies": {
+ "undici-types": "~6.19.2"
+ }
+ },
+ "node_modules/@types/pug": {
+ "version": "2.0.10",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/resolve": {
+ "version": "1.20.2",
+ "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
+ "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
+ "dev": true
+ },
+ "node_modules/@types/ws": {
+ "version": "8.5.12",
+ "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz",
+ "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "8.0.0-alpha.25",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.10.0",
+ "@typescript-eslint/scope-manager": "8.0.0-alpha.25",
+ "@typescript-eslint/type-utils": "8.0.0-alpha.25",
+ "@typescript-eslint/utils": "8.0.0-alpha.25",
+ "@typescript-eslint/visitor-keys": "8.0.0-alpha.25",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.3.1",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^1.3.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
+ "eslint": "^8.57.0 || ^9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "8.0.0-alpha.25",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.0.0-alpha.25",
+ "@typescript-eslint/types": "8.0.0-alpha.25",
+ "@typescript-eslint/typescript-estree": "8.0.0-alpha.25",
+ "@typescript-eslint/visitor-keys": "8.0.0-alpha.25",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.0.0-alpha.25",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.0.0-alpha.25",
+ "@typescript-eslint/visitor-keys": "8.0.0-alpha.25"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "8.0.0-alpha.25",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/typescript-estree": "8.0.0-alpha.25",
+ "@typescript-eslint/utils": "8.0.0-alpha.25",
+ "debug": "^4.3.4",
+ "ts-api-utils": "^1.3.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "8.0.0-alpha.25",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.0.0-alpha.25",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@typescript-eslint/types": "8.0.0-alpha.25",
+ "@typescript-eslint/visitor-keys": "8.0.0-alpha.25",
+ "debug": "^4.3.4",
+ "globby": "^11.1.0",
+ "is-glob": "^4.0.3",
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "ts-api-utils": "^1.3.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+ "version": "9.0.4",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "8.0.0-alpha.25",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.4.0",
+ "@typescript-eslint/scope-manager": "8.0.0-alpha.25",
+ "@typescript-eslint/types": "8.0.0-alpha.25",
+ "@typescript-eslint/typescript-estree": "8.0.0-alpha.25"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.0.0-alpha.25",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.0.0-alpha.25",
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.11.3",
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/any-promise": {
+ "version": "1.3.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/anymatch/node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/arg": {
+ "version": "5.0.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/aria-query": {
+ "version": "5.3.0",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "dequal": "^2.0.3"
+ }
+ },
+ "node_modules/array-union": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/autoprefixer": {
+ "version": "10.4.19",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz",
+ "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/autoprefixer"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "browserslist": "^4.23.0",
+ "caniuse-lite": "^1.0.30001599",
+ "fraction.js": "^4.3.7",
+ "normalize-range": "^0.1.2",
+ "picocolors": "^1.0.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "bin": {
+ "autoprefixer": "bin/autoprefixer"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/axobject-query": {
+ "version": "4.0.0",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "dequal": "^2.0.3"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.3.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.23.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "caniuse-lite": "^1.0.30001587",
+ "electron-to-chromium": "^1.4.668",
+ "node-releases": "^2.0.14",
+ "update-browserslist-db": "^1.0.13"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/buffer-crc32": {
+ "version": "0.2.13",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/camelcase-css": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001627",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "3.6.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/chokidar/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/code-red": {
+ "version": "1.0.4",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.4.15",
+ "@types/estree": "^1.0.1",
+ "acorn": "^8.10.0",
+ "estree-walker": "^3.0.3",
+ "periscopic": "^3.1.0"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/commander": {
+ "version": "4.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/commondir": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
+ "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
+ "dev": true
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cookie": {
+ "version": "0.6.0",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/css-tree": {
+ "version": "2.3.1",
+ "license": "MIT",
+ "dependencies": {
+ "mdn-data": "2.0.30",
+ "source-map-js": "^1.0.1"
+ },
+ "engines": {
+ "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
+ }
+ },
+ "node_modules/cssesc": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "cssesc": "bin/cssesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.3.5",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/deepmerge": {
+ "version": "4.3.1",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/dequal": {
+ "version": "2.0.3",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/detect-indent": {
+ "version": "6.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/devalue": {
+ "version": "5.0.0",
+ "license": "MIT"
+ },
+ "node_modules/didyoumean": {
+ "version": "1.2.2",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/dir-glob": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-type": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/dlv": {
+ "version": "1.1.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/dotenv": {
+ "version": "16.4.5",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
+ "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.4.788",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/es6-promise": {
+ "version": "3.3.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/esbuild": {
+ "version": "0.20.2",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.20.2",
+ "@esbuild/android-arm": "0.20.2",
+ "@esbuild/android-arm64": "0.20.2",
+ "@esbuild/android-x64": "0.20.2",
+ "@esbuild/darwin-arm64": "0.20.2",
+ "@esbuild/darwin-x64": "0.20.2",
+ "@esbuild/freebsd-arm64": "0.20.2",
+ "@esbuild/freebsd-x64": "0.20.2",
+ "@esbuild/linux-arm": "0.20.2",
+ "@esbuild/linux-arm64": "0.20.2",
+ "@esbuild/linux-ia32": "0.20.2",
+ "@esbuild/linux-loong64": "0.20.2",
+ "@esbuild/linux-mips64el": "0.20.2",
+ "@esbuild/linux-ppc64": "0.20.2",
+ "@esbuild/linux-riscv64": "0.20.2",
+ "@esbuild/linux-s390x": "0.20.2",
+ "@esbuild/linux-x64": "0.20.2",
+ "@esbuild/netbsd-x64": "0.20.2",
+ "@esbuild/openbsd-x64": "0.20.2",
+ "@esbuild/sunos-x64": "0.20.2",
+ "@esbuild/win32-arm64": "0.20.2",
+ "@esbuild/win32-ia32": "0.20.2",
+ "@esbuild/win32-x64": "0.20.2"
+ }
+ },
+ "node_modules/esbuild/node_modules/@esbuild/aix-ppc64": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz",
+ "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==",
+ "cpu": [
+ "ppc64"
+ ],
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.1.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "9.4.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.6.1",
+ "@eslint/config-array": "^0.15.1",
+ "@eslint/eslintrc": "^3.1.0",
+ "@eslint/js": "9.4.0",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.3.0",
+ "@nodelib/fs.walk": "^1.2.8",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^8.0.1",
+ "eslint-visitor-keys": "^4.0.0",
+ "espree": "^10.0.1",
+ "esquery": "^1.4.2",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "is-path-inside": "^3.0.3",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3",
+ "strip-ansi": "^6.0.1",
+ "text-table": "^0.2.0"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-compat-utils": {
+ "version": "0.5.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^7.5.4"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "peerDependencies": {
+ "eslint": ">=6.0.0"
+ }
+ },
+ "node_modules/eslint-config-prettier": {
+ "version": "9.1.0",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "eslint-config-prettier": "bin/cli.js"
+ },
+ "peerDependencies": {
+ "eslint": ">=7.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-svelte": {
+ "version": "2.39.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.4.0",
+ "@jridgewell/sourcemap-codec": "^1.4.15",
+ "debug": "^4.3.4",
+ "eslint-compat-utils": "^0.5.0",
+ "esutils": "^2.0.3",
+ "known-css-properties": "^0.31.0",
+ "postcss": "^8.4.38",
+ "postcss-load-config": "^3.1.4",
+ "postcss-safe-parser": "^6.0.0",
+ "postcss-selector-parser": "^6.0.16",
+ "semver": "^7.6.0",
+ "svelte-eslint-parser": ">=0.36.0 <1.0.0"
+ },
+ "engines": {
+ "node": "^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ota-meshi"
+ },
+ "peerDependencies": {
+ "eslint": "^7.0.0 || ^8.0.0-0 || ^9.0.0-0",
+ "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0-next.112"
+ },
+ "peerDependenciesMeta": {
+ "svelte": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "8.0.1",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esm-env": {
+ "version": "1.0.0",
+ "license": "MIT"
+ },
+ "node_modules/espree": {
+ "version": "10.0.1",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.11.3",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.0.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.5.0",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estree-walker": {
+ "version": "3.0.3",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-glob/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fastq": {
+ "version": "1.17.1",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/fdir": {
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.0.tgz",
+ "integrity": "sha512-3oB133prH1o4j/L5lLW7uOCF1PlD+/It2L0eL/iAqWMB91RBbqTewABqxhj0ibBd90EEmWZq7ntIWzVaWcXTGQ==",
+ "dev": true,
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "8.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.1",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/foreground-child": {
+ "version": "3.1.1",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "cross-spawn": "^7.0.0",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/fraction.js": {
+ "version": "4.3.7",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "type": "patreon",
+ "url": "https://github.com/sponsors/rawify"
+ }
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/glob": {
+ "version": "7.2.3",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/globals": {
+ "version": "15.3.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globalyzer": {
+ "version": "0.1.0",
+ "license": "MIT"
+ },
+ "node_modules/globby": {
+ "version": "11.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-union": "^2.1.0",
+ "dir-glob": "^3.0.1",
+ "fast-glob": "^3.2.9",
+ "ignore": "^5.2.0",
+ "merge2": "^1.4.1",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globrex": {
+ "version": "0.1.2",
+ "license": "MIT"
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.3.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/import-meta-resolve": {
+ "version": "4.1.0",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.13.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-module": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
+ "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==",
+ "dev": true
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-path-inside": {
+ "version": "3.0.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-reference": {
+ "version": "3.0.2",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "*"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/jackspeak": {
+ "version": "3.1.2",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "@isaacs/cliui": "^8.0.2"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ },
+ "optionalDependencies": {
+ "@pkgjs/parseargs": "^0.11.0"
+ }
+ },
+ "node_modules/jiti": {
+ "version": "1.21.0",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jiti": "bin/jiti.js"
+ }
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/kleur": {
+ "version": "4.1.5",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/known-css-properties": {
+ "version": "0.31.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/lilconfig": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/locate-character": {
+ "version": "3.0.0",
+ "license": "MIT"
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lru-cache": {
+ "version": "10.2.2",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "14 || >=16.14"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.10",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.4.15"
+ }
+ },
+ "node_modules/mdn-data": {
+ "version": "2.0.30",
+ "license": "CC0-1.0"
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/micromatch/node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/min-indent": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "7.1.2",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/mkdirp": {
+ "version": "0.5.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minimist": "^1.2.6"
+ },
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ }
+ },
+ "node_modules/mri": {
+ "version": "1.2.0",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/mrmime": {
+ "version": "2.0.0",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "license": "MIT"
+ },
+ "node_modules/mz": {
+ "version": "2.7.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "any-promise": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "thenify-all": "^1.0.0"
+ }
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.7",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.14",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/normalize-range": {
+ "version": "0.1.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-hash": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/path-scurry": {
+ "version": "1.11.1",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "lru-cache": "^10.2.0",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/path-type": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/periscopic": {
+ "version": "3.1.0",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "estree-walker": "^3.0.0",
+ "is-reference": "^3.0.0"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.0.1",
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
+ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
+ "dev": true,
+ "optional": true,
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pify": {
+ "version": "2.3.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/pirates": {
+ "version": "4.0.6",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.4.38",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
+ "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "nanoid": "^3.3.7",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.2.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/postcss-import": {
+ "version": "15.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-value-parser": "^4.0.0",
+ "read-cache": "^1.0.0",
+ "resolve": "^1.1.7"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.0.0"
+ }
+ },
+ "node_modules/postcss-js": {
+ "version": "4.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "camelcase-css": "^2.0.1"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >= 16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.21"
+ }
+ },
+ "node_modules/postcss-load-config": {
+ "version": "3.1.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lilconfig": "^2.0.5",
+ "yaml": "^1.10.2"
+ },
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ "peerDependencies": {
+ "postcss": ">=8.0.9",
+ "ts-node": ">=9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "postcss": {
+ "optional": true
+ },
+ "ts-node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/postcss-nested": {
+ "version": "6.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.11"
+ },
+ "engines": {
+ "node": ">=12.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.14"
+ }
+ },
+ "node_modules/postcss-safe-parser": {
+ "version": "6.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ "peerDependencies": {
+ "postcss": "^8.3.3"
+ }
+ },
+ "node_modules/postcss-scss": {
+ "version": "4.0.9",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss-scss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.29"
+ }
+ },
+ "node_modules/postcss-selector-parser": {
+ "version": "6.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postcss-value-parser": {
+ "version": "4.2.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/prettier": {
+ "version": "3.3.0",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "node_modules/prettier-plugin-svelte": {
+ "version": "3.2.3",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "prettier": "^3.0.0",
+ "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/read-cache": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pify": "^2.3.0"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/readdirp/node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/resolve": {
+ "version": "1.22.8",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-core-module": "^2.13.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.0.4",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rimraf": {
+ "version": "2.7.1",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.18.0",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.5"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.18.0",
+ "@rollup/rollup-android-arm64": "4.18.0",
+ "@rollup/rollup-darwin-arm64": "4.18.0",
+ "@rollup/rollup-darwin-x64": "4.18.0",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.18.0",
+ "@rollup/rollup-linux-arm-musleabihf": "4.18.0",
+ "@rollup/rollup-linux-arm64-gnu": "4.18.0",
+ "@rollup/rollup-linux-arm64-musl": "4.18.0",
+ "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0",
+ "@rollup/rollup-linux-riscv64-gnu": "4.18.0",
+ "@rollup/rollup-linux-s390x-gnu": "4.18.0",
+ "@rollup/rollup-linux-x64-gnu": "4.18.0",
+ "@rollup/rollup-linux-x64-musl": "4.18.0",
+ "@rollup/rollup-win32-arm64-msvc": "4.18.0",
+ "@rollup/rollup-win32-ia32-msvc": "4.18.0",
+ "@rollup/rollup-win32-x64-msvc": "4.18.0",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/sade": {
+ "version": "1.8.1",
+ "license": "MIT",
+ "dependencies": {
+ "mri": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/sander": {
+ "version": "0.5.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es6-promise": "^3.1.2",
+ "graceful-fs": "^4.1.3",
+ "mkdirp": "^0.5.1",
+ "rimraf": "^2.5.2"
+ }
+ },
+ "node_modules/semver": {
+ "version": "7.6.2",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/set-cookie-parser": {
+ "version": "2.6.0",
+ "license": "MIT"
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/signal-exit": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/sirv": {
+ "version": "2.0.4",
+ "license": "MIT",
+ "dependencies": {
+ "@polka/url": "^1.0.0-next.24",
+ "mrmime": "^2.0.0",
+ "totalist": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/slash": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/sorcery": {
+ "version": "0.11.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.4.14",
+ "buffer-crc32": "^0.2.5",
+ "minimist": "^1.2.0",
+ "sander": "^0.5.0"
+ },
+ "bin": {
+ "sorcery": "bin/sorcery"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.0",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "5.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/string-width-cjs": {
+ "name": "string-width",
+ "version": "4.2.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/string-width/node_modules/ansi-regex": {
+ "version": "6.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/string-width/node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi-cjs": {
+ "name": "strip-ansi",
+ "version": "6.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-indent": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "min-indent": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/sucrase": {
+ "version": "3.35.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.2",
+ "commander": "^4.0.0",
+ "glob": "^10.3.10",
+ "lines-and-columns": "^1.1.6",
+ "mz": "^2.7.0",
+ "pirates": "^4.0.1",
+ "ts-interface-checker": "^0.1.9"
+ },
+ "bin": {
+ "sucrase": "bin/sucrase",
+ "sucrase-node": "bin/sucrase-node"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/sucrase/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/sucrase/node_modules/glob": {
+ "version": "10.4.1",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "path-scurry": "^1.11.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/sucrase/node_modules/minimatch": {
+ "version": "9.0.4",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/svelte": {
+ "version": "4.2.17",
+ "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.17.tgz",
+ "integrity": "sha512-N7m1YnoXtRf5wya5Gyx3TWuTddI4nAyayyIWFojiWV5IayDYNV5i2mRp/7qNGol4DtxEYxljmrbgp1HM6hUbmQ==",
+ "dependencies": {
+ "@ampproject/remapping": "^2.2.1",
+ "@jridgewell/sourcemap-codec": "^1.4.15",
+ "@jridgewell/trace-mapping": "^0.3.18",
+ "@types/estree": "^1.0.1",
+ "acorn": "^8.9.0",
+ "aria-query": "^5.3.0",
+ "axobject-query": "^4.0.0",
+ "code-red": "^1.0.3",
+ "css-tree": "^2.3.1",
+ "estree-walker": "^3.0.3",
+ "is-reference": "^3.0.1",
+ "locate-character": "^3.0.0",
+ "magic-string": "^0.30.4",
+ "periscopic": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/svelte-check": {
+ "version": "3.8.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/trace-mapping": "^0.3.17",
+ "chokidar": "^3.4.1",
+ "fast-glob": "^3.2.7",
+ "import-fresh": "^3.2.1",
+ "picocolors": "^1.0.0",
+ "sade": "^1.7.4",
+ "svelte-preprocess": "^5.1.3",
+ "typescript": "^5.0.3"
+ },
+ "bin": {
+ "svelte-check": "bin/svelte-check"
+ },
+ "peerDependencies": {
+ "svelte": "^3.55.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0"
+ }
+ },
+ "node_modules/svelte-eslint-parser": {
+ "version": "0.36.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-scope": "^7.2.2",
+ "eslint-visitor-keys": "^3.4.3",
+ "espree": "^9.6.1",
+ "postcss": "^8.4.38",
+ "postcss-scss": "^4.0.9"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ota-meshi"
+ },
+ "peerDependencies": {
+ "svelte": "^3.37.0 || ^4.0.0 || ^5.0.0-next.115"
+ },
+ "peerDependenciesMeta": {
+ "svelte": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/svelte-eslint-parser/node_modules/eslint-scope": {
+ "version": "7.2.2",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/svelte-eslint-parser/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/svelte-eslint-parser/node_modules/espree": {
+ "version": "9.6.1",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.9.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.4.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/svelte-hmr": {
+ "version": "0.16.0",
+ "license": "ISC",
+ "engines": {
+ "node": "^12.20 || ^14.13.1 || >= 16"
+ },
+ "peerDependencies": {
+ "svelte": "^3.19.0 || ^4.0.0"
+ }
+ },
+ "node_modules/svelte-preprocess": {
+ "version": "5.1.4",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/pug": "^2.0.6",
+ "detect-indent": "^6.1.0",
+ "magic-string": "^0.30.5",
+ "sorcery": "^0.11.0",
+ "strip-indent": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 16.0.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.10.2",
+ "coffeescript": "^2.5.1",
+ "less": "^3.11.3 || ^4.0.0",
+ "postcss": "^7 || ^8",
+ "postcss-load-config": "^2.1.0 || ^3.0.0 || ^4.0.0 || ^5.0.0",
+ "pug": "^3.0.0",
+ "sass": "^1.26.8",
+ "stylus": "^0.55.0",
+ "sugarss": "^2.0.0 || ^3.0.0 || ^4.0.0",
+ "svelte": "^3.23.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0",
+ "typescript": ">=3.9.5 || ^4.0.0 || ^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@babel/core": {
+ "optional": true
+ },
+ "coffeescript": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "postcss": {
+ "optional": true
+ },
+ "postcss-load-config": {
+ "optional": true
+ },
+ "pug": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tailwindcss": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz",
+ "integrity": "sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==",
+ "dev": true,
+ "dependencies": {
+ "@alloc/quick-lru": "^5.2.0",
+ "arg": "^5.0.2",
+ "chokidar": "^3.5.3",
+ "didyoumean": "^1.2.2",
+ "dlv": "^1.1.3",
+ "fast-glob": "^3.3.0",
+ "glob-parent": "^6.0.2",
+ "is-glob": "^4.0.3",
+ "jiti": "^1.21.0",
+ "lilconfig": "^2.1.0",
+ "micromatch": "^4.0.5",
+ "normalize-path": "^3.0.0",
+ "object-hash": "^3.0.0",
+ "picocolors": "^1.0.0",
+ "postcss": "^8.4.23",
+ "postcss-import": "^15.1.0",
+ "postcss-js": "^4.0.1",
+ "postcss-load-config": "^4.0.1",
+ "postcss-nested": "^6.0.1",
+ "postcss-selector-parser": "^6.0.11",
+ "resolve": "^1.22.2",
+ "sucrase": "^3.32.0"
+ },
+ "bin": {
+ "tailwind": "lib/cli.js",
+ "tailwindcss": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/tailwindcss/node_modules/postcss-load-config": {
+ "version": "4.0.2",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "lilconfig": "^3.0.0",
+ "yaml": "^2.3.4"
+ },
+ "engines": {
+ "node": ">= 14"
+ },
+ "peerDependencies": {
+ "postcss": ">=8.0.9",
+ "ts-node": ">=9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "postcss": {
+ "optional": true
+ },
+ "ts-node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tailwindcss/node_modules/postcss-load-config/node_modules/lilconfig": {
+ "version": "3.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antonk52"
+ }
+ },
+ "node_modules/tailwindcss/node_modules/yaml": {
+ "version": "2.4.3",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "yaml": "bin.mjs"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/text-table": {
+ "version": "0.2.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/thenify": {
+ "version": "3.3.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "any-promise": "^1.0.0"
+ }
+ },
+ "node_modules/thenify-all": {
+ "version": "1.6.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "thenify": ">= 3.1.0 < 4"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/tiny-glob": {
+ "version": "0.2.9",
+ "license": "MIT",
+ "dependencies": {
+ "globalyzer": "0.1.0",
+ "globrex": "^0.1.2"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/totalist": {
+ "version": "3.0.1",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/ts-api-utils": {
+ "version": "1.3.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.2.0"
+ }
+ },
+ "node_modules/ts-interface-checker": {
+ "version": "0.1.13",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/tslib": {
+ "version": "2.6.2",
+ "dev": true,
+ "license": "0BSD"
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.4.5",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/typescript-eslint": {
+ "version": "8.0.0-alpha.25",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/eslint-plugin": "8.0.0-alpha.25",
+ "@typescript-eslint/parser": "8.0.0-alpha.25",
+ "@typescript-eslint/utils": "8.0.0-alpha.25"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.19.8",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
+ "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
+ "devOptional": true
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.0.16",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.1.2",
+ "picocolors": "^1.0.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/vite": {
+ "version": "5.2.12",
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.20.1",
+ "postcss": "^8.4.38",
+ "rollup": "^4.13.0"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^18.0.0 || >=20.0.0",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.4.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vitefu": {
+ "version": "0.2.5",
+ "license": "MIT",
+ "peerDependencies": {
+ "vite": "^3.0.0 || ^4.0.0 || ^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "vite": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "8.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs": {
+ "name": "wrap-ansi",
+ "version": "7.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/string-width": {
+ "version": "4.2.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/ansi-regex": {
+ "version": "6.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/ws": {
+ "version": "8.18.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
+ "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/yaml": {
+ "version": "1.10.2",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..539764b
--- /dev/null
+++ b/package.json
@@ -0,0 +1,44 @@
+{
+ "name": "visualization",
+ "version": "0.0.1",
+ "private": true,
+ "scripts": {
+ "dev": "vite dev",
+ "build": "vite build",
+ "preview": "vite preview",
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
+ "lint": "prettier --check . && eslint .",
+ "format": "prettier --write ."
+ },
+ "devDependencies": {
+ "@sveltejs/adapter-auto": "^3.0.0",
+ "@sveltejs/adapter-node": "^5.2.5",
+ "@sveltejs/kit": "^2.0.0",
+ "@sveltejs/vite-plugin-svelte": "^3.0.0",
+ "@types/eslint": "^8.56.7",
+ "@types/ws": "^8.5.12",
+ "autoprefixer": "^10.4.19",
+ "eslint": "^9.0.0",
+ "eslint-config-prettier": "^9.1.0",
+ "eslint-plugin-svelte": "^2.36.0",
+ "globals": "^15.0.0",
+ "postcss": "^8.4.38",
+ "prettier": "^3.1.1",
+ "prettier-plugin-svelte": "^3.1.2",
+ "svelte": "^4.2.7",
+ "svelte-check": "^3.6.0",
+ "tailwindcss": "^3.4.3",
+ "tslib": "^2.4.1",
+ "typescript": "^5.0.0",
+ "typescript-eslint": "^8.0.0-alpha.20",
+ "vite": "^5.0.3"
+ },
+ "type": "module",
+ "dependencies": {
+ "@openai/realtime-api-beta": "github:openai/openai-realtime-api-beta",
+ "@sveltejs/adapter-static": "^3.0.5",
+ "dotenv": "^16.4.5"
+ },
+ "license": "MIT"
+}
\ No newline at end of file
diff --git a/postcss.config.js b/postcss.config.js
new file mode 100644
index 0000000..2e7af2b
--- /dev/null
+++ b/postcss.config.js
@@ -0,0 +1,6 @@
+export default {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+}
diff --git a/relay-server/index.js b/relay-server/index.js
new file mode 100644
index 0000000..4160921
--- /dev/null
+++ b/relay-server/index.js
@@ -0,0 +1,18 @@
+import { RealtimeRelay } from './lib/relay.js';
+import dotenv from 'dotenv';
+dotenv.config({ override: true });
+
+const OPENAI_API_KEY = process.env.OPENAI_API_KEY;
+
+if (!OPENAI_API_KEY) {
+ console.error(
+ `Environment variable "OPENAI_API_KEY" is required.\n` +
+ `Please set it in your .env file.`
+ );
+ process.exit(1);
+}
+
+const PORT = parseInt(process.env.PORT) || 8081;
+
+const relay = new RealtimeRelay(OPENAI_API_KEY);
+relay.listen(PORT);
diff --git a/relay-server/lib/relay.js b/relay-server/lib/relay.js
new file mode 100644
index 0000000..39dc309
--- /dev/null
+++ b/relay-server/lib/relay.js
@@ -0,0 +1,84 @@
+import { WebSocketServer } from 'ws';
+import { RealtimeClient } from '@openai/realtime-api-beta';
+
+export class RealtimeRelay {
+ constructor(apiKey) {
+ this.apiKey = apiKey;
+ this.sockets = new WeakMap();
+ this.wss = null;
+ }
+
+ listen(port) {
+ this.wss = new WebSocketServer({ port });
+ this.wss.on('connection', this.connectionHandler.bind(this));
+ this.log(`Listening on ws://localhost:${port}`);
+ }
+
+ async connectionHandler(ws, req) {
+ if (!req.url) {
+ this.log('No URL provided, closing connection.');
+ ws.close();
+ return;
+ }
+
+ const url = new URL(req.url, `http://${req.headers.host}`);
+ const pathname = url.pathname;
+
+ if (pathname !== '/') {
+ this.log(`Invalid pathname: "${pathname}"`);
+ ws.close();
+ return;
+ }
+
+ // Instantiate new client
+ this.log(`Connecting with key "${this.apiKey.slice(0, 3)}..."`);
+ const client = new RealtimeClient({ apiKey: this.apiKey });
+
+ // Relay: OpenAI Realtime API Event -> Browser Event
+ client.realtime.on('server.*', (event) => {
+ this.log(`Relaying "${event.type}" to Client`);
+ ws.send(JSON.stringify(event));
+ });
+ client.realtime.on('close', () => ws.close());
+
+ // Relay: Browser Event -> OpenAI Realtime API Event
+ // We need to queue data waiting for the OpenAI connection
+ const messageQueue = [];
+ const messageHandler = (data) => {
+ try {
+ const event = JSON.parse(data);
+ this.log(`Relaying "${event.type}" to OpenAI`);
+ client.realtime.send(event.type, event);
+ } catch (e) {
+ console.error(e.message);
+ this.log(`Error parsing event from client: ${data}`);
+ }
+ };
+ ws.on('message', (data) => {
+ if (!client.isConnected()) {
+ messageQueue.push(data);
+ } else {
+ messageHandler(data);
+ }
+ });
+ ws.on('close', () => client.disconnect());
+
+ // Connect to OpenAI Realtime API
+ try {
+ this.log(`Connecting to OpenAI...`);
+ await client.connect();
+ } catch (e) {
+ this.log(`Error connecting to OpenAI: ${e.message}`);
+ ws.close();
+ return;
+ }
+ this.log(`Connected to OpenAI successfully!`);
+ while (messageQueue.length) {
+ messageHandler(messageQueue.shift());
+ }
+ }
+
+ log(...args) {
+ console.log(`[RealtimeRelay]`, ...args);
+ }
+}
diff --git a/src/app.css b/src/app.css
new file mode 100644
index 0000000..b5c61c9
--- /dev/null
+++ b/src/app.css
@@ -0,0 +1,3 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
diff --git a/src/app.d.ts b/src/app.d.ts
new file mode 100644
index 0000000..743f07b
--- /dev/null
+++ b/src/app.d.ts
@@ -0,0 +1,13 @@
+// See https://kit.svelte.dev/docs/types#app
+// for information about these interfaces
+declare global {
+ namespace App {
+ // interface Error {}
+ // interface Locals {}
+ // interface PageData {}
+ // interface PageState {}
+ // interface Platform {}
+ }
+}
+
+export {};
diff --git a/src/app.html b/src/app.html
new file mode 100644
index 0000000..8ce1ebe
--- /dev/null
+++ b/src/app.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+ svelte realtime api
+ %sveltekit.head%
+
+
+ %sveltekit.body%
+
+
diff --git a/src/lib/index.ts b/src/lib/index.ts
new file mode 100644
index 0000000..856f2b6
--- /dev/null
+++ b/src/lib/index.ts
@@ -0,0 +1 @@
+// place files you want to import through the `$lib` alias in this folder.
diff --git a/src/lib/realtime/realtime.svelte b/src/lib/realtime/realtime.svelte
new file mode 100644
index 0000000..e328b61
--- /dev/null
+++ b/src/lib/realtime/realtime.svelte
@@ -0,0 +1,208 @@
+
diff --git a/src/lib/realtime/realtime_server.ts b/src/lib/realtime/realtime_server.ts
new file mode 100644
index 0000000..860920e
--- /dev/null
+++ b/src/lib/realtime/realtime_server.ts
@@ -0,0 +1,73 @@
+import { WebSocketServer } from 'ws';
+import { RealtimeClient } from '@openai/realtime-api-beta';
+import { loadEnv } from 'vite';
+
+export const realtimeWebSocketServer = {
+ name: 'realtimeWebSocketServer',
+ configureServer(server) {
+ console.log('Configuring WebSocket Server...');
+ if (!server.httpServer) return;
+
+ const wss = new WebSocketServer({ noServer: true });
+
+ server.httpServer.on('upgrade', (request, socket, head) => {
+ // if starts with /api/chat, then upgrade the connection
+ if (request.url?.startsWith('/api/realtime')) {
+ wss.handleUpgrade(request, socket, head, (ws) => {
+ wss.emit('connection', ws, request);
+ });
+ }
+ });
+
+ wss.on('connection', async (ws) => {
+ const env = loadEnv(process.env.NODE_ENV, process.cwd(), '');
+
+ const apiKey = env.OPENAI_API_KEY ?? 'your-fallback-api-key';
+ console.log(`Connecting with key "${apiKey.slice(0, 10)}..."`);
+
+ const messageQueue = [];
+
+ const client = new RealtimeClient({ apiKey });
+
+ const messageHandler = (data) => {
+ try {
+ const event = JSON.parse(data);
+ console.log(`Relaying "${event.type}" to OpenAI`);
+ client.realtime.send(event.type, event);
+ } catch (e) {
+ console.error(e.message);
+ console.log(`Error parsing event from client: ${data}`);
+ }
+ };
+
+ client.realtime.on('server.*', (event) => {
+ console.log(`Relaying "${event.type}" to Client`);
+ ws.send(JSON.stringify(event));
+ });
+
+ client.realtime.on('close', () => ws.close());
+
+ ws.on('message', (data) => {
+ if (!client.isConnected()) {
+ messageQueue.push(data);
+ } else {
+ messageHandler(data);
+ }
+ });
+
+ ws.on('close', () => client.disconnect());
+
+ try {
+ console.log('Connecting to OpenAI...');
+ await client.connect();
+ console.log('Connected to OpenAI successfully!');
+ } catch (e) {
+ console.log(`Error connecting to OpenAI: ${e.message}`);
+ ws.close();
+ }
+ while (messageQueue.length) {
+ messageHandler(messageQueue.shift());
+ }
+ });
+ }
+};
diff --git a/src/lib/realtime/wavtools/dist/index.d.ts b/src/lib/realtime/wavtools/dist/index.d.ts
new file mode 100644
index 0000000..9529532
--- /dev/null
+++ b/src/lib/realtime/wavtools/dist/index.d.ts
@@ -0,0 +1,6 @@
+import { AudioAnalysis } from './lib/analysis/audio_analysis.js';
+import { WavPacker } from './lib/wav_packer.js';
+import { WavStreamPlayer } from './lib/wav_stream_player.js';
+import { WavRecorder } from './lib/wav_recorder.js';
+export { AudioAnalysis, WavPacker, WavStreamPlayer, WavRecorder };
+//# sourceMappingURL=index.d.ts.map
\ No newline at end of file
diff --git a/src/lib/realtime/wavtools/dist/index.d.ts.map b/src/lib/realtime/wavtools/dist/index.d.ts.map
new file mode 100644
index 0000000..a80c055
--- /dev/null
+++ b/src/lib/realtime/wavtools/dist/index.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.js"],"names":[],"mappings":"8BAC8B,kCAAkC;0BADtC,qBAAqB;gCAEf,4BAA4B;4BAChC,uBAAuB"}
\ No newline at end of file
diff --git a/src/lib/realtime/wavtools/dist/lib/analysis/audio_analysis.d.ts b/src/lib/realtime/wavtools/dist/lib/analysis/audio_analysis.d.ts
new file mode 100644
index 0000000..fc50758
--- /dev/null
+++ b/src/lib/realtime/wavtools/dist/lib/analysis/audio_analysis.d.ts
@@ -0,0 +1,70 @@
+/**
+ * Output of AudioAnalysis for the frequency domain of the audio
+ * @typedef {Object} AudioAnalysisOutputType
+ * @property {Float32Array} values Amplitude of this frequency between {0, 1} inclusive
+ * @property {number[]} frequencies Raw frequency bucket values
+ * @property {string[]} labels Labels for the frequency bucket values
+ */
+/**
+ * Analyzes audio for visual output
+ * @class
+ */
+export class AudioAnalysis {
+ /**
+ * Retrieves frequency domain data from an AnalyserNode adjusted to a decibel range
+ * returns human-readable formatting and labels
+ * @param {AnalyserNode} analyser
+ * @param {number} sampleRate
+ * @param {Float32Array} [fftResult]
+ * @param {"frequency"|"music"|"voice"} [analysisType]
+ * @param {number} [minDecibels] default -100
+ * @param {number} [maxDecibels] default -30
+ * @returns {AudioAnalysisOutputType}
+ */
+ static getFrequencies(analyser: AnalyserNode, sampleRate: number, fftResult?: Float32Array, analysisType?: "frequency" | "music" | "voice", minDecibels?: number, maxDecibels?: number): AudioAnalysisOutputType;
+ /**
+ * Creates a new AudioAnalysis instance for an HTMLAudioElement
+ * @param {HTMLAudioElement} audioElement
+ * @param {AudioBuffer|null} [audioBuffer] If provided, will cache all frequency domain data from the buffer
+ * @returns {AudioAnalysis}
+ */
+ constructor(audioElement: HTMLAudioElement, audioBuffer?: AudioBuffer | null);
+ fftResults: any[];
+ audio: HTMLAudioElement;
+ context: any;
+ analyser: any;
+ sampleRate: any;
+ audioBuffer: any;
+ /**
+ * Gets the current frequency domain data from the playing audio track
+ * @param {"frequency"|"music"|"voice"} [analysisType]
+ * @param {number} [minDecibels] default -100
+ * @param {number} [maxDecibels] default -30
+ * @returns {AudioAnalysisOutputType}
+ */
+ getFrequencies(analysisType?: "frequency" | "music" | "voice", minDecibels?: number, maxDecibels?: number): AudioAnalysisOutputType;
+ /**
+ * Resume the internal AudioContext if it was suspended due to the lack of
+ * user interaction when the AudioAnalysis was instantiated.
+ * @returns {Promise}
+ */
+ resumeIfSuspended(): Promise;
+}
+/**
+ * Output of AudioAnalysis for the frequency domain of the audio
+ */
+export type AudioAnalysisOutputType = {
+ /**
+ * Amplitude of this frequency between {0, 1} inclusive
+ */
+ values: Float32Array;
+ /**
+ * Raw frequency bucket values
+ */
+ frequencies: number[];
+ /**
+ * Labels for the frequency bucket values
+ */
+ labels: string[];
+};
+//# sourceMappingURL=audio_analysis.d.ts.map
\ No newline at end of file
diff --git a/src/lib/realtime/wavtools/dist/lib/analysis/audio_analysis.d.ts.map b/src/lib/realtime/wavtools/dist/lib/analysis/audio_analysis.d.ts.map
new file mode 100644
index 0000000..abb292b
--- /dev/null
+++ b/src/lib/realtime/wavtools/dist/lib/analysis/audio_analysis.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"audio_analysis.d.ts","sourceRoot":"","sources":["../../../lib/analysis/audio_analysis.js"],"names":[],"mappings":"AAOA;;;;;;GAMG;AAEH;;;GAGG;AACH;IACE;;;;;;;;;;OAUG;IACH,gCARW,YAAY,cACZ,MAAM,cACN,YAAY,iBACZ,WAAW,GAAC,OAAO,GAAC,OAAO,gBAC3B,MAAM,gBACN,MAAM,GACJ,uBAAuB,CAwDnC;IAED;;;;;OAKG;IACH,0BAJW,gBAAgB,gBAChB,WAAW,GAAC,IAAI,EAkE1B;IA9DC,kBAAoB;IA2ClB,wBAAyB;IACzB,aAAkC;IAClC,cAAwB;IACxB,gBAA4B;IAC5B,iBAA8B;IAiBlC;;;;;;OAMG;IACH,8BALW,WAAW,GAAC,OAAO,GAAC,OAAO,gBAC3B,MAAM,gBACN,MAAM,GACJ,uBAAuB,CAwBnC;IAED;;;;OAIG;IACH,qBAFa,OAAO,CAAC,IAAI,CAAC,CAOzB;CACF;;;;;;;;YA9La,YAAY;;;;iBACZ,MAAM,EAAE;;;;YACR,MAAM,EAAE"}
\ No newline at end of file
diff --git a/src/lib/realtime/wavtools/dist/lib/analysis/constants.d.ts b/src/lib/realtime/wavtools/dist/lib/analysis/constants.d.ts
new file mode 100644
index 0000000..868ba15
--- /dev/null
+++ b/src/lib/realtime/wavtools/dist/lib/analysis/constants.d.ts
@@ -0,0 +1,9 @@
+/**
+ * All note frequencies from 1st to 8th octave
+ * in format "A#8" (A#, 8th octave)
+ */
+export const noteFrequencies: any[];
+export const noteFrequencyLabels: any[];
+export const voiceFrequencies: any[];
+export const voiceFrequencyLabels: any[];
+//# sourceMappingURL=constants.d.ts.map
\ No newline at end of file
diff --git a/src/lib/realtime/wavtools/dist/lib/analysis/constants.d.ts.map b/src/lib/realtime/wavtools/dist/lib/analysis/constants.d.ts.map
new file mode 100644
index 0000000..0f5d851
--- /dev/null
+++ b/src/lib/realtime/wavtools/dist/lib/analysis/constants.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../lib/analysis/constants.js"],"names":[],"mappings":"AA6BA;;;GAGG;AACH,oCAAkC;AAClC,wCAAsC;AActC,qCAKG;AACH,yCAKG"}
\ No newline at end of file
diff --git a/src/lib/realtime/wavtools/dist/lib/wav_packer.d.ts b/src/lib/realtime/wavtools/dist/lib/wav_packer.d.ts
new file mode 100644
index 0000000..4fe1187
--- /dev/null
+++ b/src/lib/realtime/wavtools/dist/lib/wav_packer.d.ts
@@ -0,0 +1,58 @@
+/**
+ * Raw wav audio file contents
+ * @typedef {Object} WavPackerAudioType
+ * @property {Blob} blob
+ * @property {string} url
+ * @property {number} channelCount
+ * @property {number} sampleRate
+ * @property {number} duration
+ */
+/**
+ * Utility class for assembling PCM16 "audio/wav" data
+ * @class
+ */
+export class WavPacker {
+ /**
+ * Converts Float32Array of amplitude data to ArrayBuffer in Int16Array format
+ * @param {Float32Array} float32Array
+ * @returns {ArrayBuffer}
+ */
+ static floatTo16BitPCM(float32Array: Float32Array): ArrayBuffer;
+ /**
+ * Concatenates two ArrayBuffers
+ * @param {ArrayBuffer} leftBuffer
+ * @param {ArrayBuffer} rightBuffer
+ * @returns {ArrayBuffer}
+ */
+ static mergeBuffers(leftBuffer: ArrayBuffer, rightBuffer: ArrayBuffer): ArrayBuffer;
+ /**
+ * Packs data into an Int16 format
+ * @private
+ * @param {number} size 0 = 1x Int16, 1 = 2x Int16
+ * @param {number} arg value to pack
+ * @returns
+ */
+ private _packData;
+ /**
+ * Packs audio into "audio/wav" Blob
+ * @param {number} sampleRate
+ * @param {{bitsPerSample: number, channels: Array, data: Int16Array}} audio
+ * @returns {WavPackerAudioType}
+ */
+ pack(sampleRate: number, audio: {
+ bitsPerSample: number;
+ channels: Array;
+ data: Int16Array;
+ }): WavPackerAudioType;
+}
+/**
+ * Raw wav audio file contents
+ */
+export type WavPackerAudioType = {
+ blob: Blob;
+ url: string;
+ channelCount: number;
+ sampleRate: number;
+ duration: number;
+};
+//# sourceMappingURL=wav_packer.d.ts.map
\ No newline at end of file
diff --git a/src/lib/realtime/wavtools/dist/lib/wav_packer.d.ts.map b/src/lib/realtime/wavtools/dist/lib/wav_packer.d.ts.map
new file mode 100644
index 0000000..96477a9
--- /dev/null
+++ b/src/lib/realtime/wavtools/dist/lib/wav_packer.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"wav_packer.d.ts","sourceRoot":"","sources":["../../lib/wav_packer.js"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;GAGG;AACH;IACE;;;;OAIG;IACH,qCAHW,YAAY,GACV,WAAW,CAWvB;IAED;;;;;OAKG;IACH,gCAJW,WAAW,eACX,WAAW,GACT,WAAW,CASvB;IAED;;;;;;OAMG;IACH,kBAKC;IAED;;;;;OAKG;IACH,iBAJW,MAAM,SACN;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC;QAAC,IAAI,EAAE,UAAU,CAAA;KAAC,GACtE,kBAAkB,CA6C9B;CACF;;;;;UA3Ga,IAAI;SACJ,MAAM;kBACN,MAAM;gBACN,MAAM;cACN,MAAM"}
\ No newline at end of file
diff --git a/src/lib/realtime/wavtools/dist/lib/wav_recorder.d.ts b/src/lib/realtime/wavtools/dist/lib/wav_recorder.d.ts
new file mode 100644
index 0000000..03cd269
--- /dev/null
+++ b/src/lib/realtime/wavtools/dist/lib/wav_recorder.d.ts
@@ -0,0 +1,167 @@
+/**
+ * Decodes audio into a wav file
+ * @typedef {Object} DecodedAudioType
+ * @property {Blob} blob
+ * @property {string} url
+ * @property {Float32Array} values
+ * @property {AudioBuffer} audioBuffer
+ */
+/**
+ * Records live stream of user audio as PCM16 "audio/wav" data
+ * @class
+ */
+export class WavRecorder {
+ /**
+ * Decodes audio data from multiple formats to a Blob, url, Float32Array and AudioBuffer
+ * @param {Blob|Float32Array|Int16Array|ArrayBuffer|number[]} audioData
+ * @param {number} sampleRate
+ * @param {number} fromSampleRate
+ * @returns {Promise}
+ */
+ static decode(audioData: Blob | Float32Array | Int16Array | ArrayBuffer | number[], sampleRate?: number, fromSampleRate?: number): Promise;
+ /**
+ * Create a new WavRecorder instance
+ * @param {{sampleRate?: number, outputToSpeakers?: boolean, debug?: boolean}} [options]
+ * @returns {WavRecorder}
+ */
+ constructor({ sampleRate, outputToSpeakers, debug, }?: {
+ sampleRate?: number;
+ outputToSpeakers?: boolean;
+ debug?: boolean;
+ });
+ scriptSrc: any;
+ sampleRate: number;
+ outputToSpeakers: boolean;
+ debug: boolean;
+ _deviceChangeCallback: () => Promise;
+ _devices: any[];
+ stream: any;
+ processor: any;
+ source: any;
+ node: any;
+ recording: boolean;
+ _lastEventId: number;
+ eventReceipts: {};
+ eventTimeout: number;
+ _chunkProcessor: () => void;
+ _chunkProcessorBuffer: {
+ raw: ArrayBuffer;
+ mono: ArrayBuffer;
+ };
+ /**
+ * Logs data in debug mode
+ * @param {...any} arguments
+ * @returns {true}
+ */
+ log(...args: any[]): true;
+ /**
+ * Retrieves the current sampleRate for the recorder
+ * @returns {number}
+ */
+ getSampleRate(): number;
+ /**
+ * Retrieves the current status of the recording
+ * @returns {"ended"|"paused"|"recording"}
+ */
+ getStatus(): "ended" | "paused" | "recording";
+ /**
+ * Sends an event to the AudioWorklet
+ * @private
+ * @param {string} name
+ * @param {{[key: string]: any}} data
+ * @param {AudioWorkletNode} [_processor]
+ * @returns {Promise<{[key: string]: any}>}
+ */
+ private _event;
+ /**
+ * Sets device change callback, remove if callback provided is `null`
+ * @param {(Array): void|null} callback
+ * @returns {true}
+ */
+ listenForDeviceChange(callback: any): true;
+ /**
+ * Manually request permission to use the microphone
+ * @returns {Promise}
+ */
+ requestPermission(): Promise;
+ /**
+ * List all eligible devices for recording, will request permission to use microphone
+ * @returns {Promise>}
+ */
+ listDevices(): Promise>;
+ /**
+ * Begins a recording session and requests microphone permissions if not already granted
+ * Microphone recording indicator will appear on browser tab but status will be "paused"
+ * @param {string} [deviceId] if no device provided, default device will be used
+ * @returns {Promise}
+ */
+ begin(deviceId?: string): Promise;
+ analyser: any;
+ /**
+ * Gets the current frequency domain data from the recording track
+ * @param {"frequency"|"music"|"voice"} [analysisType]
+ * @param {number} [minDecibels] default -100
+ * @param {number} [maxDecibels] default -30
+ * @returns {import('./analysis/audio_analysis.js').AudioAnalysisOutputType}
+ */
+ getFrequencies(analysisType?: "frequency" | "music" | "voice", minDecibels?: number, maxDecibels?: number): import("./analysis/audio_analysis.js").AudioAnalysisOutputType;
+ /**
+ * Pauses the recording
+ * Keeps microphone stream open but halts storage of audio
+ * @returns {Promise}
+ */
+ pause(): Promise;
+ /**
+ * Start recording stream and storing to memory from the connected audio source
+ * @param {(data: { mono: Int16Array; raw: Int16Array }) => any} [chunkProcessor]
+ * @param {number} [chunkSize] chunkProcessor will not be triggered until this size threshold met in mono audio
+ * @returns {Promise}
+ */
+ record(chunkProcessor?: (data: {
+ mono: Int16Array;
+ raw: Int16Array;
+ }) => any, chunkSize?: number): Promise;
+ _chunkProcessorSize: number;
+ /**
+ * Clears the audio buffer, empties stored recording
+ * @returns {Promise}
+ */
+ clear(): Promise;
+ /**
+ * Reads the current audio stream data
+ * @returns {Promise<{meanValues: Float32Array, channels: Array}>}
+ */
+ read(): Promise<{
+ meanValues: Float32Array;
+ channels: Array;
+ }>;
+ /**
+ * Saves the current audio stream to a file
+ * @param {boolean} [force] Force saving while still recording
+ * @returns {Promise}
+ */
+ save(force?: boolean): Promise;
+ /**
+ * Ends the current recording session and saves the result
+ * @returns {Promise}
+ */
+ end(): Promise;
+ /**
+ * Performs a full cleanup of WavRecorder instance
+ * Stops actively listening via microphone and removes existing listeners
+ * @returns {Promise}
+ */
+ quit(): Promise;
+}
+/**
+ * Decodes audio into a wav file
+ */
+export type DecodedAudioType = {
+ blob: Blob;
+ url: string;
+ values: Float32Array;
+ audioBuffer: AudioBuffer;
+};
+//# sourceMappingURL=wav_recorder.d.ts.map
\ No newline at end of file
diff --git a/src/lib/realtime/wavtools/dist/lib/wav_recorder.d.ts.map b/src/lib/realtime/wavtools/dist/lib/wav_recorder.d.ts.map
new file mode 100644
index 0000000..7954106
--- /dev/null
+++ b/src/lib/realtime/wavtools/dist/lib/wav_recorder.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"wav_recorder.d.ts","sourceRoot":"","sources":["../../lib/wav_recorder.js"],"names":[],"mappings":"AAIA;;;;;;;GAOG;AAEH;;;GAGG;AACH;IAsCE;;;;;;OAMG;IACH,yBALW,IAAI,GAAC,YAAY,GAAC,UAAU,GAAC,WAAW,GAAC,MAAM,EAAE,eACjD,MAAM,mBACN,MAAM,GACJ,OAAO,CAAC,gBAAgB,CAAC,CAqErC;IA/GD;;;;OAIG;IACH,uDAHW;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,gBAAgB,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAC,EAiC5E;IAxBC,eAAkC;IAElC,mBAA4B;IAC5B,0BAAwC;IACxC,eAAoB;IACpB,2CAAiC;IACjC,gBAAkB;IAElB,YAAkB;IAClB,eAAqB;IACrB,YAAkB;IAClB,UAAgB;IAChB,mBAAsB;IAEtB,qBAAqB;IACrB,kBAAuB;IACvB,qBAAwB;IAExB,4BAA+B;IAE/B;;;MAGC;IA+EH;;;;OAIG;IACH,qBAFa,IAAI,CAOhB;IAED;;;OAGG;IACH,iBAFa,MAAM,CAIlB;IAED;;;OAGG;IACH,aAFa,OAAO,GAAC,QAAQ,GAAC,WAAW,CAUxC;IAED;;;;;;;OAOG;IACH,eAqBC;IAED;;;;OAIG;IACH,sCAFa,IAAI,CAmChB;IAED;;;OAGG;IACH,qBAFa,OAAO,CAAC,IAAI,CAAC,CAoBzB;IAED;;;OAGG;IACH,eAFa,OAAO,CAAC,KAAK,CAAC,eAAe,GAAG;QAAC,OAAO,EAAE,OAAO,CAAA;KAAC,CAAC,CAAC,CA8BhE;IAED;;;;;OAKG;IACH,iBAHW,MAAM,GACJ,OAAO,CAAC,IAAI,CAAC,CAkFzB;IAHC,cAAwB;IAK1B;;;;;;OAMG;IACH,8BALW,WAAW,GAAC,OAAO,GAAC,OAAO,gBAC3B,MAAM,gBACN,MAAM,GACJ,OAAO,8BAA8B,EAAE,uBAAuB,CAkB1E;IAED;;;;OAIG;IACH,SAFa,OAAO,CAAC,IAAI,CAAC,CAezB;IAED;;;;;OAKG;IACH,wBAJW,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,UAAU,CAAC;QAAC,GAAG,EAAE,UAAU,CAAA;KAAE,KAAK,GAAG,cACpD,MAAM,GACJ,OAAO,CAAC,IAAI,CAAC,CAoBzB;IATC,4BAAoC;IAWtC;;;OAGG;IACH,SAFa,OAAO,CAAC,IAAI,CAAC,CAQzB;IAED;;;OAGG;IACH,QAFa,OAAO,CAAC;QAAC,UAAU,EAAE,YAAY,CAAC;QAAC,QAAQ,EAAE,KAAK,CAAC,YAAY,CAAC,CAAA;KAAC,CAAC,CAS9E;IAED;;;;OAIG;IACH,aAHW,OAAO,GACL,OAAO,CAAC,OAAO,iBAAiB,EAAE,kBAAkB,CAAC,CAgBjE;IAED;;;OAGG;IACH,OAFa,OAAO,CAAC,OAAO,iBAAiB,EAAE,kBAAkB,CAAC,CA8BjE;IAED;;;;OAIG;IACH,QAFa,OAAO,CAAC,IAAI,CAAC,CAQzB;CACF;;;;;UA1hBa,IAAI;SACJ,MAAM;YACN,YAAY;iBACZ,WAAW"}
\ No newline at end of file
diff --git a/src/lib/realtime/wavtools/dist/lib/wav_stream_player.d.ts b/src/lib/realtime/wavtools/dist/lib/wav_stream_player.d.ts
new file mode 100644
index 0000000..91a2263
--- /dev/null
+++ b/src/lib/realtime/wavtools/dist/lib/wav_stream_player.d.ts
@@ -0,0 +1,69 @@
+/**
+ * Plays audio streams received in raw PCM16 chunks from the browser
+ * @class
+ */
+export class WavStreamPlayer {
+ /**
+ * Creates a new WavStreamPlayer instance
+ * @param {{sampleRate?: number}} options
+ * @returns {WavStreamPlayer}
+ */
+ constructor({ sampleRate }?: {
+ sampleRate?: number;
+ });
+ scriptSrc: any;
+ sampleRate: number;
+ context: any;
+ stream: any;
+ analyser: any;
+ trackSampleOffsets: {};
+ interruptedTrackIds: {};
+ /**
+ * Connects the audio context and enables output to speakers
+ * @returns {Promise}
+ */
+ connect(): Promise;
+ /**
+ * Gets the current frequency domain data from the playing track
+ * @param {"frequency"|"music"|"voice"} [analysisType]
+ * @param {number} [minDecibels] default -100
+ * @param {number} [maxDecibels] default -30
+ * @returns {import('./analysis/audio_analysis.js').AudioAnalysisOutputType}
+ */
+ getFrequencies(analysisType?: "frequency" | "music" | "voice", minDecibels?: number, maxDecibels?: number): import("./analysis/audio_analysis.js").AudioAnalysisOutputType;
+ /**
+ * Starts audio streaming
+ * @private
+ * @returns {Promise}
+ */
+ private _start;
+ /**
+ * Adds 16BitPCM data to the currently playing audio stream
+ * You can add chunks beyond the current play point and they will be queued for play
+ * @param {ArrayBuffer|Int16Array} arrayBuffer
+ * @param {string} [trackId]
+ * @returns {Int16Array}
+ */
+ add16BitPCM(arrayBuffer: ArrayBuffer | Int16Array, trackId?: string): Int16Array;
+ /**
+ * Gets the offset (sample count) of the currently playing stream
+ * @param {boolean} [interrupt]
+ * @returns {{trackId: string|null, offset: number, currentTime: number}}
+ */
+ getTrackSampleOffset(interrupt?: boolean): {
+ trackId: string | null;
+ offset: number;
+ currentTime: number;
+ };
+ /**
+ * Strips the current stream and returns the sample offset of the audio
+ * @param {boolean} [interrupt]
+ * @returns {{trackId: string|null, offset: number, currentTime: number}}
+ */
+ interrupt(): {
+ trackId: string | null;
+ offset: number;
+ currentTime: number;
+ };
+}
+//# sourceMappingURL=wav_stream_player.d.ts.map
\ No newline at end of file
diff --git a/src/lib/realtime/wavtools/dist/lib/wav_stream_player.d.ts.map b/src/lib/realtime/wavtools/dist/lib/wav_stream_player.d.ts.map
new file mode 100644
index 0000000..500126c
--- /dev/null
+++ b/src/lib/realtime/wavtools/dist/lib/wav_stream_player.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"wav_stream_player.d.ts","sourceRoot":"","sources":["../../lib/wav_stream_player.js"],"names":[],"mappings":"AAGA;;;GAGG;AACH;IACE;;;;OAIG;IACH,6BAHW;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAC,EAW/B;IAPC,eAAmC;IACnC,mBAA4B;IAC5B,aAAmB;IACnB,YAAkB;IAClB,cAAoB;IACpB,uBAA4B;IAC5B,wBAA6B;IAG/B;;;OAGG;IACH,WAFa,OAAO,CAAC,IAAI,CAAC,CAkBzB;IAED;;;;;;OAMG;IACH,8BALW,WAAW,GAAC,OAAO,GAAC,OAAO,gBAC3B,MAAM,gBACN,MAAM,GACJ,OAAO,8BAA8B,EAAE,uBAAuB,CAkB1E;IAED;;;;OAIG;IACH,eAkBC;IAED;;;;;;OAMG;IACH,yBAJW,WAAW,GAAC,UAAU,YACtB,MAAM,GACJ,UAAU,CAqBtB;IAED;;;;OAIG;IACH,iCAHW,OAAO,GACL;QAAC,OAAO,EAAE,MAAM,GAAC,IAAI,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAC,CAqBvE;IAED;;;;OAIG;IACH,aAFa;QAAC,OAAO,EAAE,MAAM,GAAC,IAAI,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAC,CAIvE;CACF"}
\ No newline at end of file
diff --git a/src/lib/realtime/wavtools/dist/lib/worklets/audio_processor.d.ts b/src/lib/realtime/wavtools/dist/lib/worklets/audio_processor.d.ts
new file mode 100644
index 0000000..8b7c8ac
--- /dev/null
+++ b/src/lib/realtime/wavtools/dist/lib/worklets/audio_processor.d.ts
@@ -0,0 +1,2 @@
+export const AudioProcessorSrc: any;
+//# sourceMappingURL=audio_processor.d.ts.map
\ No newline at end of file
diff --git a/src/lib/realtime/wavtools/dist/lib/worklets/audio_processor.d.ts.map b/src/lib/realtime/wavtools/dist/lib/worklets/audio_processor.d.ts.map
new file mode 100644
index 0000000..d651100
--- /dev/null
+++ b/src/lib/realtime/wavtools/dist/lib/worklets/audio_processor.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"audio_processor.d.ts","sourceRoot":"","sources":["../../../lib/worklets/audio_processor.js"],"names":[],"mappings":"AAqNA,oCAAqC"}
\ No newline at end of file
diff --git a/src/lib/realtime/wavtools/dist/lib/worklets/stream_processor.d.ts b/src/lib/realtime/wavtools/dist/lib/worklets/stream_processor.d.ts
new file mode 100644
index 0000000..627da71
--- /dev/null
+++ b/src/lib/realtime/wavtools/dist/lib/worklets/stream_processor.d.ts
@@ -0,0 +1,3 @@
+export const StreamProcessorWorklet: "\nclass StreamProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n this.hasStarted = false;\n this.hasInterrupted = false;\n this.outputBuffers = [];\n this.bufferLength = 128;\n this.write = { buffer: new Float32Array(this.bufferLength), trackId: null };\n this.writeOffset = 0;\n this.trackSampleOffsets = {};\n this.port.onmessage = (event) => {\n if (event.data) {\n const payload = event.data;\n if (payload.event === 'write') {\n const int16Array = payload.buffer;\n const float32Array = new Float32Array(int16Array.length);\n for (let i = 0; i < int16Array.length; i++) {\n float32Array[i] = int16Array[i] / 0x8000; // Convert Int16 to Float32\n }\n this.writeData(float32Array, payload.trackId);\n } else if (\n payload.event === 'offset' ||\n payload.event === 'interrupt'\n ) {\n const requestId = payload.requestId;\n const trackId = this.write.trackId;\n const offset = this.trackSampleOffsets[trackId] || 0;\n this.port.postMessage({\n event: 'offset',\n requestId,\n trackId,\n offset,\n });\n if (payload.event === 'interrupt') {\n this.hasInterrupted = true;\n }\n } else {\n throw new Error(`Unhandled event \"${payload.event}\"`);\n }\n }\n };\n }\n\n writeData(float32Array, trackId = null) {\n let { buffer } = this.write;\n let offset = this.writeOffset;\n for (let i = 0; i < float32Array.length; i++) {\n buffer[offset++] = float32Array[i];\n if (offset >= buffer.length) {\n this.outputBuffers.push(this.write);\n this.write = { buffer: new Float32Array(this.bufferLength), trackId };\n buffer = this.write.buffer;\n offset = 0;\n }\n }\n this.writeOffset = offset;\n return true;\n }\n\n process(inputs, outputs, parameters) {\n const output = outputs[0];\n const outputChannelData = output[0];\n const outputBuffers = this.outputBuffers;\n if (this.hasInterrupted) {\n this.port.postMessage({ event: 'stop' });\n return false;\n } else if (outputBuffers.length) {\n this.hasStarted = true;\n const { buffer, trackId } = outputBuffers.shift();\n for (let i = 0; i < outputChannelData.length; i++) {\n outputChannelData[i] = buffer[i] || 0;\n }\n if (trackId) {\n this.trackSampleOffsets[trackId] =\n this.trackSampleOffsets[trackId] || 0;\n this.trackSampleOffsets[trackId] += buffer.length;\n }\n return true;\n } else if (this.hasStarted) {\n this.port.postMessage({ event: 'stop' });\n return false;\n } else {\n return true;\n }\n }\n}\n\nregisterProcessor('stream_processor', StreamProcessor);\n";
+export const StreamProcessorSrc: any;
+//# sourceMappingURL=stream_processor.d.ts.map
\ No newline at end of file
diff --git a/src/lib/realtime/wavtools/dist/lib/worklets/stream_processor.d.ts.map b/src/lib/realtime/wavtools/dist/lib/worklets/stream_processor.d.ts.map
new file mode 100644
index 0000000..c372e0b
--- /dev/null
+++ b/src/lib/realtime/wavtools/dist/lib/worklets/stream_processor.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"stream_processor.d.ts","sourceRoot":"","sources":["../../../lib/worklets/stream_processor.js"],"names":[],"mappings":"AAAA,q4FAyFE;AAMF,qCAAsC"}
\ No newline at end of file
diff --git a/src/lib/realtime/wavtools/index.js b/src/lib/realtime/wavtools/index.js
new file mode 100644
index 0000000..7123894
--- /dev/null
+++ b/src/lib/realtime/wavtools/index.js
@@ -0,0 +1,6 @@
+import { WavPacker } from './lib/wav_packer.js';
+import { AudioAnalysis } from './lib/analysis/audio_analysis.js';
+import { WavStreamPlayer } from './lib/wav_stream_player.js';
+import { WavRecorder } from './lib/wav_recorder.js';
+
+export { AudioAnalysis, WavPacker, WavStreamPlayer, WavRecorder };
diff --git a/src/lib/realtime/wavtools/lib/analysis/audio_analysis.js b/src/lib/realtime/wavtools/lib/analysis/audio_analysis.js
new file mode 100644
index 0000000..4af34d5
--- /dev/null
+++ b/src/lib/realtime/wavtools/lib/analysis/audio_analysis.js
@@ -0,0 +1,203 @@
+import {
+ noteFrequencies,
+ noteFrequencyLabels,
+ voiceFrequencies,
+ voiceFrequencyLabels,
+} from './constants.js';
+
+/**
+ * Output of AudioAnalysis for the frequency domain of the audio
+ * @typedef {Object} AudioAnalysisOutputType
+ * @property {Float32Array} values Amplitude of this frequency between {0, 1} inclusive
+ * @property {number[]} frequencies Raw frequency bucket values
+ * @property {string[]} labels Labels for the frequency bucket values
+ */
+
+/**
+ * Analyzes audio for visual output
+ * @class
+ */
+export class AudioAnalysis {
+ /**
+ * Retrieves frequency domain data from an AnalyserNode adjusted to a decibel range
+ * returns human-readable formatting and labels
+ * @param {AnalyserNode} analyser
+ * @param {number} sampleRate
+ * @param {Float32Array} [fftResult]
+ * @param {"frequency"|"music"|"voice"} [analysisType]
+ * @param {number} [minDecibels] default -100
+ * @param {number} [maxDecibels] default -30
+ * @returns {AudioAnalysisOutputType}
+ */
+ static getFrequencies(
+ analyser,
+ sampleRate,
+ fftResult,
+ analysisType = 'frequency',
+ minDecibels = -100,
+ maxDecibels = -30,
+ ) {
+ if (!fftResult) {
+ fftResult = new Float32Array(analyser.frequencyBinCount);
+ analyser.getFloatFrequencyData(fftResult);
+ }
+ const nyquistFrequency = sampleRate / 2;
+ const frequencyStep = (1 / fftResult.length) * nyquistFrequency;
+ let outputValues;
+ let frequencies;
+ let labels;
+ if (analysisType === 'music' || analysisType === 'voice') {
+ const useFrequencies =
+ analysisType === 'voice' ? voiceFrequencies : noteFrequencies;
+ const aggregateOutput = Array(useFrequencies.length).fill(minDecibels);
+ for (let i = 0; i < fftResult.length; i++) {
+ const frequency = i * frequencyStep;
+ const amplitude = fftResult[i];
+ for (let n = useFrequencies.length - 1; n >= 0; n--) {
+ if (frequency > useFrequencies[n]) {
+ aggregateOutput[n] = Math.max(aggregateOutput[n], amplitude);
+ break;
+ }
+ }
+ }
+ outputValues = aggregateOutput;
+ frequencies =
+ analysisType === 'voice' ? voiceFrequencies : noteFrequencies;
+ labels =
+ analysisType === 'voice' ? voiceFrequencyLabels : noteFrequencyLabels;
+ } else {
+ outputValues = Array.from(fftResult);
+ frequencies = outputValues.map((_, i) => frequencyStep * i);
+ labels = frequencies.map((f) => `${f.toFixed(2)} Hz`);
+ }
+ // We normalize to {0, 1}
+ const normalizedOutput = outputValues.map((v) => {
+ return Math.max(
+ 0,
+ Math.min((v - minDecibels) / (maxDecibels - minDecibels), 1),
+ );
+ });
+ const values = new Float32Array(normalizedOutput);
+ return {
+ values,
+ frequencies,
+ labels,
+ };
+ }
+
+ /**
+ * Creates a new AudioAnalysis instance for an HTMLAudioElement
+ * @param {HTMLAudioElement} audioElement
+ * @param {AudioBuffer|null} [audioBuffer] If provided, will cache all frequency domain data from the buffer
+ * @returns {AudioAnalysis}
+ */
+ constructor(audioElement, audioBuffer = null) {
+ this.fftResults = [];
+ if (audioBuffer) {
+ /**
+ * Modified from
+ * https://stackoverflow.com/questions/75063715/using-the-web-audio-api-to-analyze-a-song-without-playing
+ *
+ * We do this to populate FFT values for the audio if provided an `audioBuffer`
+ * The reason to do this is that Safari fails when using `createMediaElementSource`
+ * This has a non-zero RAM cost so we only opt-in to run it on Safari, Chrome is better
+ */
+ const { length, sampleRate } = audioBuffer;
+ const offlineAudioContext = new OfflineAudioContext({
+ length,
+ sampleRate,
+ });
+ const source = offlineAudioContext.createBufferSource();
+ source.buffer = audioBuffer;
+ const analyser = offlineAudioContext.createAnalyser();
+ analyser.fftSize = 8192;
+ analyser.smoothingTimeConstant = 0.1;
+ source.connect(analyser);
+ // limit is :: 128 / sampleRate;
+ // but we just want 60fps - cuts ~1s from 6MB to 1MB of RAM
+ const renderQuantumInSeconds = 1 / 60;
+ const durationInSeconds = length / sampleRate;
+ const analyze = (index) => {
+ const suspendTime = renderQuantumInSeconds * index;
+ if (suspendTime < durationInSeconds) {
+ offlineAudioContext.suspend(suspendTime).then(() => {
+ const fftResult = new Float32Array(analyser.frequencyBinCount);
+ analyser.getFloatFrequencyData(fftResult);
+ this.fftResults.push(fftResult);
+ analyze(index + 1);
+ });
+ }
+ if (index === 1) {
+ offlineAudioContext.startRendering();
+ } else {
+ offlineAudioContext.resume();
+ }
+ };
+ source.start(0);
+ analyze(1);
+ this.audio = audioElement;
+ this.context = offlineAudioContext;
+ this.analyser = analyser;
+ this.sampleRate = sampleRate;
+ this.audioBuffer = audioBuffer;
+ } else {
+ const audioContext = new AudioContext();
+ const track = audioContext.createMediaElementSource(audioElement);
+ const analyser = audioContext.createAnalyser();
+ analyser.fftSize = 8192;
+ analyser.smoothingTimeConstant = 0.1;
+ track.connect(analyser);
+ analyser.connect(audioContext.destination);
+ this.audio = audioElement;
+ this.context = audioContext;
+ this.analyser = analyser;
+ this.sampleRate = this.context.sampleRate;
+ this.audioBuffer = null;
+ }
+ }
+
+ /**
+ * Gets the current frequency domain data from the playing audio track
+ * @param {"frequency"|"music"|"voice"} [analysisType]
+ * @param {number} [minDecibels] default -100
+ * @param {number} [maxDecibels] default -30
+ * @returns {AudioAnalysisOutputType}
+ */
+ getFrequencies(
+ analysisType = 'frequency',
+ minDecibels = -100,
+ maxDecibels = -30,
+ ) {
+ let fftResult = null;
+ if (this.audioBuffer && this.fftResults.length) {
+ const pct = this.audio.currentTime / this.audio.duration;
+ const index = Math.min(
+ (pct * this.fftResults.length) | 0,
+ this.fftResults.length - 1,
+ );
+ fftResult = this.fftResults[index];
+ }
+ return AudioAnalysis.getFrequencies(
+ this.analyser,
+ this.sampleRate,
+ fftResult,
+ analysisType,
+ minDecibels,
+ maxDecibels,
+ );
+ }
+
+ /**
+ * Resume the internal AudioContext if it was suspended due to the lack of
+ * user interaction when the AudioAnalysis was instantiated.
+ * @returns {Promise}
+ */
+ async resumeIfSuspended() {
+ if (this.context.state === 'suspended') {
+ await this.context.resume();
+ }
+ return true;
+ }
+}
+
+globalThis.AudioAnalysis = AudioAnalysis;
diff --git a/src/lib/realtime/wavtools/lib/analysis/constants.js b/src/lib/realtime/wavtools/lib/analysis/constants.js
new file mode 100644
index 0000000..f14da38
--- /dev/null
+++ b/src/lib/realtime/wavtools/lib/analysis/constants.js
@@ -0,0 +1,60 @@
+/**
+ * Constants for help with visualization
+ * Helps map frequency ranges from Fast Fourier Transform
+ * to human-interpretable ranges, notably music ranges and
+ * human vocal ranges.
+ */
+
+// Eighth octave frequencies
+const octave8Frequencies = [
+ 4186.01, 4434.92, 4698.63, 4978.03, 5274.04, 5587.65, 5919.91, 6271.93,
+ 6644.88, 7040.0, 7458.62, 7902.13,
+];
+
+// Labels for each of the above frequencies
+const octave8FrequencyLabels = [
+ 'C',
+ 'C#',
+ 'D',
+ 'D#',
+ 'E',
+ 'F',
+ 'F#',
+ 'G',
+ 'G#',
+ 'A',
+ 'A#',
+ 'B',
+];
+
+/**
+ * All note frequencies from 1st to 8th octave
+ * in format "A#8" (A#, 8th octave)
+ */
+export const noteFrequencies = [];
+export const noteFrequencyLabels = [];
+for (let i = 1; i <= 8; i++) {
+ for (let f = 0; f < octave8Frequencies.length; f++) {
+ const freq = octave8Frequencies[f];
+ noteFrequencies.push(freq / Math.pow(2, 8 - i));
+ noteFrequencyLabels.push(octave8FrequencyLabels[f] + i);
+ }
+}
+
+/**
+ * Subset of the note frequencies between 32 and 2000 Hz
+ * 6 octave range: C1 to B6
+ */
+const voiceFrequencyRange = [32.0, 2000.0];
+export const voiceFrequencies = noteFrequencies.filter((_, i) => {
+ return (
+ noteFrequencies[i] > voiceFrequencyRange[0] &&
+ noteFrequencies[i] < voiceFrequencyRange[1]
+ );
+});
+export const voiceFrequencyLabels = noteFrequencyLabels.filter((_, i) => {
+ return (
+ noteFrequencies[i] > voiceFrequencyRange[0] &&
+ noteFrequencies[i] < voiceFrequencyRange[1]
+ );
+});
diff --git a/src/lib/realtime/wavtools/lib/wav_packer.js b/src/lib/realtime/wavtools/lib/wav_packer.js
new file mode 100644
index 0000000..7146b7f
--- /dev/null
+++ b/src/lib/realtime/wavtools/lib/wav_packer.js
@@ -0,0 +1,113 @@
+/**
+ * Raw wav audio file contents
+ * @typedef {Object} WavPackerAudioType
+ * @property {Blob} blob
+ * @property {string} url
+ * @property {number} channelCount
+ * @property {number} sampleRate
+ * @property {number} duration
+ */
+
+/**
+ * Utility class for assembling PCM16 "audio/wav" data
+ * @class
+ */
+export class WavPacker {
+ /**
+ * Converts Float32Array of amplitude data to ArrayBuffer in Int16Array format
+ * @param {Float32Array} float32Array
+ * @returns {ArrayBuffer}
+ */
+ static floatTo16BitPCM(float32Array) {
+ const buffer = new ArrayBuffer(float32Array.length * 2);
+ const view = new DataView(buffer);
+ let offset = 0;
+ for (let i = 0; i < float32Array.length; i++, offset += 2) {
+ let s = Math.max(-1, Math.min(1, float32Array[i]));
+ view.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7fff, true);
+ }
+ return buffer;
+ }
+
+ /**
+ * Concatenates two ArrayBuffers
+ * @param {ArrayBuffer} leftBuffer
+ * @param {ArrayBuffer} rightBuffer
+ * @returns {ArrayBuffer}
+ */
+ static mergeBuffers(leftBuffer, rightBuffer) {
+ const tmpArray = new Uint8Array(
+ leftBuffer.byteLength + rightBuffer.byteLength
+ );
+ tmpArray.set(new Uint8Array(leftBuffer), 0);
+ tmpArray.set(new Uint8Array(rightBuffer), leftBuffer.byteLength);
+ return tmpArray.buffer;
+ }
+
+ /**
+ * Packs data into an Int16 format
+ * @private
+ * @param {number} size 0 = 1x Int16, 1 = 2x Int16
+ * @param {number} arg value to pack
+ * @returns
+ */
+ _packData(size, arg) {
+ return [
+ new Uint8Array([arg, arg >> 8]),
+ new Uint8Array([arg, arg >> 8, arg >> 16, arg >> 24]),
+ ][size];
+ }
+
+ /**
+ * Packs audio into "audio/wav" Blob
+ * @param {number} sampleRate
+ * @param {{bitsPerSample: number, channels: Array, data: Int16Array}} audio
+ * @returns {WavPackerAudioType}
+ */
+ pack(sampleRate, audio) {
+ if (!audio?.bitsPerSample) {
+ throw new Error(`Missing "bitsPerSample"`);
+ } else if (!audio?.channels) {
+ throw new Error(`Missing "channels"`);
+ } else if (!audio?.data) {
+ throw new Error(`Missing "data"`);
+ }
+ const { bitsPerSample, channels, data } = audio;
+ const output = [
+ // Header
+ 'RIFF',
+ this._packData(
+ 1,
+ 4 + (8 + 24) /* chunk 1 length */ + (8 + 8) /* chunk 2 length */
+ ), // Length
+ 'WAVE',
+ // chunk 1
+ 'fmt ', // Sub-chunk identifier
+ this._packData(1, 16), // Chunk length
+ this._packData(0, 1), // Audio format (1 is linear quantization)
+ this._packData(0, channels.length),
+ this._packData(1, sampleRate),
+ this._packData(1, (sampleRate * channels.length * bitsPerSample) / 8), // Byte rate
+ this._packData(0, (channels.length * bitsPerSample) / 8),
+ this._packData(0, bitsPerSample),
+ // chunk 2
+ 'data', // Sub-chunk identifier
+ this._packData(
+ 1,
+ (channels[0].length * channels.length * bitsPerSample) / 8
+ ), // Chunk length
+ data,
+ ];
+ const blob = new Blob(output, { type: 'audio/mpeg' });
+ const url = URL.createObjectURL(blob);
+ return {
+ blob,
+ url,
+ channelCount: channels.length,
+ sampleRate,
+ duration: data.byteLength / (channels.length * sampleRate * 2),
+ };
+ }
+}
+
+globalThis.WavPacker = WavPacker;
diff --git a/src/lib/realtime/wavtools/lib/wav_recorder.js b/src/lib/realtime/wavtools/lib/wav_recorder.js
new file mode 100644
index 0000000..a4f1d04
--- /dev/null
+++ b/src/lib/realtime/wavtools/lib/wav_recorder.js
@@ -0,0 +1,548 @@
+import { AudioProcessorSrc } from './worklets/audio_processor.js';
+import { AudioAnalysis } from './analysis/audio_analysis.js';
+import { WavPacker } from './wav_packer.js';
+
+/**
+ * Decodes audio into a wav file
+ * @typedef {Object} DecodedAudioType
+ * @property {Blob} blob
+ * @property {string} url
+ * @property {Float32Array} values
+ * @property {AudioBuffer} audioBuffer
+ */
+
+/**
+ * Records live stream of user audio as PCM16 "audio/wav" data
+ * @class
+ */
+export class WavRecorder {
+ /**
+ * Create a new WavRecorder instance
+ * @param {{sampleRate?: number, outputToSpeakers?: boolean, debug?: boolean}} [options]
+ * @returns {WavRecorder}
+ */
+ constructor({
+ sampleRate = 44100,
+ outputToSpeakers = false,
+ debug = false,
+ } = {}) {
+ // Script source
+ this.scriptSrc = AudioProcessorSrc;
+ // Config
+ this.sampleRate = sampleRate;
+ this.outputToSpeakers = outputToSpeakers;
+ this.debug = !!debug;
+ this._deviceChangeCallback = null;
+ this._devices = [];
+ // State variables
+ this.stream = null;
+ this.processor = null;
+ this.source = null;
+ this.node = null;
+ this.recording = false;
+ // Event handling with AudioWorklet
+ this._lastEventId = 0;
+ this.eventReceipts = {};
+ this.eventTimeout = 5000;
+ // Process chunks of audio
+ this._chunkProcessor = () => {};
+ this._chunkProcessorSize = void 0;
+ this._chunkProcessorBuffer = {
+ raw: new ArrayBuffer(0),
+ mono: new ArrayBuffer(0),
+ };
+ }
+
+ /**
+ * Decodes audio data from multiple formats to a Blob, url, Float32Array and AudioBuffer
+ * @param {Blob|Float32Array|Int16Array|ArrayBuffer|number[]} audioData
+ * @param {number} sampleRate
+ * @param {number} fromSampleRate
+ * @returns {Promise}
+ */
+ static async decode(audioData, sampleRate = 44100, fromSampleRate = -1) {
+ const context = new AudioContext({ sampleRate });
+ let arrayBuffer;
+ let blob;
+ if (audioData instanceof Blob) {
+ if (fromSampleRate !== -1) {
+ throw new Error(
+ `Can not specify "fromSampleRate" when reading from Blob`,
+ );
+ }
+ blob = audioData;
+ arrayBuffer = await blob.arrayBuffer();
+ } else if (audioData instanceof ArrayBuffer) {
+ if (fromSampleRate !== -1) {
+ throw new Error(
+ `Can not specify "fromSampleRate" when reading from ArrayBuffer`,
+ );
+ }
+ arrayBuffer = audioData;
+ blob = new Blob([arrayBuffer], { type: 'audio/wav' });
+ } else {
+ let float32Array;
+ let data;
+ if (audioData instanceof Int16Array) {
+ data = audioData;
+ float32Array = new Float32Array(audioData.length);
+ for (let i = 0; i < audioData.length; i++) {
+ float32Array[i] = audioData[i] / 0x8000;
+ }
+ } else if (audioData instanceof Float32Array) {
+ float32Array = audioData;
+ } else if (audioData instanceof Array) {
+ float32Array = new Float32Array(audioData);
+ } else {
+ throw new Error(
+ `"audioData" must be one of: Blob, Float32Arrray, Int16Array, ArrayBuffer, Array`,
+ );
+ }
+ if (fromSampleRate === -1) {
+ throw new Error(
+ `Must specify "fromSampleRate" when reading from Float32Array, In16Array or Array`,
+ );
+ } else if (fromSampleRate < 3000) {
+ throw new Error(`Minimum "fromSampleRate" is 3000 (3kHz)`);
+ }
+ if (!data) {
+ data = WavPacker.floatTo16BitPCM(float32Array);
+ }
+ const audio = {
+ bitsPerSample: 16,
+ channels: [float32Array],
+ data,
+ };
+ const packer = new WavPacker();
+ const result = packer.pack(fromSampleRate, audio);
+ blob = result.blob;
+ arrayBuffer = await blob.arrayBuffer();
+ }
+ const audioBuffer = await context.decodeAudioData(arrayBuffer);
+ const values = audioBuffer.getChannelData(0);
+ const url = URL.createObjectURL(blob);
+ return {
+ blob,
+ url,
+ values,
+ audioBuffer,
+ };
+ }
+
+ /**
+ * Logs data in debug mode
+ * @param {...any} arguments
+ * @returns {true}
+ */
+ log() {
+ if (this.debug) {
+ this.log(...arguments);
+ }
+ return true;
+ }
+
+ /**
+ * Retrieves the current sampleRate for the recorder
+ * @returns {number}
+ */
+ getSampleRate() {
+ return this.sampleRate;
+ }
+
+ /**
+ * Retrieves the current status of the recording
+ * @returns {"ended"|"paused"|"recording"}
+ */
+ getStatus() {
+ if (!this.processor) {
+ return 'ended';
+ } else if (!this.recording) {
+ return 'paused';
+ } else {
+ return 'recording';
+ }
+ }
+
+ /**
+ * Sends an event to the AudioWorklet
+ * @private
+ * @param {string} name
+ * @param {{[key: string]: any}} data
+ * @param {AudioWorkletNode} [_processor]
+ * @returns {Promise<{[key: string]: any}>}
+ */
+ async _event(name, data = {}, _processor = null) {
+ _processor = _processor || this.processor;
+ if (!_processor) {
+ throw new Error('Can not send events without recording first');
+ }
+ const message = {
+ event: name,
+ id: this._lastEventId++,
+ data,
+ };
+ _processor.port.postMessage(message);
+ const t0 = new Date().valueOf();
+ while (!this.eventReceipts[message.id]) {
+ if (new Date().valueOf() - t0 > this.eventTimeout) {
+ throw new Error(`Timeout waiting for "${name}" event`);
+ }
+ await new Promise((res) => setTimeout(() => res(true), 1));
+ }
+ const payload = this.eventReceipts[message.id];
+ delete this.eventReceipts[message.id];
+ return payload;
+ }
+
+ /**
+ * Sets device change callback, remove if callback provided is `null`
+ * @param {(Array): void|null} callback
+ * @returns {true}
+ */
+ listenForDeviceChange(callback) {
+ if (callback === null && this._deviceChangeCallback) {
+ navigator.mediaDevices.removeEventListener(
+ 'devicechange',
+ this._deviceChangeCallback,
+ );
+ this._deviceChangeCallback = null;
+ } else if (callback !== null) {
+ // Basically a debounce; we only want this called once when devices change
+ // And we only want the most recent callback() to be executed
+ // if a few are operating at the same time
+ let lastId = 0;
+ let lastDevices = [];
+ const serializeDevices = (devices) =>
+ devices
+ .map((d) => d.deviceId)
+ .sort()
+ .join(',');
+ const cb = async () => {
+ let id = ++lastId;
+ const devices = await this.listDevices();
+ if (id === lastId) {
+ if (serializeDevices(lastDevices) !== serializeDevices(devices)) {
+ lastDevices = devices;
+ callback(devices.slice());
+ }
+ }
+ };
+ navigator.mediaDevices.addEventListener('devicechange', cb);
+ cb();
+ this._deviceChangeCallback = cb;
+ }
+ return true;
+ }
+
+ /**
+ * Manually request permission to use the microphone
+ * @returns {Promise}
+ */
+ async requestPermission() {
+ const permissionStatus = await navigator.permissions.query({
+ name: 'microphone',
+ });
+ if (permissionStatus.state === 'denied') {
+ window.alert('You must grant microphone access to use this feature.');
+ } else if (permissionStatus.state === 'prompt') {
+ try {
+ const stream = await navigator.mediaDevices.getUserMedia({
+ audio: true,
+ });
+ const tracks = stream.getTracks();
+ tracks.forEach((track) => track.stop());
+ } catch (e) {
+ window.alert('You must grant microphone access to use this feature.');
+ }
+ }
+ return true;
+ }
+
+ /**
+ * List all eligible devices for recording, will request permission to use microphone
+ * @returns {Promise>}
+ */
+ async listDevices() {
+ if (
+ !navigator.mediaDevices ||
+ !('enumerateDevices' in navigator.mediaDevices)
+ ) {
+ throw new Error('Could not request user devices');
+ }
+ await this.requestPermission();
+ const devices = await navigator.mediaDevices.enumerateDevices();
+ const audioDevices = devices.filter(
+ (device) => device.kind === 'audioinput',
+ );
+ const defaultDeviceIndex = audioDevices.findIndex(
+ (device) => device.deviceId === 'default',
+ );
+ const deviceList = [];
+ if (defaultDeviceIndex !== -1) {
+ let defaultDevice = audioDevices.splice(defaultDeviceIndex, 1)[0];
+ let existingIndex = audioDevices.findIndex(
+ (device) => device.groupId === defaultDevice.groupId,
+ );
+ if (existingIndex !== -1) {
+ defaultDevice = audioDevices.splice(existingIndex, 1)[0];
+ }
+ defaultDevice.default = true;
+ deviceList.push(defaultDevice);
+ }
+ return deviceList.concat(audioDevices);
+ }
+
+ /**
+ * Begins a recording session and requests microphone permissions if not already granted
+ * Microphone recording indicator will appear on browser tab but status will be "paused"
+ * @param {string} [deviceId] if no device provided, default device will be used
+ * @returns {Promise}
+ */
+ async begin(deviceId) {
+ if (this.processor) {
+ throw new Error(
+ `Already connected: please call .end() to start a new session`,
+ );
+ }
+
+ if (
+ !navigator.mediaDevices ||
+ !('getUserMedia' in navigator.mediaDevices)
+ ) {
+ throw new Error('Could not request user media');
+ }
+ try {
+ const config = { audio: true };
+ if (deviceId) {
+ config.audio = { deviceId: { exact: deviceId } };
+ }
+ this.stream = await navigator.mediaDevices.getUserMedia(config);
+ } catch (err) {
+ throw new Error('Could not start media stream');
+ }
+
+ const context = new AudioContext({ sampleRate: this.sampleRate });
+ const source = context.createMediaStreamSource(this.stream);
+ // Load and execute the module script.
+ try {
+ await context.audioWorklet.addModule(this.scriptSrc);
+ } catch (e) {
+ console.error(e);
+ throw new Error(`Could not add audioWorklet module: ${this.scriptSrc}`);
+ }
+ const processor = new AudioWorkletNode(context, 'audio_processor');
+ processor.port.onmessage = (e) => {
+ const { event, id, data } = e.data;
+ if (event === 'receipt') {
+ this.eventReceipts[id] = data;
+ } else if (event === 'chunk') {
+ if (this._chunkProcessorSize) {
+ const buffer = this._chunkProcessorBuffer;
+ this._chunkProcessorBuffer = {
+ raw: WavPacker.mergeBuffers(buffer.raw, data.raw),
+ mono: WavPacker.mergeBuffers(buffer.mono, data.mono),
+ };
+ if (
+ this._chunkProcessorBuffer.mono.byteLength >=
+ this._chunkProcessorSize
+ ) {
+ this._chunkProcessor(this._chunkProcessorBuffer);
+ this._chunkProcessorBuffer = {
+ raw: new ArrayBuffer(0),
+ mono: new ArrayBuffer(0),
+ };
+ }
+ } else {
+ this._chunkProcessor(data);
+ }
+ }
+ };
+
+ const node = source.connect(processor);
+ const analyser = context.createAnalyser();
+ analyser.fftSize = 8192;
+ analyser.smoothingTimeConstant = 0.1;
+ node.connect(analyser);
+ if (this.outputToSpeakers) {
+ // eslint-disable-next-line no-console
+ console.warn(
+ 'Warning: Output to speakers may affect sound quality,\n' +
+ 'especially due to system audio feedback preventative measures.\n' +
+ 'use only for debugging',
+ );
+ analyser.connect(context.destination);
+ }
+
+ this.source = source;
+ this.node = node;
+ this.analyser = analyser;
+ this.processor = processor;
+ return true;
+ }
+
+ /**
+ * Gets the current frequency domain data from the recording track
+ * @param {"frequency"|"music"|"voice"} [analysisType]
+ * @param {number} [minDecibels] default -100
+ * @param {number} [maxDecibels] default -30
+ * @returns {import('./analysis/audio_analysis.js').AudioAnalysisOutputType}
+ */
+ getFrequencies(
+ analysisType = 'frequency',
+ minDecibels = -100,
+ maxDecibels = -30,
+ ) {
+ if (!this.processor) {
+ throw new Error('Session ended: please call .begin() first');
+ }
+ return AudioAnalysis.getFrequencies(
+ this.analyser,
+ this.sampleRate,
+ null,
+ analysisType,
+ minDecibels,
+ maxDecibels,
+ );
+ }
+
+ /**
+ * Pauses the recording
+ * Keeps microphone stream open but halts storage of audio
+ * @returns {Promise}
+ */
+ async pause() {
+ if (!this.processor) {
+ throw new Error('Session ended: please call .begin() first');
+ } else if (!this.recording) {
+ throw new Error('Already paused: please call .record() first');
+ }
+ if (this._chunkProcessorBuffer.raw.byteLength) {
+ this._chunkProcessor(this._chunkProcessorBuffer);
+ }
+ this.log('Pausing ...');
+ await this._event('stop');
+ this.recording = false;
+ return true;
+ }
+
+ /**
+ * Start recording stream and storing to memory from the connected audio source
+ * @param {(data: { mono: Int16Array; raw: Int16Array }) => any} [chunkProcessor]
+ * @param {number} [chunkSize] chunkProcessor will not be triggered until this size threshold met in mono audio
+ * @returns {Promise}
+ */
+ async record(chunkProcessor = () => {}, chunkSize = 8192) {
+ if (!this.processor) {
+ throw new Error('Session ended: please call .begin() first');
+ } else if (this.recording) {
+ throw new Error('Already recording: please call .pause() first');
+ } else if (typeof chunkProcessor !== 'function') {
+ throw new Error(`chunkProcessor must be a function`);
+ }
+ this._chunkProcessor = chunkProcessor;
+ this._chunkProcessorSize = chunkSize;
+ this._chunkProcessorBuffer = {
+ raw: new ArrayBuffer(0),
+ mono: new ArrayBuffer(0),
+ };
+ this.log('Recording ...');
+ await this._event('start');
+ this.recording = true;
+ return true;
+ }
+
+ /**
+ * Clears the audio buffer, empties stored recording
+ * @returns {Promise}
+ */
+ async clear() {
+ if (!this.processor) {
+ throw new Error('Session ended: please call .begin() first');
+ }
+ await this._event('clear');
+ return true;
+ }
+
+ /**
+ * Reads the current audio stream data
+ * @returns {Promise<{meanValues: Float32Array, channels: Array}>}
+ */
+ async read() {
+ if (!this.processor) {
+ throw new Error('Session ended: please call .begin() first');
+ }
+ this.log('Reading ...');
+ const result = await this._event('read');
+ return result;
+ }
+
+ /**
+ * Saves the current audio stream to a file
+ * @param {boolean} [force] Force saving while still recording
+ * @returns {Promise}
+ */
+ async save(force = false) {
+ if (!this.processor) {
+ throw new Error('Session ended: please call .begin() first');
+ }
+ if (!force && this.recording) {
+ throw new Error(
+ 'Currently recording: please call .pause() first, or call .save(true) to force',
+ );
+ }
+ this.log('Exporting ...');
+ const exportData = await this._event('export');
+ const packer = new WavPacker();
+ const result = packer.pack(this.sampleRate, exportData.audio);
+ return result;
+ }
+
+ /**
+ * Ends the current recording session and saves the result
+ * @returns {Promise}
+ */
+ async end() {
+ if (!this.processor) {
+ throw new Error('Session ended: please call .begin() first');
+ }
+
+ const _processor = this.processor;
+
+ this.log('Stopping ...');
+ await this._event('stop');
+ this.recording = false;
+ const tracks = this.stream.getTracks();
+ tracks.forEach((track) => track.stop());
+
+ this.log('Exporting ...');
+ const exportData = await this._event('export', {}, _processor);
+
+ this.processor.disconnect();
+ this.source.disconnect();
+ this.node.disconnect();
+ this.analyser.disconnect();
+ this.stream = null;
+ this.processor = null;
+ this.source = null;
+ this.node = null;
+
+ const packer = new WavPacker();
+ const result = packer.pack(this.sampleRate, exportData.audio);
+ return result;
+ }
+
+ /**
+ * Performs a full cleanup of WavRecorder instance
+ * Stops actively listening via microphone and removes existing listeners
+ * @returns {Promise}
+ */
+ async quit() {
+ this.listenForDeviceChange(null);
+ if (this.processor) {
+ await this.end();
+ }
+ return true;
+ }
+}
+
+globalThis.WavRecorder = WavRecorder;
diff --git a/src/lib/realtime/wavtools/lib/wav_stream_player.js b/src/lib/realtime/wavtools/lib/wav_stream_player.js
new file mode 100644
index 0000000..500eff6
--- /dev/null
+++ b/src/lib/realtime/wavtools/lib/wav_stream_player.js
@@ -0,0 +1,160 @@
+import { StreamProcessorSrc } from './worklets/stream_processor.js';
+import { AudioAnalysis } from './analysis/audio_analysis.js';
+
+/**
+ * Plays audio streams received in raw PCM16 chunks from the browser
+ * @class
+ */
+export class WavStreamPlayer {
+ /**
+ * Creates a new WavStreamPlayer instance
+ * @param {{sampleRate?: number}} options
+ * @returns {WavStreamPlayer}
+ */
+ constructor({ sampleRate = 44100 } = {}) {
+ this.scriptSrc = StreamProcessorSrc;
+ this.sampleRate = sampleRate;
+ this.context = null;
+ this.stream = null;
+ this.analyser = null;
+ this.trackSampleOffsets = {};
+ this.interruptedTrackIds = {};
+ }
+
+ /**
+ * Connects the audio context and enables output to speakers
+ * @returns {Promise}
+ */
+ async connect() {
+ this.context = new AudioContext({ sampleRate: this.sampleRate });
+ if (this.context.state === 'suspended') {
+ await this.context.resume();
+ }
+ try {
+ await this.context.audioWorklet.addModule(this.scriptSrc);
+ } catch (e) {
+ console.error(e);
+ throw new Error(`Could not add audioWorklet module: ${this.scriptSrc}`);
+ }
+ const analyser = this.context.createAnalyser();
+ analyser.fftSize = 8192;
+ analyser.smoothingTimeConstant = 0.1;
+ this.analyser = analyser;
+ return true;
+ }
+
+ /**
+ * Gets the current frequency domain data from the playing track
+ * @param {"frequency"|"music"|"voice"} [analysisType]
+ * @param {number} [minDecibels] default -100
+ * @param {number} [maxDecibels] default -30
+ * @returns {import('./analysis/audio_analysis.js').AudioAnalysisOutputType}
+ */
+ getFrequencies(
+ analysisType = 'frequency',
+ minDecibels = -100,
+ maxDecibels = -30
+ ) {
+ if (!this.analyser) {
+ throw new Error('Not connected, please call .connect() first');
+ }
+ return AudioAnalysis.getFrequencies(
+ this.analyser,
+ this.sampleRate,
+ null,
+ analysisType,
+ minDecibels,
+ maxDecibels
+ );
+ }
+
+ /**
+ * Starts audio streaming
+ * @private
+ * @returns {Promise}
+ */
+ _start() {
+ const streamNode = new AudioWorkletNode(this.context, 'stream_processor');
+ streamNode.connect(this.context.destination);
+ streamNode.port.onmessage = (e) => {
+ const { event } = e.data;
+ if (event === 'stop') {
+ streamNode.disconnect();
+ this.stream = null;
+ } else if (event === 'offset') {
+ const { requestId, trackId, offset } = e.data;
+ const currentTime = offset / this.sampleRate;
+ this.trackSampleOffsets[requestId] = { trackId, offset, currentTime };
+ }
+ };
+ this.analyser.disconnect();
+ streamNode.connect(this.analyser);
+ this.stream = streamNode;
+ return true;
+ }
+
+ /**
+ * Adds 16BitPCM data to the currently playing audio stream
+ * You can add chunks beyond the current play point and they will be queued for play
+ * @param {ArrayBuffer|Int16Array} arrayBuffer
+ * @param {string} [trackId]
+ * @returns {Int16Array}
+ */
+ add16BitPCM(arrayBuffer, trackId = 'default') {
+ if (typeof trackId !== 'string') {
+ throw new Error(`trackId must be a string`);
+ } else if (this.interruptedTrackIds[trackId]) {
+ return;
+ }
+ if (!this.stream) {
+ this._start();
+ }
+ let buffer;
+ if (arrayBuffer instanceof Int16Array) {
+ buffer = arrayBuffer;
+ } else if (arrayBuffer instanceof ArrayBuffer) {
+ buffer = new Int16Array(arrayBuffer);
+ } else {
+ throw new Error(`argument must be Int16Array or ArrayBuffer`);
+ }
+ this.stream.port.postMessage({ event: 'write', buffer, trackId });
+ return buffer;
+ }
+
+ /**
+ * Gets the offset (sample count) of the currently playing stream
+ * @param {boolean} [interrupt]
+ * @returns {{trackId: string|null, offset: number, currentTime: number}}
+ */
+ async getTrackSampleOffset(interrupt = false) {
+ if (!this.stream) {
+ return null;
+ }
+ const requestId = crypto.randomUUID();
+ this.stream.port.postMessage({
+ event: interrupt ? 'interrupt' : 'offset',
+ requestId,
+ });
+ let trackSampleOffset;
+ while (!trackSampleOffset) {
+ trackSampleOffset = this.trackSampleOffsets[requestId];
+ await new Promise((r) => setTimeout(() => r(), 1));
+ }
+ const { trackId } = trackSampleOffset;
+ if (interrupt && trackId) {
+ this.interruptedTrackIds[trackId] = true;
+ }
+ return trackSampleOffset;
+ }
+
+ /**
+ * Strips the current stream and returns the sample offset of the audio
+ * @param {boolean} [interrupt]
+ * @returns {{trackId: string|null, offset: number, currentTime: number}}
+ */
+ async interrupt() {
+ return this.getTrackSampleOffset(true);
+ }
+}
+
+globalThis.WavStreamPlayer = WavStreamPlayer;
diff --git a/src/lib/realtime/wavtools/lib/worklets/audio_processor.js b/src/lib/realtime/wavtools/lib/worklets/audio_processor.js
new file mode 100644
index 0000000..61dd7ec
--- /dev/null
+++ b/src/lib/realtime/wavtools/lib/worklets/audio_processor.js
@@ -0,0 +1,214 @@
+const AudioProcessorWorklet = `
+class AudioProcessor extends AudioWorkletProcessor {
+
+ constructor() {
+ super();
+ this.port.onmessage = this.receive.bind(this);
+ this.initialize();
+ }
+
+ initialize() {
+ this.foundAudio = false;
+ this.recording = false;
+ this.chunks = [];
+ }
+
+ /**
+ * Concatenates sampled chunks into channels
+ * Format is chunk[Left[], Right[]]
+ */
+ readChannelData(chunks, channel = -1, maxChannels = 9) {
+ let channelLimit;
+ if (channel !== -1) {
+ if (chunks[0] && chunks[0].length - 1 < channel) {
+ throw new Error(
+ \`Channel \${channel} out of range: max \${chunks[0].length}\`
+ );
+ }
+ channelLimit = channel + 1;
+ } else {
+ channel = 0;
+ channelLimit = Math.min(chunks[0] ? chunks[0].length : 1, maxChannels);
+ }
+ const channels = [];
+ for (let n = channel; n < channelLimit; n++) {
+ const length = chunks.reduce((sum, chunk) => {
+ return sum + chunk[n].length;
+ }, 0);
+ const buffers = chunks.map((chunk) => chunk[n]);
+ const result = new Float32Array(length);
+ let offset = 0;
+ for (let i = 0; i < buffers.length; i++) {
+ result.set(buffers[i], offset);
+ offset += buffers[i].length;
+ }
+ channels[n] = result;
+ }
+ return channels;
+ }
+
+ /**
+ * Combines parallel audio data into correct format,
+ * channels[Left[], Right[]] to float32Array[LRLRLRLR...]
+ */
+ formatAudioData(channels) {
+ if (channels.length === 1) {
+ // Simple case is only one channel
+ const float32Array = channels[0].slice();
+ const meanValues = channels[0].slice();
+ return { float32Array, meanValues };
+ } else {
+ const float32Array = new Float32Array(
+ channels[0].length * channels.length
+ );
+ const meanValues = new Float32Array(channels[0].length);
+ for (let i = 0; i < channels[0].length; i++) {
+ const offset = i * channels.length;
+ let meanValue = 0;
+ for (let n = 0; n < channels.length; n++) {
+ float32Array[offset + n] = channels[n][i];
+ meanValue += channels[n][i];
+ }
+ meanValues[i] = meanValue / channels.length;
+ }
+ return { float32Array, meanValues };
+ }
+ }
+
+ /**
+ * Converts 32-bit float data to 16-bit integers
+ */
+ floatTo16BitPCM(float32Array) {
+ const buffer = new ArrayBuffer(float32Array.length * 2);
+ const view = new DataView(buffer);
+ let offset = 0;
+ for (let i = 0; i < float32Array.length; i++, offset += 2) {
+ let s = Math.max(-1, Math.min(1, float32Array[i]));
+ view.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7fff, true);
+ }
+ return buffer;
+ }
+
+ /**
+ * Retrieves the most recent amplitude values from the audio stream
+ * @param {number} channel
+ */
+ getValues(channel = -1) {
+ const channels = this.readChannelData(this.chunks, channel);
+ const { meanValues } = this.formatAudioData(channels);
+ return { meanValues, channels };
+ }
+
+ /**
+ * Exports chunks as an audio/wav file
+ */
+ export() {
+ const channels = this.readChannelData(this.chunks);
+ const { float32Array, meanValues } = this.formatAudioData(channels);
+ const audioData = this.floatTo16BitPCM(float32Array);
+ return {
+ meanValues: meanValues,
+ audio: {
+ bitsPerSample: 16,
+ channels: channels,
+ data: audioData,
+ },
+ };
+ }
+
+ receive(e) {
+ const { event, id } = e.data;
+ let receiptData = {};
+ switch (event) {
+ case 'start':
+ this.recording = true;
+ break;
+ case 'stop':
+ this.recording = false;
+ break;
+ case 'clear':
+ this.initialize();
+ break;
+ case 'export':
+ receiptData = this.export();
+ break;
+ case 'read':
+ receiptData = this.getValues();
+ break;
+ default:
+ break;
+ }
+ // Always send back receipt
+ this.port.postMessage({ event: 'receipt', id, data: receiptData });
+ }
+
+ sendChunk(chunk) {
+ const channels = this.readChannelData([chunk]);
+ const { float32Array, meanValues } = this.formatAudioData(channels);
+ const rawAudioData = this.floatTo16BitPCM(float32Array);
+ const monoAudioData = this.floatTo16BitPCM(meanValues);
+ this.port.postMessage({
+ event: 'chunk',
+ data: {
+ mono: monoAudioData,
+ raw: rawAudioData,
+ },
+ });
+ }
+
+ process(inputList, outputList, parameters) {
+ // Copy input to output (e.g. speakers)
+ // Note that this creates choppy sounds with Mac products
+ const sourceLimit = Math.min(inputList.length, outputList.length);
+ for (let inputNum = 0; inputNum < sourceLimit; inputNum++) {
+ const input = inputList[inputNum];
+ const output = outputList[inputNum];
+ const channelCount = Math.min(input.length, output.length);
+ for (let channelNum = 0; channelNum < channelCount; channelNum++) {
+ input[channelNum].forEach((sample, i) => {
+ output[channelNum][i] = sample;
+ });
+ }
+ }
+ const inputs = inputList[0];
+ // There's latency at the beginning of a stream before recording starts
+ // Make sure we actually receive audio data before we start storing chunks
+ let sliceIndex = 0;
+ if (!this.foundAudio) {
+ for (const channel of inputs) {
+ sliceIndex = 0; // reset for each channel
+ if (this.foundAudio) {
+ break;
+ }
+ if (channel) {
+ for (const value of channel) {
+ if (value !== 0) {
+ // find only one non-zero entry in any channel
+ this.foundAudio = true;
+ break;
+ } else {
+ sliceIndex++;
+ }
+ }
+ }
+ }
+ }
+ if (inputs && inputs[0] && this.foundAudio && this.recording) {
+ // We need to copy the TypedArray, because the \`process\`
+ // internals will reuse the same buffer to hold each input
+ const chunk = inputs.map((input) => input.slice(sliceIndex));
+ this.chunks.push(chunk);
+ this.sendChunk(chunk);
+ }
+ return true;
+ }
+}
+
+registerProcessor('audio_processor', AudioProcessor);
+`;
+
+const script = new Blob([AudioProcessorWorklet], {
+ type: 'application/javascript',
+});
+const src = URL.createObjectURL(script);
+export const AudioProcessorSrc = src;
diff --git a/src/lib/realtime/wavtools/lib/worklets/stream_processor.js b/src/lib/realtime/wavtools/lib/worklets/stream_processor.js
new file mode 100644
index 0000000..d3c794a
--- /dev/null
+++ b/src/lib/realtime/wavtools/lib/worklets/stream_processor.js
@@ -0,0 +1,96 @@
+export const StreamProcessorWorklet = `
+class StreamProcessor extends AudioWorkletProcessor {
+ constructor() {
+ super();
+ this.hasStarted = false;
+ this.hasInterrupted = false;
+ this.outputBuffers = [];
+ this.bufferLength = 128;
+ this.write = { buffer: new Float32Array(this.bufferLength), trackId: null };
+ this.writeOffset = 0;
+ this.trackSampleOffsets = {};
+ this.port.onmessage = (event) => {
+ if (event.data) {
+ const payload = event.data;
+ if (payload.event === 'write') {
+ const int16Array = payload.buffer;
+ const float32Array = new Float32Array(int16Array.length);
+ for (let i = 0; i < int16Array.length; i++) {
+ float32Array[i] = int16Array[i] / 0x8000; // Convert Int16 to Float32
+ }
+ this.writeData(float32Array, payload.trackId);
+ } else if (
+ payload.event === 'offset' ||
+ payload.event === 'interrupt'
+ ) {
+ const requestId = payload.requestId;
+ const trackId = this.write.trackId;
+ const offset = this.trackSampleOffsets[trackId] || 0;
+ this.port.postMessage({
+ event: 'offset',
+ requestId,
+ trackId,
+ offset,
+ });
+ if (payload.event === 'interrupt') {
+ this.hasInterrupted = true;
+ }
+ } else {
+ throw new Error(\`Unhandled event "\${payload.event}"\`);
+ }
+ }
+ };
+ }
+
+ writeData(float32Array, trackId = null) {
+ let { buffer } = this.write;
+ let offset = this.writeOffset;
+ for (let i = 0; i < float32Array.length; i++) {
+ buffer[offset++] = float32Array[i];
+ if (offset >= buffer.length) {
+ this.outputBuffers.push(this.write);
+ this.write = { buffer: new Float32Array(this.bufferLength), trackId };
+ buffer = this.write.buffer;
+ offset = 0;
+ }
+ }
+ this.writeOffset = offset;
+ return true;
+ }
+
+ process(inputs, outputs, parameters) {
+ const output = outputs[0];
+ const outputChannelData = output[0];
+ const outputBuffers = this.outputBuffers;
+ if (this.hasInterrupted) {
+ this.port.postMessage({ event: 'stop' });
+ return false;
+ } else if (outputBuffers.length) {
+ this.hasStarted = true;
+ const { buffer, trackId } = outputBuffers.shift();
+ for (let i = 0; i < outputChannelData.length; i++) {
+ outputChannelData[i] = buffer[i] || 0;
+ }
+ if (trackId) {
+ this.trackSampleOffsets[trackId] =
+ this.trackSampleOffsets[trackId] || 0;
+ this.trackSampleOffsets[trackId] += buffer.length;
+ }
+ return true;
+ } else if (this.hasStarted) {
+ this.port.postMessage({ event: 'stop' });
+ return false;
+ } else {
+ return true;
+ }
+ }
+}
+
+registerProcessor('stream_processor', StreamProcessor);
+`;
+
+const script = new Blob([StreamProcessorWorklet], {
+ type: 'application/javascript',
+});
+const src = URL.createObjectURL(script);
+export const StreamProcessorSrc = src;
diff --git a/src/lib/ui/Layout.svelte b/src/lib/ui/Layout.svelte
new file mode 100644
index 0000000..62b40cf
--- /dev/null
+++ b/src/lib/ui/Layout.svelte
@@ -0,0 +1,15 @@
+
+
+
diff --git a/src/lib/ui/Switch.svelte b/src/lib/ui/Switch.svelte
new file mode 100644
index 0000000..cf86ee2
--- /dev/null
+++ b/src/lib/ui/Switch.svelte
@@ -0,0 +1,25 @@
+
+
+
+ {#each options as option, index}
+
+ {/each}
+
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
new file mode 100644
index 0000000..2e511e0
--- /dev/null
+++ b/src/routes/+layout.svelte
@@ -0,0 +1,5 @@
+
+
+
diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts
new file mode 100644
index 0000000..189f71e
--- /dev/null
+++ b/src/routes/+layout.ts
@@ -0,0 +1 @@
+export const prerender = true;
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
new file mode 100644
index 0000000..880eafc
--- /dev/null
+++ b/src/routes/+page.svelte
@@ -0,0 +1,124 @@
+
+
+
+
+
+
svelte openai realtime api
+
+
+
+
+
+
+ {#key items}
+ {#each items as item, index}
+ {#if item.formatted && index > 0}
+
+ {item.role}:
+ {item.role === 'user'
+ ? item.formatted.transcript || '(empty)'
+ : item.formatted.transcript || '(truncated)'}
+
+ {/if}
+ {/each}
+ {/key}
+
+
+
+
+
+
+ {#if turnDetection === 'none' && isConnected}
+
+ {/if}
+
+
+
+
+
+
+{#if apiKey}
+
+{/if}
diff --git a/svelte.config.js b/svelte.config.js
new file mode 100644
index 0000000..14a5c3d
--- /dev/null
+++ b/svelte.config.js
@@ -0,0 +1,21 @@
+import adapter from '@sveltejs/adapter-static';
+import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
+
+/** @type {import('@sveltejs/kit').Config} */
+const config = {
+ // Consult https://kit.svelte.dev/docs/integrations#preprocessors
+ // for more information about preprocessors
+ preprocess: vitePreprocess(),
+
+ kit: {
+ // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
+ // If your environment is not supported, or you settled on a specific environment, switch out the adapter.
+ // See https://kit.svelte.dev/docs/adapters for more information about adapters.
+ adapter: adapter(),
+ paths: {
+ base: '/svelte-realtime-api'
+ }
+ }
+};
+
+export default config;
diff --git a/tailwind.config.js b/tailwind.config.js
new file mode 100644
index 0000000..bfe0bc2
--- /dev/null
+++ b/tailwind.config.js
@@ -0,0 +1,8 @@
+/** @type {import('tailwindcss').Config} */
+export default {
+ content: ['./src/**/*.{html,js,svelte,ts}'],
+ theme: {
+ extend: {}
+ },
+ plugins: []
+};
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..fc93cbd
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,19 @@
+{
+ "extends": "./.svelte-kit/tsconfig.json",
+ "compilerOptions": {
+ "allowJs": true,
+ "checkJs": true,
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "resolveJsonModule": true,
+ "skipLibCheck": true,
+ "sourceMap": true,
+ "strict": true,
+ "moduleResolution": "bundler"
+ }
+ // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
+ // except $lib which is handled by https://kit.svelte.dev/docs/configuration#files
+ //
+ // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
+ // from the referenced tsconfig.json - TypeScript does not merge them in
+}
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 0000000..ccd3604
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,14 @@
+import { sveltekit } from '@sveltejs/kit/vite';
+import { defineConfig } from 'vite';
+
+export default defineConfig({
+ plugins: [sveltekit()]
+});
+
+// import { realtimeWebSocketServer } from './src/lib/realtime/realtime_server';
+// import { sveltekit } from '@sveltejs/kit/vite';
+// import { defineConfig } from 'vite';
+
+// export default defineConfig({
+// plugins: [sveltekit(), realtimeWebSocketServer]
+// });