From 319629b37be5458acf75af3dbe17b5d53d661b17 Mon Sep 17 00:00:00 2001 From: David Blass Date: Fri, 25 Jul 2025 15:37:10 -0400 Subject: [PATCH 01/12] iterate on fake_chef dependencies and config --- eslint.config.mjs | 1 + package.json | 4 +- pnpm-lock.yaml | 7597 ++++++++++++++++++ src/test/fake_chef/_generated/api.d.ts | 311 + src/test/fake_chef/_generated/dataModel.d.ts | 60 + src/test/fake_chef/_generated/server.d.ts | 149 + src/test/fake_chef/admin.ts | 197 + src/test/fake_chef/apiKeys.ts | 274 + src/test/fake_chef/cleanup.ts | 52 + src/test/fake_chef/compressMessages.ts | 15 + src/test/fake_chef/convexProjects.ts | 422 + src/test/fake_chef/crons.ts | 13 + src/test/fake_chef/debugPrompt.ts | 106 + src/test/fake_chef/deploy.ts | 31 + src/test/fake_chef/dev.ts | 61 + src/test/fake_chef/http.ts | 286 + src/test/fake_chef/lz4.ts | 147 + src/test/fake_chef/lz4Wasm.ts | 2 + src/test/fake_chef/messages.ts | 791 ++ src/test/fake_chef/migrations.ts | 49 + src/test/fake_chef/openaiProxy.ts | 178 + src/test/fake_chef/package.json | 13 + src/test/fake_chef/pnpm-lock.yaml | 260 + src/test/fake_chef/rateLimiter.ts | 18 + src/test/fake_chef/resendProxy.ts | 184 + src/test/fake_chef/schema.ts | 224 + src/test/fake_chef/sessions.ts | 210 + src/test/fake_chef/share.ts | 256 + src/test/fake_chef/snapshot.ts | 52 + src/test/fake_chef/socialShare.ts | 243 + src/test/fake_chef/tsconfig.json | 19 + src/test/types.bench.ts | 3 + src/type_utils.ts | 9 +- 33 files changed, 12230 insertions(+), 7 deletions(-) create mode 100644 pnpm-lock.yaml create mode 100644 src/test/fake_chef/_generated/api.d.ts create mode 100644 src/test/fake_chef/_generated/dataModel.d.ts create mode 100644 src/test/fake_chef/_generated/server.d.ts create mode 100644 src/test/fake_chef/admin.ts create mode 100644 src/test/fake_chef/apiKeys.ts create mode 100644 src/test/fake_chef/cleanup.ts create mode 100644 src/test/fake_chef/compressMessages.ts create mode 100644 src/test/fake_chef/convexProjects.ts create mode 100644 src/test/fake_chef/crons.ts create mode 100644 src/test/fake_chef/debugPrompt.ts create mode 100644 src/test/fake_chef/deploy.ts create mode 100644 src/test/fake_chef/dev.ts create mode 100644 src/test/fake_chef/http.ts create mode 100644 src/test/fake_chef/lz4.ts create mode 100644 src/test/fake_chef/lz4Wasm.ts create mode 100644 src/test/fake_chef/messages.ts create mode 100644 src/test/fake_chef/migrations.ts create mode 100644 src/test/fake_chef/openaiProxy.ts create mode 100644 src/test/fake_chef/package.json create mode 100644 src/test/fake_chef/pnpm-lock.yaml create mode 100644 src/test/fake_chef/rateLimiter.ts create mode 100644 src/test/fake_chef/resendProxy.ts create mode 100644 src/test/fake_chef/schema.ts create mode 100644 src/test/fake_chef/sessions.ts create mode 100644 src/test/fake_chef/share.ts create mode 100644 src/test/fake_chef/snapshot.ts create mode 100644 src/test/fake_chef/socialShare.ts create mode 100644 src/test/fake_chef/tsconfig.json create mode 100644 src/test/types.bench.ts diff --git a/eslint.config.mjs b/eslint.config.mjs index c8efa761..bde46119 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -28,6 +28,7 @@ export default [ "tmpDist*", "**/tmpPackage*", "**/custom-vitest-environment.ts", + "**/fake_chef", // TODO use a separate config for files that doesn't use TypeScript "**/*.js", "vitest.config.js", diff --git a/package.json b/package.json index 1cb24bf7..e9316ead 100644 --- a/package.json +++ b/package.json @@ -258,6 +258,7 @@ "a bundle." ], "devDependencies": { + "@ark/attest": "0.48.2", "@auth0/auth0-react": "2.3.0", "@babel/parser": "^7.27.1", "@babel/types": "^7.27.1", @@ -339,5 +340,6 @@ "engines": { "npm": ">=7.0.0", "node": ">=18.0.0" - } + }, + "packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 00000000..2bbf51bc --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,7597 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + esbuild: + specifier: 0.25.4 + version: 0.25.4 + jwt-decode: + specifier: ^4.0.0 + version: 4.0.0 + prettier: + specifier: 3.5.3 + version: 3.5.3 + devDependencies: + '@ark/attest': + specifier: 0.48.2 + version: 0.48.2(typescript@5.0.4) + '@auth0/auth0-react': + specifier: 2.3.0 + version: 2.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@babel/parser': + specifier: ^7.27.1 + version: 7.28.0 + '@babel/types': + specifier: ^7.27.1 + version: 7.28.1 + '@clerk/clerk-react': + specifier: ^5.20.0 + version: 5.34.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@commander-js/extra-typings': + specifier: ^11.1.0 + version: 11.1.0(commander@11.1.0) + '@eslint/compat': + specifier: ~1.3.0 + version: 1.3.1(eslint@9.28.0(jiti@2.4.2)) + '@eslint/eslintrc': + specifier: ~3.3.0 + version: 3.3.1 + '@eslint/js': + specifier: ~9.28.0 + version: 9.28.0 + '@modelcontextprotocol/sdk': + specifier: ^1.2.0 + version: 1.15.1 + '@octokit/openapi-types': + specifier: ~25.0.0 + version: 25.0.0 + '@sentry/node': + specifier: ^7.23.0 + version: 7.120.3 + '@sentry/tracing': + specifier: ^7.23.0 + version: 7.120.3 + '@swc/core': + specifier: 1.10.4 + version: 1.10.4 + '@testing-library/react': + specifier: ~16.3.0 + version: 16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@types/adm-zip': + specifier: ^0.5.7 + version: 0.5.7 + '@types/deep-equal': + specifier: 1.0.4 + version: 1.0.4 + '@types/detect-port': + specifier: ~1.3.5 + version: 1.3.5 + '@types/inquirer': + specifier: ^9.0.0 + version: 9.0.8 + '@types/jwt-encode': + specifier: ~1.0.0 + version: 1.0.3 + '@types/node': + specifier: ^18.17.0 + version: 18.19.119 + '@types/progress': + specifier: ~2.0.7 + version: 2.0.7 + '@types/react': + specifier: ^18.0.0 + version: 18.3.23 + '@types/react-dom': + specifier: ^18.0.0 + version: 18.3.7(@types/react@18.3.23) + '@types/semver': + specifier: ^7.3.13 + version: 7.7.0 + '@types/serve-handler': + specifier: ~6.1.4 + version: 6.1.4 + '@types/ws': + specifier: ^8.5.13 + version: 8.18.1 + '@vitest/eslint-plugin': + specifier: ~1.3.0 + version: 1.3.4(eslint@9.28.0(jiti@2.4.2))(typescript@5.0.4)(vitest@3.2.4(@types/node@18.19.119)(happy-dom@17.4.9)(jiti@2.4.2)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.3)) + adm-zip: + specifier: ^0.5.10 + version: 0.5.16 + bufferutil: + specifier: ^4.0.7 + version: 4.0.9 + chalk: + specifier: '5' + version: 5.4.1 + chokidar: + specifier: 3.6.0 + version: 3.6.0 + commander: + specifier: ^11.1.0 + version: 11.1.0 + deep-equal: + specifier: 2.2.3 + version: 2.2.3 + depcheck: + specifier: ~1.4.7 + version: 1.4.7 + detect-port: + specifier: ^2.1.0 + version: 2.1.0 + dotenv: + specifier: ^16.4.7 + version: 16.6.1 + encoding: + specifier: ^0.1.13 + version: 0.1.13 + esbuild-plugin-external-global: + specifier: ~1.0.1 + version: 1.0.1 + eslint: + specifier: 9.28.0 + version: 9.28.0(jiti@2.4.2) + eslint-config-prettier: + specifier: ^10.0.0 + version: 10.1.5(eslint@9.28.0(jiti@2.4.2)) + eslint-plugin-react: + specifier: ^7.37.2 + version: 7.37.5(eslint@9.28.0(jiti@2.4.2)) + eslint-plugin-react-hooks: + specifier: ^5.1.0-beta-26f2496093-20240514 + version: 5.2.0(eslint@9.28.0(jiti@2.4.2)) + eslint-plugin-require-extensions: + specifier: ~0.1.3 + version: 0.1.3(eslint@9.28.0(jiti@2.4.2)) + fetch-retry: + specifier: ~6.0.0 + version: 6.0.0 + find-up: + specifier: ^7.0.0 + version: 7.0.0 + globals: + specifier: ~15.15.0 + version: 15.15.0 + happy-dom: + specifier: ~17.4.0 + version: 17.4.9 + inquirer: + specifier: ^9.1.4 + version: 9.3.7 + inquirer-search-list: + specifier: ~1.2.6 + version: 1.2.6 + jsdom: + specifier: ~26.1.0 + version: 26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + json5: + specifier: ~2.2.3 + version: 2.2.3 + jwt-encode: + specifier: ~1.0.1 + version: 1.0.1 + knip: + specifier: ~5.60.0 + version: 5.60.2(@types/node@18.19.119)(typescript@5.0.4) + napi-wasm: + specifier: 1.1.3 + version: 1.1.3 + open: + specifier: ^8.3.0 + version: 8.4.2 + openid-client: + specifier: ^5.3.1 + version: 5.7.1 + ora: + specifier: ^8.1.1 + version: 8.2.0 + progress: + specifier: ~2.0.3 + version: 2.0.3 + react: + specifier: ^18.0.0 + version: 18.3.1 + react-dom: + specifier: ^18.0.0 + version: 18.3.1(react@18.3.1) + semver: + specifier: ^7.6.0 + version: 7.7.2 + serve-handler: + specifier: ~6.1.6 + version: 6.1.6 + shx: + specifier: ~0.3.4 + version: 0.3.4 + skott: + specifier: ~0.35.4 + version: 0.35.4 + strip-ansi: + specifier: ^7.0.1 + version: 7.1.0 + tar: + specifier: ^7.4.3 + version: 7.4.3 + tsx: + specifier: ^4.19.4 + version: 4.20.3 + typedoc: + specifier: ^0.24.6 + version: 0.24.8(typescript@5.0.4) + typescript: + specifier: ~5.0.3 + version: 5.0.4 + typescript-eslint: + specifier: ~8.34.0 + version: 8.34.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.0.4) + utf-8-validate: + specifier: ^5.0.10 + version: 5.0.10 + vitest: + specifier: ^3.1.4 + version: 3.2.4(@types/node@18.19.119)(happy-dom@17.4.9)(jiti@2.4.2)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.3) + wait-for-expect: + specifier: ~3.0.2 + version: 3.0.2 + ws: + specifier: 8.18.0 + version: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + zod: + specifier: ^3.24.0 + version: 3.25.76 + zod-to-json-schema: + specifier: ^3.24.0 + version: 3.24.6(zod@3.25.76) + +packages: + + '@ark/attest@0.48.2': + resolution: {integrity: sha512-3k+PRxQSXk55Dqhghg+B+Ftpk+le+s2TBJpFoFRBseTOL0FNjZmXFuy9ZNBXCTGVT7nAUDBZrcmz+AAMQNr+Bw==} + hasBin: true + peerDependencies: + typescript: '*' + + '@ark/fs@0.46.0': + resolution: {integrity: sha512-lBW6Vv6dZ74Gcc+zvJP8gjZACMo5o6hEuOvAtX6EJ5xNYBmX7nrXQaDdRfQNGDzgaX5UHGqi/vxk5moK94K7Yw==} + + '@ark/schema@0.46.0': + resolution: {integrity: sha512-c2UQdKgP2eqqDArfBqQIJppxJHvNNXuQPeuSPlDML4rjw+f1cu0qAlzOG4b8ujgm9ctIDWwhpyw6gjG5ledIVQ==} + + '@ark/util@0.46.0': + resolution: {integrity: sha512-JPy/NGWn/lvf1WmGCPw2VGpBg5utZraE84I7wli18EDF3p3zc/e9WolT35tINeZO3l7C77SjqRJeAUoT0CvMRg==} + + '@arr/every@1.0.1': + resolution: {integrity: sha512-UQFQ6SgyJ6LX42W8rHCs8KVc0JS0tzVL9ct4XYedJukskYVWTo49tNiMEK9C2HTyarbNiT/RVIRSY82vH+6sTg==} + engines: {node: '>=4'} + + '@asamuzakjp/css-color@3.2.0': + resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} + + '@auth0/auth0-react@2.3.0': + resolution: {integrity: sha512-YYTc/DWWigKC9fURufR/79h3+3DAnIzbfEzJLZ8Z4Q0BXE0azru3pKUbU+vYzS4lMAJkclwLuAbUnLjK81vCpA==} + peerDependencies: + react: ^16.11.0 || ^17 || ^18 || ^19 + react-dom: ^16.11.0 || ^17 || ^18 || ^19 + + '@auth0/auth0-spa-js@2.2.0': + resolution: {integrity: sha512-YaHHCxiSQxDb+Ju9gXOqcqgXWq8EkUSpZC4g24D3MoEBUaADKwOosrAnmjDZcslBZpnSFFdrl4dLYedAer3xlQ==} + + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.28.0': + resolution: {integrity: sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.0': + resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/runtime@7.27.6': + resolution: {integrity: sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.28.0': + resolution: {integrity: sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.28.1': + resolution: {integrity: sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==} + engines: {node: '>=6.9.0'} + + '@clerk/clerk-react@5.34.0': + resolution: {integrity: sha512-wUddgE3ziz7qsDKLVPIGk5SsDuO4kcyvGhxPjyknqidxa5Sx8h8zMw1PnthTaQikP/EXjA5non5NNzdNSmmHAA==} + engines: {node: '>=18.17.0'} + peerDependencies: + react: ^18.0.0 || ^19.0.0 || ^19.0.0-0 + react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-0 + + '@clerk/shared@3.12.0': + resolution: {integrity: sha512-oywfkqejGLGZ1rYqEyUKCI3JPgn8+cQgGch0poDdfdJRpxoWfvzzrB3n6cjMnENsZmOeyyvlMuzUmR+uYV4xSA==} + engines: {node: '>=18.17.0'} + peerDependencies: + react: ^18.0.0 || ^19.0.0 || ^19.0.0-0 + react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + + '@clerk/types@4.65.0': + resolution: {integrity: sha512-jmZWlauq83ipye6XwPYvphoNcSO8YB+KikDNKY1zbovfGOXi1aWSvuWpXgF6ry3ZvlAfNrYZP66rDfqKWoAHlw==} + engines: {node: '>=18.17.0'} + + '@commander-js/extra-typings@11.1.0': + resolution: {integrity: sha512-GuvZ38d23H+7Tz2C9DhzCepivsOsky03s5NI+KCy7ke1FNUvsJ2oO47scQ9YaGGhgjgNW5OYYNSADmbjcSoIhw==} + peerDependencies: + commander: 11.1.x + + '@csstools/color-helpers@5.0.2': + resolution: {integrity: sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==} + engines: {node: '>=18'} + + '@csstools/css-calc@2.1.4': + resolution: {integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-color-parser@3.0.10': + resolution: {integrity: sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-parser-algorithms@3.0.5': + resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-tokenizer@3.0.4': + resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} + engines: {node: '>=18'} + + '@emnapi/core@1.4.4': + resolution: {integrity: sha512-A9CnAbC6ARNMKcIcrQwq6HeHCjpcBZ5wSx4U01WXCqEKlrzB9F9315WDNHkrs2xbx7YjjSxbUYxuN6EQzpcY2g==} + + '@emnapi/runtime@1.4.4': + resolution: {integrity: sha512-hHyapA4A3gPaDCNfiqyZUStTMqIkKRshqPIuDOXv1hcBnD4U3l8cP0T1HMCfGRxQ6V64TGCcoswChANyOAwbQg==} + + '@emnapi/wasi-threads@1.0.3': + resolution: {integrity: sha512-8K5IFFsQqF9wQNJptGbS6FNKgUTsSRYnTqNCG1vPP8jFdjSv18n2mQfJpkt2Oibo9iBEzcDnDxNwKTzC7svlJw==} + + '@esbuild/aix-ppc64@0.25.4': + resolution: {integrity: sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.4': + resolution: {integrity: sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.4': + resolution: {integrity: sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.4': + resolution: {integrity: sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.4': + resolution: {integrity: sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.4': + resolution: {integrity: sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.4': + resolution: {integrity: sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.4': + resolution: {integrity: sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.4': + resolution: {integrity: sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.4': + resolution: {integrity: sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.4': + resolution: {integrity: sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.4': + resolution: {integrity: sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.4': + resolution: {integrity: sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.4': + resolution: {integrity: sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.4': + resolution: {integrity: sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.4': + resolution: {integrity: sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.4': + resolution: {integrity: sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.4': + resolution: {integrity: sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.4': + resolution: {integrity: sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.4': + resolution: {integrity: sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.4': + resolution: {integrity: sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.25.4': + resolution: {integrity: sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.4': + resolution: {integrity: sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.4': + resolution: {integrity: sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.4': + resolution: {integrity: sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.7.0': + resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.1': + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/compat@1.3.1': + resolution: {integrity: sha512-k8MHony59I5EPic6EQTCNOuPoVBnoYXkP+20xvwFjN7t0qI3ImyvyBgg+hIVPwC8JaxVjjUZld+cLfBLFDLucg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.40 || 9 + peerDependenciesMeta: + eslint: + optional: true + + '@eslint/config-array@0.20.1': + resolution: {integrity: sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.2.3': + resolution: {integrity: sha512-u180qk2Um1le4yf0ruXH3PYFeEZeYC3p/4wCTKrr2U1CmGdzGi3KtY0nuPDH48UJxlKCC5RDzbcbh4X0XlqgHg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.14.0': + resolution: {integrity: sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.15.1': + resolution: {integrity: sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.1': + resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.28.0': + resolution: {integrity: sha512-fnqSjGWd/CoIp4EXIxWVK/sHA6DOHN4+8Ix2cX5ycOY7LG0UY8nHCU5pIp2eaE1Mc7Qd8kHspYNzYXT2ojPLzg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.6': + resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.3.3': + resolution: {integrity: sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.6': + resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.3.1': + resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} + engines: {node: '>=18.18'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@inquirer/figures@1.0.12': + resolution: {integrity: sha512-MJttijd8rMFcKJC8NYmprWr6hD3r9Gd9qUC0XwPNwoEPWSMVJwA2MlXxF+nhZZNMY+HXsWa+o7KY2emWYIn0jQ==} + engines: {node: '>=18'} + + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + + '@jridgewell/gen-mapping@0.3.12': + resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.4': + resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==} + + '@jridgewell/trace-mapping@0.3.29': + resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==} + + '@modelcontextprotocol/sdk@1.15.1': + resolution: {integrity: sha512-W/XlN9c528yYn+9MQkVjxiTPgPxoxt+oczfjHBDsJx0+59+O7B75Zhsp0B16Xbwbz8ANISDajh6+V7nIcPMc5w==} + engines: {node: '>=18'} + + '@napi-rs/wasm-runtime@0.2.12': + resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@octokit/openapi-types@25.0.0': + resolution: {integrity: sha512-FZvktFu7HfOIJf2BScLKIEYjDsw6RKc7rBJCdvCTfKsVnx2GEB/Nbzjr29DUdb7vQhlzS/j8qDzdditP0OC6aw==} + + '@oxc-resolver/binding-android-arm-eabi@11.5.2': + resolution: {integrity: sha512-g3Dh0uN8E1fJAi+m5LxDU1frUz5q4ox/arqXGpEmt+u7wRXBpXnGsxDV/GFB59AmVWbQAiyhVCiM2GymkaxwwQ==} + cpu: [arm] + os: [android] + + '@oxc-resolver/binding-android-arm64@11.5.2': + resolution: {integrity: sha512-bij8HIMXYGsxdxuvycpkgvTfBpj6tv5jKaZ4tcPKPJjewH5WYIaSAT4PJYlAidP/0m8jyPu5GGkslF7/qPUhAg==} + cpu: [arm64] + os: [android] + + '@oxc-resolver/binding-darwin-arm64@11.5.2': + resolution: {integrity: sha512-C2hjujTOPgyi4sgc4UL+JHlEiClTNncLUdwiilMnwjiEcxSe7ubBmeZRENUd9bx8P9DbS1ApaBjwv13ZngrZRw==} + cpu: [arm64] + os: [darwin] + + '@oxc-resolver/binding-darwin-x64@11.5.2': + resolution: {integrity: sha512-Llf2qMBzs4PdbnrA7s3tVjW7MXnjUXepfqQkEXM2klxIggcbtbIESe3KupYHoo0Q0p6hLHwWoadyM32Ho2hLzA==} + cpu: [x64] + os: [darwin] + + '@oxc-resolver/binding-freebsd-x64@11.5.2': + resolution: {integrity: sha512-dKCHhqgKW3eqnJBlgLC03qoDSVeZSZJVcSVpyomu0XrrNha3wVyv6aJjN7A8HnjUCqJDibbZfTtD3/gnsm30eQ==} + cpu: [x64] + os: [freebsd] + + '@oxc-resolver/binding-linux-arm-gnueabihf@11.5.2': + resolution: {integrity: sha512-AMV4MbHdUvwA6oBLk90/gPo3gPMZl9+DHeas8BxRdq/uX1BFQ05s+mhy9ATGElGQsRVVOPya9qczOdb8eAlM6w==} + cpu: [arm] + os: [linux] + + '@oxc-resolver/binding-linux-arm-musleabihf@11.5.2': + resolution: {integrity: sha512-hTCkii4HwQushiD3L86cefvojTY6OSDzcrQZHVaUmrtkL0OQnRT9qUff83lJIQhb94rjaEfQsgUdVl1bvuUK/Q==} + cpu: [arm] + os: [linux] + + '@oxc-resolver/binding-linux-arm64-gnu@11.5.2': + resolution: {integrity: sha512-EXkMvem90Pdw0bw0TlOhAHFAGLopb1LaVwsxF+iSc/zQtuR62kl2jGMQRvsW4NHaF+nUN29H8IYQDzox4gxsRw==} + cpu: [arm64] + os: [linux] + + '@oxc-resolver/binding-linux-arm64-musl@11.5.2': + resolution: {integrity: sha512-UvA2QZB73XPXmFweDRyXyUchN1YnEx+cca7a/ojdhT+stDe0WKMK32y27oabWokJJsZZOd+W40dD7sxjzx7K/g==} + cpu: [arm64] + os: [linux] + + '@oxc-resolver/binding-linux-ppc64-gnu@11.5.2': + resolution: {integrity: sha512-0rllGQIAmeb+vAtmco0PnTzqlMs0DQs+QvHu/8AQAmgrlKBZDJJmRvLqMv6EXgTrLlWxoM0o9oNf7mZ0tEenUQ==} + cpu: [ppc64] + os: [linux] + + '@oxc-resolver/binding-linux-riscv64-gnu@11.5.2': + resolution: {integrity: sha512-kfE5ALnGsxEyz/e6lZbNUyPjZwTIuExTVJLVzjT/RjvaltSZ6J0u/6/CKsVFD3t686yqse1fnXuydUsgAFmuXg==} + cpu: [riscv64] + os: [linux] + + '@oxc-resolver/binding-linux-riscv64-musl@11.5.2': + resolution: {integrity: sha512-O6lbEl+heEd3QS2GOwm+iDGMqEWA18X/b9JNodzEHe2TJeOJAV/5xJ7jQTGA2seoy6/REhW744O35DyPFxZ2aQ==} + cpu: [riscv64] + os: [linux] + + '@oxc-resolver/binding-linux-s390x-gnu@11.5.2': + resolution: {integrity: sha512-6ZASmeqVq+xEQZz/EH+U4j1hPeqVQ8Eo58oYrt9FGJhseowAh6TAOHXe80HAJH6HQTcws1fhS/A7I4hm6NOgZA==} + cpu: [s390x] + os: [linux] + + '@oxc-resolver/binding-linux-x64-gnu@11.5.2': + resolution: {integrity: sha512-MYTtU3sKGZfvOYVpUfFHFcxLGOI8WN5BIQeWgNnNDEBHasthEDnyeNYpj6QbLd3XMz84gGA1G+mKMm/lVUF6hA==} + cpu: [x64] + os: [linux] + + '@oxc-resolver/binding-linux-x64-musl@11.5.2': + resolution: {integrity: sha512-7u1ANU1jkDUbC5ZxGXWDs0OLuUvV3DzqHUI+g41wHdz0iLoVSJ7rR+hl/crHIm4PpFkYbpU+joRslM5OLxeKlw==} + cpu: [x64] + os: [linux] + + '@oxc-resolver/binding-wasm32-wasi@11.5.2': + resolution: {integrity: sha512-2tOsCVH+THg9b9h6MiTymTrveSUWAOaQGj2CPQ4XJncxECsZY6MfxKLul+XsW4KLpstE89KBemRIQi6Il0Twew==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@oxc-resolver/binding-win32-arm64-msvc@11.5.2': + resolution: {integrity: sha512-NmpFIoT86wD2cNAweoEMLKZ4aaGzbYzmeMcYK65Ml9PbH53YXe5XZOXdzVExLKGJ3Rorf055n/67pRRvpIm/sQ==} + cpu: [arm64] + os: [win32] + + '@oxc-resolver/binding-win32-ia32-msvc@11.5.2': + resolution: {integrity: sha512-1EwjnPP5sEKdQl4+3edw+8xMZ79qk7iPXOJRUtdE0jLEdlFmzpnLBfsz54G7mOiQvnc6uR8YePBQb1iCRnysNA==} + cpu: [ia32] + os: [win32] + + '@oxc-resolver/binding-win32-x64-msvc@11.5.2': + resolution: {integrity: sha512-eB8eV8SdO+OpbJJ3dvTgSPOsDsW7SJp+ih5WIBWt7pWMlVbQyjBwDgTI8gGTqg2iwdEEUVqlfivEEs22hKnxRw==} + cpu: [x64] + os: [win32] + + '@parcel/watcher-android-arm64@2.5.1': + resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [android] + + '@parcel/watcher-darwin-arm64@2.5.1': + resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [darwin] + + '@parcel/watcher-darwin-x64@2.5.1': + resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [darwin] + + '@parcel/watcher-freebsd-x64@2.5.1': + resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [freebsd] + + '@parcel/watcher-linux-arm-glibc@2.5.1': + resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm-musl@2.5.1': + resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm64-glibc@2.5.1': + resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-arm64-musl@2.5.1': + resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-x64-glibc@2.5.1': + resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-linux-x64-musl@2.5.1': + resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-win32-arm64@2.5.1': + resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [win32] + + '@parcel/watcher-win32-ia32@2.5.1': + resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==} + engines: {node: '>= 10.0.0'} + cpu: [ia32] + os: [win32] + + '@parcel/watcher-win32-x64@2.5.1': + resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [win32] + + '@parcel/watcher@2.5.1': + resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==} + engines: {node: '>= 10.0.0'} + + '@polka/url@0.5.0': + resolution: {integrity: sha512-oZLYFEAzUKyi3SKnXvj32ZCEGH6RDnao7COuCVhDydMS9NrCSVXhM79VaKyP5+Zc33m0QXEd2DN3UkU7OsHcfw==} + + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + + '@prettier/sync@0.5.5': + resolution: {integrity: sha512-6BMtNr7aQhyNcGzmumkL0tgr1YQGfm9d7ZdmRpWqWuqpc9vZBind4xMe5NMiRECOhjuSiWHfBWLBnXkpeE90bw==} + peerDependencies: + prettier: '*' + + '@rollup/rollup-android-arm-eabi@4.45.0': + resolution: {integrity: sha512-2o/FgACbji4tW1dzXOqAV15Eu7DdgbKsF2QKcxfG4xbh5iwU7yr5RRP5/U+0asQliSYv5M4o7BevlGIoSL0LXg==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.45.0': + resolution: {integrity: sha512-PSZ0SvMOjEAxwZeTx32eI/j5xSYtDCRxGu5k9zvzoY77xUNssZM+WV6HYBLROpY5CkXsbQjvz40fBb7WPwDqtQ==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.45.0': + resolution: {integrity: sha512-BA4yPIPssPB2aRAWzmqzQ3y2/KotkLyZukVB7j3psK/U3nVJdceo6qr9pLM2xN6iRP/wKfxEbOb1yrlZH6sYZg==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.45.0': + resolution: {integrity: sha512-Pr2o0lvTwsiG4HCr43Zy9xXrHspyMvsvEw4FwKYqhli4FuLE5FjcZzuQ4cfPe0iUFCvSQG6lACI0xj74FDZKRA==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.45.0': + resolution: {integrity: sha512-lYE8LkE5h4a/+6VnnLiL14zWMPnx6wNbDG23GcYFpRW1V9hYWHAw9lBZ6ZUIrOaoK7NliF1sdwYGiVmziUF4vA==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.45.0': + resolution: {integrity: sha512-PVQWZK9sbzpvqC9Q0GlehNNSVHR+4m7+wET+7FgSnKG3ci5nAMgGmr9mGBXzAuE5SvguCKJ6mHL6vq1JaJ/gvw==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.45.0': + resolution: {integrity: sha512-hLrmRl53prCcD+YXTfNvXd776HTxNh8wPAMllusQ+amcQmtgo3V5i/nkhPN6FakW+QVLoUUr2AsbtIRPFU3xIA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.45.0': + resolution: {integrity: sha512-XBKGSYcrkdiRRjl+8XvrUR3AosXU0NvF7VuqMsm7s5nRy+nt58ZMB19Jdp1RdqewLcaYnpk8zeVs/4MlLZEJxw==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.45.0': + resolution: {integrity: sha512-fRvZZPUiBz7NztBE/2QnCS5AtqLVhXmUOPj9IHlfGEXkapgImf4W9+FSkL8cWqoAjozyUzqFmSc4zh2ooaeF6g==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.45.0': + resolution: {integrity: sha512-Btv2WRZOcUGi8XU80XwIvzTg4U6+l6D0V6sZTrZx214nrwxw5nAi8hysaXj/mctyClWgesyuxbeLylCBNauimg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.45.0': + resolution: {integrity: sha512-Li0emNnwtUZdLwHjQPBxn4VWztcrw/h7mgLyHiEI5Z0MhpeFGlzaiBHpSNVOMB/xucjXTTcO+dhv469Djr16KA==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.45.0': + resolution: {integrity: sha512-sB8+pfkYx2kvpDCfd63d5ScYT0Fz1LO6jIb2zLZvmK9ob2D8DeVqrmBDE0iDK8KlBVmsTNzrjr3G1xV4eUZhSw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.45.0': + resolution: {integrity: sha512-5GQ6PFhh7E6jQm70p1aW05G2cap5zMOvO0se5JMecHeAdj5ZhWEHbJ4hiKpfi1nnnEdTauDXxPgXae/mqjow9w==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.45.0': + resolution: {integrity: sha512-N/euLsBd1rekWcuduakTo/dJw6U6sBP3eUq+RXM9RNfPuWTvG2w/WObDkIvJ2KChy6oxZmOSC08Ak2OJA0UiAA==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.45.0': + resolution: {integrity: sha512-2l9sA7d7QdikL0xQwNMO3xURBUNEWyHVHfAsHsUdq+E/pgLTUcCE+gih5PCdmyHmfTDeXUWVhqL0WZzg0nua3g==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.45.0': + resolution: {integrity: sha512-XZdD3fEEQcwG2KrJDdEQu7NrHonPxxaV0/w2HpvINBdcqebz1aL+0vM2WFJq4DeiAVT6F5SUQas65HY5JDqoPw==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.45.0': + resolution: {integrity: sha512-7ayfgvtmmWgKWBkCGg5+xTQ0r5V1owVm67zTrsEY1008L5ro7mCyGYORomARt/OquB9KY7LpxVBZes+oSniAAQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.45.0': + resolution: {integrity: sha512-B+IJgcBnE2bm93jEW5kHisqvPITs4ddLOROAcOc/diBgrEiQJJ6Qcjby75rFSmH5eMGrqJryUgJDhrfj942apQ==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.45.0': + resolution: {integrity: sha512-+CXwwG66g0/FpWOnP/v1HnrGVSOygK/osUbu3wPRy8ECXjoYKjRAyfxYpDQOfghC5qPJYLPH0oN4MCOjwgdMug==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.45.0': + resolution: {integrity: sha512-SRf1cytG7wqcHVLrBc9VtPK4pU5wxiB/lNIkNmW2ApKXIg+RpqwHfsaEK+e7eH4A1BpI6BX/aBWXxZCIrJg3uA==} + cpu: [x64] + os: [win32] + + '@sentry-internal/tracing@7.120.3': + resolution: {integrity: sha512-Ausx+Jw1pAMbIBHStoQ6ZqDZR60PsCByvHdw/jdH9AqPrNE9xlBSf9EwcycvmrzwyKspSLaB52grlje2cRIUMg==} + engines: {node: '>=8'} + + '@sentry/core@7.120.3': + resolution: {integrity: sha512-vyy11fCGpkGK3qI5DSXOjgIboBZTriw0YDx/0KyX5CjIjDDNgp5AGgpgFkfZyiYiaU2Ww3iFuKo4wHmBusz1uA==} + engines: {node: '>=8'} + + '@sentry/integrations@7.120.3': + resolution: {integrity: sha512-6i/lYp0BubHPDTg91/uxHvNui427df9r17SsIEXa2eKDwQ9gW2qRx5IWgvnxs2GV/GfSbwcx4swUB3RfEWrXrQ==} + engines: {node: '>=8'} + + '@sentry/node@7.120.3': + resolution: {integrity: sha512-t+QtekZedEfiZjbkRAk1QWJPnJlFBH/ti96tQhEq7wmlk3VszDXraZvLWZA0P2vXyglKzbWRGkT31aD3/kX+5Q==} + engines: {node: '>=8'} + + '@sentry/tracing@7.120.3': + resolution: {integrity: sha512-B7bqyYFgHuab1Pn7w5KXsZP/nfFo4VDBDdSXDSWYk5+TYJ3IDruO3eJFhOrircfsz4YwazWm9kbeZhkpsHDyHg==} + engines: {node: '>=8'} + + '@sentry/types@7.120.3': + resolution: {integrity: sha512-C4z+3kGWNFJ303FC+FxAd4KkHvxpNFYAFN8iMIgBwJdpIl25KZ8Q/VdGn0MLLUEHNLvjob0+wvwlcRBBNLXOow==} + engines: {node: '>=8'} + + '@sentry/utils@7.120.3': + resolution: {integrity: sha512-UDAOQJtJDxZHQ5Nm1olycBIsz2wdGX8SdzyGVHmD8EOQYAeDZQyIlQYohDe9nazdIOQLZCIc3fU0G9gqVLkaGQ==} + engines: {node: '>=8'} + + '@swc/core-darwin-arm64@1.10.4': + resolution: {integrity: sha512-sV/eurLhkjn/197y48bxKP19oqcLydSel42Qsy2zepBltqUx+/zZ8+/IS0Bi7kaWVFxerbW1IPB09uq8Zuvm3g==} + engines: {node: '>=10'} + cpu: [arm64] + os: [darwin] + + '@swc/core-darwin-x64@1.10.4': + resolution: {integrity: sha512-gjYNU6vrAUO4+FuovEo9ofnVosTFXkF0VDuo1MKPItz6e2pxc2ale4FGzLw0Nf7JB1sX4a8h06CN16/pLJ8Q2w==} + engines: {node: '>=10'} + cpu: [x64] + os: [darwin] + + '@swc/core-linux-arm-gnueabihf@1.10.4': + resolution: {integrity: sha512-zd7fXH5w8s+Sfvn2oO464KDWl+ZX1MJiVmE4Pdk46N3PEaNwE0koTfgx2vQRqRG4vBBobzVvzICC3618WcefOA==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] + + '@swc/core-linux-arm64-gnu@1.10.4': + resolution: {integrity: sha512-+UGfoHDxsMZgFD3tABKLeEZHqLNOkxStu+qCG7atGBhS4Slri6h6zijVvf4yI5X3kbXdvc44XV/hrP/Klnui2A==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + + '@swc/core-linux-arm64-musl@1.10.4': + resolution: {integrity: sha512-cDDj2/uYsOH0pgAnDkovLZvKJpFmBMyXkxEG6Q4yw99HbzO6QzZ5HDGWGWVq/6dLgYKlnnmpjZCPPQIu01mXEg==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + + '@swc/core-linux-x64-gnu@1.10.4': + resolution: {integrity: sha512-qJXh9D6Kf5xSdGWPINpLGixAbB5JX8JcbEJpRamhlDBoOcQC79dYfOMEIxWPhTS1DGLyFakAx2FX/b2VmQmj0g==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + + '@swc/core-linux-x64-musl@1.10.4': + resolution: {integrity: sha512-A76lIAeyQnHCVt0RL/pG+0er8Qk9+acGJqSZOZm67Ve3B0oqMd871kPtaHBM0BW3OZAhoILgfHW3Op9Q3mx3Cw==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + + '@swc/core-win32-arm64-msvc@1.10.4': + resolution: {integrity: sha512-e6j5kBu4fIY7fFxFxnZI0MlEovRvp50Lg59Fw+DVbtqHk3C85dckcy5xKP+UoXeuEmFceauQDczUcGs19SRGSQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [win32] + + '@swc/core-win32-ia32-msvc@1.10.4': + resolution: {integrity: sha512-RSYHfdKgNXV/amY5Tqk1EWVsyQnhlsM//jeqMLw5Fy9rfxP592W9UTumNikNRPdjI8wKKzNMXDb1U29tQjN0dg==} + engines: {node: '>=10'} + cpu: [ia32] + os: [win32] + + '@swc/core-win32-x64-msvc@1.10.4': + resolution: {integrity: sha512-1ujYpaqfqNPYdwKBlvJnOqcl+Syn3UrQ4XE0Txz6zMYgyh6cdU6a3pxqLqIUSJ12MtXRA9ZUhEz1ekU3LfLWXw==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + + '@swc/core@1.10.4': + resolution: {integrity: sha512-ut3zfiTLORMxhr6y/GBxkHmzcGuVpwJYX4qyXWuBKkpw/0g0S5iO1/wW7RnLnZbAi8wS/n0atRZoaZlXWBkeJg==} + engines: {node: '>=10'} + peerDependencies: + '@swc/helpers': '*' + peerDependenciesMeta: + '@swc/helpers': + optional: true + + '@swc/counter@0.1.3': + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + + '@swc/types@0.1.23': + resolution: {integrity: sha512-u1iIVZV9Q0jxY+yM2vw/hZGDNudsN85bBpTqzAQ9rzkxW9D+e3aEM4Han+ow518gSewkXgjmEK0BD79ZcNVgPw==} + + '@testing-library/dom@10.4.0': + resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==} + engines: {node: '>=18'} + + '@testing-library/react@16.3.0': + resolution: {integrity: sha512-kFSyxiEDwv1WLl2fgsq6pPBbw5aWKrsY2/noi1Id0TK0UParSF62oFQFGHXIyaG4pp2tEub/Zlel+fjjZILDsw==} + engines: {node: '>=18'} + peerDependencies: + '@testing-library/dom': ^10.0.0 + '@types/react': ^18.0.0 || ^19.0.0 + '@types/react-dom': ^18.0.0 || ^19.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@tybys/wasm-util@0.10.0': + resolution: {integrity: sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==} + + '@types/adm-zip@0.5.7': + resolution: {integrity: sha512-DNEs/QvmyRLurdQPChqq0Md4zGvPwHerAJYWk9l2jCbD1VPpnzRJorOdiq4zsw09NFbYnhfsoEhWtxIzXpn2yw==} + + '@types/aria-query@5.0.4': + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + + '@types/chai@5.2.2': + resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/deep-equal@1.0.4': + resolution: {integrity: sha512-tqdiS4otQP4KmY0PR3u6KbZ5EWvhNdUoS/jc93UuK23C220lOZ/9TvjfxdPcKvqwwDVtmtSCrnr0p/2dirAxkA==} + + '@types/detect-port@1.3.5': + resolution: {integrity: sha512-Rf3/lB9WkDfIL9eEKaSYKc+1L/rNVYBjThk22JTqQw0YozXarX8YljFAz+HCoC6h4B4KwCMsBPZHaFezwT4BNA==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/inquirer@9.0.8': + resolution: {integrity: sha512-CgPD5kFGWsb8HJ5K7rfWlifao87m4ph8uioU7OTncJevmE/VLIqAAjfQtko578JZg7/f69K4FgqYym3gNr7DeA==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/jwt-encode@1.0.3': + resolution: {integrity: sha512-4IAoiMbBSbiUfYkBZYvj6Sj68d5qaaYeHfeoxQp5qpYkjOPcou9chpfEqw7OfoTQxsrS6HnTVN/RTCF866Z9sw==} + + '@types/minimatch@3.0.5': + resolution: {integrity: sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==} + + '@types/node@18.19.119': + resolution: {integrity: sha512-d0F6m9itIPaKnrvEMlzE48UjwZaAnFW7Jwibacw9MNdqadjKNpUm9tfJYDwmShJmgqcoqYUX3EMKO1+RWiuuNg==} + + '@types/parse-json@4.0.2': + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + + '@types/progress@2.0.7': + resolution: {integrity: sha512-iadjw02vte8qWx7U0YM++EybBha2CQLPGu9iJ97whVgJUT5Zq9MjAPYUnbfRI2Kpehimf1QjFJYxD0t8nqzu5w==} + + '@types/prop-types@15.7.15': + resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} + + '@types/react-dom@18.3.7': + resolution: {integrity: sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==} + peerDependencies: + '@types/react': ^18.0.0 + + '@types/react@18.3.23': + resolution: {integrity: sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w==} + + '@types/semver@7.7.0': + resolution: {integrity: sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==} + + '@types/serve-handler@6.1.4': + resolution: {integrity: sha512-aXy58tNie0NkuSCY291xUxl0X+kGYy986l4kqW6Gi4kEXgr6Tx0fpSH7YwUSa5usPpG3s9DBeIR6hHcDtL2IvQ==} + + '@types/through@0.0.33': + resolution: {integrity: sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==} + + '@types/ws@8.18.1': + resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + + '@typescript-eslint/eslint-plugin@8.34.1': + resolution: {integrity: sha512-STXcN6ebF6li4PxwNeFnqF8/2BNDvBupf2OPx2yWNzr6mKNGF7q49VM00Pz5FaomJyqvbXpY6PhO+T9w139YEQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.34.1 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/parser@8.34.1': + resolution: {integrity: sha512-4O3idHxhyzjClSMJ0a29AcoK0+YwnEqzI6oz3vlRf3xw0zbzt15MzXwItOlnr5nIth6zlY2RENLsOPvhyrKAQA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/project-service@8.34.1': + resolution: {integrity: sha512-nuHlOmFZfuRwLJKDGQOVc0xnQrAmuq1Mj/ISou5044y1ajGNp2BNliIqp7F2LPQ5sForz8lempMFCovfeS1XoA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/project-service@8.37.0': + resolution: {integrity: sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/scope-manager@8.34.1': + resolution: {integrity: sha512-beu6o6QY4hJAgL1E8RaXNC071G4Kso2MGmJskCFQhRhg8VOH/FDbC8soP8NHN7e/Hdphwp8G8cE6OBzC8o41ZA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/scope-manager@8.37.0': + resolution: {integrity: sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.34.1': + resolution: {integrity: sha512-K4Sjdo4/xF9NEeA2khOb7Y5nY6NSXBnod87uniVYW9kHP+hNlDV8trUSFeynA2uxWam4gIWgWoygPrv9VMWrYg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/tsconfig-utils@8.37.0': + resolution: {integrity: sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/type-utils@8.34.1': + resolution: {integrity: sha512-Tv7tCCr6e5m8hP4+xFugcrwTOucB8lshffJ6zf1mF1TbU67R+ntCc6DzLNKM+s/uzDyv8gLq7tufaAhIBYeV8g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/types@7.13.1': + resolution: {integrity: sha512-7K7HMcSQIAND6RBL4kDl24sG/xKM13cA85dc7JnmQXw2cBDngg7c19B++JzvJHRG3zG36n9j1i451GBzRuHchw==} + engines: {node: ^18.18.0 || >=20.0.0} + + '@typescript-eslint/types@8.34.1': + resolution: {integrity: sha512-rjLVbmE7HR18kDsjNIZQHxmv9RZwlgzavryL5Lnj2ujIRTeXlKtILHgRNmQ3j4daw7zd+mQgy+uyt6Zo6I0IGA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/types@8.37.0': + resolution: {integrity: sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@7.13.1': + resolution: {integrity: sha512-uxNr51CMV7npU1BxZzYjoVz9iyjckBduFBP0S5sLlh1tXYzHzgZ3BR9SVsNed+LmwKrmnqN3Kdl5t7eZ5TS1Yw==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/typescript-estree@8.34.1': + resolution: {integrity: sha512-rjCNqqYPuMUF5ODD+hWBNmOitjBWghkGKJg6hiCHzUvXRy6rK22Jd3rwbP2Xi+R7oYVvIKhokHVhH41BxPV5mA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/typescript-estree@8.37.0': + resolution: {integrity: sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/utils@8.34.1': + resolution: {integrity: sha512-mqOwUdZ3KjtGk7xJJnLbHxTuWVn3GO2WZZuM+Slhkun4+qthLdXx32C8xIXbO1kfCECb3jIs3eoxK3eryk7aoQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/utils@8.37.0': + resolution: {integrity: sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/visitor-keys@7.13.1': + resolution: {integrity: sha512-k/Bfne7lrP7hcb7m9zSsgcBmo+8eicqqfNAJ7uUY+jkTFpKeH2FSkWpFRtimBxgkyvqfu9jTPRbYOvud6isdXA==} + engines: {node: ^18.18.0 || >=20.0.0} + + '@typescript-eslint/visitor-keys@8.34.1': + resolution: {integrity: sha512-xoh5rJ+tgsRKoXnkBPFRLZ7rjKM0AfVbC68UZ/ECXoDbfggb9RbEySN359acY1vS3qZ0jVTVWzbtfapwm5ztxw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/visitor-keys@8.37.0': + resolution: {integrity: sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript/analyze-trace@0.10.1': + resolution: {integrity: sha512-RnlSOPh14QbopGCApgkSx5UBgGda5MX1cHqp2fsqfiDyCwGL/m1jaeB9fzu7didVS81LQqGZZuxFBcg8YU8EVw==} + hasBin: true + + '@typescript/vfs@1.6.1': + resolution: {integrity: sha512-JwoxboBh7Oz1v38tPbkrZ62ZXNHAk9bJ7c9x0eI5zBfBnBYGhURdbnh7Z4smN/MV48Y5OCcZb58n972UtbazsA==} + peerDependencies: + typescript: '*' + + '@vitest/eslint-plugin@1.3.4': + resolution: {integrity: sha512-EOg8d0jn3BAiKnR55WkFxmxfWA3nmzrbIIuOXyTe6A72duryNgyU+bdBEauA97Aab3ho9kLmAwgPX63Ckj4QEg==} + peerDependencies: + eslint: '>= 8.57.0' + typescript: '>= 5.0.0' + vitest: '*' + peerDependenciesMeta: + typescript: + optional: true + vitest: + optional: true + + '@vitest/expect@3.2.4': + resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} + + '@vitest/mocker@3.2.4': + resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@3.2.4': + resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} + + '@vitest/runner@3.2.4': + resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} + + '@vitest/snapshot@3.2.4': + resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} + + '@vitest/spy@3.2.4': + resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} + + '@vitest/utils@3.2.4': + resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + + '@vue/compiler-core@3.5.17': + resolution: {integrity: sha512-Xe+AittLbAyV0pabcN7cP7/BenRBNcteM4aSDCtRvGw0d9OL+HG1u/XHLY/kt1q4fyMeZYXyIYrsHuPSiDPosA==} + + '@vue/compiler-dom@3.5.17': + resolution: {integrity: sha512-+2UgfLKoaNLhgfhV5Ihnk6wB4ljyW1/7wUIog2puUqajiC29Lp5R/IKDdkebh9jTbTogTbsgB+OY9cEWzG95JQ==} + + '@vue/compiler-sfc@3.5.17': + resolution: {integrity: sha512-rQQxbRJMgTqwRugtjw0cnyQv9cP4/4BxWfTdRBkqsTfLOHWykLzbOc3C4GGzAmdMDxhzU/1Ija5bTjMVrddqww==} + + '@vue/compiler-ssr@3.5.17': + resolution: {integrity: sha512-hkDbA0Q20ZzGgpj5uZjb9rBzQtIHLS78mMilwrlpWk2Ep37DYntUz0PonQ6kr113vfOEdM+zTBuJDaceNIW0tQ==} + + '@vue/shared@3.5.17': + resolution: {integrity: sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg==} + + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + address@2.0.3: + resolution: {integrity: sha512-XNAb/a6TCqou+TufU8/u11HCu9x1gYvOoxLwtlXgIqmkrYQADVv6ljyW2zwiPhHz9R1gItAWpuDrdJMmrOBFEA==} + engines: {node: '>= 16.0.0'} + + adm-zip@0.5.16: + resolution: {integrity: sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==} + engines: {node: '>=12.0'} + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ansi-escapes@3.2.0: + resolution: {integrity: sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==} + engines: {node: '>=4'} + + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + + ansi-regex@3.0.1: + resolution: {integrity: sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==} + engines: {node: '>=4'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.1.0: + resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + engines: {node: '>=12'} + + ansi-sequence-parser@1.1.3: + resolution: {integrity: sha512-+fksAx9eG3Ab6LDnLs3ZqZa8KVJ/jYnX+D4Qe1azX+LFGFAXqynCQLOdLpNYN/l9e7l6hMWwZbrnctqr6eSQSw==} + + ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + + arktype@2.1.20: + resolution: {integrity: sha512-IZCEEXaJ8g+Ijd59WtSYwtjnqXiwM8sWQ5EjGamcto7+HVN9eK0C4p0zDlCuAwWhpqr6fIBkxPuYDl4/Mcj/+Q==} + + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + array-differ@3.0.0: + resolution: {integrity: sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==} + engines: {node: '>=8'} + + array-includes@3.1.9: + resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} + engines: {node: '>= 0.4'} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + array.prototype.findlast@1.2.5: + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.3: + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.3: + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} + engines: {node: '>= 0.4'} + + array.prototype.tosorted@1.1.4: + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + + arrify@2.0.1: + resolution: {integrity: sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==} + engines: {node: '>=8'} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + + bl@5.1.0: + resolution: {integrity: sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==} + + body-parser@2.2.0: + resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} + engines: {node: '>=18'} + + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + + bufferutil@4.0.9: + resolution: {integrity: sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==} + engines: {node: '>=6.14.2'} + + bytes@3.0.0: + resolution: {integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==} + engines: {node: '>= 0.8'} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsite@1.0.0: + resolution: {integrity: sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ==} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + chai@5.2.1: + resolution: {integrity: sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A==} + engines: {node: '>=18'} + + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.4.1: + resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + chardet@0.4.2: + resolution: {integrity: sha512-j/Toj7f1z98Hh2cYo2BVr85EpIRWqUi7rtRSGxh/cqUjqrnJe9l9UE7IUGd2vQ2p+kSHLkSzObQPZPLUC6TQwg==} + + chardet@0.7.0: + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + + check-error@2.1.1: + resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} + engines: {node: '>= 16'} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + + cli-cursor@2.1.0: + resolution: {integrity: sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==} + engines: {node: '>=4'} + + cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + + cli-cursor@4.0.0: + resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + + cli-width@2.2.1: + resolution: {integrity: sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==} + + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + + cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + + clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + commander@11.1.0: + resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} + engines: {node: '>=16'} + + compressible@2.0.18: + resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} + engines: {node: '>= 0.6'} + + compression@1.8.0: + resolution: {integrity: sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==} + engines: {node: '>= 0.8.0'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + content-disposition@0.5.2: + resolution: {integrity: sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==} + engines: {node: '>= 0.6'} + + content-disposition@1.0.0: + resolution: {integrity: sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==} + engines: {node: '>= 0.6'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + + cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + cssstyle@4.6.0: + resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==} + engines: {node: '>=18'} + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + data-urls@5.0.0: + resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} + engines: {node: '>=18'} + + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + + deep-equal@2.2.3: + resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==} + engines: {node: '>= 0.4'} + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-lazy-prop@2.0.0: + resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} + engines: {node: '>=8'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + depcheck@1.4.7: + resolution: {integrity: sha512-1lklS/bV5chOxwNKA/2XUUk/hPORp8zihZsXflr8x0kLwmcZ9Y9BsS6Hs3ssvA+2wUVbG0U2Ciqvm1SokNjPkA==} + engines: {node: '>=10'} + hasBin: true + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + deps-regex@0.2.0: + resolution: {integrity: sha512-PwuBojGMQAYbWkMXOY9Pd/NWCDNHVH12pnS7WHqZkTSeMESe4hwnKKRp0yR87g37113x4JPbo/oIvXY+s/f56Q==} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + detect-file@1.0.0: + resolution: {integrity: sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==} + engines: {node: '>=0.10.0'} + + detect-libc@1.0.3: + resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} + engines: {node: '>=0.10'} + hasBin: true + + detect-port@2.1.0: + resolution: {integrity: sha512-epZuWb/6Q62L+nDHJc/hQAqf8pylsqgk3BpZXVBx1CDnr3nkrVNn73Uu1rXcFzkNcc+hkP3whuOg7JZYaQB65Q==} + engines: {node: '>= 16.0.0'} + hasBin: true + + digraph-js@2.2.3: + resolution: {integrity: sha512-btynrARSW6pBmDz9+cwCxkBJ91CGBxIaNQo7V+ul9/rCRr3HddwehpEMnL6Ru2OeC2pKdRteB1v5TgZRrAAYKQ==} + engines: {node: '>=16.0.0'} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + + dotenv@16.6.1: + resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} + engines: {node: '>=12'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + effect@3.3.2: + resolution: {integrity: sha512-695XQBtp+UUYG50oREG9ujnRoeQU7xhwHDhT6ZAexm3Q+umdml1kjxcPoYRrS65crmaLlhVpjZHePJNzWOODnA==} + + emoji-regex@10.4.0: + resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + encoding@0.1.13: + resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + + error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + + es-abstract@1.24.0: + resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-get-iterator@1.1.3: + resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} + + es-iterator-helpers@1.2.1: + resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} + engines: {node: '>= 0.4'} + + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.1.0: + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + engines: {node: '>= 0.4'} + + es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + engines: {node: '>= 0.4'} + + esbuild-plugin-external-global@1.0.1: + resolution: {integrity: sha512-NDzYHRoShpvLqNcrgV8ZQh61sMIFAry5KLTQV83BPG5iTXCCu7h72SCfJ97bW0GqtuqDD/1aqLbKinI/rNgUsg==} + + esbuild@0.25.4: + resolution: {integrity: sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-config-prettier@10.1.5: + resolution: {integrity: sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-plugin-react-hooks@5.2.0: + resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + + eslint-plugin-react@7.37.5: + resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + + eslint-plugin-require-extensions@0.1.3: + resolution: {integrity: sha512-T3c1PZ9PIdI3hjV8LdunfYI8gj017UQjzAnCrxuo3wAjneDbTPHdE3oNWInOjMA+z/aBkUtlW5vC0YepYMZIug==} + engines: {node: '>=16'} + peerDependencies: + eslint: '*' + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.28.0: + resolution: {integrity: sha512-ocgh41VhRlf9+fVpe7QKzwLj9c92fDiqOj8Y3Sd4/ZmVA4Btx4PlUYPq4pp9JDyupkf1upbEXecxL2mwNV7jPQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + eventsource-parser@3.0.3: + resolution: {integrity: sha512-nVpZkTMM9rF6AQ9gPJpFsNAMt48wIzB5TQgiTLdHiuO8XEDhUgZEhqKlZWXbIzo9VmJ/HvysHqEaVeD5v9TPvA==} + engines: {node: '>=20.0.0'} + + eventsource@3.0.7: + resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} + engines: {node: '>=18.0.0'} + + exit@0.1.2: + resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} + engines: {node: '>= 0.8.0'} + + expand-tilde@2.0.2: + resolution: {integrity: sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==} + engines: {node: '>=0.10.0'} + + expect-type@1.2.2: + resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} + engines: {node: '>=12.0.0'} + + express-rate-limit@7.5.1: + resolution: {integrity: sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==} + engines: {node: '>= 16'} + peerDependencies: + express: '>= 4.11' + + express@5.1.0: + resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==} + engines: {node: '>= 18'} + + external-editor@2.2.0: + resolution: {integrity: sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==} + engines: {node: '>=0.12'} + + external-editor@3.1.0: + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} + engines: {node: '>=4'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + + fd-package-json@2.0.0: + resolution: {integrity: sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ==} + + fdir@6.4.6: + resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fetch-retry@6.0.0: + resolution: {integrity: sha512-BUFj1aMubgib37I3v4q78fYo63Po7t4HUPTpQ6/QE6yK6cIQrP+W43FYToeTEyg5m2Y7eFUtijUuAv/PDlWuag==} + + figures@2.0.0: + resolution: {integrity: sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==} + engines: {node: '>=4'} + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + finalhandler@2.1.0: + resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} + engines: {node: '>= 0.8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + find-up@7.0.0: + resolution: {integrity: sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==} + engines: {node: '>=18'} + + findup-sync@5.0.0: + resolution: {integrity: sha512-MzwXju70AuyflbgeOhzvQWAvvQdo1XL0A9bVvlXsYcFEBM87WR4OakL4OfZq+QRmr+duJubio+UtNQCPsVESzQ==} + engines: {node: '>= 10.13.0'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + formatly@0.2.4: + resolution: {integrity: sha512-lIN7GpcvX/l/i24r/L9bnJ0I8Qn01qijWpQpDDvTLL29nKqSaJJu4h20+7VJ6m2CAhQ2/En/GbxDiHCzq/0MyA==} + engines: {node: '>=18.3.0'} + hasBin: true + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fp-ts@2.5.0: + resolution: {integrity: sha512-xkC9ZKl/i2cU+8FAsdyLcTvPRXphp42FcK5WmZpB47VXb4gggC3DHlVDKNLdbC+U8zz6yp1b0bj0mZg0axmZYQ==} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + + fs-tree-structure@0.0.5: + resolution: {integrity: sha512-827ACYnAMC1DQRvhLUzZH0fCPhBJLo9P7WfxxwP4cibIzlrSzbD+Fh9W4FxFtSU+p9GlX0BoQUWLJ2LFJuoKuQ==} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + fuzzy@0.1.3: + resolution: {integrity: sha512-/gZffu4ykarLrCiP3Ygsa86UAo1E5vEVlvTrpkKywXSbP9Xhln3oSp9QSV57gEq3JFFpGJ4GZ+5zdEp3FcUh4w==} + engines: {node: '>= 0.6.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-east-asian-width@1.3.0: + resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} + engines: {node: '>=18'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.10.1: + resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + global-modules@1.0.0: + resolution: {integrity: sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==} + engines: {node: '>=0.10.0'} + + global-prefix@1.0.2: + resolution: {integrity: sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==} + engines: {node: '>=0.10.0'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@15.15.0: + resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} + engines: {node: '>=18'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + happy-dom@17.4.9: + resolution: {integrity: sha512-h1x7f+dzKutYjTULzBdk1KxaZdiXAAaKWusTsrLtGrdKP/Bgz0U0GxZHKvNfFcBg6Tr/6edPuXqluilXmnjojA==} + engines: {node: '>=18.0.0'} + + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + homedir-polyfill@1.0.3: + resolution: {integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==} + engines: {node: '>=0.10.0'} + + html-encoding-sniffer@4.0.0: + resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} + engines: {node: '>=18'} + + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + ignore-walk@6.0.5: + resolution: {integrity: sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + immediate@3.0.6: + resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + inquirer-search-list@1.2.6: + resolution: {integrity: sha512-C4pKSW7FOYnkAloH8rB4FiM91H1v08QFZZJh6KRt//bMfdDBIhgdX8wjHvrVH2bu5oIo6wYqGpzSBxkeClPxew==} + + inquirer@3.3.0: + resolution: {integrity: sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==} + + inquirer@9.3.7: + resolution: {integrity: sha512-LJKFHCSeIRq9hanN14IlOtPSTe3lNES7TYDTE2xxdAy1LS5rYphajK1qtwvj3YmQXvvk0U2Vbmcni8P9EIQW9w==} + engines: {node: '>=18'} + + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + + interpret@1.4.0: + resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==} + engines: {node: '>= 0.10'} + + io-ts@2.2.22: + resolution: {integrity: sha512-FHCCztTkHoV9mdBsHpocLpdTAfh956ZQcIkWQxxS0U5HT53vtrcuYdQneEJKH6xILaLNzXVl2Cvwtoy8XNN0AA==} + peerDependencies: + fp-ts: ^2.5.0 + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + is-arguments@1.2.0: + resolution: {integrity: sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==} + engines: {node: '>= 0.4'} + + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + + is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + + is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + + is-fullwidth-code-point@2.0.0: + resolution: {integrity: sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==} + engines: {node: '>=4'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-generator-function@1.1.0: + resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + + is-interactive@1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + + is-interactive@2.0.0: + resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} + engines: {node: '>=12'} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + + is-unicode-supported@1.3.0: + resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} + engines: {node: '>=12'} + + is-unicode-supported@2.1.0: + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + engines: {node: '>=18'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + + is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + + is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + + is-wsl@3.1.0: + resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} + engines: {node: '>=16'} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + iterator.prototype@1.1.5: + resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} + engines: {node: '>= 0.4'} + + jiti@2.4.2: + resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} + hasBin: true + + jose@4.15.9: + resolution: {integrity: sha512-1vUQX+IdDMVPj4k8kOxgUqlcK518yluMuGZwqlr44FS1ppZB/5GWh4rZG89erpOBOJjU/OBsnCVFfapsRz6nEA==} + + js-cookie@3.0.5: + resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} + engines: {node: '>=14'} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + + js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsdom@26.1.0: + resolution: {integrity: sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==} + engines: {node: '>=18'} + peerDependencies: + canvas: ^3.0.0 + peerDependenciesMeta: + canvas: + optional: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonc-parser@3.3.1: + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + + jsonparse@1.3.1: + resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} + engines: {'0': node >= 0.2.0} + + jsonstream-next@3.0.0: + resolution: {integrity: sha512-aAi6oPhdt7BKyQn1SrIIGZBt0ukKuOUE1qV6kJ3GgioSOYzsRc8z9Hfr1BVmacA/jLe9nARfmgMGgn68BqIAgg==} + engines: {node: '>=10'} + hasBin: true + + jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + + jwt-decode@4.0.0: + resolution: {integrity: sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==} + engines: {node: '>=18'} + + jwt-encode@1.0.1: + resolution: {integrity: sha512-QrGiNhynbAYyFdbC1GbjborzenSFs5Ga+2nE0uBokGXsH11xrgd1AX55HR7P+wGQyyZOn6LUO5iKlh74dlhBkA==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + + knip@5.60.2: + resolution: {integrity: sha512-TsYqEsoL3802RmhGL5MN7RLI6/03kocMYx/4BpMmwo3dSwEJxmzV7HqRxMVZr6c1llbd25+MqjgA86bv1IwsPA==} + engines: {node: '>=18.18.0'} + hasBin: true + peerDependencies: + '@types/node': '>=18' + typescript: '>=5.0.4' + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lie@3.1.1: + resolution: {integrity: sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + localforage@1.10.0: + resolution: {integrity: sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + locate-path@7.2.0: + resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + lodash-es@4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + + lodash.isequal@4.5.0: + resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.uniqwith@4.5.0: + resolution: {integrity: sha512-7lYL8bLopMoy4CTICbxygAUq6CdRJ36vFc80DucPueUee+d5NBRxz3FdT9Pes/HEx5mPoT9jwnsEJWz1N7uq7Q==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + + log-symbols@5.1.0: + resolution: {integrity: sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==} + engines: {node: '>=12'} + + log-symbols@6.0.0: + resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} + engines: {node: '>=18'} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + loupe@3.1.4: + resolution: {integrity: sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + + lunr@2.3.9: + resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} + + lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + + make-synchronized@0.4.2: + resolution: {integrity: sha512-EwEJSg8gSGLicKXp/VzNi1tvzhdmNBxOzslkkJSoNUCQFZKH/NIUIp7xlfN+noaHrz4BJDN73gne8IHnjl/F/A==} + + marked@4.3.0: + resolution: {integrity: sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==} + engines: {node: '>= 12'} + hasBin: true + + matchit@1.1.0: + resolution: {integrity: sha512-+nGYoOlfHmxe5BW5tE0EMJppXEwdSf8uBA1GTZC7Q77kbT35+VKLYJMzVNWCHSsga1ps1tPYFtFyvxvKzWVmMA==} + engines: {node: '>=6'} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + media-typer@1.1.0: + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} + + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + meriyah@4.5.0: + resolution: {integrity: sha512-Rbiu0QPIxTXgOXwiIpRVJfZRQ2FWyfzYrOGBs9SN5RbaXg1CN5ELn/plodwWwluX93yzc4qO/bNIen1ThGFCxw==} + engines: {node: '>=10.4.0'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.33.0: + resolution: {integrity: sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==} + engines: {node: '>= 0.6'} + + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@2.1.18: + resolution: {integrity: sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==} + engines: {node: '>= 0.6'} + + mime-types@3.0.1: + resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} + engines: {node: '>= 0.6'} + + mimic-fn@1.2.0: + resolution: {integrity: sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==} + engines: {node: '>=4'} + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@7.4.6: + resolution: {integrity: sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==} + engines: {node: '>=10'} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + minizlib@3.0.2: + resolution: {integrity: sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==} + engines: {node: '>= 18'} + + mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} + hasBin: true + + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + multimatch@5.0.0: + resolution: {integrity: sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==} + engines: {node: '>=10'} + + mute-stream@0.0.7: + resolution: {integrity: sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ==} + + mute-stream@1.0.0: + resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + napi-postinstall@0.3.0: + resolution: {integrity: sha512-M7NqKyhODKV1gRLdkwE7pDsZP2/SC2a2vHkOYh9MCpKMbWVfyVfUw5MaH83Fv6XMjxr5jryUp3IDDL9rlxsTeA==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + hasBin: true + + napi-wasm@1.1.3: + resolution: {integrity: sha512-h/4nMGsHjZDCYmQVNODIrYACVJ+I9KItbG+0si6W/jSjdA9JbWDoU4LLeMXVcEQGHjttI2tuXqDrbGF7qkUHHg==} + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + negotiator@0.6.4: + resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==} + engines: {node: '>= 0.6'} + + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + + node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + + node-gyp-build@4.8.4: + resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} + hasBin: true + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + nwsapi@2.2.20: + resolution: {integrity: sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-hash@2.2.0: + resolution: {integrity: sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==} + engines: {node: '>= 6'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-is@1.1.6: + resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + object.entries@1.1.9: + resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.values@1.2.1: + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} + engines: {node: '>= 0.4'} + + oidc-token-hash@5.1.0: + resolution: {integrity: sha512-y0W+X7Ppo7oZX6eovsRkuzcSM40Bicg2JEJkDJ4irIt1wsYAP5MLSNv+QAogO8xivMffw/9OvV3um1pxXgt1uA==} + engines: {node: ^10.13.0 || >=12.0.0} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + on-headers@1.0.2: + resolution: {integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@2.0.1: + resolution: {integrity: sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==} + engines: {node: '>=4'} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + + open@8.4.2: + resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} + engines: {node: '>=12'} + + openid-client@5.7.1: + resolution: {integrity: sha512-jDBPgSVfTnkIh71Hg9pRvtJc6wTwqjRkN88+gCFtYWrlP4Yx2Dsrow8uPi3qLr/aeymPF3o2+dS+wOpglK04ew==} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + ora@5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} + + ora@6.3.1: + resolution: {integrity: sha512-ERAyNnZOfqM+Ao3RAvIXkYh5joP220yf59gVe2X/cI6SiCxIdi4c9HZKZD8R6q/RDXEje1THBju6iExiSsgJaQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + ora@8.2.0: + resolution: {integrity: sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==} + engines: {node: '>=18'} + + os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + + oxc-resolver@11.5.2: + resolution: {integrity: sha512-mYkOsrgvlm4OLPCgSR2XCMkJ203PwSOASxzHYzW7Kz3GXONVbe2VTpgwL/yBo0igSUwlZWTUKEbRJLscJ6N5QQ==} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + p-locate@6.0.0: + resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-gitignore@2.0.0: + resolution: {integrity: sha512-RmVuCHWsfu0QPNW+mraxh/xjQVw/lhUCUru8Zni3Ctq3AoMhpDTq0OVdKS6iesd6Kqb7viCV3isAL43dciOSog==} + engines: {node: '>=14'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + parse-passwd@1.0.0: + resolution: {integrity: sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==} + engines: {node: '>=0.10.0'} + + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-exists@5.0.0: + resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-is-inside@1.0.2: + resolution: {integrity: sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-to-regexp@3.3.0: + resolution: {integrity: sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==} + + path-to-regexp@8.2.0: + resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==} + engines: {node: '>=16'} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} + engines: {node: '>= 14.16'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + + pkce-challenge@5.0.0: + resolution: {integrity: sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==} + engines: {node: '>=16.20.0'} + + please-upgrade-node@3.2.0: + resolution: {integrity: sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==} + + polka@0.5.2: + resolution: {integrity: sha512-FVg3vDmCqP80tOrs+OeNlgXYmFppTXdjD5E7I4ET1NjvtNmQrb1/mJibybKkb/d4NA7YWAr1ojxuhpL3FHqdlw==} + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier@3.5.3: + resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==} + engines: {node: '>=14'} + hasBin: true + + pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + progress@2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + qs@6.14.0: + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + engines: {node: '>=0.6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + range-parser@1.2.0: + resolution: {integrity: sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==} + engines: {node: '>= 0.6'} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@3.0.0: + resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==} + engines: {node: '>= 0.8'} + + react-dom@18.3.1: + resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + peerDependencies: + react: ^18.3.1 + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + + react@18.3.1: + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + engines: {node: '>=0.10.0'} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + rechoir@0.6.2: + resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} + engines: {node: '>= 0.10'} + + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-package-name@2.0.1: + resolution: {integrity: sha512-uuoJ1hU/k6M0779t3VMVIYpb2VMJk05cehCaABFhXaibcbvfgR8wKiozLjVFSzJPmQMRqIcO0HMyTFqfV09V6Q==} + + resolve-dir@1.0.1: + resolution: {integrity: sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve@1.22.10: + resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + engines: {node: '>= 0.4'} + hasBin: true + + resolve@2.0.0-next.5: + resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} + hasBin: true + + restore-cursor@2.0.0: + resolution: {integrity: sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==} + engines: {node: '>=4'} + + restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + + restore-cursor@4.0.0: + resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rollup@4.45.0: + resolution: {integrity: sha512-WLjEcJRIo7i3WDDgOIJqVI2d+lAC3EwvOGy+Xfq6hs+GQuAA4Di/H72xmXkOhrIWFg2PFYSKZYfH0f4vfKXN4A==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + + rrweb-cssom@0.8.0: + resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} + + run-async@2.4.1: + resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} + engines: {node: '>=0.12.0'} + + run-async@3.0.0: + resolution: {integrity: sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==} + engines: {node: '>=0.12.0'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + rx-lite-aggregates@4.0.8: + resolution: {integrity: sha512-3xPNZGW93oCjiO7PtKxRK6iOVYBWBvtf9QHDfU23Oc+dLIQmAV//UnyXV/yihv81VS/UqoQPk4NegS8EFi55Hg==} + + rx-lite@4.0.8: + resolution: {integrity: sha512-Cun9QucwK6MIrp3mry/Y7hqD1oFqTYLQ4pGxaHTjIdaFDWRGGLikqp6u8LcWJnzpoALg9hap+JGk8sFIUuEGNA==} + + rxjs@7.8.2: + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} + + safe-array-concat@1.1.3: + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} + engines: {node: '>=0.4'} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + + scheduler@0.23.2: + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + + semver-compare@1.0.0: + resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + engines: {node: '>=10'} + hasBin: true + + send@1.2.0: + resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} + engines: {node: '>= 18'} + + serve-handler@6.1.6: + resolution: {integrity: sha512-x5RL9Y2p5+Sh3D38Fh9i/iQ5ZK+e4xuXRd/pGbM4D13tgo/MGwbttUk8emytcr1YYzBYs+apnUngBDFYfpjPuQ==} + + serve-static@2.2.0: + resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==} + engines: {node: '>= 18'} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shelljs@0.8.5: + resolution: {integrity: sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==} + engines: {node: '>=4'} + hasBin: true + + shiki@0.14.7: + resolution: {integrity: sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==} + + shx@0.3.4: + resolution: {integrity: sha512-N6A9MLVqjxZYcVn8hLmtneQWIJtp8IKzMP4eMnx+nqkvXoqinUPCbUFLp2UcWTEIUONhlk0ewxr/jaVGlc+J+g==} + engines: {node: '>=6'} + hasBin: true + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + sirv@2.0.4: + resolution: {integrity: sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==} + engines: {node: '>= 10'} + + skott-webapp@2.3.0: + resolution: {integrity: sha512-nmt+ilxGOqX5zN2WDKv1Y5gLfxy/lceHgbB8HM/ym/Cm8572ypD1s2S+pcN+jOw13xqoavHJPonX1WT2QvkpDg==} + + skott@0.35.4: + resolution: {integrity: sha512-z6Ww+Z+TdLO1Z1HXiW9iltburLEWkapxk2MkI+8UQGsJ7d1HiO0dj7ZI0Q/kV/nwbOaOZyRUgMO7hj1/d8h8hw==} + hasBin: true + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + smol-toml@1.4.1: + resolution: {integrity: sha512-CxdwHXyYTONGHThDbq5XdwbFsuY4wlClRGejfE2NtwUtiHYsP1QtNsHb/hnj31jKYSchztJsaA8pSQoVzkfCFg==} + engines: {node: '>= 18'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + split2@3.2.2: + resolution: {integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + std-env@3.9.0: + resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} + + stdin-discarder@0.1.0: + resolution: {integrity: sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + stdin-discarder@0.2.2: + resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} + engines: {node: '>=18'} + + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + + string-width@2.1.1: + resolution: {integrity: sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==} + engines: {node: '>=4'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + string.prototype.matchall@4.0.12: + resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} + engines: {node: '>= 0.4'} + + string.prototype.repeat@1.0.0: + resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} + + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@4.0.0: + resolution: {integrity: sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==} + engines: {node: '>=4'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + strip-json-comments@5.0.2: + resolution: {integrity: sha512-4X2FR3UwhNUE9G49aIsJW5hRRR3GXGTBTZRMfv568O60ojM8HcWjV/VxAxCDW3SUND33O6ZY66ZuRcdkj73q2g==} + engines: {node: '>=14.16'} + + strip-literal@3.0.0: + resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==} + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + swr@2.3.4: + resolution: {integrity: sha512-bYd2lrhc+VarcpkgWclcUi92wYCpOgMws9Sd1hG1ntAu0NEy+14CbotuFjshBU2kt9rYj9TSmDcybpxpeTU1fg==} + peerDependencies: + react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + + tar@7.4.3: + resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} + engines: {node: '>=18'} + + through2@4.0.2: + resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==} + + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tinyglobby@0.2.14: + resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} + engines: {node: '>=12.0.0'} + + tinypool@1.1.1: + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + + tinyspy@4.0.3: + resolution: {integrity: sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==} + engines: {node: '>=14.0.0'} + + tldts-core@6.1.86: + resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==} + + tldts@6.1.86: + resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==} + hasBin: true + + tmp@0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + + tough-cookie@5.1.2: + resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==} + engines: {node: '>=16'} + + tr46@5.1.1: + resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} + engines: {node: '>=18'} + + treeify@1.1.0: + resolution: {integrity: sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==} + engines: {node: '>=0.6'} + + trouter@2.0.1: + resolution: {integrity: sha512-kr8SKKw94OI+xTGOkfsvwZQ8mWoikZDd2n8XZHjJVZUARZT+4/VV6cacRS6CLsH9bNm+HFIPU1Zx4CnNnb4qlQ==} + engines: {node: '>=6'} + + ts-api-utils@1.4.3: + resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==} + engines: {node: '>=16'} + peerDependencies: + typescript: '>=4.2.0' + + ts-api-utils@2.1.0: + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + ts.cryptojs256@1.0.1: + resolution: {integrity: sha512-9XtEgRVOZBCdpPcCEhfvv9I2AVXdvfI81I/KpFM0wEfbq5JVHlXH7bfIjGQmYrIHGiBEHKsIaStQ87k926J7dA==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tsx@4.20.3: + resolution: {integrity: sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==} + engines: {node: '>=18.0.0'} + hasBin: true + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + type-is@2.0.1: + resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} + engines: {node: '>= 0.6'} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + engines: {node: '>= 0.4'} + + typedoc@0.24.8: + resolution: {integrity: sha512-ahJ6Cpcvxwaxfu4KtjA8qZNqS43wYt6JL27wYiIgl1vd38WW/KWX11YuAeZhuz9v+ttrutSsgK+XO1CjL1kA3w==} + engines: {node: '>= 14.14'} + hasBin: true + peerDependencies: + typescript: 4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x + + typescript-eslint@8.34.1: + resolution: {integrity: sha512-XjS+b6Vg9oT1BaIUfkW3M3LvqZE++rbzAMEHuccCfO/YkP43ha6w3jTEMilQxMF92nVOYCcdjv1ZUhAa1D/0ow==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + typescript@5.0.4: + resolution: {integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==} + engines: {node: '>=12.20'} + hasBin: true + + typescript@5.4.5: + resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} + engines: {node: '>=14.17'} + hasBin: true + + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + + undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + + unicorn-magic@0.1.0: + resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} + engines: {node: '>=18'} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + use-sync-external-store@1.5.0: + resolution: {integrity: sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + utf-8-validate@5.0.10: + resolution: {integrity: sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==} + engines: {node: '>=6.14.2'} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + vite-node@3.2.4: + resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + + vite@7.0.4: + resolution: {integrity: sha512-SkaSguuS7nnmV7mfJ8l81JGBFV7Gvzp8IzgE8A8t23+AxuNX61Q5H1Tpz5efduSN7NHC8nQXD3sKQKZAu5mNEA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@3.2.4: + resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@vitest/browser': 3.2.4 + '@vitest/ui': 3.2.4 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + vscode-oniguruma@1.7.0: + resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==} + + vscode-textmate@8.0.0: + resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==} + + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + + wait-for-expect@3.0.2: + resolution: {integrity: sha512-cfS1+DZxuav1aBYbaO/kE06EOS8yRw7qOFoD3XtjTkYvCvh3zUvNST8DXK/nPaeqIzIv3P3kL3lRJn8iwOiSag==} + + walk-up-path@4.0.0: + resolution: {integrity: sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==} + engines: {node: 20 || >=22} + + wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + + whatwg-mimetype@3.0.0: + resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} + engines: {node: '>=12'} + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + whatwg-url@14.2.0: + resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==} + engines: {node: '>=18'} + + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.19: + resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} + engines: {node: '>= 0.4'} + + which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + ws@8.18.0: + resolution: {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 + + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + + yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + + yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + + yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + yocto-queue@1.2.1: + resolution: {integrity: sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==} + engines: {node: '>=12.20'} + + yoctocolors-cjs@2.1.2: + resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} + engines: {node: '>=18'} + + zod-to-json-schema@3.24.6: + resolution: {integrity: sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==} + peerDependencies: + zod: ^3.24.1 + + zod-validation-error@3.5.3: + resolution: {integrity: sha512-OT5Y8lbUadqVZCsnyFaTQ4/O2mys4tj7PqhdbBCp7McPwvIEKfPtdA6QfPeFQK2/Rz5LgwmAXRJTugBNBi0btw==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + +snapshots: + + '@ark/attest@0.48.2(typescript@5.0.4)': + dependencies: + '@ark/fs': 0.46.0 + '@ark/util': 0.46.0 + '@prettier/sync': 0.5.5(prettier@3.5.3) + '@typescript/analyze-trace': 0.10.1 + '@typescript/vfs': 1.6.1(typescript@5.0.4) + arktype: 2.1.20 + prettier: 3.5.3 + typescript: 5.0.4 + transitivePeerDependencies: + - supports-color + + '@ark/fs@0.46.0': {} + + '@ark/schema@0.46.0': + dependencies: + '@ark/util': 0.46.0 + + '@ark/util@0.46.0': {} + + '@arr/every@1.0.1': {} + + '@asamuzakjp/css-color@3.2.0': + dependencies: + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-color-parser': 3.0.10(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + lru-cache: 10.4.3 + + '@auth0/auth0-react@2.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@auth0/auth0-spa-js': 2.2.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@auth0/auth0-spa-js@2.2.0': {} + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.27.1 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/generator@7.28.0': + dependencies: + '@babel/parser': 7.28.0 + '@babel/types': 7.28.1 + '@jridgewell/gen-mapping': 0.3.12 + '@jridgewell/trace-mapping': 0.3.29 + jsesc: 3.1.0 + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.27.1': {} + + '@babel/parser@7.28.0': + dependencies: + '@babel/types': 7.28.1 + + '@babel/runtime@7.27.6': {} + + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.0 + '@babel/types': 7.28.1 + + '@babel/traverse@7.28.0': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.0 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.0 + '@babel/template': 7.27.2 + '@babel/types': 7.28.1 + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.28.1': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + + '@clerk/clerk-react@5.34.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@clerk/shared': 3.12.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@clerk/types': 4.65.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + tslib: 2.8.1 + + '@clerk/shared@3.12.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@clerk/types': 4.65.0 + dequal: 2.0.3 + glob-to-regexp: 0.4.1 + js-cookie: 3.0.5 + std-env: 3.9.0 + swr: 2.3.4(react@18.3.1) + optionalDependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@clerk/types@4.65.0': + dependencies: + csstype: 3.1.3 + + '@commander-js/extra-typings@11.1.0(commander@11.1.0)': + dependencies: + commander: 11.1.0 + + '@csstools/color-helpers@5.0.2': {} + + '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-color-parser@3.0.10(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/color-helpers': 5.0.2 + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-tokenizer@3.0.4': {} + + '@emnapi/core@1.4.4': + dependencies: + '@emnapi/wasi-threads': 1.0.3 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.4.4': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.0.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@esbuild/aix-ppc64@0.25.4': + optional: true + + '@esbuild/android-arm64@0.25.4': + optional: true + + '@esbuild/android-arm@0.25.4': + optional: true + + '@esbuild/android-x64@0.25.4': + optional: true + + '@esbuild/darwin-arm64@0.25.4': + optional: true + + '@esbuild/darwin-x64@0.25.4': + optional: true + + '@esbuild/freebsd-arm64@0.25.4': + optional: true + + '@esbuild/freebsd-x64@0.25.4': + optional: true + + '@esbuild/linux-arm64@0.25.4': + optional: true + + '@esbuild/linux-arm@0.25.4': + optional: true + + '@esbuild/linux-ia32@0.25.4': + optional: true + + '@esbuild/linux-loong64@0.25.4': + optional: true + + '@esbuild/linux-mips64el@0.25.4': + optional: true + + '@esbuild/linux-ppc64@0.25.4': + optional: true + + '@esbuild/linux-riscv64@0.25.4': + optional: true + + '@esbuild/linux-s390x@0.25.4': + optional: true + + '@esbuild/linux-x64@0.25.4': + optional: true + + '@esbuild/netbsd-arm64@0.25.4': + optional: true + + '@esbuild/netbsd-x64@0.25.4': + optional: true + + '@esbuild/openbsd-arm64@0.25.4': + optional: true + + '@esbuild/openbsd-x64@0.25.4': + optional: true + + '@esbuild/sunos-x64@0.25.4': + optional: true + + '@esbuild/win32-arm64@0.25.4': + optional: true + + '@esbuild/win32-ia32@0.25.4': + optional: true + + '@esbuild/win32-x64@0.25.4': + optional: true + + '@eslint-community/eslint-utils@4.7.0(eslint@9.28.0(jiti@2.4.2))': + dependencies: + eslint: 9.28.0(jiti@2.4.2) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.1': {} + + '@eslint/compat@1.3.1(eslint@9.28.0(jiti@2.4.2))': + optionalDependencies: + eslint: 9.28.0(jiti@2.4.2) + + '@eslint/config-array@0.20.1': + dependencies: + '@eslint/object-schema': 2.1.6 + debug: 4.4.1 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.2.3': {} + + '@eslint/core@0.14.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/core@0.15.1': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.1': + dependencies: + ajv: 6.12.6 + debug: 4.4.1 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.28.0': {} + + '@eslint/object-schema@2.1.6': {} + + '@eslint/plugin-kit@0.3.3': + dependencies: + '@eslint/core': 0.15.1 + levn: 0.4.1 + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.6': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.3.1 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.3.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@inquirer/figures@1.0.12': {} + + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.2 + + '@jridgewell/gen-mapping@0.3.12': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.4 + '@jridgewell/trace-mapping': 0.3.29 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.4': {} + + '@jridgewell/trace-mapping@0.3.29': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.4 + + '@modelcontextprotocol/sdk@1.15.1': + dependencies: + ajv: 6.12.6 + content-type: 1.0.5 + cors: 2.8.5 + cross-spawn: 7.0.6 + eventsource: 3.0.7 + eventsource-parser: 3.0.3 + express: 5.1.0 + express-rate-limit: 7.5.1(express@5.1.0) + pkce-challenge: 5.0.0 + raw-body: 3.0.0 + zod: 3.25.76 + zod-to-json-schema: 3.24.6(zod@3.25.76) + transitivePeerDependencies: + - supports-color + + '@napi-rs/wasm-runtime@0.2.12': + dependencies: + '@emnapi/core': 1.4.4 + '@emnapi/runtime': 1.4.4 + '@tybys/wasm-util': 0.10.0 + optional: true + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.19.1 + + '@octokit/openapi-types@25.0.0': {} + + '@oxc-resolver/binding-android-arm-eabi@11.5.2': + optional: true + + '@oxc-resolver/binding-android-arm64@11.5.2': + optional: true + + '@oxc-resolver/binding-darwin-arm64@11.5.2': + optional: true + + '@oxc-resolver/binding-darwin-x64@11.5.2': + optional: true + + '@oxc-resolver/binding-freebsd-x64@11.5.2': + optional: true + + '@oxc-resolver/binding-linux-arm-gnueabihf@11.5.2': + optional: true + + '@oxc-resolver/binding-linux-arm-musleabihf@11.5.2': + optional: true + + '@oxc-resolver/binding-linux-arm64-gnu@11.5.2': + optional: true + + '@oxc-resolver/binding-linux-arm64-musl@11.5.2': + optional: true + + '@oxc-resolver/binding-linux-ppc64-gnu@11.5.2': + optional: true + + '@oxc-resolver/binding-linux-riscv64-gnu@11.5.2': + optional: true + + '@oxc-resolver/binding-linux-riscv64-musl@11.5.2': + optional: true + + '@oxc-resolver/binding-linux-s390x-gnu@11.5.2': + optional: true + + '@oxc-resolver/binding-linux-x64-gnu@11.5.2': + optional: true + + '@oxc-resolver/binding-linux-x64-musl@11.5.2': + optional: true + + '@oxc-resolver/binding-wasm32-wasi@11.5.2': + dependencies: + '@napi-rs/wasm-runtime': 0.2.12 + optional: true + + '@oxc-resolver/binding-win32-arm64-msvc@11.5.2': + optional: true + + '@oxc-resolver/binding-win32-ia32-msvc@11.5.2': + optional: true + + '@oxc-resolver/binding-win32-x64-msvc@11.5.2': + optional: true + + '@parcel/watcher-android-arm64@2.5.1': + optional: true + + '@parcel/watcher-darwin-arm64@2.5.1': + optional: true + + '@parcel/watcher-darwin-x64@2.5.1': + optional: true + + '@parcel/watcher-freebsd-x64@2.5.1': + optional: true + + '@parcel/watcher-linux-arm-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-arm-musl@2.5.1': + optional: true + + '@parcel/watcher-linux-arm64-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-arm64-musl@2.5.1': + optional: true + + '@parcel/watcher-linux-x64-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-x64-musl@2.5.1': + optional: true + + '@parcel/watcher-win32-arm64@2.5.1': + optional: true + + '@parcel/watcher-win32-ia32@2.5.1': + optional: true + + '@parcel/watcher-win32-x64@2.5.1': + optional: true + + '@parcel/watcher@2.5.1': + dependencies: + detect-libc: 1.0.3 + is-glob: 4.0.3 + micromatch: 4.0.8 + node-addon-api: 7.1.1 + optionalDependencies: + '@parcel/watcher-android-arm64': 2.5.1 + '@parcel/watcher-darwin-arm64': 2.5.1 + '@parcel/watcher-darwin-x64': 2.5.1 + '@parcel/watcher-freebsd-x64': 2.5.1 + '@parcel/watcher-linux-arm-glibc': 2.5.1 + '@parcel/watcher-linux-arm-musl': 2.5.1 + '@parcel/watcher-linux-arm64-glibc': 2.5.1 + '@parcel/watcher-linux-arm64-musl': 2.5.1 + '@parcel/watcher-linux-x64-glibc': 2.5.1 + '@parcel/watcher-linux-x64-musl': 2.5.1 + '@parcel/watcher-win32-arm64': 2.5.1 + '@parcel/watcher-win32-ia32': 2.5.1 + '@parcel/watcher-win32-x64': 2.5.1 + + '@polka/url@0.5.0': {} + + '@polka/url@1.0.0-next.29': {} + + '@prettier/sync@0.5.5(prettier@3.5.3)': + dependencies: + make-synchronized: 0.4.2 + prettier: 3.5.3 + + '@rollup/rollup-android-arm-eabi@4.45.0': + optional: true + + '@rollup/rollup-android-arm64@4.45.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.45.0': + optional: true + + '@rollup/rollup-darwin-x64@4.45.0': + optional: true + + '@rollup/rollup-freebsd-arm64@4.45.0': + optional: true + + '@rollup/rollup-freebsd-x64@4.45.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.45.0': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.45.0': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.45.0': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.45.0': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.45.0': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.45.0': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.45.0': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.45.0': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.45.0': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.45.0': + optional: true + + '@rollup/rollup-linux-x64-musl@4.45.0': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.45.0': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.45.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.45.0': + optional: true + + '@sentry-internal/tracing@7.120.3': + dependencies: + '@sentry/core': 7.120.3 + '@sentry/types': 7.120.3 + '@sentry/utils': 7.120.3 + + '@sentry/core@7.120.3': + dependencies: + '@sentry/types': 7.120.3 + '@sentry/utils': 7.120.3 + + '@sentry/integrations@7.120.3': + dependencies: + '@sentry/core': 7.120.3 + '@sentry/types': 7.120.3 + '@sentry/utils': 7.120.3 + localforage: 1.10.0 + + '@sentry/node@7.120.3': + dependencies: + '@sentry-internal/tracing': 7.120.3 + '@sentry/core': 7.120.3 + '@sentry/integrations': 7.120.3 + '@sentry/types': 7.120.3 + '@sentry/utils': 7.120.3 + + '@sentry/tracing@7.120.3': + dependencies: + '@sentry-internal/tracing': 7.120.3 + + '@sentry/types@7.120.3': {} + + '@sentry/utils@7.120.3': + dependencies: + '@sentry/types': 7.120.3 + + '@swc/core-darwin-arm64@1.10.4': + optional: true + + '@swc/core-darwin-x64@1.10.4': + optional: true + + '@swc/core-linux-arm-gnueabihf@1.10.4': + optional: true + + '@swc/core-linux-arm64-gnu@1.10.4': + optional: true + + '@swc/core-linux-arm64-musl@1.10.4': + optional: true + + '@swc/core-linux-x64-gnu@1.10.4': + optional: true + + '@swc/core-linux-x64-musl@1.10.4': + optional: true + + '@swc/core-win32-arm64-msvc@1.10.4': + optional: true + + '@swc/core-win32-ia32-msvc@1.10.4': + optional: true + + '@swc/core-win32-x64-msvc@1.10.4': + optional: true + + '@swc/core@1.10.4': + dependencies: + '@swc/counter': 0.1.3 + '@swc/types': 0.1.23 + optionalDependencies: + '@swc/core-darwin-arm64': 1.10.4 + '@swc/core-darwin-x64': 1.10.4 + '@swc/core-linux-arm-gnueabihf': 1.10.4 + '@swc/core-linux-arm64-gnu': 1.10.4 + '@swc/core-linux-arm64-musl': 1.10.4 + '@swc/core-linux-x64-gnu': 1.10.4 + '@swc/core-linux-x64-musl': 1.10.4 + '@swc/core-win32-arm64-msvc': 1.10.4 + '@swc/core-win32-ia32-msvc': 1.10.4 + '@swc/core-win32-x64-msvc': 1.10.4 + + '@swc/counter@0.1.3': {} + + '@swc/types@0.1.23': + dependencies: + '@swc/counter': 0.1.3 + + '@testing-library/dom@10.4.0': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/runtime': 7.27.6 + '@types/aria-query': 5.0.4 + aria-query: 5.3.0 + chalk: 4.1.2 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + pretty-format: 27.5.1 + + '@testing-library/react@16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.27.6 + '@testing-library/dom': 10.4.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7(@types/react@18.3.23) + + '@tybys/wasm-util@0.10.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/adm-zip@0.5.7': + dependencies: + '@types/node': 18.19.119 + + '@types/aria-query@5.0.4': {} + + '@types/chai@5.2.2': + dependencies: + '@types/deep-eql': 4.0.2 + + '@types/deep-eql@4.0.2': {} + + '@types/deep-equal@1.0.4': {} + + '@types/detect-port@1.3.5': {} + + '@types/estree@1.0.8': {} + + '@types/inquirer@9.0.8': + dependencies: + '@types/through': 0.0.33 + rxjs: 7.8.2 + + '@types/json-schema@7.0.15': {} + + '@types/jwt-encode@1.0.3': {} + + '@types/minimatch@3.0.5': {} + + '@types/node@18.19.119': + dependencies: + undici-types: 5.26.5 + + '@types/parse-json@4.0.2': {} + + '@types/progress@2.0.7': + dependencies: + '@types/node': 18.19.119 + + '@types/prop-types@15.7.15': {} + + '@types/react-dom@18.3.7(@types/react@18.3.23)': + dependencies: + '@types/react': 18.3.23 + + '@types/react@18.3.23': + dependencies: + '@types/prop-types': 15.7.15 + csstype: 3.1.3 + + '@types/semver@7.7.0': {} + + '@types/serve-handler@6.1.4': + dependencies: + '@types/node': 18.19.119 + + '@types/through@0.0.33': + dependencies: + '@types/node': 18.19.119 + + '@types/ws@8.18.1': + dependencies: + '@types/node': 18.19.119 + + '@typescript-eslint/eslint-plugin@8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.0.4))(eslint@9.28.0(jiti@2.4.2))(typescript@5.0.4)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 8.34.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.0.4) + '@typescript-eslint/scope-manager': 8.34.1 + '@typescript-eslint/type-utils': 8.34.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.0.4) + '@typescript-eslint/utils': 8.34.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.0.4) + '@typescript-eslint/visitor-keys': 8.34.1 + eslint: 9.28.0(jiti@2.4.2) + graphemer: 1.4.0 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.1.0(typescript@5.0.4) + typescript: 5.0.4 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.34.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.0.4)': + dependencies: + '@typescript-eslint/scope-manager': 8.34.1 + '@typescript-eslint/types': 8.34.1 + '@typescript-eslint/typescript-estree': 8.34.1(typescript@5.0.4) + '@typescript-eslint/visitor-keys': 8.34.1 + debug: 4.4.1 + eslint: 9.28.0(jiti@2.4.2) + typescript: 5.0.4 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.34.1(typescript@5.0.4)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.34.1(typescript@5.0.4) + '@typescript-eslint/types': 8.34.1 + debug: 4.4.1 + typescript: 5.0.4 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.37.0(typescript@5.0.4)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.37.0(typescript@5.0.4) + '@typescript-eslint/types': 8.37.0 + debug: 4.4.1 + typescript: 5.0.4 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.34.1': + dependencies: + '@typescript-eslint/types': 8.34.1 + '@typescript-eslint/visitor-keys': 8.34.1 + + '@typescript-eslint/scope-manager@8.37.0': + dependencies: + '@typescript-eslint/types': 8.37.0 + '@typescript-eslint/visitor-keys': 8.37.0 + + '@typescript-eslint/tsconfig-utils@8.34.1(typescript@5.0.4)': + dependencies: + typescript: 5.0.4 + + '@typescript-eslint/tsconfig-utils@8.37.0(typescript@5.0.4)': + dependencies: + typescript: 5.0.4 + + '@typescript-eslint/type-utils@8.34.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.0.4)': + dependencies: + '@typescript-eslint/typescript-estree': 8.34.1(typescript@5.0.4) + '@typescript-eslint/utils': 8.34.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.0.4) + debug: 4.4.1 + eslint: 9.28.0(jiti@2.4.2) + ts-api-utils: 2.1.0(typescript@5.0.4) + typescript: 5.0.4 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@7.13.1': {} + + '@typescript-eslint/types@8.34.1': {} + + '@typescript-eslint/types@8.37.0': {} + + '@typescript-eslint/typescript-estree@7.13.1(typescript@5.4.5)': + dependencies: + '@typescript-eslint/types': 7.13.1 + '@typescript-eslint/visitor-keys': 7.13.1 + debug: 4.4.1 + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.2 + ts-api-utils: 1.4.3(typescript@5.4.5) + optionalDependencies: + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/typescript-estree@8.34.1(typescript@5.0.4)': + dependencies: + '@typescript-eslint/project-service': 8.34.1(typescript@5.0.4) + '@typescript-eslint/tsconfig-utils': 8.34.1(typescript@5.0.4) + '@typescript-eslint/types': 8.34.1 + '@typescript-eslint/visitor-keys': 8.34.1 + debug: 4.4.1 + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.2 + ts-api-utils: 2.1.0(typescript@5.0.4) + typescript: 5.0.4 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/typescript-estree@8.37.0(typescript@5.0.4)': + dependencies: + '@typescript-eslint/project-service': 8.37.0(typescript@5.0.4) + '@typescript-eslint/tsconfig-utils': 8.37.0(typescript@5.0.4) + '@typescript-eslint/types': 8.37.0 + '@typescript-eslint/visitor-keys': 8.37.0 + debug: 4.4.1 + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.2 + ts-api-utils: 2.1.0(typescript@5.0.4) + typescript: 5.0.4 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.34.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.0.4)': + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.28.0(jiti@2.4.2)) + '@typescript-eslint/scope-manager': 8.34.1 + '@typescript-eslint/types': 8.34.1 + '@typescript-eslint/typescript-estree': 8.34.1(typescript@5.0.4) + eslint: 9.28.0(jiti@2.4.2) + typescript: 5.0.4 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.37.0(eslint@9.28.0(jiti@2.4.2))(typescript@5.0.4)': + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.28.0(jiti@2.4.2)) + '@typescript-eslint/scope-manager': 8.37.0 + '@typescript-eslint/types': 8.37.0 + '@typescript-eslint/typescript-estree': 8.37.0(typescript@5.0.4) + eslint: 9.28.0(jiti@2.4.2) + typescript: 5.0.4 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@7.13.1': + dependencies: + '@typescript-eslint/types': 7.13.1 + eslint-visitor-keys: 3.4.3 + + '@typescript-eslint/visitor-keys@8.34.1': + dependencies: + '@typescript-eslint/types': 8.34.1 + eslint-visitor-keys: 4.2.1 + + '@typescript-eslint/visitor-keys@8.37.0': + dependencies: + '@typescript-eslint/types': 8.37.0 + eslint-visitor-keys: 4.2.1 + + '@typescript/analyze-trace@0.10.1': + dependencies: + chalk: 4.1.2 + exit: 0.1.2 + jsonparse: 1.3.1 + jsonstream-next: 3.0.0 + p-limit: 3.1.0 + split2: 3.2.2 + treeify: 1.1.0 + yargs: 16.2.0 + + '@typescript/vfs@1.6.1(typescript@5.0.4)': + dependencies: + debug: 4.4.1 + typescript: 5.0.4 + transitivePeerDependencies: + - supports-color + + '@vitest/eslint-plugin@1.3.4(eslint@9.28.0(jiti@2.4.2))(typescript@5.0.4)(vitest@3.2.4(@types/node@18.19.119)(happy-dom@17.4.9)(jiti@2.4.2)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.3))': + dependencies: + '@typescript-eslint/utils': 8.37.0(eslint@9.28.0(jiti@2.4.2))(typescript@5.0.4) + eslint: 9.28.0(jiti@2.4.2) + optionalDependencies: + typescript: 5.0.4 + vitest: 3.2.4(@types/node@18.19.119)(happy-dom@17.4.9)(jiti@2.4.2)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.3) + transitivePeerDependencies: + - supports-color + + '@vitest/expect@3.2.4': + dependencies: + '@types/chai': 5.2.2 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.2.1 + tinyrainbow: 2.0.0 + + '@vitest/mocker@3.2.4(vite@7.0.4(@types/node@18.19.119)(jiti@2.4.2)(tsx@4.20.3))': + dependencies: + '@vitest/spy': 3.2.4 + estree-walker: 3.0.3 + magic-string: 0.30.17 + optionalDependencies: + vite: 7.0.4(@types/node@18.19.119)(jiti@2.4.2)(tsx@4.20.3) + + '@vitest/pretty-format@3.2.4': + dependencies: + tinyrainbow: 2.0.0 + + '@vitest/runner@3.2.4': + dependencies: + '@vitest/utils': 3.2.4 + pathe: 2.0.3 + strip-literal: 3.0.0 + + '@vitest/snapshot@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + magic-string: 0.30.17 + pathe: 2.0.3 + + '@vitest/spy@3.2.4': + dependencies: + tinyspy: 4.0.3 + + '@vitest/utils@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + loupe: 3.1.4 + tinyrainbow: 2.0.0 + + '@vue/compiler-core@3.5.17': + dependencies: + '@babel/parser': 7.28.0 + '@vue/shared': 3.5.17 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.17': + dependencies: + '@vue/compiler-core': 3.5.17 + '@vue/shared': 3.5.17 + + '@vue/compiler-sfc@3.5.17': + dependencies: + '@babel/parser': 7.28.0 + '@vue/compiler-core': 3.5.17 + '@vue/compiler-dom': 3.5.17 + '@vue/compiler-ssr': 3.5.17 + '@vue/shared': 3.5.17 + estree-walker: 2.0.2 + magic-string: 0.30.17 + postcss: 8.5.6 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.17': + dependencies: + '@vue/compiler-dom': 3.5.17 + '@vue/shared': 3.5.17 + + '@vue/shared@3.5.17': {} + + accepts@2.0.0: + dependencies: + mime-types: 3.0.1 + negotiator: 1.0.0 + + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} + + address@2.0.3: {} + + adm-zip@0.5.16: {} + + agent-base@7.1.4: {} + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-escapes@3.2.0: {} + + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + + ansi-regex@3.0.1: {} + + ansi-regex@5.0.1: {} + + ansi-regex@6.1.0: {} + + ansi-sequence-parser@1.1.3: {} + + ansi-styles@3.2.1: + dependencies: + color-convert: 1.9.3 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@5.2.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + argparse@2.0.1: {} + + aria-query@5.3.0: + dependencies: + dequal: 2.0.3 + + arktype@2.1.20: + dependencies: + '@ark/schema': 0.46.0 + '@ark/util': 0.46.0 + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + array-differ@3.0.0: {} + + array-includes@3.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + is-string: 1.1.1 + math-intrinsics: 1.1.0 + + array-union@2.1.0: {} + + array.prototype.findlast@1.2.5: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + + array.prototype.flat@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-shim-unscopables: 1.1.0 + + array.prototype.flatmap@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-shim-unscopables: 1.1.0 + + array.prototype.tosorted@1.1.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + es-shim-unscopables: 1.1.0 + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + + arrify@2.0.1: {} + + assertion-error@2.0.1: {} + + async-function@1.0.0: {} + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + balanced-match@1.0.2: {} + + base64-js@1.5.1: {} + + binary-extensions@2.3.0: {} + + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + + bl@5.1.0: + dependencies: + buffer: 6.0.3 + inherits: 2.0.4 + readable-stream: 3.6.2 + + body-parser@2.2.0: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 4.4.1 + http-errors: 2.0.0 + iconv-lite: 0.6.3 + on-finished: 2.4.1 + qs: 6.14.0 + raw-body: 3.0.0 + type-is: 2.0.1 + transitivePeerDependencies: + - supports-color + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + bufferutil@4.0.9: + dependencies: + node-gyp-build: 4.8.4 + + bytes@3.0.0: {} + + bytes@3.1.2: {} + + cac@6.7.14: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsite@1.0.0: {} + + callsites@3.1.0: {} + + camelcase@6.3.0: {} + + chai@5.2.1: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.1 + deep-eql: 5.0.2 + loupe: 3.1.4 + pathval: 2.0.1 + + chalk@2.4.2: + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@5.4.1: {} + + chardet@0.4.2: {} + + chardet@0.7.0: {} + + check-error@2.1.1: {} + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + chownr@3.0.0: {} + + cli-cursor@2.1.0: + dependencies: + restore-cursor: 2.0.0 + + cli-cursor@3.1.0: + dependencies: + restore-cursor: 3.1.0 + + cli-cursor@4.0.0: + dependencies: + restore-cursor: 4.0.0 + + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-spinners@2.9.2: {} + + cli-width@2.2.1: {} + + cli-width@4.1.0: {} + + cliui@7.0.4: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + clone@1.0.4: {} + + color-convert@1.9.3: + dependencies: + color-name: 1.1.3 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.3: {} + + color-name@1.1.4: {} + + commander@11.1.0: {} + + compressible@2.0.18: + dependencies: + mime-db: 1.54.0 + + compression@1.8.0: + dependencies: + bytes: 3.1.2 + compressible: 2.0.18 + debug: 2.6.9 + negotiator: 0.6.4 + on-headers: 1.0.2 + safe-buffer: 5.2.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + concat-map@0.0.1: {} + + content-disposition@0.5.2: {} + + content-disposition@1.0.0: + dependencies: + safe-buffer: 5.2.1 + + content-type@1.0.5: {} + + cookie-signature@1.2.2: {} + + cookie@0.7.2: {} + + cors@2.8.5: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + + cosmiconfig@7.1.0: + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.1 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + cssstyle@4.6.0: + dependencies: + '@asamuzakjp/css-color': 3.2.0 + rrweb-cssom: 0.8.0 + + csstype@3.1.3: {} + + data-urls@5.0.0: + dependencies: + whatwg-mimetype: 4.0.0 + whatwg-url: 14.2.0 + + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + debug@2.6.9: + dependencies: + ms: 2.0.0 + + debug@4.4.1: + dependencies: + ms: 2.1.3 + + decimal.js@10.6.0: {} + + deep-eql@5.0.2: {} + + deep-equal@2.2.3: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 + es-get-iterator: 1.1.3 + get-intrinsic: 1.3.0 + is-arguments: 1.2.0 + is-array-buffer: 3.0.5 + is-date-object: 1.1.0 + is-regex: 1.2.1 + is-shared-array-buffer: 1.0.4 + isarray: 2.0.5 + object-is: 1.1.6 + object-keys: 1.1.1 + object.assign: 4.1.7 + regexp.prototype.flags: 1.5.4 + side-channel: 1.1.0 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.19 + + deep-is@0.1.4: {} + + defaults@1.0.4: + dependencies: + clone: 1.0.4 + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-lazy-prop@2.0.0: {} + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + depcheck@1.4.7: + dependencies: + '@babel/parser': 7.28.0 + '@babel/traverse': 7.28.0 + '@vue/compiler-sfc': 3.5.17 + callsite: 1.0.0 + camelcase: 6.3.0 + cosmiconfig: 7.1.0 + debug: 4.4.1 + deps-regex: 0.2.0 + findup-sync: 5.0.0 + ignore: 5.3.2 + is-core-module: 2.16.1 + js-yaml: 3.14.1 + json5: 2.2.3 + lodash: 4.17.21 + minimatch: 7.4.6 + multimatch: 5.0.0 + please-upgrade-node: 3.2.0 + readdirp: 3.6.0 + require-package-name: 2.0.1 + resolve: 1.22.10 + resolve-from: 5.0.0 + semver: 7.7.2 + yargs: 16.2.0 + transitivePeerDependencies: + - supports-color + + depd@2.0.0: {} + + deps-regex@0.2.0: {} + + dequal@2.0.3: {} + + detect-file@1.0.0: {} + + detect-libc@1.0.3: {} + + detect-port@2.1.0: + dependencies: + address: 2.0.3 + + digraph-js@2.2.3: + dependencies: + lodash.isequal: 4.5.0 + lodash.uniqwith: 4.5.0 + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + doctrine@2.1.0: + dependencies: + esutils: 2.0.3 + + dom-accessibility-api@0.5.16: {} + + dotenv@16.6.1: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + ee-first@1.1.1: {} + + effect@3.3.2: {} + + emoji-regex@10.4.0: {} + + emoji-regex@8.0.0: {} + + encodeurl@2.0.0: {} + + encoding@0.1.13: + dependencies: + iconv-lite: 0.6.3 + + entities@4.5.0: {} + + entities@6.0.1: {} + + error-ex@1.3.2: + dependencies: + is-arrayish: 0.2.1 + + es-abstract@1.24.0: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.3 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.19 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-get-iterator@1.1.3: + dependencies: + call-bind: 1.0.8 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + is-arguments: 1.2.0 + is-map: 2.0.3 + is-set: 2.0.3 + is-string: 1.1.1 + isarray: 2.0.5 + stop-iteration-iterator: 1.1.0 + + es-iterator-helpers@1.2.1: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + es-set-tostringtag: 2.1.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + iterator.prototype: 1.1.5 + safe-array-concat: 1.1.3 + + es-module-lexer@1.7.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es-shim-unscopables@1.1.0: + dependencies: + hasown: 2.0.2 + + es-to-primitive@1.3.0: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + esbuild-plugin-external-global@1.0.1: {} + + esbuild@0.25.4: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.4 + '@esbuild/android-arm': 0.25.4 + '@esbuild/android-arm64': 0.25.4 + '@esbuild/android-x64': 0.25.4 + '@esbuild/darwin-arm64': 0.25.4 + '@esbuild/darwin-x64': 0.25.4 + '@esbuild/freebsd-arm64': 0.25.4 + '@esbuild/freebsd-x64': 0.25.4 + '@esbuild/linux-arm': 0.25.4 + '@esbuild/linux-arm64': 0.25.4 + '@esbuild/linux-ia32': 0.25.4 + '@esbuild/linux-loong64': 0.25.4 + '@esbuild/linux-mips64el': 0.25.4 + '@esbuild/linux-ppc64': 0.25.4 + '@esbuild/linux-riscv64': 0.25.4 + '@esbuild/linux-s390x': 0.25.4 + '@esbuild/linux-x64': 0.25.4 + '@esbuild/netbsd-arm64': 0.25.4 + '@esbuild/netbsd-x64': 0.25.4 + '@esbuild/openbsd-arm64': 0.25.4 + '@esbuild/openbsd-x64': 0.25.4 + '@esbuild/sunos-x64': 0.25.4 + '@esbuild/win32-arm64': 0.25.4 + '@esbuild/win32-ia32': 0.25.4 + '@esbuild/win32-x64': 0.25.4 + + escalade@3.2.0: {} + + escape-html@1.0.3: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@4.0.0: {} + + eslint-config-prettier@10.1.5(eslint@9.28.0(jiti@2.4.2)): + dependencies: + eslint: 9.28.0(jiti@2.4.2) + + eslint-plugin-react-hooks@5.2.0(eslint@9.28.0(jiti@2.4.2)): + dependencies: + eslint: 9.28.0(jiti@2.4.2) + + eslint-plugin-react@7.37.5(eslint@9.28.0(jiti@2.4.2)): + dependencies: + array-includes: 3.1.9 + array.prototype.findlast: 1.2.5 + array.prototype.flatmap: 1.3.3 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.2.1 + eslint: 9.28.0(jiti@2.4.2) + estraverse: 5.3.0 + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.2 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.values: 1.2.1 + prop-types: 15.8.1 + resolve: 2.0.0-next.5 + semver: 6.3.1 + string.prototype.matchall: 4.0.12 + string.prototype.repeat: 1.0.0 + + eslint-plugin-require-extensions@0.1.3(eslint@9.28.0(jiti@2.4.2)): + dependencies: + eslint: 9.28.0(jiti@2.4.2) + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint@9.28.0(jiti@2.4.2): + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.28.0(jiti@2.4.2)) + '@eslint-community/regexpp': 4.12.1 + '@eslint/config-array': 0.20.1 + '@eslint/config-helpers': 0.2.3 + '@eslint/core': 0.14.0 + '@eslint/eslintrc': 3.3.1 + '@eslint/js': 9.28.0 + '@eslint/plugin-kit': 0.3.3 + '@humanfs/node': 0.16.6 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.1 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.4.2 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 4.2.1 + + esprima@4.0.1: {} + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-walker@2.0.2: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + esutils@2.0.3: {} + + etag@1.8.1: {} + + eventsource-parser@3.0.3: {} + + eventsource@3.0.7: + dependencies: + eventsource-parser: 3.0.3 + + exit@0.1.2: {} + + expand-tilde@2.0.2: + dependencies: + homedir-polyfill: 1.0.3 + + expect-type@1.2.2: {} + + express-rate-limit@7.5.1(express@5.1.0): + dependencies: + express: 5.1.0 + + express@5.1.0: + dependencies: + accepts: 2.0.0 + body-parser: 2.2.0 + content-disposition: 1.0.0 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.1 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.0 + fresh: 2.0.0 + http-errors: 2.0.0 + merge-descriptors: 2.0.0 + mime-types: 3.0.1 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.14.0 + range-parser: 1.2.1 + router: 2.2.0 + send: 1.2.0 + serve-static: 2.2.0 + statuses: 2.0.2 + type-is: 2.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + external-editor@2.2.0: + dependencies: + chardet: 0.4.2 + iconv-lite: 0.4.24 + tmp: 0.0.33 + + external-editor@3.1.0: + dependencies: + chardet: 0.7.0 + iconv-lite: 0.4.24 + tmp: 0.0.33 + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fastq@1.19.1: + dependencies: + reusify: 1.1.0 + + fd-package-json@2.0.0: + dependencies: + walk-up-path: 4.0.0 + + fdir@6.4.6(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + + fetch-retry@6.0.0: {} + + figures@2.0.0: + dependencies: + escape-string-regexp: 1.0.5 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + finalhandler@2.1.0: + dependencies: + debug: 4.4.1 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + find-up@7.0.0: + dependencies: + locate-path: 7.2.0 + path-exists: 5.0.0 + unicorn-magic: 0.1.0 + + findup-sync@5.0.0: + dependencies: + detect-file: 1.0.0 + is-glob: 4.0.3 + micromatch: 4.0.8 + resolve-dir: 1.0.1 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + + flatted@3.3.3: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + formatly@0.2.4: + dependencies: + fd-package-json: 2.0.0 + + forwarded@0.2.0: {} + + fp-ts@2.5.0: {} + + fresh@2.0.0: {} + + fs-tree-structure@0.0.5: + dependencies: + lodash-es: 4.17.21 + + fs.realpath@1.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + function.prototype.name@1.1.8: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + functions-have-names: 1.2.3 + hasown: 2.0.2 + is-callable: 1.2.7 + + functions-have-names@1.2.3: {} + + fuzzy@0.1.3: {} + + get-caller-file@2.0.5: {} + + get-east-asian-width@1.3.0: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + get-tsconfig@4.10.1: + dependencies: + resolve-pkg-maps: 1.0.0 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob-to-regexp@0.4.1: {} + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + global-modules@1.0.0: + dependencies: + global-prefix: 1.0.2 + is-windows: 1.0.2 + resolve-dir: 1.0.1 + + global-prefix@1.0.2: + dependencies: + expand-tilde: 2.0.2 + homedir-polyfill: 1.0.3 + ini: 1.3.8 + is-windows: 1.0.2 + which: 1.3.1 + + globals@14.0.0: {} + + globals@15.15.0: {} + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + gopd@1.2.0: {} + + graphemer@1.4.0: {} + + happy-dom@17.4.9: + dependencies: + webidl-conversions: 7.0.0 + whatwg-mimetype: 3.0.0 + + has-bigints@1.1.0: {} + + has-flag@3.0.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + homedir-polyfill@1.0.3: + dependencies: + parse-passwd: 1.0.0 + + html-encoding-sniffer@4.0.0: + dependencies: + whatwg-encoding: 3.1.1 + + http-errors@2.0.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.4 + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.1 + transitivePeerDependencies: + - supports-color + + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + ieee754@1.2.1: {} + + ignore-walk@6.0.5: + dependencies: + minimatch: 9.0.5 + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + immediate@3.0.6: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + ini@1.3.8: {} + + inquirer-search-list@1.2.6: + dependencies: + chalk: 2.4.2 + figures: 2.0.0 + fuzzy: 0.1.3 + inquirer: 3.3.0 + + inquirer@3.3.0: + dependencies: + ansi-escapes: 3.2.0 + chalk: 2.4.2 + cli-cursor: 2.1.0 + cli-width: 2.2.1 + external-editor: 2.2.0 + figures: 2.0.0 + lodash: 4.17.21 + mute-stream: 0.0.7 + run-async: 2.4.1 + rx-lite: 4.0.8 + rx-lite-aggregates: 4.0.8 + string-width: 2.1.1 + strip-ansi: 4.0.0 + through: 2.3.8 + + inquirer@9.3.7: + dependencies: + '@inquirer/figures': 1.0.12 + ansi-escapes: 4.3.2 + cli-width: 4.1.0 + external-editor: 3.1.0 + mute-stream: 1.0.0 + ora: 5.4.1 + run-async: 3.0.0 + rxjs: 7.8.2 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.2 + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.1.0 + + interpret@1.4.0: {} + + io-ts@2.2.22(fp-ts@2.5.0): + dependencies: + fp-ts: 2.5.0 + + ipaddr.js@1.9.1: {} + + is-arguments@1.2.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-arrayish@0.2.1: {} + + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-callable@1.2.7: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-docker@2.2.1: {} + + is-docker@3.0.0: {} + + is-extglob@2.1.1: {} + + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-fullwidth-code-point@2.0.0: {} + + is-fullwidth-code-point@3.0.0: {} + + is-generator-function@1.1.0: + dependencies: + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.0 + + is-interactive@1.0.0: {} + + is-interactive@2.0.0: {} + + is-map@2.0.3: {} + + is-negative-zero@2.0.3: {} + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-number@7.0.0: {} + + is-potential-custom-element-name@1.0.1: {} + + is-promise@4.0.0: {} + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.19 + + is-unicode-supported@0.1.0: {} + + is-unicode-supported@1.3.0: {} + + is-unicode-supported@2.1.0: {} + + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-windows@1.0.2: {} + + is-wsl@2.2.0: + dependencies: + is-docker: 2.2.1 + + is-wsl@3.1.0: + dependencies: + is-inside-container: 1.0.0 + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + iterator.prototype@1.1.5: + dependencies: + define-data-property: 1.1.4 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + has-symbols: 1.1.0 + set-function-name: 2.0.2 + + jiti@2.4.2: {} + + jose@4.15.9: {} + + js-cookie@3.0.5: {} + + js-tokens@4.0.0: {} + + js-tokens@9.0.1: {} + + js-yaml@3.14.1: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10): + dependencies: + cssstyle: 4.6.0 + data-urls: 5.0.0 + decimal.js: 10.6.0 + html-encoding-sniffer: 4.0.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.20 + parse5: 7.3.0 + rrweb-cssom: 0.8.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 5.1.2 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 3.1.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 14.2.0 + ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + jsesc@3.1.0: {} + + json-buffer@3.0.1: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@2.2.3: {} + + jsonc-parser@3.3.1: {} + + jsonparse@1.3.1: {} + + jsonstream-next@3.0.0: + dependencies: + jsonparse: 1.3.1 + through2: 4.0.2 + + jsx-ast-utils@3.3.5: + dependencies: + array-includes: 3.1.9 + array.prototype.flat: 1.3.3 + object.assign: 4.1.7 + object.values: 1.2.1 + + jwt-decode@4.0.0: {} + + jwt-encode@1.0.1: + dependencies: + ts.cryptojs256: 1.0.1 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + kleur@4.1.5: {} + + knip@5.60.2(@types/node@18.19.119)(typescript@5.0.4): + dependencies: + '@nodelib/fs.walk': 1.2.8 + '@types/node': 18.19.119 + fast-glob: 3.3.3 + formatly: 0.2.4 + jiti: 2.4.2 + js-yaml: 4.1.0 + minimist: 1.2.8 + oxc-resolver: 11.5.2 + picocolors: 1.1.1 + picomatch: 4.0.2 + smol-toml: 1.4.1 + strip-json-comments: 5.0.2 + typescript: 5.0.4 + zod: 3.25.76 + zod-validation-error: 3.5.3(zod@3.25.76) + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lie@3.1.1: + dependencies: + immediate: 3.0.6 + + lines-and-columns@1.2.4: {} + + localforage@1.10.0: + dependencies: + lie: 3.1.1 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + locate-path@7.2.0: + dependencies: + p-locate: 6.0.0 + + lodash-es@4.17.21: {} + + lodash.isequal@4.5.0: {} + + lodash.merge@4.6.2: {} + + lodash.uniqwith@4.5.0: {} + + lodash@4.17.21: {} + + log-symbols@4.1.0: + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + + log-symbols@5.1.0: + dependencies: + chalk: 5.4.1 + is-unicode-supported: 1.3.0 + + log-symbols@6.0.0: + dependencies: + chalk: 5.4.1 + is-unicode-supported: 1.3.0 + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + loupe@3.1.4: {} + + lru-cache@10.4.3: {} + + lru-cache@6.0.0: + dependencies: + yallist: 4.0.0 + + lunr@2.3.9: {} + + lz-string@1.5.0: {} + + magic-string@0.30.17: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.4 + + make-synchronized@0.4.2: {} + + marked@4.3.0: {} + + matchit@1.1.0: + dependencies: + '@arr/every': 1.0.1 + + math-intrinsics@1.1.0: {} + + media-typer@1.1.0: {} + + merge-descriptors@2.0.0: {} + + merge2@1.4.1: {} + + meriyah@4.5.0: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.33.0: {} + + mime-db@1.54.0: {} + + mime-types@2.1.18: + dependencies: + mime-db: 1.33.0 + + mime-types@3.0.1: + dependencies: + mime-db: 1.54.0 + + mimic-fn@1.2.0: {} + + mimic-fn@2.1.0: {} + + mimic-function@5.0.1: {} + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 + + minimatch@7.4.6: + dependencies: + brace-expansion: 2.0.2 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + minimist@1.2.8: {} + + minipass@7.1.2: {} + + minizlib@3.0.2: + dependencies: + minipass: 7.1.2 + + mkdirp@3.0.1: {} + + mrmime@2.0.1: {} + + ms@2.0.0: {} + + ms@2.1.3: {} + + multimatch@5.0.0: + dependencies: + '@types/minimatch': 3.0.5 + array-differ: 3.0.0 + array-union: 2.1.0 + arrify: 2.0.1 + minimatch: 3.1.2 + + mute-stream@0.0.7: {} + + mute-stream@1.0.0: {} + + nanoid@3.3.11: {} + + napi-postinstall@0.3.0: {} + + napi-wasm@1.1.3: {} + + natural-compare@1.4.0: {} + + negotiator@0.6.4: {} + + negotiator@1.0.0: {} + + node-addon-api@7.1.1: {} + + node-gyp-build@4.8.4: {} + + normalize-path@3.0.0: {} + + nwsapi@2.2.20: {} + + object-assign@4.1.1: {} + + object-hash@2.2.0: {} + + object-inspect@1.13.4: {} + + object-is@1.1.6: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + + object-keys@1.1.1: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + object.entries@1.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + object.fromentries@2.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-object-atoms: 1.1.1 + + object.values@1.2.1: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + oidc-token-hash@5.1.0: {} + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + on-headers@1.0.2: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@2.0.1: + dependencies: + mimic-fn: 1.2.0 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + + open@8.4.2: + dependencies: + define-lazy-prop: 2.0.0 + is-docker: 2.2.1 + is-wsl: 2.2.0 + + openid-client@5.7.1: + dependencies: + jose: 4.15.9 + lru-cache: 6.0.0 + object-hash: 2.2.0 + oidc-token-hash: 5.1.0 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + ora@5.4.1: + dependencies: + bl: 4.1.0 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.9.2 + is-interactive: 1.0.0 + is-unicode-supported: 0.1.0 + log-symbols: 4.1.0 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + + ora@6.3.1: + dependencies: + chalk: 5.4.1 + cli-cursor: 4.0.0 + cli-spinners: 2.9.2 + is-interactive: 2.0.0 + is-unicode-supported: 1.3.0 + log-symbols: 5.1.0 + stdin-discarder: 0.1.0 + strip-ansi: 7.1.0 + wcwidth: 1.0.1 + + ora@8.2.0: + dependencies: + chalk: 5.4.1 + cli-cursor: 5.0.0 + cli-spinners: 2.9.2 + is-interactive: 2.0.0 + is-unicode-supported: 2.1.0 + log-symbols: 6.0.0 + stdin-discarder: 0.2.2 + string-width: 7.2.0 + strip-ansi: 7.1.0 + + os-tmpdir@1.0.2: {} + + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + oxc-resolver@11.5.2: + dependencies: + napi-postinstall: 0.3.0 + optionalDependencies: + '@oxc-resolver/binding-android-arm-eabi': 11.5.2 + '@oxc-resolver/binding-android-arm64': 11.5.2 + '@oxc-resolver/binding-darwin-arm64': 11.5.2 + '@oxc-resolver/binding-darwin-x64': 11.5.2 + '@oxc-resolver/binding-freebsd-x64': 11.5.2 + '@oxc-resolver/binding-linux-arm-gnueabihf': 11.5.2 + '@oxc-resolver/binding-linux-arm-musleabihf': 11.5.2 + '@oxc-resolver/binding-linux-arm64-gnu': 11.5.2 + '@oxc-resolver/binding-linux-arm64-musl': 11.5.2 + '@oxc-resolver/binding-linux-ppc64-gnu': 11.5.2 + '@oxc-resolver/binding-linux-riscv64-gnu': 11.5.2 + '@oxc-resolver/binding-linux-riscv64-musl': 11.5.2 + '@oxc-resolver/binding-linux-s390x-gnu': 11.5.2 + '@oxc-resolver/binding-linux-x64-gnu': 11.5.2 + '@oxc-resolver/binding-linux-x64-musl': 11.5.2 + '@oxc-resolver/binding-wasm32-wasi': 11.5.2 + '@oxc-resolver/binding-win32-arm64-msvc': 11.5.2 + '@oxc-resolver/binding-win32-ia32-msvc': 11.5.2 + '@oxc-resolver/binding-win32-x64-msvc': 11.5.2 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-limit@4.0.0: + dependencies: + yocto-queue: 1.2.1 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + p-locate@6.0.0: + dependencies: + p-limit: 4.0.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-gitignore@2.0.0: {} + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.27.1 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + parse-passwd@1.0.0: {} + + parse5@7.3.0: + dependencies: + entities: 6.0.1 + + parseurl@1.3.3: {} + + path-exists@4.0.0: {} + + path-exists@5.0.0: {} + + path-is-absolute@1.0.1: {} + + path-is-inside@1.0.2: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-to-regexp@3.3.0: {} + + path-to-regexp@8.2.0: {} + + path-type@4.0.0: {} + + pathe@2.0.3: {} + + pathval@2.0.1: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.2: {} + + pkce-challenge@5.0.0: {} + + please-upgrade-node@3.2.0: + dependencies: + semver-compare: 1.0.0 + + polka@0.5.2: + dependencies: + '@polka/url': 0.5.0 + trouter: 2.0.1 + + possible-typed-array-names@1.1.0: {} + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + prettier@3.5.3: {} + + pretty-format@27.5.1: + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + + progress@2.0.3: {} + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + punycode@2.3.1: {} + + qs@6.14.0: + dependencies: + side-channel: 1.1.0 + + queue-microtask@1.2.3: {} + + range-parser@1.2.0: {} + + range-parser@1.2.1: {} + + raw-body@3.0.0: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.6.3 + unpipe: 1.0.0 + + react-dom@18.3.1(react@18.3.1): + dependencies: + loose-envify: 1.4.0 + react: 18.3.1 + scheduler: 0.23.2 + + react-is@16.13.1: {} + + react-is@17.0.2: {} + + react@18.3.1: + dependencies: + loose-envify: 1.4.0 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + rechoir@0.6.2: + dependencies: + resolve: 1.22.10 + + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + require-directory@2.1.1: {} + + require-package-name@2.0.1: {} + + resolve-dir@1.0.1: + dependencies: + expand-tilde: 2.0.2 + global-modules: 1.0.0 + + resolve-from@4.0.0: {} + + resolve-from@5.0.0: {} + + resolve-pkg-maps@1.0.0: {} + + resolve@1.22.10: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + resolve@2.0.0-next.5: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + restore-cursor@2.0.0: + dependencies: + onetime: 2.0.1 + signal-exit: 3.0.7 + + restore-cursor@3.1.0: + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + + restore-cursor@4.0.0: + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + + reusify@1.1.0: {} + + rollup@4.45.0: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.45.0 + '@rollup/rollup-android-arm64': 4.45.0 + '@rollup/rollup-darwin-arm64': 4.45.0 + '@rollup/rollup-darwin-x64': 4.45.0 + '@rollup/rollup-freebsd-arm64': 4.45.0 + '@rollup/rollup-freebsd-x64': 4.45.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.45.0 + '@rollup/rollup-linux-arm-musleabihf': 4.45.0 + '@rollup/rollup-linux-arm64-gnu': 4.45.0 + '@rollup/rollup-linux-arm64-musl': 4.45.0 + '@rollup/rollup-linux-loongarch64-gnu': 4.45.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.45.0 + '@rollup/rollup-linux-riscv64-gnu': 4.45.0 + '@rollup/rollup-linux-riscv64-musl': 4.45.0 + '@rollup/rollup-linux-s390x-gnu': 4.45.0 + '@rollup/rollup-linux-x64-gnu': 4.45.0 + '@rollup/rollup-linux-x64-musl': 4.45.0 + '@rollup/rollup-win32-arm64-msvc': 4.45.0 + '@rollup/rollup-win32-ia32-msvc': 4.45.0 + '@rollup/rollup-win32-x64-msvc': 4.45.0 + fsevents: 2.3.3 + + router@2.2.0: + dependencies: + debug: 4.4.1 + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.2.0 + transitivePeerDependencies: + - supports-color + + rrweb-cssom@0.8.0: {} + + run-async@2.4.1: {} + + run-async@3.0.0: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + rx-lite-aggregates@4.0.8: + dependencies: + rx-lite: 4.0.8 + + rx-lite@4.0.8: {} + + rxjs@7.8.2: + dependencies: + tslib: 2.8.1 + + safe-array-concat@1.1.3: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-buffer@5.2.1: {} + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + safer-buffer@2.1.2: {} + + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + + scheduler@0.23.2: + dependencies: + loose-envify: 1.4.0 + + semver-compare@1.0.0: {} + + semver@6.3.1: {} + + semver@7.7.2: {} + + send@1.2.0: + dependencies: + debug: 4.4.1 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.0 + mime-types: 3.0.1 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + serve-handler@6.1.6: + dependencies: + bytes: 3.0.0 + content-disposition: 0.5.2 + mime-types: 2.1.18 + minimatch: 3.1.2 + path-is-inside: 1.0.2 + path-to-regexp: 3.3.0 + range-parser: 1.2.0 + + serve-static@2.2.0: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.0 + transitivePeerDependencies: + - supports-color + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + + setprototypeof@1.2.0: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + shelljs@0.8.5: + dependencies: + glob: 7.2.3 + interpret: 1.4.0 + rechoir: 0.6.2 + + shiki@0.14.7: + dependencies: + ansi-sequence-parser: 1.1.3 + jsonc-parser: 3.3.1 + vscode-oniguruma: 1.7.0 + vscode-textmate: 8.0.0 + + shx@0.3.4: + dependencies: + minimist: 1.2.8 + shelljs: 0.8.5 + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + siginfo@2.0.0: {} + + signal-exit@3.0.7: {} + + signal-exit@4.1.0: {} + + sirv@2.0.4: + dependencies: + '@polka/url': 1.0.0-next.29 + mrmime: 2.0.1 + totalist: 3.0.1 + + skott-webapp@2.3.0: + dependencies: + digraph-js: 2.2.3 + + skott@0.35.4: + dependencies: + '@parcel/watcher': 2.5.1 + '@typescript-eslint/typescript-estree': 7.13.1(typescript@5.4.5) + commander: 11.1.0 + compression: 1.8.0 + depcheck: 1.4.7 + digraph-js: 2.2.3 + effect: 3.3.2 + estree-walker: 3.0.3 + fp-ts: 2.5.0 + fs-tree-structure: 0.0.5 + ignore-walk: 6.0.5 + io-ts: 2.2.22(fp-ts@2.5.0) + is-wsl: 3.1.0 + json5: 2.2.3 + kleur: 4.1.5 + lodash-es: 4.17.21 + meriyah: 4.5.0 + minimatch: 9.0.5 + ora: 6.3.1 + parse-gitignore: 2.0.0 + polka: 0.5.2 + sirv: 2.0.4 + skott-webapp: 2.3.0 + typescript: 5.4.5 + transitivePeerDependencies: + - supports-color + + slash@3.0.0: {} + + smol-toml@1.4.1: {} + + source-map-js@1.2.1: {} + + split2@3.2.2: + dependencies: + readable-stream: 3.6.2 + + sprintf-js@1.0.3: {} + + stackback@0.0.2: {} + + statuses@2.0.1: {} + + statuses@2.0.2: {} + + std-env@3.9.0: {} + + stdin-discarder@0.1.0: + dependencies: + bl: 5.1.0 + + stdin-discarder@0.2.2: {} + + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + + string-width@2.1.1: + dependencies: + is-fullwidth-code-point: 2.0.0 + strip-ansi: 4.0.0 + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@7.2.0: + dependencies: + emoji-regex: 10.4.0 + get-east-asian-width: 1.3.0 + strip-ansi: 7.1.0 + + string.prototype.matchall@4.0.12: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + regexp.prototype.flags: 1.5.4 + set-function-name: 2.0.2 + side-channel: 1.1.0 + + string.prototype.repeat@1.0.0: + dependencies: + define-properties: 1.2.1 + es-abstract: 1.24.0 + + string.prototype.trim@1.2.10: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-object-atoms: 1.1.1 + has-property-descriptors: 1.0.2 + + string.prototype.trimend@1.0.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-ansi@4.0.0: + dependencies: + ansi-regex: 3.0.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.1.0 + + strip-json-comments@3.1.1: {} + + strip-json-comments@5.0.2: {} + + strip-literal@3.0.0: + dependencies: + js-tokens: 9.0.1 + + supports-color@5.5.0: + dependencies: + has-flag: 3.0.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + swr@2.3.4(react@18.3.1): + dependencies: + dequal: 2.0.3 + react: 18.3.1 + use-sync-external-store: 1.5.0(react@18.3.1) + + symbol-tree@3.2.4: {} + + tar@7.4.3: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.2 + minizlib: 3.0.2 + mkdirp: 3.0.1 + yallist: 5.0.0 + + through2@4.0.2: + dependencies: + readable-stream: 3.6.2 + + through@2.3.8: {} + + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + + tinyglobby@0.2.14: + dependencies: + fdir: 6.4.6(picomatch@4.0.2) + picomatch: 4.0.2 + + tinypool@1.1.1: {} + + tinyrainbow@2.0.0: {} + + tinyspy@4.0.3: {} + + tldts-core@6.1.86: {} + + tldts@6.1.86: + dependencies: + tldts-core: 6.1.86 + + tmp@0.0.33: + dependencies: + os-tmpdir: 1.0.2 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + toidentifier@1.0.1: {} + + totalist@3.0.1: {} + + tough-cookie@5.1.2: + dependencies: + tldts: 6.1.86 + + tr46@5.1.1: + dependencies: + punycode: 2.3.1 + + treeify@1.1.0: {} + + trouter@2.0.1: + dependencies: + matchit: 1.1.0 + + ts-api-utils@1.4.3(typescript@5.4.5): + dependencies: + typescript: 5.4.5 + + ts-api-utils@2.1.0(typescript@5.0.4): + dependencies: + typescript: 5.0.4 + + ts.cryptojs256@1.0.1: {} + + tslib@2.8.1: {} + + tsx@4.20.3: + dependencies: + esbuild: 0.25.4 + get-tsconfig: 4.10.1 + optionalDependencies: + fsevents: 2.3.3 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-fest@0.21.3: {} + + type-is@2.0.1: + dependencies: + content-type: 1.0.5 + media-typer: 1.1.0 + mime-types: 3.0.1 + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.7: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + + typedoc@0.24.8(typescript@5.0.4): + dependencies: + lunr: 2.3.9 + marked: 4.3.0 + minimatch: 9.0.5 + shiki: 0.14.7 + typescript: 5.0.4 + + typescript-eslint@8.34.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.0.4): + dependencies: + '@typescript-eslint/eslint-plugin': 8.34.1(@typescript-eslint/parser@8.34.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.0.4))(eslint@9.28.0(jiti@2.4.2))(typescript@5.0.4) + '@typescript-eslint/parser': 8.34.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.0.4) + '@typescript-eslint/utils': 8.34.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.0.4) + eslint: 9.28.0(jiti@2.4.2) + typescript: 5.0.4 + transitivePeerDependencies: + - supports-color + + typescript@5.0.4: {} + + typescript@5.4.5: {} + + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + + undici-types@5.26.5: {} + + unicorn-magic@0.1.0: {} + + unpipe@1.0.0: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + use-sync-external-store@1.5.0(react@18.3.1): + dependencies: + react: 18.3.1 + + utf-8-validate@5.0.10: + dependencies: + node-gyp-build: 4.8.4 + + util-deprecate@1.0.2: {} + + vary@1.1.2: {} + + vite-node@3.2.4(@types/node@18.19.119)(jiti@2.4.2)(tsx@4.20.3): + dependencies: + cac: 6.7.14 + debug: 4.4.1 + es-module-lexer: 1.7.0 + pathe: 2.0.3 + vite: 7.0.4(@types/node@18.19.119)(jiti@2.4.2)(tsx@4.20.3) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vite@7.0.4(@types/node@18.19.119)(jiti@2.4.2)(tsx@4.20.3): + dependencies: + esbuild: 0.25.4 + fdir: 6.4.6(picomatch@4.0.2) + picomatch: 4.0.2 + postcss: 8.5.6 + rollup: 4.45.0 + tinyglobby: 0.2.14 + optionalDependencies: + '@types/node': 18.19.119 + fsevents: 2.3.3 + jiti: 2.4.2 + tsx: 4.20.3 + + vitest@3.2.4(@types/node@18.19.119)(happy-dom@17.4.9)(jiti@2.4.2)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.3): + dependencies: + '@types/chai': 5.2.2 + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(vite@7.0.4(@types/node@18.19.119)(jiti@2.4.2)(tsx@4.20.3)) + '@vitest/pretty-format': 3.2.4 + '@vitest/runner': 3.2.4 + '@vitest/snapshot': 3.2.4 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.2.1 + debug: 4.4.1 + expect-type: 1.2.2 + magic-string: 0.30.17 + pathe: 2.0.3 + picomatch: 4.0.2 + std-env: 3.9.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.14 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 7.0.4(@types/node@18.19.119)(jiti@2.4.2)(tsx@4.20.3) + vite-node: 3.2.4(@types/node@18.19.119)(jiti@2.4.2)(tsx@4.20.3) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 18.19.119 + happy-dom: 17.4.9 + jsdom: 26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vscode-oniguruma@1.7.0: {} + + vscode-textmate@8.0.0: {} + + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + + wait-for-expect@3.0.2: {} + + walk-up-path@4.0.0: {} + + wcwidth@1.0.1: + dependencies: + defaults: 1.0.4 + + webidl-conversions@7.0.0: {} + + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + + whatwg-mimetype@3.0.0: {} + + whatwg-mimetype@4.0.0: {} + + whatwg-url@14.2.0: + dependencies: + tr46: 5.1.1 + webidl-conversions: 7.0.0 + + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.1.8 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.0 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.19 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-typed-array@1.1.19: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + which@1.3.1: + dependencies: + isexe: 2.0.0 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + word-wrap@1.2.5: {} + + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrappy@1.0.2: {} + + ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10): + optionalDependencies: + bufferutil: 4.0.9 + utf-8-validate: 5.0.10 + + xml-name-validator@5.0.0: {} + + xmlchars@2.2.0: {} + + y18n@5.0.8: {} + + yallist@4.0.0: {} + + yallist@5.0.0: {} + + yaml@1.10.2: {} + + yargs-parser@20.2.9: {} + + yargs@16.2.0: + dependencies: + cliui: 7.0.4 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.9 + + yocto-queue@0.1.0: {} + + yocto-queue@1.2.1: {} + + yoctocolors-cjs@2.1.2: {} + + zod-to-json-schema@3.24.6(zod@3.25.76): + dependencies: + zod: 3.25.76 + + zod-validation-error@3.5.3(zod@3.25.76): + dependencies: + zod: 3.25.76 + + zod@3.25.76: {} diff --git a/src/test/fake_chef/_generated/api.d.ts b/src/test/fake_chef/_generated/api.d.ts new file mode 100644 index 00000000..f64f867d --- /dev/null +++ b/src/test/fake_chef/_generated/api.d.ts @@ -0,0 +1,311 @@ +/* eslint-disable */ +/** + * Generated `api` utility. + * + * THIS CODE IS AUTOMATICALLY GENERATED. + * + * To regenerate, run `npx convex dev`. + * @module + */ + +import type * as admin from "../admin.js"; +import type * as apiKeys from "../apiKeys.js"; +import type * as cleanup from "../cleanup.js"; +import type * as compressMessages from "../compressMessages.js"; +import type * as convexProjects from "../convexProjects.js"; +import type * as crons from "../crons.js"; +import type * as debugPrompt from "../debugPrompt.js"; +import type * as deploy from "../deploy.js"; +import type * as dev from "../dev.js"; +import type * as http from "../http.js"; +import type * as lz4 from "../lz4.js"; +import type * as lz4Wasm from "../lz4Wasm.js"; +import type * as messages from "../messages.js"; +import type * as migrations from "../migrations.js"; +import type * as openaiProxy from "../openaiProxy.js"; +import type * as rateLimiter from "../rateLimiter.js"; +import type * as resendProxy from "../resendProxy.js"; +import type * as sessions from "../sessions.js"; +import type * as share from "../share.js"; +import type * as snapshot from "../snapshot.js"; +import type * as socialShare from "../socialShare.js"; + +import type { + ApiFromModules, + FilterApi, + FunctionReference, +} from "convex/server"; + +/** + * A utility for referencing Convex functions in your app's API. + * + * Usage: + * ```js + * const myFunctionReference = api.myModule.myFunction; + * ``` + */ +declare const fullApi: ApiFromModules<{ + admin: typeof admin; + apiKeys: typeof apiKeys; + cleanup: typeof cleanup; + compressMessages: typeof compressMessages; + convexProjects: typeof convexProjects; + crons: typeof crons; + debugPrompt: typeof debugPrompt; + deploy: typeof deploy; + dev: typeof dev; + http: typeof http; + lz4: typeof lz4; + lz4Wasm: typeof lz4Wasm; + messages: typeof messages; + migrations: typeof migrations; + openaiProxy: typeof openaiProxy; + rateLimiter: typeof rateLimiter; + resendProxy: typeof resendProxy; + sessions: typeof sessions; + share: typeof share; + snapshot: typeof snapshot; + socialShare: typeof socialShare; +}>; +declare const fullApiWithMounts: typeof fullApi; + +export declare const api: FilterApi< + typeof fullApiWithMounts, + FunctionReference +>; +export declare const internal: FilterApi< + typeof fullApiWithMounts, + FunctionReference +>; + +export declare const components: { + rateLimiter: { + lib: { + checkRateLimit: FunctionReference< + "query", + "internal", + { + config: + | { + capacity?: number; + kind: "token bucket"; + maxReserved?: number; + period: number; + rate: number; + shards?: number; + } + | { + capacity?: number; + kind: "fixed window"; + maxReserved?: number; + period: number; + rate: number; + shards?: number; + start?: number; + }; + count?: number; + key?: string; + name: string; + reserve?: boolean; + throws?: boolean; + }, + { ok: true; retryAfter?: number } | { ok: false; retryAfter: number } + >; + clearAll: FunctionReference< + "mutation", + "internal", + { before?: number }, + null + >; + rateLimit: FunctionReference< + "mutation", + "internal", + { + config: + | { + capacity?: number; + kind: "token bucket"; + maxReserved?: number; + period: number; + rate: number; + shards?: number; + } + | { + capacity?: number; + kind: "fixed window"; + maxReserved?: number; + period: number; + rate: number; + shards?: number; + start?: number; + }; + count?: number; + key?: string; + name: string; + reserve?: boolean; + throws?: boolean; + }, + { ok: true; retryAfter?: number } | { ok: false; retryAfter: number } + >; + resetRateLimit: FunctionReference< + "mutation", + "internal", + { key?: string; name: string }, + null + >; + }; + public: { + checkRateLimit: FunctionReference< + "query", + "internal", + { + config: + | { + capacity?: number; + kind: "token bucket"; + maxReserved?: number; + period: number; + rate: number; + shards?: number; + } + | { + capacity?: number; + kind: "fixed window"; + maxReserved?: number; + period: number; + rate: number; + shards?: number; + start?: number; + }; + count?: number; + key?: string; + name: string; + reserve?: boolean; + throws?: boolean; + }, + { ok: true; retryAfter?: number } | { ok: false; retryAfter: number } + >; + rateLimit: FunctionReference< + "mutation", + "internal", + { + config: + | { + capacity?: number; + kind: "token bucket"; + maxReserved?: number; + period: number; + rate: number; + shards?: number; + } + | { + capacity?: number; + kind: "fixed window"; + maxReserved?: number; + period: number; + rate: number; + shards?: number; + start?: number; + }; + count?: number; + key?: string; + name: string; + reserve?: boolean; + throws?: boolean; + }, + { ok: true; retryAfter?: number } | { ok: false; retryAfter: number } + >; + resetRateLimit: FunctionReference< + "mutation", + "internal", + { key?: string; name: string }, + null + >; + }; + }; + migrations: { + lib: { + cancel: FunctionReference< + "mutation", + "internal", + { name: string }, + { + batchSize?: number; + cursor?: string | null; + error?: string; + isDone: boolean; + latestEnd?: number; + latestStart: number; + name: string; + next?: Array; + processed: number; + state: "inProgress" | "success" | "failed" | "canceled" | "unknown"; + } + >; + cancelAll: FunctionReference< + "mutation", + "internal", + { sinceTs?: number }, + Array<{ + batchSize?: number; + cursor?: string | null; + error?: string; + isDone: boolean; + latestEnd?: number; + latestStart: number; + name: string; + next?: Array; + processed: number; + state: "inProgress" | "success" | "failed" | "canceled" | "unknown"; + }> + >; + clearAll: FunctionReference< + "mutation", + "internal", + { before?: number }, + null + >; + getStatus: FunctionReference< + "query", + "internal", + { limit?: number; names?: Array }, + Array<{ + batchSize?: number; + cursor?: string | null; + error?: string; + isDone: boolean; + latestEnd?: number; + latestStart: number; + name: string; + next?: Array; + processed: number; + state: "inProgress" | "success" | "failed" | "canceled" | "unknown"; + }> + >; + migrate: FunctionReference< + "mutation", + "internal", + { + batchSize?: number; + cursor?: string | null; + dryRun: boolean; + fnHandle: string; + name: string; + next?: Array<{ fnHandle: string; name: string }>; + }, + { + batchSize?: number; + cursor?: string | null; + error?: string; + isDone: boolean; + latestEnd?: number; + latestStart: number; + name: string; + next?: Array; + processed: number; + state: "inProgress" | "success" | "failed" | "canceled" | "unknown"; + } + >; + }; + }; +}; diff --git a/src/test/fake_chef/_generated/dataModel.d.ts b/src/test/fake_chef/_generated/dataModel.d.ts new file mode 100644 index 00000000..8541f319 --- /dev/null +++ b/src/test/fake_chef/_generated/dataModel.d.ts @@ -0,0 +1,60 @@ +/* eslint-disable */ +/** + * Generated data model types. + * + * THIS CODE IS AUTOMATICALLY GENERATED. + * + * To regenerate, run `npx convex dev`. + * @module + */ + +import type { + DataModelFromSchemaDefinition, + DocumentByName, + TableNamesInDataModel, + SystemTableNames, +} from "convex/server"; +import type { GenericId } from "convex/values"; +import schema from "../schema.js"; + +/** + * The names of all of your Convex tables. + */ +export type TableNames = TableNamesInDataModel; + +/** + * The type of a document stored in Convex. + * + * @typeParam TableName - A string literal type of the table name (like "users"). + */ +export type Doc = DocumentByName< + DataModel, + TableName +>; + +/** + * An identifier for a document in Convex. + * + * Convex documents are uniquely identified by their `Id`, which is accessible + * on the `_id` field. To learn more, see [Document IDs](https://docs.convex.dev/using/document-ids). + * + * Documents can be loaded using `db.get(id)` in query and mutation functions. + * + * IDs are just strings at runtime, but this type can be used to distinguish them from other + * strings when type checking. + * + * @typeParam TableName - A string literal type of the table name (like "users"). + */ +export type Id = + GenericId; + +/** + * A type describing your Convex data model. + * + * This type includes information about what tables you have, the type of + * documents stored in those tables, and the indexes defined on them. + * + * This type is used to parameterize methods like `queryGeneric` and + * `mutationGeneric` to make them type-safe. + */ +export type DataModel = DataModelFromSchemaDefinition; diff --git a/src/test/fake_chef/_generated/server.d.ts b/src/test/fake_chef/_generated/server.d.ts new file mode 100644 index 00000000..b5c68288 --- /dev/null +++ b/src/test/fake_chef/_generated/server.d.ts @@ -0,0 +1,149 @@ +/* eslint-disable */ +/** + * Generated utilities for implementing server-side Convex query and mutation functions. + * + * THIS CODE IS AUTOMATICALLY GENERATED. + * + * To regenerate, run `npx convex dev`. + * @module + */ + +import { + ActionBuilder, + AnyComponents, + HttpActionBuilder, + MutationBuilder, + QueryBuilder, + GenericActionCtx, + GenericMutationCtx, + GenericQueryCtx, + GenericDatabaseReader, + GenericDatabaseWriter, + FunctionReference, +} from "convex/server"; +import type { DataModel } from "./dataModel.js"; + +type GenericCtx = + | GenericActionCtx + | GenericMutationCtx + | GenericQueryCtx; + +/** + * Define a query in this Convex app's public API. + * + * This function will be allowed to read your Convex database and will be accessible from the client. + * + * @param func - The query function. It receives a {@link QueryCtx} as its first argument. + * @returns The wrapped query. Include this as an `export` to name it and make it accessible. + */ +export declare const query: QueryBuilder; + +/** + * Define a query that is only accessible from other Convex functions (but not from the client). + * + * This function will be allowed to read from your Convex database. It will not be accessible from the client. + * + * @param func - The query function. It receives a {@link QueryCtx} as its first argument. + * @returns The wrapped query. Include this as an `export` to name it and make it accessible. + */ +export declare const internalQuery: QueryBuilder; + +/** + * Define a mutation in this Convex app's public API. + * + * This function will be allowed to modify your Convex database and will be accessible from the client. + * + * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument. + * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible. + */ +export declare const mutation: MutationBuilder; + +/** + * Define a mutation that is only accessible from other Convex functions (but not from the client). + * + * This function will be allowed to modify your Convex database. It will not be accessible from the client. + * + * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument. + * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible. + */ +export declare const internalMutation: MutationBuilder; + +/** + * Define an action in this Convex app's public API. + * + * An action is a function which can execute any JavaScript code, including non-deterministic + * code and code with side-effects, like calling third-party services. + * They can be run in Convex's JavaScript environment or in Node.js using the "use node" directive. + * They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}. + * + * @param func - The action. It receives an {@link ActionCtx} as its first argument. + * @returns The wrapped action. Include this as an `export` to name it and make it accessible. + */ +export declare const action: ActionBuilder; + +/** + * Define an action that is only accessible from other Convex functions (but not from the client). + * + * @param func - The function. It receives an {@link ActionCtx} as its first argument. + * @returns The wrapped function. Include this as an `export` to name it and make it accessible. + */ +export declare const internalAction: ActionBuilder; + +/** + * Define an HTTP action. + * + * This function will be used to respond to HTTP requests received by a Convex + * deployment if the requests matches the path and method where this action + * is routed. Be sure to route your action in `convex/http.js`. + * + * @param func - The function. It receives an {@link ActionCtx} as its first argument. + * @returns The wrapped function. Import this function from `convex/http.js` and route it to hook it up. + */ +export declare const httpAction: HttpActionBuilder; + +/** + * A set of services for use within Convex query functions. + * + * The query context is passed as the first argument to any Convex query + * function run on the server. + * + * This differs from the {@link MutationCtx} because all of the services are + * read-only. + */ +export type QueryCtx = GenericQueryCtx; + +/** + * A set of services for use within Convex mutation functions. + * + * The mutation context is passed as the first argument to any Convex mutation + * function run on the server. + */ +export type MutationCtx = GenericMutationCtx; + +/** + * A set of services for use within Convex action functions. + * + * The action context is passed as the first argument to any Convex action + * function run on the server. + */ +export type ActionCtx = GenericActionCtx; + +/** + * An interface to read from the database within Convex query functions. + * + * The two entry points are {@link DatabaseReader.get}, which fetches a single + * document by its {@link Id}, or {@link DatabaseReader.query}, which starts + * building a query. + */ +export type DatabaseReader = GenericDatabaseReader; + +/** + * An interface to read from and write to the database within Convex mutation + * functions. + * + * Convex guarantees that all writes within a single mutation are + * executed atomically, so you never have to worry about partial writes leaving + * your data in an inconsistent state. See [the Convex Guide](https://docs.convex.dev/understanding/convex-fundamentals/functions#atomicity-and-optimistic-concurrency-control) + * for the guarantees Convex provides your functions. + */ +export type DatabaseWriter = GenericDatabaseWriter; diff --git a/src/test/fake_chef/admin.ts b/src/test/fake_chef/admin.ts new file mode 100644 index 00000000..0d6fa6b2 --- /dev/null +++ b/src/test/fake_chef/admin.ts @@ -0,0 +1,197 @@ +import { ConvexError, v } from "convex/values"; +import { + mutation, + internalMutation, + internalAction, + query, +} from "./_generated/server"; +import type { QueryCtx } from "./_generated/server"; +import { internal } from "./_generated/api"; + +const PROVISION_HOST = process.env.PROVISION_HOST || "https://api.convex.dev"; +const CONVEX_TEAM_ID = 4916; +const RETRY_MS = 60 * 60 * 1000; +const STALE_ADMIN_STATUS_ALLOWED_MS = 7 * 24 * 60 * 60 * 1000; + +export async function assertIsConvexAdmin(ctx: QueryCtx) { + const identity = await ctx.auth.getUserIdentity(); + if (!identity) { + throw new ConvexError({ code: "NotAuthorized", message: "Unauthorized" }); + } + + const member = await ctx.db + .query("convexMembers") + .withIndex("byTokenIdentifier", (q) => + q.eq("tokenIdentifier", identity.tokenIdentifier), + ) + .unique(); + + if (!member) { + throw new ConvexError({ code: "NotAuthorized", message: "Unauthorized" }); + } + + const adminStatus = await ctx.db + .query("convexAdmins") + .withIndex("byConvexMemberId", (q) => q.eq("convexMemberId", member._id)) + .unique(); + + if (!adminStatus) { + throw new ConvexError({ + code: "NotAuthorized", + message: "Not a Convex admin", + }); + } + + if (!adminStatus.wasAdmin) { + throw new ConvexError({ + code: "NotAuthorized", + message: "Not a Convex admin", + }); + } + + if ( + adminStatus.lastCheckedForAdminStatus < + Date.now() - STALE_ADMIN_STATUS_ALLOWED_MS + ) { + throw new ConvexError({ + code: "NotAuthorized", + message: "Need to refresh auth", + }); + } + + return { member, adminStatus }; +} + +export const updateAdminStatus = internalMutation({ + args: { + memberId: v.id("convexMembers"), + wasAdmin: v.boolean(), + }, + handler: async (ctx, args) => { + const existing = await ctx.db + .query("convexAdmins") + .withIndex("byConvexMemberId", (q) => + q.eq("convexMemberId", args.memberId), + ) + .unique(); + + if (existing) { + await ctx.db.patch(existing._id, { + wasAdmin: args.wasAdmin, + lastCheckedForAdminStatus: Date.now(), + }); + } else { + await ctx.db.insert("convexAdmins", { + convexMemberId: args.memberId, + wasAdmin: args.wasAdmin, + lastCheckedForAdminStatus: Date.now(), + }); + } + }, +}); + +export const checkIsConvexAdminInternal = internalAction({ + args: { + token: v.string(), + memberId: v.id("convexMembers"), + }, + handler: async (ctx, args): Promise => { + try { + const response = await fetch(`${PROVISION_HOST}/api/dashboard/teams`, { + headers: { + Authorization: `Bearer ${args.token}`, + }, + }); + if (!response.ok) { + const body = await response.text(); + throw new Error( + `Failed to fetch teams: ${response.statusText}: ${body}`, + ); + } + const teams = (await response.json()) as { id: number; slug: number }[]; + const isAdmin = teams.some((team) => team.id === CONVEX_TEAM_ID); + + await ctx.runMutation(internal.admin.updateAdminStatus, { + memberId: args.memberId, + wasAdmin: isAdmin, + }); + } catch (error) { + console.error("Error checking Convex admin status:", error); + throw new ConvexError({ + code: "Internal", + message: "Failed to verify admin status", + }); + } + }, +}); + +export const requestAdminCheck = mutation({ + args: { + token: v.string(), + }, + handler: async (ctx, args) => { + const identity = await ctx.auth.getUserIdentity(); + if (!identity) { + throw new ConvexError({ code: "NotAuthorized", message: "Unauthorized" }); + } + + const member = await ctx.db + .query("convexMembers") + .withIndex("byTokenIdentifier", (q) => + q.eq("tokenIdentifier", identity.tokenIdentifier), + ) + .unique(); + + if (!member) { + throw new ConvexError({ code: "NotAuthorized", message: "Unauthorized" }); + } + + const adminStatus = await ctx.db + .query("convexAdmins") + .withIndex("byConvexMemberId", (q) => q.eq("convexMemberId", member._id)) + .unique(); + + if (adminStatus?.lastCheckedForAdminStatus) { + const timeSinceLastCheck = + Date.now() - adminStatus.lastCheckedForAdminStatus; + if (timeSinceLastCheck < RETRY_MS) { + throw new Error( + `Please wait ${Math.ceil((RETRY_MS - timeSinceLastCheck) / 1000 / 60)} minutes before checking again`, + ); + } + } + + await ctx.scheduler.runAfter(0, internal.admin.checkIsConvexAdminInternal, { + token: args.token, + memberId: member._id, + }); + }, +}); + +export const isCurrentUserAdmin = query({ + args: {}, + handler: async (ctx) => { + const identity = await ctx.auth.getUserIdentity(); + if (!identity) { + return false; + } + + const member = await ctx.db + .query("convexMembers") + .withIndex("byTokenIdentifier", (q) => + q.eq("tokenIdentifier", identity.tokenIdentifier), + ) + .unique(); + + if (!member) { + return false; + } + + const adminStatus = await ctx.db + .query("convexAdmins") + .withIndex("byConvexMemberId", (q) => q.eq("convexMemberId", member._id)) + .unique(); + + return adminStatus?.wasAdmin ?? false; + }, +}); diff --git a/src/test/fake_chef/apiKeys.ts b/src/test/fake_chef/apiKeys.ts new file mode 100644 index 00000000..6fd302be --- /dev/null +++ b/src/test/fake_chef/apiKeys.ts @@ -0,0 +1,274 @@ +import { ConvexError, v } from "convex/values"; +import { action, mutation, query } from "./_generated/server"; +import { apiKeyValidator } from "./schema"; + +export const apiKeyForCurrentMember = query({ + args: {}, + returns: v.union(v.null(), apiKeyValidator), + handler: async (ctx) => { + const identity = await ctx.auth.getUserIdentity(); + if (!identity) { + return null; + } + const existingMember = await ctx.db + .query("convexMembers") + .withIndex("byTokenIdentifier", (q) => q.eq("tokenIdentifier", identity.tokenIdentifier)) + .unique(); + + return existingMember?.apiKey; + }, +}); + +export const setApiKeyForCurrentMember = mutation({ + args: { + apiKey: apiKeyValidator, + }, + returns: v.null(), + handler: async (ctx, args) => { + const identity = await ctx.auth.getUserIdentity(); + if (!identity) { + throw new ConvexError({ code: "NotAuthorized", message: "Unauthorized" }); + } + + const existingMember = await ctx.db + .query("convexMembers") + .withIndex("byTokenIdentifier", (q) => q.eq("tokenIdentifier", identity.tokenIdentifier)) + .unique(); + + if (!existingMember) { + throw new ConvexError({ code: "NotAuthorized", message: "Unauthorized" }); + } + + await ctx.db.patch(existingMember._id, { apiKey: args.apiKey }); + }, +}); + +export const deleteApiKeyForCurrentMember = mutation({ + args: {}, + returns: v.null(), + handler: async (ctx) => { + const identity = await ctx.auth.getUserIdentity(); + if (!identity) { + throw new ConvexError({ code: "NotAuthorized", message: "Unauthorized" }); + } + + const existingMember = await ctx.db + .query("convexMembers") + .withIndex("byTokenIdentifier", (q) => q.eq("tokenIdentifier", identity.tokenIdentifier)) + .unique(); + + if (!existingMember) { + throw new ConvexError({ code: "NotAuthorized", message: "Unauthorized" }); + } + + await ctx.db.patch(existingMember._id, { apiKey: undefined }); + }, +}); + +export const deleteAnthropicApiKeyForCurrentMember = mutation({ + args: {}, + returns: v.null(), + handler: async (ctx) => { + const identity = await ctx.auth.getUserIdentity(); + if (!identity) { + throw new ConvexError({ code: "NotAuthorized", message: "Unauthorized" }); + } + + const existingMember = await ctx.db + .query("convexMembers") + .withIndex("byTokenIdentifier", (q) => q.eq("tokenIdentifier", identity.tokenIdentifier)) + .unique(); + + if (!existingMember) { + throw new ConvexError({ code: "NotAuthorized", message: "Unauthorized" }); + } + if (!existingMember.apiKey) { + return; + } + await ctx.db.patch(existingMember._id, { + apiKey: { + ...existingMember.apiKey, + value: undefined, + }, + }); + }, +}); + +export const deleteOpenaiApiKeyForCurrentMember = mutation({ + args: {}, + returns: v.null(), + handler: async (ctx) => { + const identity = await ctx.auth.getUserIdentity(); + if (!identity) { + throw new ConvexError({ code: "NotAuthorized", message: "Unauthorized" }); + } + + const existingMember = await ctx.db + .query("convexMembers") + .withIndex("byTokenIdentifier", (q) => q.eq("tokenIdentifier", identity.tokenIdentifier)) + .unique(); + + if (!existingMember) { + throw new ConvexError({ code: "NotAuthorized", message: "Unauthorized" }); + } + if (!existingMember.apiKey) { + return; + } + await ctx.db.patch(existingMember._id, { + apiKey: { + ...existingMember.apiKey, + openai: undefined, + }, + }); + }, +}); + +export const deleteXaiApiKeyForCurrentMember = mutation({ + args: {}, + returns: v.null(), + handler: async (ctx) => { + const identity = await ctx.auth.getUserIdentity(); + if (!identity) { + throw new ConvexError({ code: "NotAuthorized", message: "Unauthorized" }); + } + + const existingMember = await ctx.db + .query("convexMembers") + .withIndex("byTokenIdentifier", (q) => q.eq("tokenIdentifier", identity.tokenIdentifier)) + .unique(); + + if (!existingMember) { + throw new ConvexError({ code: "NotAuthorized", message: "Unauthorized" }); + } + if (!existingMember.apiKey) { + return; + } + await ctx.db.patch(existingMember._id, { + apiKey: { + ...existingMember.apiKey, + xai: undefined, + }, + }); + }, +}); + +export const deleteGoogleApiKeyForCurrentMember = mutation({ + args: {}, + returns: v.null(), + handler: async (ctx) => { + const identity = await ctx.auth.getUserIdentity(); + if (!identity) { + throw new ConvexError({ code: "NotAuthorized", message: "Unauthorized" }); + } + + const existingMember = await ctx.db + .query("convexMembers") + .withIndex("byTokenIdentifier", (q) => q.eq("tokenIdentifier", identity.tokenIdentifier)) + .unique(); + + if (!existingMember) { + throw new ConvexError({ code: "NotAuthorized", message: "Unauthorized" }); + } + if (!existingMember.apiKey) { + return; + } + await ctx.db.patch(existingMember._id, { + apiKey: { + ...existingMember.apiKey, + google: undefined, + }, + }); + }, +}); + +export const validateAnthropicApiKey = action({ + args: { + apiKey: v.string(), + }, + handler: async (ctx, args) => { + const identity = await ctx.auth.getUserIdentity(); + if (!identity) { + throw new ConvexError({ code: "NotAuthorized", message: "Unauthorized" }); + } + + const response = await fetch("https://api.anthropic.com/v1/messages", { + method: "POST", + headers: { + "Content-Type": "application/json", + "x-api-key": args.apiKey, + }, + }); + + if (response.status === 401) { + return false; + } + return true; + }, +}); + +export const validateOpenaiApiKey = action({ + args: { + apiKey: v.string(), + }, + handler: async (ctx, args) => { + const identity = await ctx.auth.getUserIdentity(); + if (!identity) { + throw new ConvexError({ code: "NotAuthorized", message: "Unauthorized" }); + } + + const response = await fetch("https://api.openai.com/v1/chat/completions", { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${args.apiKey}`, + }, + }); + + if (response.status === 401) { + return false; + } + return true; + }, +}); + +export const validateGoogleApiKey = action({ + args: { + apiKey: v.string(), + }, + handler: async (ctx, args) => { + const identity = await ctx.auth.getUserIdentity(); + if (!identity) { + throw new ConvexError({ code: "NotAuthorized", message: "Unauthorized" }); + } + + const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models?key=${args.apiKey}`); + + if (response.status === 400) { + return false; + } + return true; + }, +}); + +export const validateXaiApiKey = action({ + args: { + apiKey: v.string(), + }, + handler: async (ctx, args) => { + const identity = await ctx.auth.getUserIdentity(); + if (!identity) { + throw new ConvexError({ code: "NotAuthorized", message: "Unauthorized" }); + } + + const response = await fetch("https://api.x.ai/v1/models", { + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${args.apiKey}`, + }, + }); + if (response.status === 400) { + return false; + } + return true; + }, +}); diff --git a/src/test/fake_chef/cleanup.ts b/src/test/fake_chef/cleanup.ts new file mode 100644 index 00000000..5113d6c9 --- /dev/null +++ b/src/test/fake_chef/cleanup.ts @@ -0,0 +1,52 @@ +import { v } from "convex/values"; +import { internalMutation } from "./_generated/server"; +import { internal } from "./_generated/api"; + +const delayInMs = parseFloat(process.env.DEBUG_FILE_CLEANUP_DELAY_MS ?? "500"); +const debugFileCleanupBatchSize = parseInt(process.env.DEBUG_FILE_CLEANUP_BATCH_SIZE ?? "100"); + +export const deleteDebugFilesForInactiveChats = internalMutation({ + args: { + forReal: v.boolean(), + cursor: v.optional(v.string()), + shouldScheduleNext: v.boolean(), + daysInactive: v.number(), + }, + handler: async (ctx, { forReal, cursor, shouldScheduleNext, daysInactive }) => { + const { page, isDone, continueCursor } = await ctx.db.query("debugChatApiRequestLog").paginate({ + numItems: debugFileCleanupBatchSize, + cursor: cursor ?? null, + }); + for (const doc of page) { + if (doc._creationTime > Date.now() - 1000 * 60 * 60 * 24 * daysInactive) { + return; + } + const storageState = await ctx.db + .query("chatMessagesStorageState") + .withIndex("byChatId", (q) => q.eq("chatId", doc.chatId)) + .order("desc") + .first(); + if (storageState === null) { + throw new Error(`Chat ${doc.chatId} not found in chatMessagesStorageState`); + } + if (storageState._creationTime < Date.now() - 1000 * 60 * 60 * 24 * daysInactive) { + const lastActiveDate = new Date(storageState._creationTime).toISOString(); + if (forReal) { + ctx.storage.delete(doc.promptCoreMessagesStorageId); + await ctx.db.delete(doc._id); + console.log(`Deleted debug file for chat ${doc.chatId} last active at ${lastActiveDate}`); + } else { + console.log(`Would delete debug file for chat ${doc.chatId} last active at ${lastActiveDate}`); + } + } + } + if (shouldScheduleNext && !isDone) { + await ctx.scheduler.runAfter(delayInMs, internal.cleanup.deleteDebugFilesForInactiveChats, { + forReal, + cursor: continueCursor, + shouldScheduleNext, + daysInactive, + }); + } + }, +}); diff --git a/src/test/fake_chef/compressMessages.ts b/src/test/fake_chef/compressMessages.ts new file mode 100644 index 00000000..658cebb8 --- /dev/null +++ b/src/test/fake_chef/compressMessages.ts @@ -0,0 +1,15 @@ +import { Lz4 } from "./lz4"; +import type { SerializedMessage } from "./messages"; + +export async function compressMessages(messages: SerializedMessage[]) { + const lz4 = await Lz4.initialize(); + const compressed = lz4.compress(new TextEncoder().encode(JSON.stringify(messages))); + return compressed; +} + +export async function decompressMessages(response: Response) { + const lz4 = await Lz4.initialize(); + const compressed = await response.arrayBuffer(); + const decompressed = lz4.decompress(new Uint8Array(compressed)); + return JSON.parse(new TextDecoder().decode(decompressed)); +} diff --git a/src/test/fake_chef/convexProjects.ts b/src/test/fake_chef/convexProjects.ts new file mode 100644 index 00000000..66ed68c2 --- /dev/null +++ b/src/test/fake_chef/convexProjects.ts @@ -0,0 +1,422 @@ +import { + internalAction, + internalMutation, + internalQuery, + mutation, + query, + type ActionCtx, + type MutationCtx, +} from "./_generated/server"; +import { ConvexError, v } from "convex/values"; +import { getChatByIdOrUrlIdEnsuringAccess } from "./messages"; +import { internal } from "./_generated/api"; +import type { Id } from "./_generated/dataModel"; + +export const hasConnectedConvexProject = query({ + args: { + sessionId: v.id("sessions"), + chatId: v.string(), + }, + handler: async (ctx, args) => { + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: args.chatId, sessionId: args.sessionId }); + return chat?.convexProject !== undefined; + }, +}); + +export const loadConnectedConvexProjectCredentials = query({ + args: { + sessionId: v.id("sessions"), + chatId: v.string(), + }, + returns: v.union( + v.object({ + kind: v.literal("connected"), + projectSlug: v.string(), + teamSlug: v.string(), + deploymentUrl: v.string(), + deploymentName: v.string(), + adminKey: v.string(), + warningMessage: v.optional(v.string()), + }), + v.object({ + kind: v.literal("connecting"), + }), + v.object({ + kind: v.literal("failed"), + errorMessage: v.string(), + }), + v.null(), + ), + handler: async (ctx, args) => { + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: args.chatId, sessionId: args.sessionId }); + if (!chat) { + return null; + } + const project = chat.convexProject; + if (project === undefined) { + return null; + } + if (project.kind === "connecting") { + return { kind: "connecting" } as const; + } + if (project.kind === "failed") { + return { kind: "failed", errorMessage: project.errorMessage } as const; + } + const credentials = await ctx.db + .query("convexProjectCredentials") + .withIndex("bySlugs", (q) => q.eq("teamSlug", project.teamSlug).eq("projectSlug", project.projectSlug)) + .first(); + if (!credentials) { + return null; + } + return { + kind: "connected", + projectSlug: project.projectSlug, + teamSlug: project.teamSlug, + deploymentUrl: project.deploymentUrl, + deploymentName: project.deploymentName, + adminKey: credentials.projectDeployKey, + warningMessage: project.warningMessage, + } as const; + }, +}); + +const CHECK_CONNECTION_DEADLINE_MS = 15000; + +export const startProvisionConvexProject = mutation({ + args: { + sessionId: v.id("sessions"), + chatId: v.string(), + projectInitParams: v.optional( + v.object({ + teamSlug: v.string(), + auth0AccessToken: v.string(), + }), + ), + }, + handler: async (ctx, args) => { + await startProvisionConvexProjectHelper(ctx, args); + }, +}); + +export async function startProvisionConvexProjectHelper( + ctx: MutationCtx, + args: { + sessionId: Id<"sessions">; + chatId: string; + projectInitParams?: { + teamSlug: string; + auth0AccessToken: string; + }; + }, +): Promise { + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: args.chatId, sessionId: args.sessionId }); + if (!chat) { + throw new ConvexError({ code: "NotAuthorized", message: "Chat not found" }); + } + const session = await ctx.db.get(args.sessionId); + if (!session) { + console.error(`Session not found: ${args.sessionId}`); + throw new ConvexError({ code: "NotAuthorized", message: "Chat not found" }); + } + if (session.memberId === undefined) { + throw new ConvexError({ code: "NotAuthorized", message: "Must be logged in to connect a project" }); + } + // OAuth flow + if (args.projectInitParams === undefined) { + console.error(`Must provide projectInitParams for oauth: ${args.sessionId}`); + throw new ConvexError({ code: "NotAuthorized", message: "Invalid flow for connecting a project" }); + } + + await ctx.scheduler.runAfter(0, internal.convexProjects.connectConvexProjectForOauth, { + sessionId: args.sessionId, + chatId: args.chatId, + accessToken: args.projectInitParams.auth0AccessToken, + teamSlug: args.projectInitParams.teamSlug, + }); + const jobId = await ctx.scheduler.runAfter(CHECK_CONNECTION_DEADLINE_MS, internal.convexProjects.checkConnection, { + sessionId: args.sessionId, + chatId: args.chatId, + }); + await ctx.db.patch(chat._id, { convexProject: { kind: "connecting", checkConnectionJobId: jobId } }); + return; +} + +export const recordProvisionedConvexProjectCredentials = internalMutation({ + args: { + sessionId: v.id("sessions"), + chatId: v.string(), + projectSlug: v.string(), + teamSlug: v.optional(v.string()), + projectDeployKey: v.string(), + deploymentUrl: v.string(), + deploymentName: v.string(), + warningMessage: v.optional(v.string()), + }, + handler: async (ctx, args) => { + const teamSlug = args.teamSlug ?? "demo-team"; + await ctx.db.insert("convexProjectCredentials", { + projectSlug: args.projectSlug, + teamSlug, + projectDeployKey: args.projectDeployKey, + }); + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: args.chatId, sessionId: args.sessionId }); + if (!chat) { + console.error(`Chat not found: ${args.chatId}, sessionId: ${args.sessionId}`); + return; + } + if (chat.convexProject?.kind === "connecting") { + const jobId = chat.convexProject.checkConnectionJobId; + if (jobId) { + await ctx.scheduler.cancel(jobId); + } + } + await ctx.db.patch(chat._id, { + convexProject: { + kind: "connected", + projectSlug: args.projectSlug, + teamSlug, + deploymentUrl: args.deploymentUrl, + deploymentName: args.deploymentName, + warningMessage: args.warningMessage, + }, + }); + }, +}); + +const TOTAL_WAIT_TIME_MS = 5000; +const WAIT_TIME_MS = 500; + +export const connectConvexProjectForOauth = internalAction({ + args: { + sessionId: v.id("sessions"), + chatId: v.string(), + accessToken: v.string(), + teamSlug: v.string(), + }, + handler: async (ctx, args) => { + await _connectConvexProjectForMember(ctx, { + sessionId: args.sessionId, + chatId: args.chatId, + accessToken: args.accessToken, + teamSlug: args.teamSlug, + }) + .then(async (data) => { + await ctx.runMutation(internal.convexProjects.recordProvisionedConvexProjectCredentials, { + sessionId: args.sessionId, + chatId: args.chatId, + projectSlug: data.projectSlug, + teamSlug: args.teamSlug, + projectDeployKey: data.projectDeployKey, + deploymentUrl: data.deploymentUrl, + deploymentName: data.deploymentName, + warningMessage: data.warningMessage, + }); + }) + .catch(async (error) => { + console.error(`Error connecting convex project: ${error.message}`); + const errorMessage = error instanceof ConvexError ? error.data.message : "Unexpected error"; + await ctx.runMutation(internal.convexProjects.recordFailedConvexProjectConnection, { + sessionId: args.sessionId, + chatId: args.chatId, + errorMessage, + }); + }); + }, +}); + +async function _connectConvexProjectForMember( + ctx: ActionCtx, + args: { + sessionId: Id<"sessions">; + chatId: string; + accessToken: string; + teamSlug: string; + }, +): Promise<{ + projectSlug: string; + teamSlug: string; + deploymentUrl: string; + deploymentName: string; + projectDeployKey: string; + warningMessage: string | undefined; +}> { + const bigBrainHost = ensureEnvVar("BIG_BRAIN_HOST"); + let projectName: string | null = null; + let timeElapsed = 0; + // Project names get set via the first message from the LLM, so best effort + // get the name and use it to create the project. + while (timeElapsed < TOTAL_WAIT_TIME_MS) { + projectName = await ctx.runQuery(internal.convexProjects.getProjectName, { + sessionId: args.sessionId, + chatId: args.chatId, + }); + if (projectName) { + break; + } + await new Promise((resolve) => setTimeout(resolve, WAIT_TIME_MS)); + timeElapsed += WAIT_TIME_MS; + } + projectName = projectName ?? "My Project (Chef)"; + const response = await fetch(`${bigBrainHost}/api/create_project`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${args.accessToken}`, + }, + body: JSON.stringify({ + team: args.teamSlug, + projectName, + deploymentType: "dev", + }), + }); + if (!response.ok) { + const text = await response.text(); + const defaultProvisioningError = new ConvexError({ + code: "ProvisioningError", + message: `Failed to create project: ${response.status}`, + details: text, + }); + if (response.status !== 400) { + throw defaultProvisioningError; + } + let data: { code?: string; message?: string } | null = null; + try { + data = JSON.parse(text); + } catch (_e) { + throw defaultProvisioningError; + } + + // Special case this error since it's probably semi-common + if (data !== null && data.code === "ProjectQuotaReached" && typeof data.message === "string") { + throw new ConvexError({ + code: "ProvisioningError", + message: `Failed to create project: ProjectQuotaReached: ${data.message}`, + details: text, + }); + } + throw defaultProvisioningError; + } + const data: { + projectSlug: string; + projectId: number; + teamSlug: string; + deploymentName: string; + // This is in fact the dev URL + prodUrl: string; + adminKey: string; + projectsRemaining: number; + } = await response.json(); + + const projectDeployKeyResponse = await fetch(`${bigBrainHost}/api/dashboard/authorize`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${args.accessToken}`, + }, + body: JSON.stringify({ + authn_token: args.accessToken, + projectId: data.projectId, + appName: ensureEnvVar("CHEF_OAUTH_APP_NAME"), + }), + }); + if (!projectDeployKeyResponse.ok) { + const text = await projectDeployKeyResponse.text(); + throw new ConvexError({ + code: "ProvisioningError", + message: `Failed to create project deploy key: ${projectDeployKeyResponse.status}`, + details: text, + }); + } + const projectDeployKeyData: { accessToken: string } = await projectDeployKeyResponse.json(); + const projectDeployKey = `project:${args.teamSlug}:${data.projectSlug}|${projectDeployKeyData.accessToken}`; + const warningMessage = + data.projectsRemaining <= 2 ? `You have ${data.projectsRemaining} projects remaining on this team.` : undefined; + + return { + projectSlug: data.projectSlug, + teamSlug: args.teamSlug, + deploymentUrl: data.prodUrl, + deploymentName: data.deploymentName, + projectDeployKey, + warningMessage, + }; +} + +export const recordFailedConvexProjectConnection = internalMutation({ + args: { + sessionId: v.id("sessions"), + chatId: v.string(), + errorMessage: v.string(), + }, + handler: async (ctx, args) => { + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: args.chatId, sessionId: args.sessionId }); + if (!chat) { + console.error(`Chat not found: ${args.chatId}, sessionId: ${args.sessionId}`); + return; + } + if (chat.convexProject?.kind === "connecting") { + const jobId = chat.convexProject.checkConnectionJobId; + if (jobId) { + await ctx.scheduler.cancel(jobId); + } + } + await ctx.db.patch(chat._id, { + convexProject: { kind: "failed", errorMessage: args.errorMessage }, + }); + }, +}); + +export const checkConnection = internalMutation({ + args: { + sessionId: v.id("sessions"), + chatId: v.string(), + }, + handler: async (ctx, args) => { + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: args.chatId, sessionId: args.sessionId }); + if (!chat) { + console.error(`Chat not found: ${args.chatId}, sessionId: ${args.sessionId}`); + return; + } + if (chat.convexProject?.kind !== "connecting") { + return; + } + await ctx.db.patch(chat._id, { convexProject: { kind: "failed", errorMessage: "Failed to connect to project" } }); + }, +}); + +export const getProjectName = internalQuery({ + args: { + sessionId: v.id("sessions"), + chatId: v.string(), + }, + returns: v.union(v.string(), v.null()), + handler: async (ctx, args) => { + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: args.chatId, sessionId: args.sessionId }); + if (!chat) { + throw new ConvexError({ code: "NotAuthorized", message: "Chat not found" }); + } + return chat.urlId ?? null; + }, +}); + +export const disconnectConvexProject = mutation({ + args: { + sessionId: v.id("sessions"), + chatId: v.string(), + }, + handler: async (ctx, args) => { + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: args.chatId, sessionId: args.sessionId }); + if (!chat) { + throw new ConvexError({ code: "NotAuthorized", message: "Chat not found" }); + } + await ctx.db.patch(chat._id, { convexProject: undefined }); + }, +}); + +export function ensureEnvVar(name: string) { + if (!process.env[name]) { + throw new Error(`Environment variable ${name} is not set`); + } + return process.env[name]; +} diff --git a/src/test/fake_chef/crons.ts b/src/test/fake_chef/crons.ts new file mode 100644 index 00000000..04a6a0ef --- /dev/null +++ b/src/test/fake_chef/crons.ts @@ -0,0 +1,13 @@ +import { cronJobs } from "convex/server"; +import { internal } from "./_generated/api"; + +const crons = cronJobs(); + +crons.daily( + "delete debug files for inactive chats", + { hourUTC: 16, minuteUTC: 0 }, + internal.cleanup.deleteDebugFilesForInactiveChats, + { forReal: true, shouldScheduleNext: true, daysInactive: 14 }, +); + +export default crons; diff --git a/src/test/fake_chef/debugPrompt.ts b/src/test/fake_chef/debugPrompt.ts new file mode 100644 index 00000000..cfb5118d --- /dev/null +++ b/src/test/fake_chef/debugPrompt.ts @@ -0,0 +1,106 @@ +import { internalMutation, query } from "./_generated/server"; +import { v } from "convex/values"; +import type { MutationCtx, QueryCtx } from "./_generated/server"; +import { assertIsConvexAdmin } from "./admin"; +import type { Id } from "./_generated/dataModel"; +import { usageRecordValidator } from "./schema"; + +async function getChatByInitialId(ctx: QueryCtx, initialId: string) { + const chatByInitialId = await ctx.db + .query("chats") + .withIndex("byInitialId", (q: any) => q.eq("initialId", initialId).lt("isDeleted", true)) + .unique(); + if (!chatByInitialId) { + throw new Error(`No corresponding chat found for initial ID ${initialId}`); + } + return chatByInitialId; +} + +export const storeDebugPrompt = internalMutation({ + args: { + chatInitialId: v.string(), + responseCoreMessages: v.array(v.any()), + promptCoreMessagesStorageId: v.id("_storage"), + finishReason: v.string(), + modelId: v.optional(v.string()), + usage: usageRecordValidator, + chefTokens: v.number(), + }, + handler: async (ctx, args) => { + const { + chatInitialId, + responseCoreMessages, + promptCoreMessagesStorageId, + finishReason, + modelId, + usage, + chefTokens, + } = args; + const chat = await getChatByInitialId(ctx, chatInitialId); + await ctx.db.insert("debugChatApiRequestLog", { + chatId: chat._id, + responseCoreMessages, + promptCoreMessagesStorageId, + finishReason, + modelId: modelId ?? "", + usage, + chefTokens, + }); + }, +}); + +export const deleteDebugPrompt = internalMutation({ + args: { + id: v.id("debugChatApiRequestLog"), + }, + handler: async (ctx, args) => { + await _deleteDebugPrompt(ctx, args.id); + }, +}); + +async function _deleteDebugPrompt(ctx: MutationCtx, id: Id<"debugChatApiRequestLog">) { + const record = await ctx.db.get(id); + if (!record) { + return; + } + await ctx.storage.delete(record.promptCoreMessagesStorageId); + await ctx.db.delete(id); +} + +// this is going to fail on big tables, we'll need to use an action +export const deleteAllDebugPrompts = internalMutation({ + args: {}, + handler: async (ctx) => { + const records = await ctx.db.query("debugChatApiRequestLog").collect(); + + for (let i = 0; i < records.length; i += 10) { + const chunk = records.slice(i, i + 10); + await Promise.all(chunk.map((record) => _deleteDebugPrompt(ctx, record._id))); + } + }, +}); + +export const show = query({ + args: { + chatInitialId: v.string(), + }, + handler: async (ctx, args) => { + await assertIsConvexAdmin(ctx); + + const chat = await getChatByInitialId(ctx, args.chatInitialId); + + const debugPrompts = await ctx.db + .query("debugChatApiRequestLog") + .withIndex("byChatId", (q) => q.eq("chatId", chat._id)) + .collect(); + + const promptsWithUrls = await Promise.all( + debugPrompts.map(async (prompt) => ({ + ...prompt, + coreMessagesUrl: await ctx.storage.getUrl(prompt.promptCoreMessagesStorageId), + })), + ); + + return promptsWithUrls; + }, +}); diff --git a/src/test/fake_chef/deploy.ts b/src/test/fake_chef/deploy.ts new file mode 100644 index 00000000..766c4efd --- /dev/null +++ b/src/test/fake_chef/deploy.ts @@ -0,0 +1,31 @@ +import { ConvexError, v } from "convex/values"; +import { mutation, query } from "./_generated/server"; +import { getChatByIdOrUrlIdEnsuringAccess } from "./messages"; + +export const recordDeploy = mutation({ + args: { + sessionId: v.id("sessions"), + id: v.string(), + }, + handler: async (ctx, { id, sessionId }) => { + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id, sessionId }); + if (!chat) { + throw new ConvexError("Chat not found"); + } + await ctx.db.patch(chat._id, { hasBeenDeployed: true }); + }, +}); + +export const hasBeenDeployed = query({ + args: { + sessionId: v.id("sessions"), + id: v.string(), + }, + handler: async (ctx, { id, sessionId }) => { + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id, sessionId }); + if (!chat) { + throw new ConvexError("Chat not found"); + } + return !!chat.hasBeenDeployed; + }, +}); diff --git a/src/test/fake_chef/dev.ts b/src/test/fake_chef/dev.ts new file mode 100644 index 00000000..f54cc69a --- /dev/null +++ b/src/test/fake_chef/dev.ts @@ -0,0 +1,61 @@ +import { internalMutation, internalAction, internalQuery } from "./_generated/server"; +import { v } from "convex/values"; +import schema from "./schema"; +import { internal } from "./_generated/api"; + +export const deleteFromTable = internalMutation({ + args: { tableName: v.string() }, + handler: async (ctx, { tableName }) => { + // Delete 4000 rows at a time + const rows = await ctx.db.query(tableName as any).take(1000); + await Promise.all(rows.map((row) => ctx.db.delete(row._id))); + + return rows.length !== 1000; + }, +}); + +export const clearAll = internalAction({ + handler: async (ctx) => { + // Get all table names from the schema + const tableNames = Object.keys(schema.tables); + + for (const tableName of tableNames) { + let isCleared = false; + + while (!isCleared) { + isCleared = await ctx.runMutation(internal.dev.deleteFromTable, { tableName }); + } + } + }, +}); + +export const findSessionForUser = internalQuery({ + args: { githubMemberId: v.string() }, + handler: async (ctx, { githubMemberId }) => { + let normalizedGithubMemberId = githubMemberId; + if (!normalizedGithubMemberId.startsWith("github|")) { + if (!isNaN(parseInt(normalizedGithubMemberId))) { + normalizedGithubMemberId = `github|${normalizedGithubMemberId}`; + } else { + throw new Error("Invalid github member id -- these should look like github|1234567890"); + } + } + const convexMember = await ctx.db + .query("convexMembers") + .withIndex("byTokenIdentifier", (q) => + q.eq("tokenIdentifier", `https://auth.convex.dev/|${normalizedGithubMemberId}`), + ) + .first(); + if (!convexMember) { + throw new Error("Convex member not found"); + } + const session = await ctx.db + .query("sessions") + .filter((q) => q.eq(q.field("memberId"), convexMember._id)) + .first(); + if (!session) { + throw new Error("Session not found"); + } + return session; + }, +}); diff --git a/src/test/fake_chef/http.ts b/src/test/fake_chef/http.ts new file mode 100644 index 00000000..ba184f24 --- /dev/null +++ b/src/test/fake_chef/http.ts @@ -0,0 +1,286 @@ +import { httpRouter } from "convex/server"; +import { httpAction, type ActionCtx } from "./_generated/server"; +import { internal } from "./_generated/api"; +import type { Id } from "./_generated/dataModel"; +import { ConvexError } from "convex/values"; +import { openaiProxy } from "./openaiProxy"; +import { corsRouter } from "convex-helpers/server/cors"; +import { resendProxy } from "./resendProxy"; + +const http = httpRouter(); +const httpWithCors = corsRouter(http, { + allowedHeaders: ["Content-Type", "X-Chef-Admin-Token"], +}); + +// This is particularly useful with CORS, where an unhandled error won't have CORS +// headers applied to it. +function httpActionWithErrorHandling(handler: (ctx: ActionCtx, request: Request) => Promise) { + return httpAction(async (ctx, request) => { + try { + return await handler(ctx, request); + } catch (e) { + console.error(e); + return new Response( + JSON.stringify({ error: e instanceof ConvexError ? e.message : "An unknown error occurred" }), + { + status: 500, + headers: { + "Content-Type": "application/json", + }, + }, + ); + } + }); +} +httpWithCors.route({ + path: "/upload_snapshot", + method: "POST", + handler: httpActionWithErrorHandling(async (ctx, request) => { + const url = new URL(request.url); + const sessionId = url.searchParams.get("sessionId"); + if (!sessionId) { + throw new ConvexError("sessionId is required"); + } + const chatId = url.searchParams.get("chatId"); + if (!chatId) { + throw new ConvexError("chatId is required"); + } + + const blob = await request.blob(); + const storageId = await ctx.storage.store(blob); + + await ctx.runMutation(internal.snapshot.saveSnapshot, { + sessionId: sessionId as Id<"sessions">, + chatId: chatId as Id<"chats">, + storageId, + }); + + return new Response(JSON.stringify({ snapshotId: storageId }), { + status: 200, + headers: { + "Content-Type": "application/json", + }, + }); + }), +}); + +http.route({ + pathPrefix: "/openai-proxy/", + method: "POST", + handler: openaiProxy, +}); + +http.route({ + pathPrefix: "/resend-proxy/", + method: "POST", + handler: resendProxy, +}); + +httpWithCors.route({ + path: "/initial_messages", + method: "POST", + handler: httpActionWithErrorHandling(async (ctx, request) => { + const body = await request.json(); + const sessionId = body.sessionId; + const chatId = body.chatId; + if (!sessionId) { + throw new ConvexError("sessionId is required"); + } + if (!chatId) { + throw new ConvexError("chatId is required"); + } + const storageInfo = await ctx.runQuery(internal.messages.getInitialMessagesStorageInfo, { + sessionId, + chatId, + }); + if (!storageInfo) { + return new Response(`Chat not found: ${chatId}`, { + status: 404, + }); + } + if (!storageInfo.storageId) { + return new Response(null, { + status: 204, + }); + } + const blob = await ctx.storage.get(storageInfo.storageId); + return new Response(blob, { + status: 200, + }); + }), +}); + +httpWithCors.route({ + path: "/store_chat", + method: "POST", + handler: httpActionWithErrorHandling(async (ctx, request) => { + const url = new URL(request.url); + const sessionId = url.searchParams.get("sessionId"); + const chatId = url.searchParams.get("chatId"); + const lastMessageRank = url.searchParams.get("lastMessageRank"); + const lastSubchatIndex = url.searchParams.get("lastSubchatIndex"); + const partIndex = url.searchParams.get("partIndex"); + const formData = await request.formData(); + let messageStorageId: Id<"_storage"> | null = null; + let snapshotStorageId: Id<"_storage"> | null = null; + if (formData.has("messages")) { + const messageBlob = formData.get("messages") as Blob; + messageStorageId = await ctx.storage.store(messageBlob); + } + if (formData.has("snapshot")) { + const snapshotBlob = formData.get("snapshot") as Blob; + snapshotStorageId = await ctx.storage.store(snapshotBlob); + } + await ctx.runMutation(internal.messages.updateStorageState, { + sessionId: sessionId as Id<"sessions">, + chatId: chatId as Id<"chats">, + lastMessageRank: parseInt(lastMessageRank!), + // Default to the first feature if not provided + subchatIndex: parseInt(lastSubchatIndex ?? "0"), + partIndex: parseInt(partIndex!), + storageId: messageStorageId, + snapshotId: snapshotStorageId, + }); + return new Response(null, { + status: 200, + }); + }), +}); + +http.route({ + path: "/__debug/download_messages", + method: "OPTIONS", + handler: httpActionWithErrorHandling(async (ctx, request) => { + return new Response(null, { + status: 200, + headers: { + "Access-Control-Allow-Origin": request.headers.get("Origin") ?? "*", + "Access-Control-Allow-Methods": "POST, OPTIONS", + "Access-Control-Allow-Headers": "Content-Type, Authorization, X-Chef-Admin-Token", + "Access-Control-Allow-Credentials": "true", + }, + }); + }), +}); + +http.route({ + path: "/__debug/download_messages", + method: "POST", + handler: httpActionWithErrorHandling(async (ctx, request) => { + const body = await request.json(); + // We auth either via the Auth0 token or with a custom header + const header = request.headers.get("X-Chef-Admin-Token"); + const authHeader = request.headers.get("Authorization"); + if (authHeader === null) { + if (header !== process.env.CHEF_ADMIN_TOKEN) { + return new Response(JSON.stringify({ code: "Unauthorized", message: "Invalid admin token" }), { + status: 401, + headers: { + "Content-Type": "application/json", + }, + }); + } + } + const chatUuid = body.chatUuid; + const storageId = await ctx.runQuery(internal.messages.getMessagesByChatInitialIdBypassingAccessControl, { + id: chatUuid, + ensureAdmin: authHeader !== null, + }); + if (!storageId) { + return new Response(null, { + status: 204, + }); + } + const blob = await ctx.storage.get(storageId); + return new Response(blob, { + status: 200, + headers: { + "Access-Control-Allow-Origin": request.headers.get("Origin") ?? "*", + Vary: "Origin", + }, + }); + }), +}); + +httpWithCors.route({ + path: "/upload_debug_prompt", + method: "POST", + handler: httpActionWithErrorHandling(async (ctx, request) => { + const formData = await request.formData(); + const metadataStr = formData.get("metadata"); + const messagesBlob = formData.get("promptCoreMessages") as Blob; + + if (!metadataStr || !messagesBlob) { + throw new ConvexError("metadata and messages are required in form data"); + } + + let metadata; + try { + metadata = JSON.parse(metadataStr as string); + } catch (_e) { + throw new ConvexError("Invalid metadata: must be valid JSON"); + } + + const promptCoreMessagesStorageId = await ctx.storage.store(messagesBlob); + try { + await ctx.runMutation(internal.debugPrompt.storeDebugPrompt, { ...metadata, promptCoreMessagesStorageId }); + } catch (e) { + await ctx.storage.delete(promptCoreMessagesStorageId); + throw e; + } + + return new Response(JSON.stringify({ promptCoreMessagesStorageId }), { + status: 200, + headers: { + "Content-Type": "application/json", + }, + }); + }), +}); + +httpWithCors.route({ + path: "/upload_thumbnail", + method: "POST", + handler: httpActionWithErrorHandling(async (ctx, request) => { + const url = new URL(request.url); + const sessionId = url.searchParams.get("sessionId"); + const urlId = url.searchParams.get("chatId"); + + if (!sessionId || !urlId) { + return new Response("Missing sessionId or chatId", { status: 400 }); + } + + const imageBlob = await request.blob(); + + // Validate content type + const contentType = imageBlob.type; + if (!contentType.startsWith("image/")) { + return new Response(JSON.stringify({ error: "Invalid file type. Only images are allowed." }), { + status: 400, + headers: { "Content-Type": "application/json" }, + }); + } + + const MAX_THUMBNAIL_SIZE = 5 * 1024 * 1024; + if (imageBlob.size > MAX_THUMBNAIL_SIZE) { + return new Response(JSON.stringify({ error: "Thumbnail image exceeds maximum size of 5MB" }), { + status: 413, // Payload Too Large + headers: { "Content-Type": "application/json" }, + }); + } + + const storageId = await ctx.storage.store(imageBlob); + + await ctx.runMutation(internal.socialShare.saveThumbnail, { + sessionId: sessionId as Id<"sessions">, + urlId, + storageId, + }); + + return new Response(JSON.stringify({ storageId }), { + headers: { "Content-Type": "application/json" }, + }); + }), +}); + +export default httpWithCors.http; diff --git a/src/test/fake_chef/lz4.ts b/src/test/fake_chef/lz4.ts new file mode 100644 index 00000000..ec65f04e --- /dev/null +++ b/src/test/fake_chef/lz4.ts @@ -0,0 +1,147 @@ +import { wasmSource } from "./lz4Wasm"; + +// Do some work at import time to speed `vitest` up (since it reuses the same module +// across tests, unlike the Convex runtime). +const wasmBinary = atob(wasmSource); +const wasmBuffer = new Uint8Array(wasmBinary.length); +for (let i = 0; i < wasmBinary.length; i++) { + wasmBuffer[i] = wasmBinary.charCodeAt(i); +} + +export class Lz4 { + private instance?: WebAssembly.Instance; + private textDecoder: TextDecoder; + + private uint8Memory0: Uint8Array | null = null; + private int32Memory0: Int32Array | null = null; + private wasmVectorLen: number = 0; + + private heap: any[]; + private heap_next: number; + + constructor() { + this.textDecoder = new TextDecoder("utf-8", { ignoreBOM: true, fatal: true }); + this.textDecoder.decode(); + + this.heap = new Array(32).fill(undefined); + this.heap.push(undefined, null, true, false); + this.heap_next = this.heap.length; + } + + static async initialize() { + const lz4 = new Lz4(); + const { instance } = await WebAssembly.instantiate(wasmBuffer, { + "./lz4_wasm_bg.js": { + __wbindgen_string_new: (arg0: number, arg1: number) => { + const ret = lz4.getStringFromWasm0(arg0, arg1); + return lz4.addHeapObject(ret); + }, + }, + }); + lz4.instance = instance; + return lz4; + } + + compress(input: Uint8Array) { + try { + const retptr = this.exports.__wbindgen_add_to_stack_pointer(-16); + const ptr0 = this.passArray8ToWasm0(input, this.exports.__wbindgen_malloc); + const len0 = this.wasmVectorLen; + this.exports.compress(retptr, ptr0, len0); + var r0 = this.getInt32Memory0()[retptr / 4 + 0]; + var r1 = this.getInt32Memory0()[retptr / 4 + 1]; + var v1 = this.getArrayU8FromWasm0(r0, r1).slice(); + this.exports.__wbindgen_free(r0, r1 * 1); + return v1; + } finally { + this.exports.__wbindgen_add_to_stack_pointer(16); + } + } + + decompress(input: Uint8Array) { + try { + const retptr = this.exports.__wbindgen_add_to_stack_pointer(-16); + const ptr0 = this.passArray8ToWasm0(input, this.exports.__wbindgen_malloc); + const len0 = this.wasmVectorLen; + this.exports.decompress(retptr, ptr0, len0); + var r0 = this.getInt32Memory0()[retptr / 4 + 0]; + var r1 = this.getInt32Memory0()[retptr / 4 + 1]; + var r2 = this.getInt32Memory0()[retptr / 4 + 2]; + var r3 = this.getInt32Memory0()[retptr / 4 + 3]; + if (r3) { + throw this.takeObject(r2); + } + var v1 = this.getArrayU8FromWasm0(r0, r1).slice(); + this.exports.__wbindgen_free(r0, r1 * 1); + return v1; + } finally { + this.exports.__wbindgen_add_to_stack_pointer(16); + } + } + + private get exports() { + if (!this.instance) { + throw new Error("Lz4 instance not initialized"); + } + return this.instance.exports as any; + } + + private getUint8Memory0() { + if (!this.instance) { + throw new Error("Lz4 instance not initialized"); + } + if (this.uint8Memory0 === null || this.uint8Memory0.buffer !== this.exports.memory.buffer) { + this.uint8Memory0 = new Uint8Array(this.exports.memory.buffer); + } + return this.uint8Memory0; + } + + private getStringFromWasm0(ptr: number, len: number) { + return this.textDecoder.decode(this.getUint8Memory0().subarray(ptr, ptr + len)); + } + + private passArray8ToWasm0(arg: any, malloc: any) { + const ptr = malloc(arg.length * 1); + this.getUint8Memory0().set(arg, ptr / 1); + this.wasmVectorLen = arg.length; + return ptr; + } + + private getArrayU8FromWasm0(ptr: number, len: number) { + return this.getUint8Memory0().subarray(ptr / 1, ptr / 1 + len); + } + + private getInt32Memory0() { + if (!this.instance) { + throw new Error("Lz4 instance not initialized"); + } + if (this.int32Memory0 === null || this.int32Memory0.buffer !== this.exports.memory.buffer) { + this.int32Memory0 = new Int32Array(this.exports.memory.buffer); + } + return this.int32Memory0; + } + + private addHeapObject(obj: any) { + if (this.heap_next === this.heap.length) { + this.heap.push(this.heap.length + 1); + } + const idx = this.heap_next; + this.heap_next = this.heap[idx]; + this.heap[idx] = obj; + return idx; + } + + private getObject(idx: number) { + return this.heap[idx]; + } + + private dropObject(idx: number) { + this.heap[idx] = this.heap_next; + } + + private takeObject(idx: number) { + const ret = this.getObject(idx); + this.dropObject(idx); + return ret; + } +} diff --git a/src/test/fake_chef/lz4Wasm.ts b/src/test/fake_chef/lz4Wasm.ts new file mode 100644 index 00000000..b66cda86 --- /dev/null +++ b/src/test/fake_chef/lz4Wasm.ts @@ -0,0 +1,2 @@ +export const wasmSource = + "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HHdxNgIADwsgASAENgIYIAAoAhAiAgRAIAEgAjYCECACIAE2AhgLIABBFGooAgAiAEUNACABQRRqIAA2AgAgACABNgIYCwtvAQx/QfyPwAAoAgAiAkUEQEGMkMAAQf8fNgIAQQAPC0H0j8AAIQYDQCACIgEoAgghAiABKAIEIQMgASgCACEEIAFBDGooAgAaIAEhBiAFQQFqIQUgAg0AC0GMkMAAIAVB/x8gBUH/H0sbNgIAIAgLpgIBAX8jAEEwayICJAACfwJAAkACQAJAAkAgACgCAEEBaw4EAQIDBAALIAIgAEEEajYCACACIABBCGo2AgQgAkEcakECNgIAIAJBLGpBCzYCACACQgI3AgwgAkHchsAANgIIIAJBCzYCJCACIAJBIGo2AhggAiACNgIoIAIgAkEEajYCICABIAJBCGoQJwwECyACIABBCGo2AgAgAiAAQQRqNgIEIAJBHGpBAjYCACACQSxqQQs2AgAgAkICNwIMIAJBjIbAADYCCCACQQs2AiQgAiACQSBqNgIYIAIgAkEEajYCKCACIAI2AiAgASACQQhqECcMAwsgAUGrhcAAQSUQPwwCCyABQYqFwABBIRA/DAELIAFBzITAAEE+ED8LIAJBMGokAAuJAgIEfwF+IwBBMGsiAiQAIAFBBGohBCABKAIERQRAIAEoAgAhAyACQRBqIgVBADYCACACQgE3AwggAiACQQhqNgIUIAJBKGogA0EQaikCADcDACACQSBqIANBCGopAgA3AwAgAiADKQIANwMYIAJBFGpBrIjAACACQRhqEAwaIARBCGogBSgCADYCACAEIAIpAwg3AgALIAJBIGoiAyAEQQhqKAIANgIAIAFBDGpBADYCACAEKQIAIQYgAUIBNwIEIAIgBjcDGEEMQQQQRyIBRQRAQQxBBBBaAAsgASACKQMYNwIAIAFBCGogAygCADYCACAAQeSHwAA2AgQgACABNgIAIAJBMGokAAuPAgEDfyMAQSBrIgUkAEEBIQZByIzAAEHIjMAAKAIAIgdBAWo2AgACQEGQkMAALQAABEBBmJDAACgCAEEBaiEGDAELQZCQwABBAToAAAtBmJDAACAGNgIAAkACQCAHQQBIIAZBAktyDQAgBSAEOgAYIAUgAzYCFCAFIAI2AhBBvIzAACgCACICQX9MDQBBvIzAACACQQFqIgI2AgBBvIzAAEHEjMAAKAIAIgMEf0HAjMAAKAIAIAUgACABKAIQEQEAIAUgBSkDADcDCCAFQQhqIAMoAhQRAQBBvIzAACgCAAUgAgtBf2o2AgAgBkEBSw0AIAQNAQsACyMAQRBrIgIkACACIAE2AgwgAiAANgIIAAu4AQECfyMAQSBrIgMkAAJAIAEgAmoiAiABSQ0AIABBBGooAgAiAUEBdCIEIAIgBCACSxsiAkEIIAJBCEsbIQICQCABBEAgA0EYakEBNgIAIAMgATYCFCADIAAoAgA2AhAMAQsgA0EANgIQCyADIAIgA0EQahAfIAMoAgAEQCADQQhqKAIAIgBFDQEgAygCBCAAEFoACyADKAIEIQEgAEEEaiACNgIAIAAgATYCACADQSBqJAAPCxAyAAu4AQEDfyMAQSBrIgIkAAJAIAFBAWoiAyABSQ0AIABBBGooAgAiAUEBdCIEIAMgBCADSxsiA0EIIANBCEsbIQMCQCABBEAgAkEYakEBNgIAIAIgATYCFCACIAAoAgA2AhAMAQsgAkEANgIQCyACIAMgAkEQahAfIAIoAgAEQCACQQhqKAIAIgBFDQEgAigCBCAAEFoACyACKAIEIQEgAEEEaiADNgIAIAAgATYCACACQSBqJAAPCxAyAAujAQEDfyMAQRBrIgMkACADIAEgAhACIAIEQCABEAcLIANBDGooAgAhASADKAIEIQICQAJ/IAMoAgAEQEEBIQQgAgwBCwJAIAJFIANBCGooAgAgAU1yDQAgAUUEQCACEAdBASECDAELIAIgARBCIgJFDQILQQALIQUgACAENgIMIAAgBTYCCCAAIAE2AgQgACACNgIAIANBEGokAA8LIAFBARBaAAucAQECfyACQQ9LBEAgAEEAIABrQQNxIgNqIQQgAwRAA0AgACABOgAAIABBAWoiACAESQ0ACwsgBCACIANrIgJBfHEiA2ohACADQQFOBEAgAUH/AXFBgYKECGwhAwNAIAQgAzYCACAEQQRqIgQgAEkNAAsLIAJBA3EhAgsgAgRAIAAgAmohAgNAIAAgAToAACAAQQFqIgAgAkkNAAsLC6wBAQN/IwBBMGsiAiQAIAFBBGohAyABKAIERQRAIAEoAgAhASACQRBqIgRBADYCACACQgE3AwggAiACQQhqNgIUIAJBKGogAUEQaikCADcDACACQSBqIAFBCGopAgA3AwAgAiABKQIANwMYIAJBFGpBrIjAACACQRhqEAwaIANBCGogBCgCADYCACADIAIpAwg3AgALIABB5IfAADYCBCAAIAM2AgAgAkEwaiQAC5MBAQJ/AkACQAJAAkACfwJAAkACf0EBIgMgAUEASA0AGiACKAIAIgRFDQEgAigCBCICDQQgAQ0CQQEMAwshA0EAIQEMBgsgAQ0AQQEMAQsgAUEBEEcLIgJFDQEMAgsgBCABEEIiAg0BCyAAIAE2AgRBASEBDAELIAAgAjYCBEEAIQMLIAAgAzYCACAAQQhqIAE2AgALgAEBAn8jAEEQayIDJAAgAyABIAIQCyACBEAgARAHCyADKAIAIQECQCADKAIEIAMoAggiBE0EQCABIQIMAQsgAUUEQCABIQIMAQsgBEUEQEEBIQIgARAHDAELIAEgBBBCIgINACAEQQEQWgALIAAgBDYCBCAAIAI2AgAgA0EQaiQAC54BAQJ/IwBBEGsiAyQAIABBFGooAgAhBAJAAn8CQAJAIABBBGooAgAOAgABAwsgBA0CQQAhAEH8hsAADAELIAQNASAAKAIAIgQoAgQhACAEKAIACyEEIAMgADYCBCADIAQ2AgAgA0GYiMAAIAEoAgggAiABLQAQEBkACyADQQA2AgQgAyAANgIAIANBhIjAACABKAIIIAIgAS0AEBAZAAt9AQF/IwBBQGoiBSQAIAUgATYCDCAFIAA2AgggBSADNgIUIAUgAjYCECAFQSxqQQI2AgAgBUE8akEbNgIAIAVCAjcCHCAFQaCJwAA2AhggBUEcNgI0IAUgBUEwajYCKCAFIAVBEGo2AjggBSAFQQhqNgIwIAVBGGogBBAzAAt1AQN/IAAgABBfIgBBCBBBIABrIgIQXSEAQeCPwAAgASACayIBNgIAQeiPwAAgADYCACAAIAFBAXI2AgRBCEEIEEEhAkEUQQgQQSEDQRBBCBBBIQQgACABEF0gBCADIAJBCGtqajYCBEGEkMAAQYCAgAE2AgALbwEBfyMAQTBrIgIkACACIAE2AgQgAiAANgIAIAJBHGpBAjYCACACQSxqQRk2AgAgAkICNwIMIAJBrIvAADYCCCACQRk2AiQgAiACQSBqNgIYIAIgAkEEajYCKCACIAI2AiAgAkEIakHkgMAAEDMAC28BAX8jAEEwayICJAAgAiABNgIEIAIgADYCACACQRxqQQI2AgAgAkEsakEZNgIAIAJCAjcCDCACQcyLwAA2AgggAkEZNgIkIAIgAkEgajYCGCACIAJBBGo2AiggAiACNgIgIAJBCGpBrILAABAzAAtvAQF/IwBBMGsiAiQAIAIgATYCBCACIAA2AgAgAkEcakECNgIAIAJBLGpBGTYCACACQgI3AgwgAkGAjMAANgIIIAJBGTYCJCACIAJBIGo2AhggAiACQQRqNgIoIAIgAjYCICACQQhqQayCwAAQMwALVgECfyMAQSBrIgIkACAAQRxqKAIAIQMgACgCGCACQRhqIAFBEGopAgA3AwAgAkEQaiABQQhqKQIANwMAIAIgASkCADcDCCADIAJBCGoQDCACQSBqJAALWQEBfyMAQSBrIgIkACACIAAoAgA2AgQgAkEYaiABQRBqKQIANwMAIAJBEGogAUEIaikCADcDACACIAEpAgA3AwggAkEEakG0hMAAIAJBCGoQDCACQSBqJAALWQEBfyMAQSBrIgIkACACIAAoAgA2AgQgAkEYaiABQRBqKQIANwMAIAJBEGogAUEIaikCADcDACACIAEpAgA3AwggAkEEakGsiMAAIAJBCGoQDCACQSBqJAALVgEBfyMAQSBrIgIkACACIAA2AgQgAkEYaiABQRBqKQIANwMAIAJBEGogAUEIaikCADcDACACIAEpAgA3AwggAkEEakG0hMAAIAJBCGoQDCACQSBqJAALTgECfyAAKAIAIgNBBGooAgAgA0EIaiIEKAIAIgBrIAJJBEAgAyAAIAIQGiAEKAIAIQALIAMoAgAgAGogASACEFwaIAQgACACajYCAEEAC0kBAn8gAEEEaigCACAAQQhqIgQoAgAiA2sgAkkEQCAAIAMgAhAaIAQoAgAhAwsgACgCACADaiABIAIQXBogBCACIANqNgIAQQALSwACQAJ/IAFBgIDEAEcEQEEBIAAoAhggASAAQRxqKAIAKAIQEQAADQEaCyACDQFBAAsPCyAAKAIYIAJBACAAQRxqKAIAKAIMEQIAC0cBAX8jAEEgayIDJAAgA0EUakEANgIAIANBjInAADYCECADQgE3AgQgAyABNgIcIAMgADYCGCADIANBGGo2AgAgAyACEDMAC0QBAn8gASgCBCECIAEoAgAhA0EIQQQQRyIBRQRAQQhBBBBaAAsg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"; diff --git a/src/test/fake_chef/messages.ts b/src/test/fake_chef/messages.ts new file mode 100644 index 00000000..536db2ad --- /dev/null +++ b/src/test/fake_chef/messages.ts @@ -0,0 +1,791 @@ +import { + action, + internalMutation, + internalQuery, + mutation, + query, + type MutationCtx, + type QueryCtx, +} from "./_generated/server"; +import type { Message as AIMessage } from "ai"; +import { ConvexError, v } from "convex/values"; +import type { Infer } from "convex/values"; +import { isValidSession } from "./sessions"; +import type { Doc, Id } from "./_generated/dataModel"; +import { ensureEnvVar, startProvisionConvexProjectHelper } from "./convexProjects"; +import { internal } from "./_generated/api"; +import { assertIsConvexAdmin } from "./admin"; + +export type SerializedMessage = Omit & { + createdAt: number | undefined; + content?: string; +}; + +export const initializeChat = mutation({ + args: { + sessionId: v.id("sessions"), + id: v.string(), + projectInitParams: v.optional( + v.object({ + teamSlug: v.string(), + auth0AccessToken: v.string(), + }), + ), + }, + returns: v.null(), + handler: async (ctx, args) => { + const { id, sessionId, projectInitParams } = args; + let existing = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: args.id, sessionId: args.sessionId }); + + if (existing) { + return; + } + + await createNewChat(ctx, { + id, + sessionId, + projectInitParams, + }); + }, +}); + +export const setUrlId = mutation({ + args: { + sessionId: v.id("sessions"), + chatId: v.string(), + urlHint: v.string(), + description: v.string(), + }, + returns: v.object({ + urlId: v.string(), + initialId: v.string(), + }), + handler: async (ctx, args) => { + const { chatId, urlHint, description } = args; + const existing = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: chatId, sessionId: args.sessionId }); + + if (!existing) { + throw new ConvexError({ code: "NotFound", message: "Chat not found" }); + } + if (existing.urlId === undefined) { + const urlId = await _allocateUrlId(ctx, { urlHint, sessionId: args.sessionId }); + await ctx.db.patch(existing._id, { + urlId, + description: existing.description ?? description, + }); + return { urlId, initialId: existing.initialId }; + } + return { urlId: existing.urlId, initialId: existing.initialId }; + }, +}); + +export const setDescription = mutation({ + args: { + sessionId: v.id("sessions"), + id: v.string(), + description: v.string(), + }, + returns: v.null(), + handler: async (ctx, args) => { + const { id, description } = args; + const existing = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id, sessionId: args.sessionId }); + + if (!existing) { + throw new ConvexError({ code: "NotFound", message: "Chat not found" }); + } + + await ctx.db.patch(existing._id, { + description, + }); + }, +}); + +export async function getChat(ctx: QueryCtx, id: string, sessionId: Id<"sessions">) { + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id, sessionId }); + + if (!chat) { + return null; + } + + // Don't send extra fields like `messages` or `creatorId` + return { + initialId: chat.initialId, + urlId: chat.urlId, + description: chat.description, + timestamp: chat.timestamp, + snapshotId: chat.snapshotId, + }; +} + +export const get = query({ + args: { + id: v.string(), + sessionId: v.id("sessions"), + }, + returns: v.union( + v.null(), + v.object({ + initialId: v.string(), + urlId: v.optional(v.string()), + description: v.optional(v.string()), + timestamp: v.string(), + snapshotId: v.optional(v.id("_storage")), + }), + ), + handler: async (ctx, args) => { + const { id, sessionId } = args; + return await getChat(ctx, id, sessionId); + }, +}); + +export async function getLatestChatMessageStorageState( + ctx: QueryCtx, + chat: { _id: Id<"chats">; lastMessageRank?: number }, +) { + const lastMessageRank = chat.lastMessageRank; + if (lastMessageRank === undefined) { + return await ctx.db + .query("chatMessagesStorageState") + .withIndex("byChatId", (q) => q.eq("chatId", chat._id)) + .order("desc") + .first(); + } + return await ctx.db + .query("chatMessagesStorageState") + .withIndex("byChatId", (q) => q.eq("chatId", chat._id).lte("lastMessageRank", lastMessageRank)) + .order("desc") + .first(); +} + +export const storageInfo = v.object({ + storageId: v.union(v.id("_storage"), v.null()), + lastMessageRank: v.number(), + partIndex: v.number(), + snapshotId: v.optional(v.id("_storage")), + subchatIndex: v.optional(v.number()), +}); + +export type StorageInfo = Infer; + +export const getInitialMessagesStorageInfo = internalQuery({ + args: { + sessionId: v.id("sessions"), + chatId: v.string(), + }, + returns: v.union(v.null(), storageInfo), + handler: async (ctx, args): Promise => { + const { chatId, sessionId } = args; + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: chatId, sessionId }); + if (!chat) { + return null; + } + const doc = await getLatestChatMessageStorageState(ctx, chat); + if (!doc) { + return null; + } + return { + storageId: doc.storageId, + lastMessageRank: doc.lastMessageRank, + partIndex: doc.partIndex, + snapshotId: doc.snapshotId, + subchatIndex: doc.subchatIndex, + }; + }, +}); + +// Used for debugging, and thus does not do access control checks +export const getMessagesByChatInitialIdBypassingAccessControl = internalQuery({ + args: { + id: v.string(), + ensureAdmin: v.optional(v.boolean()), + }, + returns: v.union(v.id("_storage"), v.null()), + handler: async (ctx, args) => { + if (args.ensureAdmin) { + await assertIsConvexAdmin(ctx); + } + const chat = await ctx.db + .query("chats") + .withIndex("byInitialId", (q) => q.eq("initialId", args.id)) + .unique(); + if (!chat) { + throw new ConvexError({ code: "NotFound", message: "Chat not found" }); + } + const storageInfo = await getLatestChatMessageStorageState(ctx, chat); + if (storageInfo === null) { + throw new ConvexError({ code: "NotFound", message: "Chat messages storage state not found" }); + } + return storageInfo.storageId; + }, +}); + +export const updateStorageState = internalMutation({ + args: { + sessionId: v.id("sessions"), + chatId: v.string(), + storageId: v.union(v.id("_storage"), v.null()), + lastMessageRank: v.number(), + subchatIndex: v.number(), + partIndex: v.number(), + snapshotId: v.optional(v.union(v.id("_storage"), v.null())), + }, + handler: async (ctx, args): Promise => { + const { chatId, storageId, lastMessageRank, partIndex, snapshotId, sessionId } = args; + const messageHistoryStorageId = storageId; + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: chatId, sessionId }); + if (!chat) { + throw new ConvexError({ code: "NotFound", message: "Chat not found" }); + } + await deletePreviousStorageStates(ctx, { chat }); + const previous = await getLatestChatMessageStorageState(ctx, chat); + if (!previous) { + throw new Error("Chat messages storage state not found"); + } + if (previous.lastMessageRank > lastMessageRank) { + console.warn( + `Stale update -- stored messages up to ${previous.lastMessageRank} but received update up to ${lastMessageRank}`, + ); + return; + } + if (previous.lastMessageRank === lastMessageRank && previous.partIndex > partIndex) { + console.warn( + `Stale update -- stored parts in message ${previous.lastMessageRank} up to part ${previous.partIndex} but received update up to part ${partIndex}`, + ); + return; + } + + if (previous.lastMessageRank === lastMessageRank && previous.partIndex === partIndex) { + if (messageHistoryStorageId !== null && snapshotId === null) { + // Should this error? + console.warn( + `Received duplicate update for message history, message ${lastMessageRank} part ${partIndex}, ignoring`, + ); + return; + } + if (snapshotId === null) { + throw new Error("Received null snapshotId for message that is already saved and has no storageId"); + } + await ctx.db.patch(previous._id, { + snapshotId, + }); + return; + } + + if (previous.storageId !== null && storageId === null) { + throw new Error("Received null storageId for a chat with messages"); + } + + if (previous.lastMessageRank === lastMessageRank) { + if (previous.partIndex >= partIndex) { + throw new Error("Tried to update to a part that is already stored. Should have already returned."); + } + // This is a part update, so we can patch instead of inserting a new document, cleaning up the old stored state. + // We do not support rewinding to parts. + if (previous.storageId !== null) { + await ctx.storage.delete(previous.storageId); + } + if (previous.snapshotId && previous.snapshotId !== snapshotId) { + await ctx.storage.delete(previous.snapshotId); + } + await ctx.db.patch(previous._id, { + storageId, + partIndex, + snapshotId: snapshotId ?? previous.snapshotId, + }); + return; + } + + await ctx.db.insert("chatMessagesStorageState", { + chatId: chat._id, + storageId, + lastMessageRank, + subchatIndex: args.subchatIndex, + partIndex, + // Should we be using null here to distinguish between not having a snapshot and records written before we also recorded snapshots here? + snapshotId: snapshotId ?? previous.snapshotId, + }); + }, +}); + +async function deletePreviousStorageStates( + ctx: MutationCtx, + args: { + chat: Doc<"chats">; + }, +) { + const { chat } = args; + const chatLastMessageRank = chat.lastMessageRank; + if (chatLastMessageRank !== undefined) { + // Remove the storage state records for future messages on a different branch + const storageStatesToDelete = await ctx.db + .query("chatMessagesStorageState") + .withIndex("byChatId", (q) => q.eq("chatId", chat._id).gt("lastMessageRank", chatLastMessageRank)) + .collect(); + for (const storageState of storageStatesToDelete) { + await ctx.db.delete(storageState._id); + const chatStorageId = storageState.storageId; + if (chatStorageId) { + const chatHistoryRef = await ctx.db + .query("chatMessagesStorageState") + .withIndex("byStorageId", (q) => q.eq("storageId", chatStorageId)) + .first(); + if (chatHistoryRef) { + // I don't think it's possible in the current data model to have a duplicate storageId + // here because there should not be duplicate rows for the same chatId, lastMessageRank, + // and partIndex. Newer snapshots should be patched. + console.warn("Unexpectedly found chatHistoryRef for storageId", chatStorageId); + } + const shareRef = await ctx.db + .query("shares") + .withIndex("byChatHistoryId", (q) => q.eq("chatHistoryId", chatStorageId)) + .first(); + if (shareRef === null && chatHistoryRef === null) { + await ctx.storage.delete(chatStorageId); + } + } + const snapshotId = storageState.snapshotId; + if (snapshotId) { + const chatHistoryRef = await ctx.db + .query("chatMessagesStorageState") + .withIndex("bySnapshotId", (q) => q.eq("snapshotId", snapshotId)) + .first(); + const shareRef = await ctx.db + .query("shares") + .withIndex("bySnapshotId", (q) => q.eq("snapshotId", snapshotId)) + .first(); + if (shareRef === null && chatHistoryRef === null) { + await ctx.storage.delete(snapshotId); + } + } + } + ctx.db.patch(chat._id, { lastMessageRank: undefined }); + } +} + +export const earliestRewindableMessageRank = query({ + args: { + sessionId: v.id("sessions"), + chatId: v.string(), + }, + // Return null if there is no snapshot stored in chatMessagesStorageState (possible for older chats) + returns: v.union(v.null(), v.number()), + handler: async (ctx, args): Promise => { + const { chatId, sessionId } = args; + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: chatId, sessionId }); + if (!chat) { + throw new ConvexError({ code: "NotFound", message: "Chat not found" }); + } + const latestState = await getLatestChatMessageStorageState(ctx, chat); + if (!latestState) { + // This is possible for older chats that were created before we started storing storage states + return null; + } + const docs = await ctx.db + .query("chatMessagesStorageState") + .withIndex("byChatId", (q) => q.eq("chatId", chat._id).lte("lastMessageRank", latestState.lastMessageRank)) + .order("asc") + .collect(); + + const docWithSnapshot = docs.find((doc) => doc.snapshotId !== undefined && doc.snapshotId !== null); + + if (!docWithSnapshot) { + return null; + } + return docWithSnapshot.lastMessageRank; + }, +}); + +export const rewindChat = mutation({ + args: { + sessionId: v.id("sessions"), + chatId: v.string(), + lastMessageRank: v.number(), + }, + handler: async (ctx, args): Promise => { + const { chatId, sessionId, lastMessageRank } = args; + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: chatId, sessionId }); + if (!chat) { + throw new ConvexError({ code: "NotFound", message: "Chat not found" }); + } + const latestStorageState = await getLatestChatMessageStorageState(ctx, { _id: chat._id, lastMessageRank }); + if (latestStorageState === null) { + throw new Error(`Storage state not found for lastMessageRank ${lastMessageRank} in chat ${chatId}`); + } + if (latestStorageState.storageId === null) { + throw new ConvexError({ code: "NoMessagesSaved", message: "Cannot rewind to a chat with no messages saved" }); + } + + if (chat.lastMessageRank !== undefined && chat.lastMessageRank < lastMessageRank) { + throw new ConvexError({ + code: "RewindToFuture", + message: "Cannot rewind to a future message", + data: { + lastMessageRank, + currentLastMessageRank: chat.lastMessageRank, + }, + }); + } + ctx.db.patch(chat._id, { lastMessageRank }); + }, +}); + +export const maybeCleanupStaleChatHistory = internalMutation({ + args: { + storageId: v.id("_storage"), + }, + handler: async (ctx, args): Promise => { + const chatRef = await ctx.db + .query("chatMessagesStorageState") + .withIndex("byStorageId", (q) => q.eq("storageId", args.storageId)) + .first(); + if (chatRef !== null) { + return; + } + + const shareRef = await ctx.db + .query("shares") + .withIndex("byChatHistoryId", (q) => q.eq("chatHistoryId", args.storageId)) + .first(); + if (shareRef !== null) { + return; + } + + await ctx.storage.delete(args.storageId); + }, +}); + +export const getAll = query({ + args: { + sessionId: v.id("sessions"), + }, + returns: v.array( + v.object({ + id: v.string(), + initialId: v.string(), + urlId: v.optional(v.string()), + description: v.optional(v.string()), + timestamp: v.string(), + }), + ), + handler: async (ctx, args) => { + const { sessionId } = args; + const results = await ctx.db + .query("chats") + .withIndex("byCreatorAndUrlId", (q) => q.eq("creatorId", sessionId)) + .filter((q) => q.neq(q.field("isDeleted"), true)) + .collect(); + + return results.map((result) => ({ + id: getIdentifier(result), + initialId: result.initialId, + urlId: result.urlId, + description: result.description, + timestamp: result.timestamp, + })); + }, +}); + +export const remove = action({ + args: { + sessionId: v.id("sessions"), + id: v.string(), + teamSlug: v.optional(v.string()), + projectSlug: v.optional(v.string()), + shouldDeleteConvexProject: v.boolean(), + accessToken: v.string(), + }, + handler: async (ctx, args) => { + const { accessToken, id, sessionId, teamSlug, projectSlug, shouldDeleteConvexProject } = args; + let projectDeletionResult: { kind: "success" } | { kind: "error"; error: string } = { kind: "success" }; + if (shouldDeleteConvexProject) { + projectDeletionResult = await tryDeleteProject({ teamSlug, projectSlug, accessToken }); + } + + await ctx.runMutation(internal.messages.removeChat, { + id, + sessionId, + }); + + if (projectDeletionResult.kind === "error") { + return { + kind: "error", + error: `Deleted chat, but failed to delete linked Convex project:\n${projectDeletionResult.error}`, + }; + } + return { kind: "success" }; + }, +}); + +async function tryDeleteProject(args: { + teamSlug: string | undefined; + projectSlug: string | undefined; + accessToken: string; +}): Promise<{ kind: "success" } | { kind: "error"; error: string }> { + const { teamSlug, projectSlug, accessToken } = args; + if (teamSlug === undefined || projectSlug === undefined) { + return { kind: "error", error: "Team slug and project slug are required to delete a Convex project" }; + } + + const bigBrainHost = ensureEnvVar("BIG_BRAIN_HOST"); + + const projectsResponse = await fetch(`${bigBrainHost}/api/teams/${teamSlug}/projects`, { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }); + + if (!projectsResponse.ok) { + const text = await projectsResponse.text(); + try { + const error = JSON.parse(text); + if (error.code === "TeamNotFound") { + return { kind: "error", error: `Team not found: ${teamSlug}` }; + } + return { kind: "error", error: `Failed to fetch team projects: ${projectsResponse.statusText} ${text}` }; + } catch (_e) { + return { kind: "error", error: `Failed to fetch team projects: ${projectsResponse.statusText} ${text}` }; + } + } + + const projects = await projectsResponse.json(); + const project = projects.find((p: any) => p.slug === projectSlug); + + if (project) { + const response = await fetch(`${bigBrainHost}/api/dashboard/delete_project/${project.id}`, { + method: "POST", + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }); + + if (!response.ok) { + const text = await response.text(); + return { kind: "error", error: `Failed to delete project: ${response.statusText} ${text}` }; + } + } + + return { kind: "success" }; +} + +export const removeChat = internalMutation({ + args: { + id: v.string(), + sessionId: v.id("sessions"), + }, + handler: async (ctx, args) => { + const existing = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: args.id, sessionId: args.sessionId }); + + if (!existing) { + return; + } + + const convexProject = existing.convexProject; + if (convexProject !== undefined && convexProject.kind === "connected") { + const credentials = await ctx.db + .query("convexProjectCredentials") + .withIndex("bySlugs", (q) => + q.eq("teamSlug", convexProject.teamSlug).eq("projectSlug", convexProject.projectSlug), + ) + .unique(); + if (credentials !== null) { + await ctx.db.delete(credentials._id); + } + } + await ctx.db.patch(existing._id, { + isDeleted: true, + }); + }, +}); + +async function _allocateUrlId(ctx: QueryCtx, { urlHint, sessionId }: { urlHint: string; sessionId: Id<"sessions"> }) { + const existing = await getChatByUrlId(ctx, { id: urlHint, sessionId }); + + if (existing === null) { + return urlHint; + } + + let i = 2; + + while (true) { + const newUrlId = `${urlHint}-${i}`; + + const m = await getChatByUrlId(ctx, { id: newUrlId, sessionId }); + + if (m === null) { + return newUrlId; + } + + i++; + } +} + +export async function createNewChat( + ctx: MutationCtx, + args: { + id: string; + sessionId: Id<"sessions">; + projectInitParams?: { + teamSlug: string; + auth0AccessToken: string; + }; + }, +): Promise> { + const { id, sessionId, projectInitParams } = args; + const existing = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id, sessionId }); + + if (existing) { + throw new ConvexError({ code: "InvalidState", message: "Chat already exists" }); + } + + const session = await ctx.db.get(sessionId); + if (!session) { + throw new Error(`Invalid state -- session should exist: ${sessionId}`); + } + + const chatId = await ctx.db.insert("chats", { + creatorId: sessionId, + initialId: id, + timestamp: new Date().toISOString(), + isDeleted: false, + lastSubchatIndex: 0, + }); + await ctx.db.insert("chatMessagesStorageState", { + chatId, + storageId: null, + lastMessageRank: -1, + subchatIndex: 0, + partIndex: -1, + }); + + await startProvisionConvexProjectHelper(ctx, { + sessionId, + chatId: id, + projectInitParams, + }); + + return chatId; +} + +function getChatByInitialId(ctx: QueryCtx, { id, sessionId }: { id: string; sessionId: Id<"sessions"> }) { + return ctx.db + .query("chats") + .withIndex("byCreatorAndId", (q) => q.eq("creatorId", sessionId).eq("initialId", id).lt("isDeleted", true)) + .unique(); +} + +function getChatByUrlId(ctx: QueryCtx, { id, sessionId }: { id: string; sessionId: Id<"sessions"> }) { + return ctx.db + .query("chats") + .withIndex("byCreatorAndUrlId", (q) => q.eq("creatorId", sessionId).eq("urlId", id).lt("isDeleted", true)) + .unique(); +} + +export async function getChatByIdOrUrlIdEnsuringAccess( + ctx: QueryCtx, + { id, sessionId }: { id: string; sessionId: Id<"sessions"> }, +) { + const isValid = await isValidSession(ctx, { sessionId }); + if (!isValid) { + return null; + } + + const byId = await getChatByInitialId(ctx, { id, sessionId }); + if (byId !== null) { + return byId; + } + + const byUrlId = await getChatByUrlId(ctx, { id, sessionId }); + return byUrlId; +} + +function getIdentifier(chat: Doc<"chats">): string { + return chat.urlId ?? chat.initialId!; +} + +export const eraseMessageHistory = internalMutation({ + args: { + shareCode: v.string(), + dryRun: v.boolean(), + }, + handler: async (ctx, args) => { + const { shareCode, dryRun } = args; + const share = await ctx.db + .query("socialShares") + .withIndex("byCode", (q) => q.eq("code", shareCode)) + .unique(); + if (share === null) { + throw new ConvexError({ code: "NotFound", message: "Share not found" }); + } + console.log("ChatId for share is", share.chatId); + // Get the most recent filesystem snapshot + const mostRecentStorageState = await ctx.db + .query("chatMessagesStorageState") + .withIndex("byChatId", (q) => q.eq("chatId", share.chatId)) + .order("desc") + .first(); + if (mostRecentStorageState === null) { + throw new ConvexError({ code: "NotFound", message: "No storage state found for chat" }); + } + console.log("Most recent storage state is", mostRecentStorageState); + const mostRecentFilesystemSnapshot = mostRecentStorageState.snapshotId; + if (mostRecentFilesystemSnapshot === undefined) { + throw new ConvexError({ code: "NotFound", message: "No filesystem snapshot found for chat" }); + } + const earliestMessages = await ctx.db + .query("chatMessagesStorageState") + .withIndex("byChatId", (q) => q.eq("chatId", share.chatId)) + .order("asc") + .take(100); + let earliestMessageWithSnapshot: Doc<"chatMessagesStorageState"> | null = null; + for (const storageState of earliestMessages) { + if (storageState.snapshotId !== undefined) { + earliestMessageWithSnapshot = storageState; + break; + } + } + if (earliestMessageWithSnapshot === null) { + throw new ConvexError({ + code: "NotFound", + message: "No message with snapshot found for chat after looking at the first 100 message states", + }); + } + + // Get the latest part with the same lastMessageRank + const latestEarlyStorageState = await ctx.db + .query("chatMessagesStorageState") + .withIndex("byChatId", (q) => + q.eq("chatId", share.chatId).eq("lastMessageRank", earliestMessageWithSnapshot.lastMessageRank), + ) + .order("desc") + .first(); + if (latestEarlyStorageState === null) { + throw new ConvexError({ + code: "NotFound", + message: `No storage state found with lastMessageRank ${earliestMessageWithSnapshot.lastMessageRank}`, + }); + } + + // Replace the latestEarlyStorageState's filesystem snapshot with the most recent filesystem snapshot + console.log( + "Replacing filesystem snapshot for chat from", + latestEarlyStorageState.snapshotId, + "to", + mostRecentFilesystemSnapshot, + ); + if (!dryRun) { + await ctx.db.patch(latestEarlyStorageState._id, { snapshotId: mostRecentFilesystemSnapshot }); + } + + // Rewind the chat to look at the lastMessageRank of the earliestMessageWithSnapshot + console.log("Rewinding chat to lastMessageRank", earliestMessageWithSnapshot.lastMessageRank); + if (!dryRun) { + await ctx.db.patch(share.chatId, { + lastMessageRank: earliestMessageWithSnapshot.lastMessageRank, + }); + } + + if (dryRun) { + console.log("Dry run, did not update chat or filesystem snapshot"); + } + }, +}); diff --git a/src/test/fake_chef/migrations.ts b/src/test/fake_chef/migrations.ts new file mode 100644 index 00000000..1ca5cb80 --- /dev/null +++ b/src/test/fake_chef/migrations.ts @@ -0,0 +1,49 @@ +import { Migrations } from "@convex-dev/migrations"; +import { components, internal } from "./_generated/api.js"; + +export const migrations = new Migrations(components.migrations); +export const run = migrations.runner(); + +export const setDefaultDeletedFalse = migrations.define({ + table: "chats", + migrateOne: async (ctx, doc) => { + if (doc.isDeleted === undefined) { + await ctx.db.patch(doc._id, { isDeleted: false }); + } + }, +}); + +export const runSetDefaultDeletedFalse = migrations.runner(internal.migrations.setDefaultDeletedFalse); + +export const addLastSubchatIndex = migrations.define({ + table: "chats", + migrateOne: async (ctx, doc) => { + if (doc.lastSubchatIndex === undefined) { + await ctx.db.patch(doc._id, { lastSubchatIndex: 0 }); + } + }, +}); + +export const runAddLastSubchatIndex = migrations.runner(internal.migrations.addLastSubchatIndex); + +export const addSubchatIndex = migrations.define({ + table: "chatMessagesStorageState", + migrateOne: async (ctx, doc) => { + if (doc.subchatIndex === undefined) { + await ctx.db.patch(doc._id, { subchatIndex: 0 }); + } + }, +}); + +export const runAddSubchatIndex = migrations.runner(internal.migrations.addSubchatIndex); + +export const addLastSubchatIndexToShares = migrations.define({ + table: "shares", + migrateOne: async (ctx, doc) => { + if (doc.lastSubchatIndex === undefined) { + await ctx.db.patch(doc._id, { lastSubchatIndex: 0 }); + } + }, +}); + +export const runAddLastSubchatIndexToShares = migrations.runner(internal.migrations.addLastSubchatIndexToShares); diff --git a/src/test/fake_chef/openaiProxy.ts b/src/test/fake_chef/openaiProxy.ts new file mode 100644 index 00000000..510d4b48 --- /dev/null +++ b/src/test/fake_chef/openaiProxy.ts @@ -0,0 +1,178 @@ +import { v } from "convex/values"; +import { httpAction, internalMutation, mutation } from "./_generated/server"; +import { getCurrentMember } from "./sessions"; +import { internal } from "./_generated/api"; + +const ALLOWED_MODELS = ["gpt-4o-mini", "gpt-4.1-nano"]; + +export const openaiProxy = httpAction(async (ctx, req) => { + if (!openaiProxyEnabled()) { + return new Response("Convex OpenAI proxy is disabled.", { status: 400 }); + } + if (!process.env.OPENAI_API_KEY) { + throw new Error("OPENAI_API_KEY is not set"); + } + const headers = new Headers(req.headers); + const authHeader = headers.get("Authorization"); + if (!authHeader) { + return new Response("Unauthorized", { status: 401 }); + } + if (!authHeader.startsWith("Bearer ")) { + return new Response("Invalid authorization header", { status: 401 }); + } + const token = authHeader.slice(7); + const result = await ctx.runMutation(internal.openaiProxy.decrementToken, { token }); + if (!result.success) { + return new Response(result.error, { status: 401 }); + } + + const url = new URL(req.url); + if (url.pathname != "/openai-proxy/chat/completions") { + return new Response("Only the /chat/completions API is supported", { status: 400 }); + } + + let body: any; + try { + body = await req.json(); + } catch (_error) { + return new Response("Invalid request body", { status: 400 }); + } + if (!ALLOWED_MODELS.includes(body.model)) { + return new Response("Only gpt-4o-mini and gpt-4.1-nano are supported", { status: 400 }); + } + + if (body.max_completion_tokens && body.max_completion_tokens > 16384) { + return new Response("max_completion_tokens must be <= 16384", { status: 400 }); + } + if (body.max_tokens && body.max_tokens > 16384) { + return new Response("max_tokens must be <= 16384", { status: 400 }); + } + if (body.service_tier !== undefined) { + return new Response("service_tier is not supported", { status: 400 }); + } + if (body.store !== undefined) { + return new Response("store is not supported", { status: 400 }); + } + if (body.web_search_options !== undefined) { + return new Response("web_search_options is not supported", { status: 400 }); + } + // Pin allowed fields from https://platform.openai.com/docs/api-reference/chat/create on 2025-04-14. + const proxiedBody = { + messages: body.messages, + model: body.model, + audio: body.audio, + frequency_penalty: body.frequency_penalty, + function_call: body.function_call, + functions: body.functions, + logit_bias: body.logit_bias, + logprobs: body.logprobs, + max_completion_tokens: body.max_completion_tokens, + max_tokens: body.max_tokens, + metadata: body.metadata, + modalities: body.modalities, + n: body.n, + parallel_tool_calls: body.parallel_tool_calls, + prediction: body.prediction, + presence_penalty: body.presence_penalty, + reasoning_effort: body.reasoning_effort, + response_format: body.response_format, + seed: body.seed, + stop: body.stop, + stream: body.stream, + stream_options: body.stream_options, + temperature: body.temperature, + tool_choice: body.tool_choice, + tools: body.tools, + top_logprobs: body.top_logprobs, + top_p: body.top_p, + user: body.user, + }; + const response = await fetch("https://api.openai.com/v1/chat/completions", { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${process.env.OPENAI_API_KEY}`, + }, + body: JSON.stringify(proxiedBody), + }); + return response; +}); + +export const issueOpenAIToken = mutation({ + handler: async (ctx) => { + if (!openaiProxyEnabled()) { + return null; + } + const member = await getCurrentMember(ctx); + if (!member) { + console.error("Not authorized", member); + return null; + } + const existing = await ctx.db + .query("memberOpenAITokens") + .withIndex("byMemberId", (q) => q.eq("memberId", member._id)) + .unique(); + if (existing) { + return existing.token; + } + const token = crypto.randomUUID(); + await ctx.db.insert("memberOpenAITokens", { + memberId: member._id, + token, + requestsRemaining: includedRequests(), + lastUsedTime: 0, + }); + return token; + }, +}); + +export const decrementToken = internalMutation({ + args: { + token: v.string(), + }, + handler: async (ctx, args) => { + if (!openaiProxyEnabled()) { + return { success: false, error: "Convex OpenAI proxy is disabled." }; + } + const token = await ctx.db + .query("memberOpenAITokens") + .withIndex("byToken", (q) => q.eq("token", args.token)) + .unique(); + if (!token) { + return { success: false, error: "Invalid OPENAI_API_TOKEN" }; + } + if (token.requestsRemaining <= 0) { + return { + success: false, + error: + "Convex OPENAI_API_TOKEN has no requests remaining. Go sign up for an OpenAI API key at https://platform.openai.com and update your app to use that.", + }; + } + await ctx.db.patch(token._id, { + requestsRemaining: token.requestsRemaining - 1, + lastUsedTime: Date.now(), + }); + return { success: true }; + }, +}); + +// Cost per gpt-4o-mini request (2025-04-09): +// 16384 max output tokens @ $0.6/1M +// 128K max input tokens @ $0.15/1M +// => ~$0.03 per request. +// +// Cost per gpt-4.1-nano request (2025-04-14): +// output tokens: $0.40/1M +// input tokens: $0.10/1M +function includedRequests() { + const fromEnv = process.env.OPENAI_PROXY_INCLUDED_REQUESTS; + if (!fromEnv) { + return 100; + } + return Number(fromEnv); +} + +function openaiProxyEnabled() { + const fromEnv = process.env.OPENAI_PROXY_ENABLED; + return fromEnv && fromEnv == "1"; +} diff --git a/src/test/fake_chef/package.json b/src/test/fake_chef/package.json new file mode 100644 index 00000000..b704bc4e --- /dev/null +++ b/src/test/fake_chef/package.json @@ -0,0 +1,13 @@ +{ + "name": "fake-chef", + "engines": { + "node": ">=18.18.0" + }, + "dependencies": { + "@convex-dev/migrations": "^0.2.7", + "@convex-dev/rate-limiter": "^0.2.6", + "ai": "^4.3.19", + "convex": "link:../../..", + "convex-helpers": "^0.1.79" + } +} diff --git a/src/test/fake_chef/pnpm-lock.yaml b/src/test/fake_chef/pnpm-lock.yaml new file mode 100644 index 00000000..6a7f626a --- /dev/null +++ b/src/test/fake_chef/pnpm-lock.yaml @@ -0,0 +1,260 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@convex-dev/migrations': + specifier: ^0.2.7 + version: 0.2.9(convex@..) + '@convex-dev/rate-limiter': + specifier: ^0.2.6 + version: 0.2.12(convex@..)(react@19.1.0) + ai: + specifier: ^4.3.19 + version: 4.3.19(react@19.1.0)(zod@3.25.76) + convex: + specifier: link:../../.. + version: link:../../.. + convex-helpers: + specifier: ^0.1.79 + version: 0.1.100(convex@..)(react@19.1.0)(zod@3.25.76) + +packages: + + '@ai-sdk/provider-utils@2.2.8': + resolution: {integrity: sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.23.8 + + '@ai-sdk/provider@1.1.3': + resolution: {integrity: sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg==} + engines: {node: '>=18'} + + '@ai-sdk/react@1.2.12': + resolution: {integrity: sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g==} + engines: {node: '>=18'} + peerDependencies: + react: ^18 || ^19 || ^19.0.0-rc + zod: ^3.23.8 + peerDependenciesMeta: + zod: + optional: true + + '@ai-sdk/ui-utils@1.2.11': + resolution: {integrity: sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.23.8 + + '@convex-dev/migrations@0.2.9': + resolution: {integrity: sha512-HfIv6/F8VbtJ1NVT2bQRTumJGdaqROl/+s5IOnubHm7vvgie0RjcfO6YiB7JoLFWwS19vjDQDKl63ihoA08zOA==} + peerDependencies: + convex: ~1.16.5 || >=1.17.0 <1.35.0 + + '@convex-dev/rate-limiter@0.2.12': + resolution: {integrity: sha512-Z7YXDSPa28/8eqVJYUWQBwK9hTiJcXfWY2qZ44Im3VpuBFJZCqYjem7QWjvoTlBjVGqK7FKw26xRneei09hm2w==} + peerDependencies: + convex: ~1.16.5 || >=1.17.0 <1.35.0 + react: ^18.3.1 || ^19.0.0 + peerDependenciesMeta: + react: + optional: true + + '@opentelemetry/api@1.9.0': + resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} + engines: {node: '>=8.0.0'} + + '@types/diff-match-patch@1.0.36': + resolution: {integrity: sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==} + + ai@4.3.19: + resolution: {integrity: sha512-dIE2bfNpqHN3r6IINp9znguYdhIOheKW2LDigAMrgt/upT3B8eBGPSCblENvaZGoq+hxaN9fSMzjWpbqloP+7Q==} + engines: {node: '>=18'} + peerDependencies: + react: ^18 || ^19 || ^19.0.0-rc + zod: ^3.23.8 + peerDependenciesMeta: + react: + optional: true + + chalk@5.4.1: + resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + convex-helpers@0.1.100: + resolution: {integrity: sha512-In5VPKKlQdnv9WHpIKLXnLEKyHxaJYI0BXWTDJ27Ao6XUfpeiGPDGhOdqZ8y0DqTcQlDgmtSTmmttUv1fPWbdg==} + hasBin: true + peerDependencies: + '@standard-schema/spec': ^1.0.0 + convex: ^1.13.0 + hono: ^4.0.5 + react: ^17.0.2 || ^18.0.0 || ^19.0.0 + typescript: ^5.5 + zod: ^3.22.4 + peerDependenciesMeta: + '@standard-schema/spec': + optional: true + hono: + optional: true + react: + optional: true + typescript: + optional: true + zod: + optional: true + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + diff-match-patch@1.0.5: + resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==} + + json-schema@0.4.0: + resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} + + jsondiffpatch@0.6.0: + resolution: {integrity: sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + react@19.1.0: + resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==} + engines: {node: '>=0.10.0'} + + secure-json-parse@2.7.0: + resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} + + swr@2.3.4: + resolution: {integrity: sha512-bYd2lrhc+VarcpkgWclcUi92wYCpOgMws9Sd1hG1ntAu0NEy+14CbotuFjshBU2kt9rYj9TSmDcybpxpeTU1fg==} + peerDependencies: + react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + throttleit@2.1.0: + resolution: {integrity: sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==} + engines: {node: '>=18'} + + use-sync-external-store@1.5.0: + resolution: {integrity: sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + zod-to-json-schema@3.24.6: + resolution: {integrity: sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==} + peerDependencies: + zod: ^3.24.1 + + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + +snapshots: + + '@ai-sdk/provider-utils@2.2.8(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 1.1.3 + nanoid: 3.3.11 + secure-json-parse: 2.7.0 + zod: 3.25.76 + + '@ai-sdk/provider@1.1.3': + dependencies: + json-schema: 0.4.0 + + '@ai-sdk/react@1.2.12(react@19.1.0)(zod@3.25.76)': + dependencies: + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) + '@ai-sdk/ui-utils': 1.2.11(zod@3.25.76) + react: 19.1.0 + swr: 2.3.4(react@19.1.0) + throttleit: 2.1.0 + optionalDependencies: + zod: 3.25.76 + + '@ai-sdk/ui-utils@1.2.11(zod@3.25.76)': + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) + zod: 3.25.76 + zod-to-json-schema: 3.24.6(zod@3.25.76) + + '@convex-dev/migrations@0.2.9(convex@..)': + dependencies: + convex: link:../../.. + + '@convex-dev/rate-limiter@0.2.12(convex@..)(react@19.1.0)': + dependencies: + convex: link:../../.. + optionalDependencies: + react: 19.1.0 + + '@opentelemetry/api@1.9.0': {} + + '@types/diff-match-patch@1.0.36': {} + + ai@4.3.19(react@19.1.0)(zod@3.25.76): + dependencies: + '@ai-sdk/provider': 1.1.3 + '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) + '@ai-sdk/react': 1.2.12(react@19.1.0)(zod@3.25.76) + '@ai-sdk/ui-utils': 1.2.11(zod@3.25.76) + '@opentelemetry/api': 1.9.0 + jsondiffpatch: 0.6.0 + zod: 3.25.76 + optionalDependencies: + react: 19.1.0 + + chalk@5.4.1: {} + + convex-helpers@0.1.100(convex@..)(react@19.1.0)(zod@3.25.76): + dependencies: + convex: link:../../.. + optionalDependencies: + react: 19.1.0 + zod: 3.25.76 + + dequal@2.0.3: {} + + diff-match-patch@1.0.5: {} + + json-schema@0.4.0: {} + + jsondiffpatch@0.6.0: + dependencies: + '@types/diff-match-patch': 1.0.36 + chalk: 5.4.1 + diff-match-patch: 1.0.5 + + nanoid@3.3.11: {} + + react@19.1.0: {} + + secure-json-parse@2.7.0: {} + + swr@2.3.4(react@19.1.0): + dependencies: + dequal: 2.0.3 + react: 19.1.0 + use-sync-external-store: 1.5.0(react@19.1.0) + + throttleit@2.1.0: {} + + use-sync-external-store@1.5.0(react@19.1.0): + dependencies: + react: 19.1.0 + + zod-to-json-schema@3.24.6(zod@3.25.76): + dependencies: + zod: 3.25.76 + + zod@3.25.76: {} diff --git a/src/test/fake_chef/rateLimiter.ts b/src/test/fake_chef/rateLimiter.ts new file mode 100644 index 00000000..c9cba295 --- /dev/null +++ b/src/test/fake_chef/rateLimiter.ts @@ -0,0 +1,18 @@ +import RateLimiter, { MINUTE } from "@convex-dev/rate-limiter"; +import { components } from "./_generated/api"; + +function resendProxyEmailsPerMinute() { + const fromEnv = process.env.RESEND_PROXY_EMAILS_PER_MINUTE; + return fromEnv ? parseInt(fromEnv) : 20; +} + +export const rateLimiter = new RateLimiter(components.rateLimiter, { + resendProxy: { + kind: "token bucket", + // Permit 20 requests per minute => ~900k emails per month => ~$650/month on Resend's scale plan. + rate: resendProxyEmailsPerMinute(), + period: MINUTE, + // Allow accumulating at most one minute's worth of bursts. + capacity: resendProxyEmailsPerMinute(), + }, +}); diff --git a/src/test/fake_chef/resendProxy.ts b/src/test/fake_chef/resendProxy.ts new file mode 100644 index 00000000..2234ade2 --- /dev/null +++ b/src/test/fake_chef/resendProxy.ts @@ -0,0 +1,184 @@ +import { v } from "convex/values"; +import { httpAction, internalMutation, mutation } from "./_generated/server"; +import { getCurrentMember } from "./sessions"; +import { internal } from "./_generated/api"; +import { rateLimiter } from "./rateLimiter"; + +const MAX_RATELIMITER_WAIT = 60 * 1000; + +export const resendProxy = httpAction(async (ctx, req) => { + if (!resendProxyEnabled()) { + return new Response(JSON.stringify("Convex Resend proxy is disabled."), { status: 400 }); + } + if (!process.env.RESEND_API_KEY) { + throw new Error("RESEND_API_KEY is not set"); + } + + const url = new URL(req.url); + if (url.pathname != "/resend-proxy/emails") { + return new Response(JSON.stringify("Only the /emails API is supported"), { status: 400 }); + } + + const headers = new Headers(req.headers); + const body = await req.json(); + + let recipientEmail: string; + if (typeof body.to === "string") { + recipientEmail = body.to; + } else { + if (!Array.isArray(body.to) || body.to.length !== 1) { + return new Response(JSON.stringify("Convex Resend proxy only supports one recipient."), { status: 400 }); + } + recipientEmail = body.to[0]; + } + + if (body.bcc || body.cc) { + return new Response(JSON.stringify("Convex Resend proxy does not support bcc or cc."), { status: 400 }); + } + + if (body.scheduled_at) { + return new Response(JSON.stringify("Convex Resend proxy does not support scheduled emails."), { status: 400 }); + } + + if (body.headers) { + return new Response(JSON.stringify("Convex Resend proxy does not support custom headers."), { status: 400 }); + } + + const authHeader = headers.get("Authorization"); + if (!authHeader) { + return new Response(JSON.stringify("Unauthorized"), { status: 401 }); + } + if (!authHeader.startsWith("Bearer ")) { + return new Response(JSON.stringify("Invalid authorization header"), { status: 401 }); + } + const token = authHeader.slice(7); + const result = await ctx.runMutation(internal.resendProxy.decrementToken, { token, recipientEmail }); + if (!result.success) { + return new Response(JSON.stringify(result.error), { status: 401 }); + } + + // Wait for the rate limiter once we've passed validation. + let waitStart = Date.now(); + let deadline = waitStart + MAX_RATELIMITER_WAIT; + while (true) { + const status = await rateLimiter.limit(ctx, "resendProxy"); + if (status.ok) { + break; + } + const now = Date.now(); + if (now > deadline) { + return new Response(JSON.stringify("Rate limit exceeded"), { status: 429 }); + } + const remainingTime = deadline - now; + const waitTime = Math.min(status.retryAfter * (1 + Math.random()), remainingTime); + console.warn(`Rate limit exceeded, waiting ${waitTime}ms`); + await new Promise((resolve) => setTimeout(resolve, waitTime)); + } + + const deploymentName = process.env.CONVEX_CLOUD_URL?.replace("https://", "").replace(".convex.cloud", ""); + return await fetch("https://api.resend.com/emails", { + method: "POST", + headers: { + Authorization: `Bearer ${process.env.RESEND_API_KEY}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + from: `Chef Notifications <${deploymentName}@convexchef.app>`, + to: recipientEmail, + subject: body.subject, + html: body.html, + scheduled_at: body.scheduled_at, + reply_to: body.reply_to, + text: body.text, + attachments: body.attachments, + tags: body.tags, + }), + }); +}); + +export const decrementToken = internalMutation({ + args: { + token: v.string(), + recipientEmail: v.string(), + }, + handler: async (ctx, args) => { + if (!resendProxyEnabled()) { + return { success: false, error: "Convex Resend proxy is disabled." }; + } + const token = await ctx.db + .query("resendTokens") + .withIndex("byToken", (q) => q.eq("token", args.token)) + .unique(); + if (!token) { + return { success: false, error: "Invalid RESEND_API_TOKEN" }; + } + if (token.requestsRemaining <= 0) { + return { success: false, error: "Resend API token has no requests remaining." }; + } + if (token.verifiedEmail !== args.recipientEmail) { + return { + success: false, + error: `The Convex Resend API Proxy only supports sending email to your own verified email address (${token.verifiedEmail}).`, + }; + } + await ctx.db.patch(token._id, { + requestsRemaining: token.requestsRemaining - 1, + lastUsedTime: Date.now(), + }); + return { success: true }; + }, +}); + +export const issueResendToken = mutation({ + handler: async (ctx) => { + if (!resendProxyEnabled()) { + return null; + } + const member = await getCurrentMember(ctx); + if (!member) { + console.error("Not authorized", member); + return null; + } + const identity = await ctx.auth.getUserIdentity(); + if (!identity) { + console.error("Not authorized", identity); + return null; + } + if (!identity.email || !identity.emailVerified) { + console.error("User has no email or email is not verified", identity); + return null; + } + const existing = await ctx.db + .query("resendTokens") + .withIndex("byMemberId", (q) => q.eq("memberId", member._id)) + .unique(); + if (existing) { + if (existing.verifiedEmail !== identity.email) { + await ctx.db.patch(existing._id, { + verifiedEmail: identity.email, + }); + } + return existing.token; + } + const token = crypto.randomUUID(); + await ctx.db.insert("resendTokens", { + memberId: member._id, + token, + verifiedEmail: identity.email, + requestsRemaining: includedRequests(), + lastUsedTime: 0, + }); + return token; + }, +}); + +function resendProxyEnabled() { + const fromEnv = process.env.RESEND_PROXY_ENABLED; + return fromEnv && fromEnv == "1"; +} + +function includedRequests() { + const fromEnv = process.env.RESEND_INCLUDED_REQUESTS; + // Resend prices $0.06 for 100 emails. + return fromEnv ? parseInt(fromEnv) : 100; +} diff --git a/src/test/fake_chef/schema.ts b/src/test/fake_chef/schema.ts new file mode 100644 index 00000000..9ceb3be4 --- /dev/null +++ b/src/test/fake_chef/schema.ts @@ -0,0 +1,224 @@ +import { defineSchema, defineTable } from "convex/server"; +import { v } from "convex/values"; +import type { Infer, Validator } from "convex/values"; +import type { CoreMessage } from "ai"; + +export const apiKeyValidator = v.object({ + preference: v.union(v.literal("always"), v.literal("quotaExhausted")), + // NB: This is the *Anthropic* API key. + value: v.optional(v.string()), + openai: v.optional(v.string()), + xai: v.optional(v.string()), + google: v.optional(v.string()), +}); + +// A stable-enough way to store token usage. +export const usageRecordValidator = v.object({ + completionTokens: v.number(), + promptTokens: v.number(), + /** Included in promptTokens total! */ + cachedPromptTokens: v.number(), +}); + +export type UsageRecord = Infer; + +export default defineSchema({ + /* + * We create a session (if it does not exist) and store the ID in local storage. + * We only show chats for the current session, so we rely on the session ID being + * unguessable (i.e. we should never list session IDs or return them in function + * results). + */ + sessions: defineTable({ + // When auth-ing with convex.dev, we'll save a `convexMembers` document and + // reference it here. + memberId: v.optional(v.id("convexMembers")), + }).index("byMemberId", ["memberId"]), + + convexMembers: defineTable({ + tokenIdentifier: v.string(), + apiKey: v.optional(apiKeyValidator), + // Not authoritative, just a cache of the user's profile from Auth0/provision host. + cachedProfile: v.optional( + v.object({ + username: v.string(), + avatar: v.string(), + email: v.string(), + id: v.string(), + }), + ), + }).index("byTokenIdentifier", ["tokenIdentifier"]), + + /* + * Admin status means being on the convex team on the provision host. + * It doesn't work when using a local big brain (provision host). + */ + convexAdmins: defineTable({ + convexMemberId: v.id("convexMembers"), // should be unique + lastCheckedForAdminStatus: v.number(), + wasAdmin: v.boolean(), + }).index("byConvexMemberId", ["convexMemberId"]), + + /* + * All chats have two IDs -- an `initialId` that is always set (UUID) and a `urlId` + * that is more human friendly (e.g. "tic-tac-toe"). + * The `urlId` is set based on the LLM messages so is initially unset. + * Both `initialId` and `urlId` should be unique within the creatorId, all functions + * should accept either `initialId` or `urlId`, and when returning an identifier, + * we should prefer `urlId` if it is set. + */ + chats: defineTable({ + creatorId: v.id("sessions"), + initialId: v.string(), + urlId: v.optional(v.string()), + description: v.optional(v.string()), + timestamp: v.string(), + metadata: v.optional(v.any()), // TODO migration to remove this column + snapshotId: v.optional(v.id("_storage")), + lastMessageRank: v.optional(v.number()), + lastSubchatIndex: v.optional(v.number()), + hasBeenDeployed: v.optional(v.boolean()), + isDeleted: v.optional(v.boolean()), + convexProject: v.optional( + v.union( + v.object({ + kind: v.literal("connected"), + projectSlug: v.string(), + teamSlug: v.string(), + // for this member's dev deployment + deploymentUrl: v.string(), + deploymentName: v.string(), + warningMessage: v.optional(v.string()), + }), + v.object({ + kind: v.literal("connecting"), + checkConnectionJobId: v.optional(v.id("_scheduled_functions")), + }), + v.object({ + kind: v.literal("failed"), + errorMessage: v.string(), + }), + ), + ), + }) + .index("byCreatorAndId", ["creatorId", "initialId", "isDeleted"]) + .index("byCreatorAndUrlId", ["creatorId", "urlId", "isDeleted"]) + .index("bySnapshotId", ["snapshotId"]) + .index("byInitialId", ["initialId", "isDeleted"]), + + convexProjectCredentials: defineTable({ + projectSlug: v.string(), + teamSlug: v.string(), + memberId: v.optional(v.id("convexMembers")), + projectDeployKey: v.string(), + }).index("bySlugs", ["teamSlug", "projectSlug"]), + chatMessagesStorageState: defineTable({ + chatId: v.id("chats"), + storageId: v.union(v.id("_storage"), v.null()), + subchatIndex: v.optional(v.number()), + lastMessageRank: v.number(), + description: v.optional(v.string()), + partIndex: v.number(), + snapshotId: v.optional(v.id("_storage")), + }) + .index("byChatId", ["chatId", "lastMessageRank", "partIndex"]) + .index("byStorageId", ["storageId"]) + .index("bySnapshotId", ["snapshotId"]), + + // This type of share is for forking from a specific point in time. + // Call it a debugging snapshot or a fork point. There can be multiple per chat. + // The main thing they are used for is forking a project at a set point + // into another user's account. + shares: defineTable({ + chatId: v.id("chats"), + snapshotId: v.optional(v.id("_storage")), + code: v.string(), + + chatHistoryId: v.optional(v.union(v.id("_storage"), v.null())), + + // Shares are created at one point in time, so this makes sure + // people using the link don't see newer messages. + lastMessageRank: v.number(), + + // This should not be optional, but we need to migrate it. + lastSubchatIndex: v.optional(v.number()), + partIndex: v.optional(v.number()), + // The description of the chat at the time the share was created. + description: v.optional(v.string()), + }) + .index("byCode", ["code"]) + .index("bySnapshotId", ["snapshotId"]) + .index("byChatHistoryId", ["chatHistoryId"]) + .index("byChatId", ["chatId"]), + + // This type of share is for sharing a "project." + // You only get one for a given project for now. + socialShares: defineTable({ + chatId: v.id("chats"), + code: v.string(), + thumbnailImageStorageId: v.optional(v.id("_storage")), + // Does the share link work. Three states so we can immediately share on opening the share dialog. + shared: v.union(v.literal("shared"), v.literal("expresslyUnshared"), v.literal("noPreferenceExpressed")), + // Allow others to fork this project at its most recent state. Always true for now. + allowForkFromLatest: v.boolean(), + // Allow to be shown in gallery (doesn't mean we actual show it). + // Always false for now, this doesn't exist yet. + allowShowInGallery: v.boolean(), + // Link to the deployed version from the share card. Always true for now. + linkToDeployed: v.boolean(), + // Optional referral code for Convex signup bonus + referralCode: v.optional(v.union(v.string(), v.null())), + }) + .index("byCode", ["code"]) + .index("byChatId", ["chatId"]) + .index("byAllowShowInGallery", ["allowShowInGallery"]), + + memberOpenAITokens: defineTable({ + memberId: v.id("convexMembers"), + token: v.string(), + requestsRemaining: v.number(), + lastUsedTime: v.union(v.number(), v.null()), + }) + .index("byMemberId", ["memberId"]) + .index("byToken", ["token"]), + + resendTokens: defineTable({ + memberId: v.id("convexMembers"), + token: v.string(), + verifiedEmail: v.string(), + requestsRemaining: v.number(), + lastUsedTime: v.union(v.number(), v.null()), + }) + .index("byMemberId", ["memberId"]) + .index("byToken", ["token"]), + + /* + * The entire prompt sent to a LLM and the response we received. + * Associated with an initialChatId but does not reset on rewind + * and is not duplicated in a "share" (fork) to a new account. + * This is roughly equivalent to what Braintrust logs would provide. + * https://www.braintrust.dev/docs/guides/logs + * + * This is not designed to be load-bearing data, it is just for debugging. + * Do not use this table to power non-debug UI or make agent decisions, + * it may be missing or incomplete for any given chat. + */ + debugChatApiRequestLog: defineTable({ + chatId: v.id("chats"), + // Such a loose type doesn't feel so bad since this is debugging data, but if we try + // to display older versions of this we need to make any fields added to CoreMessage in + // later versions of the Vercel AI SDK optional on the read path. + responseCoreMessages: v.array(v.any() as Validator), + promptCoreMessagesStorageId: v.id("_storage"), + finishReason: v.string(), + modelId: v.string(), + + // Not necessarily the usage we billed because + // - personal API key use shows up here too + // - failed tool calls count here but we try not to bill for those + // - usage code uses the provider for the final generation to bill for all LLM calls in the same interation + // but this debug info uses the correct provider for each call + usage: usageRecordValidator, + chefTokens: v.number(), + }).index("byChatId", ["chatId"]), +}); diff --git a/src/test/fake_chef/sessions.ts b/src/test/fake_chef/sessions.ts new file mode 100644 index 00000000..0dadcaaf --- /dev/null +++ b/src/test/fake_chef/sessions.ts @@ -0,0 +1,210 @@ +import { v } from "convex/values"; +import { action, internalMutation, mutation, query, type MutationCtx, type QueryCtx } from "./_generated/server"; +import { ConvexError } from "convex/values"; +import type { Id } from "./_generated/dataModel"; +import { getChatByIdOrUrlIdEnsuringAccess } from "./messages"; +import { internal } from "./_generated/api"; + +export const verifySession = query({ + args: { + sessionId: v.string(), + flexAuthMode: v.optional(v.literal("ConvexOAuth")), + }, + returns: v.boolean(), + handler: async (ctx, args) => { + const sessionId = await ctx.db.normalizeId("sessions", args.sessionId); + if (!sessionId) { + return false; + } + const session = await ctx.db.get(sessionId); + if (!session || !session.memberId) { + return false; + } + return isValidSessionForConvexOAuth(ctx, { sessionId, memberId: session.memberId }); + }, +}); + +export async function isValidSession(ctx: QueryCtx, args: { sessionId: Id<"sessions"> }) { + const session = await ctx.db.get(args.sessionId); + if (!session || !session.memberId) { + return false; + } + return await isValidSessionForConvexOAuth(ctx, { sessionId: args.sessionId, memberId: session.memberId }); +} + +async function isValidSessionForConvexOAuth( + ctx: QueryCtx, + args: { sessionId: Id<"sessions">; memberId: Id<"convexMembers"> }, +): Promise { + const member = await ctx.db.get(args.memberId); + if (!member) { + return false; + } + const identity = await ctx.auth.getUserIdentity(); + if (!identity) { + // Having the sessionId should be enough -- they should be unguessable + return true; + } + // But if we have the identity, it better match + return identity.tokenIdentifier === member.tokenIdentifier; +} + +export const registerConvexOAuthConnection = internalMutation({ + args: { + sessionId: v.id("sessions"), + chatId: v.id("chats"), + projectSlug: v.string(), + teamSlug: v.string(), + deploymentUrl: v.string(), + deploymentName: v.string(), + projectDeployKey: v.string(), + }, + handler: async (ctx, args) => { + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { + id: args.chatId, + sessionId: args.sessionId, + }); + if (!chat) { + throw new ConvexError({ code: "NotAuthorized", message: "Chat not found" }); + } + const session = await ctx.db.get(args.sessionId); + if (!session || !session.memberId) { + throw new ConvexError({ code: "NotAuthorized", message: "Chat not found" }); + } + await ctx.db.patch(args.chatId, { + convexProject: { + kind: "connected", + projectSlug: args.projectSlug, + teamSlug: args.teamSlug, + deploymentUrl: args.deploymentUrl, + deploymentName: args.deploymentName, + }, + }); + const credentials = await ctx.db + .query("convexProjectCredentials") + .withIndex("bySlugs", (q) => q.eq("teamSlug", args.teamSlug).eq("projectSlug", args.projectSlug)) + .collect(); + if (credentials.length === 0) { + await ctx.db.insert("convexProjectCredentials", { + teamSlug: args.teamSlug, + projectSlug: args.projectSlug, + projectDeployKey: args.projectDeployKey, + memberId: session.memberId, + }); + } + }, +}); + +export const startSession = mutation({ + args: {}, + returns: v.id("sessions"), + handler: async (ctx) => { + const member = await getOrCreateCurrentMember(ctx); + const existingSession = await ctx.db + .query("sessions") + .withIndex("byMemberId", (q) => q.eq("memberId", member)) + .unique(); + if (existingSession) { + return existingSession._id; + } + return ctx.db.insert("sessions", { + memberId: member, + }); + }, +}); + +async function getOrCreateCurrentMember(ctx: MutationCtx) { + const identity = await ctx.auth.getUserIdentity(); + if (!identity) { + throw new ConvexError({ code: "NotAuthorized", message: "Unauthorized" }); + } + const existingMember = await ctx.db + .query("convexMembers") + .withIndex("byTokenIdentifier", (q) => q.eq("tokenIdentifier", identity.tokenIdentifier)) + .unique(); + if (existingMember) { + return existingMember._id; + } + return ctx.db.insert("convexMembers", { + tokenIdentifier: identity.tokenIdentifier, + }); +} + +export async function getCurrentMember(ctx: QueryCtx) { + const identity = await ctx.auth.getUserIdentity(); + if (!identity) { + throw new ConvexError({ code: "NotAuthorized", message: "Unauthorized" }); + } + const existingMember = await ctx.db + .query("convexMembers") + .withIndex("byTokenIdentifier", (q) => q.eq("tokenIdentifier", identity.tokenIdentifier)) + .unique(); + if (!existingMember) { + throw new ConvexError({ code: "NotAuthorized", message: "Unauthorized" }); + } + return existingMember; +} + +// Internal so we can trust this is actually what's in the Convex dashboard, but it's still just a cache +export const saveCachedProfile = internalMutation({ + args: { + profile: v.object({ + username: v.string(), + avatar: v.string(), + email: v.string(), + id: v.union(v.string(), v.number()), + }), + }, + handler: async (ctx, args) => { + const member = await getCurrentMember(ctx); + const profile = { + ...args.profile, + id: String(args.profile.id), + }; + await ctx.db.patch(member._id, { + cachedProfile: profile, + }); + }, +}); + +export const updateCachedProfile = action({ + args: { + convexAuthToken: v.string(), + }, + handler: async (ctx, { convexAuthToken }) => { + const auth0Profile = await ctx.auth.getUserIdentity(); + if (!auth0Profile) { + throw new ConvexError({ code: "NotAuthorized", message: "Unauthorized" }); + } + + const url = `${process.env.BIG_BRAIN_HOST}/api/dashboard/profile`; + const response = await fetch(url, { + method: "GET", + headers: { + Authorization: `Bearer ${convexAuthToken}`, + "Content-Type": "application/json", + }, + }); + if (!response.ok) { + const body = await response.text(); + throw new Error(`Failed to fetch profile: ${response.statusText}: ${body}`); + } + + const convexProfile: ConvexProfile = await response.json(); + + const profile = { + username: convexProfile.name || auth0Profile.name || auth0Profile.nickname || "", + email: convexProfile.email || auth0Profile.email || "", + avatar: auth0Profile.pictureUrl || "", + id: convexProfile.id || auth0Profile.subject || "", + }; + + await ctx.runMutation(internal.sessions.saveCachedProfile, { profile }); + }, +}); + +export interface ConvexProfile { + name: string; + email: string; + id: string; +} diff --git a/src/test/fake_chef/share.ts b/src/test/fake_chef/share.ts new file mode 100644 index 00000000..c7c95d3d --- /dev/null +++ b/src/test/fake_chef/share.ts @@ -0,0 +1,256 @@ +import { ConvexError, v } from "convex/values"; +import { mutation, query, type DatabaseReader } from "./_generated/server"; +import type { MutationCtx } from "./_generated/server"; +import { getChatByIdOrUrlIdEnsuringAccess, getLatestChatMessageStorageState } from "./messages"; +import { startProvisionConvexProjectHelper } from "./convexProjects"; +import type { Id } from "./_generated/dataModel"; + +export const create = mutation({ + args: { + sessionId: v.id("sessions"), + id: v.string(), + }, + handler: async (ctx, { sessionId, id }) => { + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id, sessionId }); + if (!chat) { + throw new ConvexError("Chat not found"); + } + + const code = await generateUniqueCode(ctx.db); + + const storageState = await getLatestChatMessageStorageState(ctx, chat); + + if (!storageState) { + throw new ConvexError("Your project has never been saved."); + } + if (storageState.storageId === null) { + throw new ConvexError("Chat history not found"); + } + const snapshotId = storageState.snapshotId ?? chat.snapshotId; + if (!snapshotId) { + throw new ConvexError("Your project has never been saved."); + } + await ctx.db.insert("shares", { + chatId: chat._id, + + // It is safe to use the snapshotId from the chat because the user’s + // snapshot excludes .env.local. + snapshotId, + + chatHistoryId: storageState.storageId, + + code, + lastMessageRank: storageState.lastMessageRank, + lastSubchatIndex: storageState.subchatIndex, + partIndex: storageState.partIndex, + description: chat.description, + }); + return { code }; + }, +}); + +export const isShareReady = query({ + args: { + code: v.string(), + }, + returns: v.boolean(), + handler: async (ctx, { code }) => { + const share = await ctx.db + .query("shares") + .withIndex("byCode", (q) => q.eq("code", code)) + .unique(); + if (!share) { + return false; + } + return share.chatHistoryId !== null; + }, +}); + +// Unique across shares and socialShares in case these two url namespaces are combined. +export async function generateUniqueCode(db: DatabaseReader) { + const code = crypto.randomUUID().replace(/-/g, "").substring(0, 6); + let existing: { _id: any } | null = await db + .query("shares") + .withIndex("byCode", (q) => q.eq("code", code)) + .first(); + if (!existing) { + existing = await db + .query("socialShares") + .withIndex("byCode", (q) => q.eq("code", code)) + .first(); + } + if (existing) { + return generateUniqueCode(db); + } + return code; +} + +export const getShareDescription = query({ + args: { + code: v.string(), + }, + returns: v.object({ + description: v.optional(v.string()), + }), + handler: async (ctx, { code }) => { + const getShare = await ctx.db + .query("shares") + .withIndex("byCode", (q) => q.eq("code", code)) + .first(); + if (!getShare) { + const getShow = await ctx.db + .query("socialShares") + .withIndex("byCode", (q) => q.eq("code", code)) + .first(); + if (!getShow) { + throw new ConvexError("Invalid share link"); + } + const chat = await ctx.db.get(getShow.chatId); + return { + description: chat?.description, + }; + } + return { + description: getShare.description, + }; + }, +}); + +async function cloneShow( + ctx: MutationCtx, + { + showCode, + sessionId, + projectInitParams, + }: { showCode: string; sessionId: Id<"sessions">; projectInitParams: { teamSlug: string; auth0AccessToken: string } }, +): Promise<{ id: string; description?: string }> { + const show = await ctx.db + .query("socialShares") + .withIndex("byCode", (q) => q.eq("code", showCode)) + .first(); + if (!show) { + throw new ConvexError("Invalid share link"); + } + if (!show.allowForkFromLatest) { + throw new ConvexError("This show is not allowed to be forked."); + } + const parentChat = await ctx.db.get(show.chatId); + if (!parentChat) { + throw new ConvexError({ + code: "NotFound", + message: "The original chat was not found. It may have been deleted.", + }); + } + + const chatId = crypto.randomUUID(); + + const storageState = await getLatestChatMessageStorageState(ctx, parentChat); + if (!storageState) { + throw new ConvexError("Chat history not found"); + } + if (storageState.storageId === null) { + throw new ConvexError("Chat history not found"); + } + const snapshotId = storageState.snapshotId ?? parentChat.snapshotId; + if (!snapshotId) { + throw new ConvexError("Your project has never been saved."); + } + + const clonedChat = { + creatorId: sessionId, + initialId: chatId, + description: parentChat.description, + timestamp: new Date().toISOString(), + snapshotId, + lastSubchatIndex: parentChat.lastSubchatIndex, + isDeleted: false, + }; + const clonedChatId = await ctx.db.insert("chats", clonedChat); + + await ctx.db.insert("chatMessagesStorageState", { + chatId: clonedChatId, + storageId: storageState.storageId, + lastMessageRank: storageState.lastMessageRank, + subchatIndex: storageState.subchatIndex, + partIndex: storageState.partIndex, + }); + + await startProvisionConvexProjectHelper(ctx, { + sessionId, + chatId: clonedChat.initialId, + projectInitParams, + }); + + return { + id: chatId, + description: parentChat.description, + }; +} + +export const clone = mutation({ + args: { + shareCode: v.string(), + sessionId: v.id("sessions"), + projectInitParams: v.object({ + teamSlug: v.string(), + auth0AccessToken: v.string(), + }), + }, + returns: v.object({ + id: v.string(), + description: v.optional(v.string()), + }), + handler: async (ctx, { shareCode, sessionId, projectInitParams }) => { + const getShare = await ctx.db + .query("shares") + .withIndex("byCode", (q) => q.eq("code", shareCode)) + .first(); + if (!getShare) { + return cloneShow(ctx, { showCode: shareCode, sessionId, projectInitParams }); + } + + const parentChat = await ctx.db.get(getShare.chatId); + if (!parentChat) { + throw new ConvexError({ + code: "NotFound", + message: "The original chat was not found. It may have been deleted.", + }); + } + const chatId = crypto.randomUUID(); + const clonedChat = { + creatorId: sessionId, + initialId: chatId, + description: parentChat.description, + timestamp: new Date().toISOString(), + snapshotId: getShare.snapshotId, + lastSubchatIndex: getShare.lastSubchatIndex, + isDeleted: false, + }; + const clonedChatId = await ctx.db.insert("chats", clonedChat); + + if (!getShare.chatHistoryId) { + throw new ConvexError({ + code: "NotFound", + message: "The original chat history was not found. It may have been deleted.", + }); + } + await ctx.db.insert("chatMessagesStorageState", { + chatId: clonedChatId, + storageId: getShare.chatHistoryId, + lastMessageRank: getShare.lastMessageRank, + subchatIndex: getShare.lastSubchatIndex, + partIndex: getShare.partIndex ?? -1, + }); + + await startProvisionConvexProjectHelper(ctx, { + sessionId, + chatId: clonedChat.initialId, + projectInitParams, + }); + + return { + id: chatId, + description: parentChat.description, + }; + }, +}); diff --git a/src/test/fake_chef/snapshot.ts b/src/test/fake_chef/snapshot.ts new file mode 100644 index 00000000..998268c9 --- /dev/null +++ b/src/test/fake_chef/snapshot.ts @@ -0,0 +1,52 @@ +import { internalMutation, query } from "./_generated/server"; +import { v } from "convex/values"; +import { getChatByIdOrUrlIdEnsuringAccess, getLatestChatMessageStorageState } from "./messages"; + +// Save the snapshot information after successful upload +export const saveSnapshot = internalMutation({ + args: { + sessionId: v.id("sessions"), + chatId: v.string(), + storageId: v.id("_storage"), + }, + handler: async (ctx, { sessionId, chatId, storageId }) => { + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: chatId, sessionId }); + + if (!chat) { + throw new Error("Chat not found"); + } + await ctx.db.patch(chat._id, { + snapshotId: storageId, + }); + }, +}); + +export const getSnapshotUrl = query({ + args: { + sessionId: v.id("sessions"), + chatId: v.string(), + }, + handler: async (ctx, { sessionId, chatId }) => { + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: chatId, sessionId }); + if (!chat) { + throw new Error("Chat not found"); + } + const latestChatStorageState = await getLatestChatMessageStorageState(ctx, chat); + if (latestChatStorageState?.snapshotId) { + const url = await ctx.storage.getUrl(latestChatStorageState.snapshotId); + return url; + } + + // Maintain backwards compatibility with older chats that don't have snapshots in the chatMessagesStorageState table + + const snapshotId = chat?.snapshotId; + if (!snapshotId) { + return null; + } + const snapshot = await ctx.storage.getUrl(snapshotId); + if (!snapshot) { + throw new Error(`Expected to find a storageUrl for snapshot with id ${snapshotId}`); + } + return snapshot; + }, +}); diff --git a/src/test/fake_chef/socialShare.ts b/src/test/fake_chef/socialShare.ts new file mode 100644 index 00000000..3db4abaf --- /dev/null +++ b/src/test/fake_chef/socialShare.ts @@ -0,0 +1,243 @@ +import { ConvexError, v } from "convex/values"; +import { mutation, query, internalMutation } from "./_generated/server"; +import type { QueryCtx } from "./_generated/server"; +import { getChatByIdOrUrlIdEnsuringAccess } from "./messages"; +import { generateUniqueCode } from "./share"; + +// Create or modify a share record +export const share = mutation({ + args: { + sessionId: v.id("sessions"), + id: v.string(), + shared: v.union(v.literal("shared"), v.literal("expresslyUnshared"), v.literal("noPreferenceExpressed")), + allowForkFromLatest: v.boolean(), + thumbnailImageStorageId: v.optional(v.id("_storage")), + referralCode: v.optional(v.union(v.string(), v.null())), + }, + handler: async (ctx, { sessionId, id, shared, allowForkFromLatest, referralCode }) => { + // Validate referral code if set + if (referralCode !== undefined && referralCode !== null) { + // Only allow alphanumeric, dashes, and underscores + if (!/^[a-zA-Z0-9_-]+$/.test(referralCode)) { + throw new ConvexError("Invalid referral code: must be alphanumeric, dashes, or underscores only"); + } + } + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id, sessionId }); + if (!chat) { + throw new ConvexError("Chat not found"); + } + const existing = await ctx.db + .query("socialShares") + .withIndex("byChatId", (q) => q.eq("chatId", chat._id)) + .unique(); + + // Not currently configurable but behavior we'll want to remember for later. + const linkToDeployed = true; + const allowShowInGallery = false; + + if (!existing) { + const code = await generateUniqueCode(ctx.db); + await ctx.db.insert("socialShares", { + chatId: chat._id, + code, + shared, + linkToDeployed, + allowForkFromLatest, + allowShowInGallery, + referralCode, + }); + } else { + await ctx.db.replace(existing._id, { + ...existing, + shared, + allowForkFromLatest, + allowShowInGallery, + referralCode, + }); + } + }, +}); + +export const getSocialShare = query({ + args: { + code: v.string(), + }, + handler: async (ctx, { code }) => { + return await getSocialShareInner(ctx, code); + }, +}); + +export const getSocialShareOrIsSnapshotShare = query({ + args: { + code: v.string(), + }, + handler: async (ctx, { code }) => { + try { + return await getSocialShareInner(ctx, code); + } catch (e: any) { + if (e instanceof NotASocialShare) { + const snapshotShare = await ctx.db + .query("shares") + .withIndex("byCode", (q) => q.eq("code", code)) + .first(); + if (snapshotShare) { + return { isSnapshotShare: true }; + } + } + throw e; + } + }, +}); + +class NotASocialShare extends ConvexError {} + +async function getSocialShareInner(ctx: QueryCtx, code: string) { + const socialShare = await ctx.db + .query("socialShares") + .withIndex("byCode", (q) => q.eq("code", code)) + .first(); + if (!socialShare) { + throw new NotASocialShare("Invalid share link"); + } + const chat = await ctx.db.get(socialShare.chatId); + if (!chat) { + throw new ConvexError("Invalid chat"); + } + + const session = await ctx.db.get(chat.creatorId); + const authorProfile = session?.memberId ? ((await ctx.db.get(session.memberId))?.cachedProfile ?? null) : null; + + const chatHasBeenDeployed = !!chat.hasBeenDeployed; + + const thumbnailUrl = socialShare.thumbnailImageStorageId + ? await ctx.storage.getUrl(socialShare.thumbnailImageStorageId) + : null; + + const deployedUrl = + chatHasBeenDeployed && chat.convexProject?.kind === "connected" + ? `https://${chat.convexProject.deploymentName}.convex.app` + : null; + + return { + description: chat.description || null, + code, + shared: socialShare.shared, + allowShowInGallery: socialShare.allowShowInGallery, + hasBeenDeployed: chatHasBeenDeployed, + deployedUrl, + thumbnailUrl, + referralCode: socialShare.referralCode || null, + author: authorProfile + ? { + username: authorProfile.username, + avatar: authorProfile.avatar, + } + : null, + }; +} + +export const getCurrentSocialShare = query({ + args: { + sessionId: v.id("sessions"), + id: v.string(), + }, + handler: async (ctx, { sessionId, id }) => { + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id, sessionId }); + if (!chat) { + throw new ConvexError("Chat not found"); + } + + const socialShare = await ctx.db + .query("socialShares") + .withIndex("byChatId", (q) => q.eq("chatId", chat._id)) + .unique(); + + if (!socialShare) { + return null; + } + + return { + shared: socialShare.shared, + allowForkFromLatest: socialShare.allowForkFromLatest, + code: socialShare.code, + thumbnailImageStorageId: socialShare.thumbnailImageStorageId, + referralCode: socialShare.referralCode, + }; + }, +}); + +export const saveThumbnail = internalMutation({ + args: { + sessionId: v.id("sessions"), + urlId: v.string(), + storageId: v.id("_storage"), + }, + handler: async (ctx, { sessionId, urlId, storageId }) => { + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: urlId, sessionId }); + if (!chat) { + throw new ConvexError("Chat not found"); + } + + // Get or create social share + const existing = await ctx.db + .query("socialShares") + .withIndex("byChatId", (q) => q.eq("chatId", chat._id)) + .unique(); + + if (!existing) { + const code = await generateUniqueCode(ctx.db); + await ctx.db.insert("socialShares", { + chatId: chat._id, + code, + thumbnailImageStorageId: storageId, + shared: "noPreferenceExpressed", + linkToDeployed: true, + allowForkFromLatest: false, + allowShowInGallery: false, + }); + } else { + // If there was a previous thumbnail, delete it + if (existing.thumbnailImageStorageId) { + await ctx.storage.delete(existing.thumbnailImageStorageId); + } + + await ctx.db.patch(existing._id, { + thumbnailImageStorageId: storageId, + }); + } + }, +}); + +// This is used for admin's to create a share link for debugging purposes. Be sure to delete the share link after use. +export const createAdminShare = internalMutation({ + args: { + chatId: v.id("chats"), + }, + handler: async (ctx, { chatId }) => { + const chat = await ctx.db.get(chatId); + if (!chat) { + throw new ConvexError("Chat not found"); + } + // Use an existing share link for this chat if it exists + const existing = await ctx.db + .query("socialShares") + .withIndex("byChatId", (q) => q.eq("chatId", chatId)) + .unique(); + if (existing) { + console.log(`Already have a share for chat ${chatId}: Go to https://chef.show/${existing.code}`); + return; + } + + const randomCode = await generateUniqueCode(ctx.db); + const code = `support-${randomCode}`; + await ctx.db.insert("socialShares", { + chatId, + code, + shared: "shared", + linkToDeployed: false, + allowForkFromLatest: true, + allowShowInGallery: false, + }); + console.log(`Created admin share for chat ${chatId}. Go to https://chef.show/${code}`); + }, +}); diff --git a/src/test/fake_chef/tsconfig.json b/src/test/fake_chef/tsconfig.json new file mode 100644 index 00000000..6fb0904d --- /dev/null +++ b/src/test/fake_chef/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + /* These settings are not required by Convex and can be modified. */ + "allowJs": true, + "strict": true, + "moduleResolution": "Bundler", + "jsx": "react-jsx", + "skipLibCheck": true, + "allowSyntheticDefaultImports": true, + + /* These compiler options are required by Convex */ + "target": "ESNext", + "lib": ["ES2021", "dom"], + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "isolatedModules": true, + "noEmit": true + } +} diff --git a/src/test/types.bench.ts b/src/test/types.bench.ts new file mode 100644 index 00000000..71fb32fe --- /dev/null +++ b/src/test/types.bench.ts @@ -0,0 +1,3 @@ +import { bench } from "@ark/attest"; + +bench("ApiFromModules", () => {}).types(); diff --git a/src/type_utils.ts b/src/type_utils.ts index 2eb90019..250d4827 100644 --- a/src/type_utils.ts +++ b/src/type_utils.ts @@ -9,12 +9,9 @@ * It is functionally the identity for object types, but in practice it can * simplify expressions like `A & B`. */ -export type Expand> = - ObjectType extends Record - ? { - [Key in keyof ObjectType]: ObjectType[Key]; - } - : never; +export type Expand> = { + [Key in keyof ObjectType]: ObjectType[Key]; +} & unknown; /** * An `Omit<>` type that: From 55454772b9e7ab5f95d8542aaaf8d2d4e27bf92c Mon Sep 17 00:00:00 2001 From: David Blass Date: Wed, 20 Aug 2025 15:27:39 -0400 Subject: [PATCH 02/12] add type bench --- package.json | 3 ++- src/test/fake_chef/package.json | 3 --- src/test/types.bench.ts | 5 ++++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 9d1a4302..87151d9a 100644 --- a/package.json +++ b/package.json @@ -195,7 +195,8 @@ "test": "vitest --silent", "test-not-silent": "vitest", "typecheck": "tsc --noEmit --emitDeclarationOnly false", - "test-esm": "node ./scripts/test-esm.mjs && ./scripts/checkdeps.mjs && ./scripts/checkimports.mjs" + "test-esm": "node ./scripts/test-esm.mjs && ./scripts/checkdeps.mjs && ./scripts/checkimports.mjs", + "type-bench": "node ./src/test/types.bench.ts" }, "files": [ "bin", diff --git a/src/test/fake_chef/package.json b/src/test/fake_chef/package.json index b704bc4e..0f7f9edd 100644 --- a/src/test/fake_chef/package.json +++ b/src/test/fake_chef/package.json @@ -1,8 +1,5 @@ { "name": "fake-chef", - "engines": { - "node": ">=18.18.0" - }, "dependencies": { "@convex-dev/migrations": "^0.2.7", "@convex-dev/rate-limiter": "^0.2.6", diff --git a/src/test/types.bench.ts b/src/test/types.bench.ts index 71fb32fe..7ac2eac3 100644 --- a/src/test/types.bench.ts +++ b/src/test/types.bench.ts @@ -1,3 +1,6 @@ import { bench } from "@ark/attest"; +import type { api } from "./fake_chef/_generated/api.js"; -bench("ApiFromModules", () => {}).types(); +bench("ApiFromModules", () => { + return {} as typeof api; +}).types([115837, "instantiations"]); From 67915c66e42143e21ccdfa75dc2ad2d9ba99d164 Mon Sep 17 00:00:00 2001 From: David Blass Date: Wed, 20 Aug 2025 16:23:14 -0400 Subject: [PATCH 03/12] point fake_chef imports to ts source --- .vscode/settings.json | 1 + src/test/fake_chef/_generated/api.d.ts | 89 +----- src/test/fake_chef/_generated/dataModel.d.ts | 4 +- src/test/fake_chef/_generated/server.d.ts | 2 +- src/test/fake_chef/admin.ts | 2 +- src/test/fake_chef/apiKeys.ts | 34 ++- src/test/fake_chef/cleanup.ts | 58 ++-- src/test/fake_chef/convexProjects.ts | 200 ++++++++++---- src/test/fake_chef/crons.ts | 2 +- src/test/fake_chef/debugPrompt.ts | 19 +- src/test/fake_chef/deploy.ts | 2 +- src/test/fake_chef/dev.ts | 21 +- src/test/fake_chef/http.ts | 87 ++++-- src/test/fake_chef/lz4.ts | 29 +- src/test/fake_chef/messages.ts | 272 ++++++++++++++----- src/test/fake_chef/migrations.ts | 49 ---- src/test/fake_chef/openaiProxy.ts | 18 +- src/test/fake_chef/rateLimiter.ts | 2 +- src/test/fake_chef/resendProxy.ts | 58 +++- src/test/fake_chef/schema.ts | 16 +- src/test/fake_chef/sessions.ts | 57 +++- src/test/fake_chef/share.ts | 22 +- src/test/fake_chef/snapshot.ts | 26 +- src/test/fake_chef/socialShare.ts | 34 ++- tsconfig.json | 1 + 25 files changed, 715 insertions(+), 390 deletions(-) create mode 100644 .vscode/settings.json delete mode 100644 src/test/fake_chef/migrations.ts diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1 @@ +{} diff --git a/src/test/fake_chef/_generated/api.d.ts b/src/test/fake_chef/_generated/api.d.ts index f64f867d..7fd42a97 100644 --- a/src/test/fake_chef/_generated/api.d.ts +++ b/src/test/fake_chef/_generated/api.d.ts @@ -21,7 +21,6 @@ import type * as http from "../http.js"; import type * as lz4 from "../lz4.js"; import type * as lz4Wasm from "../lz4Wasm.js"; import type * as messages from "../messages.js"; -import type * as migrations from "../migrations.js"; import type * as openaiProxy from "../openaiProxy.js"; import type * as rateLimiter from "../rateLimiter.js"; import type * as resendProxy from "../resendProxy.js"; @@ -34,7 +33,7 @@ import type { ApiFromModules, FilterApi, FunctionReference, -} from "convex/server"; +} from "../../../server"; /** * A utility for referencing Convex functions in your app's API. @@ -58,7 +57,6 @@ declare const fullApi: ApiFromModules<{ lz4: typeof lz4; lz4Wasm: typeof lz4Wasm; messages: typeof messages; - migrations: typeof migrations; openaiProxy: typeof openaiProxy; rateLimiter: typeof rateLimiter; resendProxy: typeof resendProxy; @@ -223,89 +221,4 @@ export declare const components: { >; }; }; - migrations: { - lib: { - cancel: FunctionReference< - "mutation", - "internal", - { name: string }, - { - batchSize?: number; - cursor?: string | null; - error?: string; - isDone: boolean; - latestEnd?: number; - latestStart: number; - name: string; - next?: Array; - processed: number; - state: "inProgress" | "success" | "failed" | "canceled" | "unknown"; - } - >; - cancelAll: FunctionReference< - "mutation", - "internal", - { sinceTs?: number }, - Array<{ - batchSize?: number; - cursor?: string | null; - error?: string; - isDone: boolean; - latestEnd?: number; - latestStart: number; - name: string; - next?: Array; - processed: number; - state: "inProgress" | "success" | "failed" | "canceled" | "unknown"; - }> - >; - clearAll: FunctionReference< - "mutation", - "internal", - { before?: number }, - null - >; - getStatus: FunctionReference< - "query", - "internal", - { limit?: number; names?: Array }, - Array<{ - batchSize?: number; - cursor?: string | null; - error?: string; - isDone: boolean; - latestEnd?: number; - latestStart: number; - name: string; - next?: Array; - processed: number; - state: "inProgress" | "success" | "failed" | "canceled" | "unknown"; - }> - >; - migrate: FunctionReference< - "mutation", - "internal", - { - batchSize?: number; - cursor?: string | null; - dryRun: boolean; - fnHandle: string; - name: string; - next?: Array<{ fnHandle: string; name: string }>; - }, - { - batchSize?: number; - cursor?: string | null; - error?: string; - isDone: boolean; - latestEnd?: number; - latestStart: number; - name: string; - next?: Array; - processed: number; - state: "inProgress" | "success" | "failed" | "canceled" | "unknown"; - } - >; - }; - }; }; diff --git a/src/test/fake_chef/_generated/dataModel.d.ts b/src/test/fake_chef/_generated/dataModel.d.ts index 8541f319..fdcbcbdc 100644 --- a/src/test/fake_chef/_generated/dataModel.d.ts +++ b/src/test/fake_chef/_generated/dataModel.d.ts @@ -13,8 +13,8 @@ import type { DocumentByName, TableNamesInDataModel, SystemTableNames, -} from "convex/server"; -import type { GenericId } from "convex/values"; +} from "../../../server"; +import type { GenericId } from "../../../values"; import schema from "../schema.js"; /** diff --git a/src/test/fake_chef/_generated/server.d.ts b/src/test/fake_chef/_generated/server.d.ts index b5c68288..c7ea141e 100644 --- a/src/test/fake_chef/_generated/server.d.ts +++ b/src/test/fake_chef/_generated/server.d.ts @@ -20,7 +20,7 @@ import { GenericDatabaseReader, GenericDatabaseWriter, FunctionReference, -} from "convex/server"; +} from "../../../server"; import type { DataModel } from "./dataModel.js"; type GenericCtx = diff --git a/src/test/fake_chef/admin.ts b/src/test/fake_chef/admin.ts index 0d6fa6b2..982d54ad 100644 --- a/src/test/fake_chef/admin.ts +++ b/src/test/fake_chef/admin.ts @@ -1,4 +1,4 @@ -import { ConvexError, v } from "convex/values"; +import { ConvexError, v } from "../../values"; import { mutation, internalMutation, diff --git a/src/test/fake_chef/apiKeys.ts b/src/test/fake_chef/apiKeys.ts index 6fd302be..e492ff25 100644 --- a/src/test/fake_chef/apiKeys.ts +++ b/src/test/fake_chef/apiKeys.ts @@ -1,4 +1,4 @@ -import { ConvexError, v } from "convex/values"; +import { ConvexError, v } from "../../values"; import { action, mutation, query } from "./_generated/server"; import { apiKeyValidator } from "./schema"; @@ -12,7 +12,9 @@ export const apiKeyForCurrentMember = query({ } const existingMember = await ctx.db .query("convexMembers") - .withIndex("byTokenIdentifier", (q) => q.eq("tokenIdentifier", identity.tokenIdentifier)) + .withIndex("byTokenIdentifier", (q) => + q.eq("tokenIdentifier", identity.tokenIdentifier), + ) .unique(); return existingMember?.apiKey; @@ -32,7 +34,9 @@ export const setApiKeyForCurrentMember = mutation({ const existingMember = await ctx.db .query("convexMembers") - .withIndex("byTokenIdentifier", (q) => q.eq("tokenIdentifier", identity.tokenIdentifier)) + .withIndex("byTokenIdentifier", (q) => + q.eq("tokenIdentifier", identity.tokenIdentifier), + ) .unique(); if (!existingMember) { @@ -54,7 +58,9 @@ export const deleteApiKeyForCurrentMember = mutation({ const existingMember = await ctx.db .query("convexMembers") - .withIndex("byTokenIdentifier", (q) => q.eq("tokenIdentifier", identity.tokenIdentifier)) + .withIndex("byTokenIdentifier", (q) => + q.eq("tokenIdentifier", identity.tokenIdentifier), + ) .unique(); if (!existingMember) { @@ -76,7 +82,9 @@ export const deleteAnthropicApiKeyForCurrentMember = mutation({ const existingMember = await ctx.db .query("convexMembers") - .withIndex("byTokenIdentifier", (q) => q.eq("tokenIdentifier", identity.tokenIdentifier)) + .withIndex("byTokenIdentifier", (q) => + q.eq("tokenIdentifier", identity.tokenIdentifier), + ) .unique(); if (!existingMember) { @@ -105,7 +113,9 @@ export const deleteOpenaiApiKeyForCurrentMember = mutation({ const existingMember = await ctx.db .query("convexMembers") - .withIndex("byTokenIdentifier", (q) => q.eq("tokenIdentifier", identity.tokenIdentifier)) + .withIndex("byTokenIdentifier", (q) => + q.eq("tokenIdentifier", identity.tokenIdentifier), + ) .unique(); if (!existingMember) { @@ -134,7 +144,9 @@ export const deleteXaiApiKeyForCurrentMember = mutation({ const existingMember = await ctx.db .query("convexMembers") - .withIndex("byTokenIdentifier", (q) => q.eq("tokenIdentifier", identity.tokenIdentifier)) + .withIndex("byTokenIdentifier", (q) => + q.eq("tokenIdentifier", identity.tokenIdentifier), + ) .unique(); if (!existingMember) { @@ -163,7 +175,9 @@ export const deleteGoogleApiKeyForCurrentMember = mutation({ const existingMember = await ctx.db .query("convexMembers") - .withIndex("byTokenIdentifier", (q) => q.eq("tokenIdentifier", identity.tokenIdentifier)) + .withIndex("byTokenIdentifier", (q) => + q.eq("tokenIdentifier", identity.tokenIdentifier), + ) .unique(); if (!existingMember) { @@ -241,7 +255,9 @@ export const validateGoogleApiKey = action({ throw new ConvexError({ code: "NotAuthorized", message: "Unauthorized" }); } - const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models?key=${args.apiKey}`); + const response = await fetch( + `https://generativelanguage.googleapis.com/v1beta/models?key=${args.apiKey}`, + ); if (response.status === 400) { return false; diff --git a/src/test/fake_chef/cleanup.ts b/src/test/fake_chef/cleanup.ts index 5113d6c9..392b3237 100644 --- a/src/test/fake_chef/cleanup.ts +++ b/src/test/fake_chef/cleanup.ts @@ -1,9 +1,11 @@ -import { v } from "convex/values"; +import { v } from "../../values"; import { internalMutation } from "./_generated/server"; import { internal } from "./_generated/api"; const delayInMs = parseFloat(process.env.DEBUG_FILE_CLEANUP_DELAY_MS ?? "500"); -const debugFileCleanupBatchSize = parseInt(process.env.DEBUG_FILE_CLEANUP_BATCH_SIZE ?? "100"); +const debugFileCleanupBatchSize = parseInt( + process.env.DEBUG_FILE_CLEANUP_BATCH_SIZE ?? "100", +); export const deleteDebugFilesForInactiveChats = internalMutation({ args: { @@ -12,11 +14,16 @@ export const deleteDebugFilesForInactiveChats = internalMutation({ shouldScheduleNext: v.boolean(), daysInactive: v.number(), }, - handler: async (ctx, { forReal, cursor, shouldScheduleNext, daysInactive }) => { - const { page, isDone, continueCursor } = await ctx.db.query("debugChatApiRequestLog").paginate({ - numItems: debugFileCleanupBatchSize, - cursor: cursor ?? null, - }); + handler: async ( + ctx, + { forReal, cursor, shouldScheduleNext, daysInactive }, + ) => { + const { page, isDone, continueCursor } = await ctx.db + .query("debugChatApiRequestLog") + .paginate({ + numItems: debugFileCleanupBatchSize, + cursor: cursor ?? null, + }); for (const doc of page) { if (doc._creationTime > Date.now() - 1000 * 60 * 60 * 24 * daysInactive) { return; @@ -27,26 +34,41 @@ export const deleteDebugFilesForInactiveChats = internalMutation({ .order("desc") .first(); if (storageState === null) { - throw new Error(`Chat ${doc.chatId} not found in chatMessagesStorageState`); + throw new Error( + `Chat ${doc.chatId} not found in chatMessagesStorageState`, + ); } - if (storageState._creationTime < Date.now() - 1000 * 60 * 60 * 24 * daysInactive) { - const lastActiveDate = new Date(storageState._creationTime).toISOString(); + if ( + storageState._creationTime < + Date.now() - 1000 * 60 * 60 * 24 * daysInactive + ) { + const lastActiveDate = new Date( + storageState._creationTime, + ).toISOString(); if (forReal) { ctx.storage.delete(doc.promptCoreMessagesStorageId); await ctx.db.delete(doc._id); - console.log(`Deleted debug file for chat ${doc.chatId} last active at ${lastActiveDate}`); + console.log( + `Deleted debug file for chat ${doc.chatId} last active at ${lastActiveDate}`, + ); } else { - console.log(`Would delete debug file for chat ${doc.chatId} last active at ${lastActiveDate}`); + console.log( + `Would delete debug file for chat ${doc.chatId} last active at ${lastActiveDate}`, + ); } } } if (shouldScheduleNext && !isDone) { - await ctx.scheduler.runAfter(delayInMs, internal.cleanup.deleteDebugFilesForInactiveChats, { - forReal, - cursor: continueCursor, - shouldScheduleNext, - daysInactive, - }); + await ctx.scheduler.runAfter( + delayInMs, + internal.cleanup.deleteDebugFilesForInactiveChats, + { + forReal, + cursor: continueCursor, + shouldScheduleNext, + daysInactive, + }, + ); } }, }); diff --git a/src/test/fake_chef/convexProjects.ts b/src/test/fake_chef/convexProjects.ts index 66ed68c2..f1cde72a 100644 --- a/src/test/fake_chef/convexProjects.ts +++ b/src/test/fake_chef/convexProjects.ts @@ -7,7 +7,7 @@ import { type ActionCtx, type MutationCtx, } from "./_generated/server"; -import { ConvexError, v } from "convex/values"; +import { ConvexError, v } from "../../values"; import { getChatByIdOrUrlIdEnsuringAccess } from "./messages"; import { internal } from "./_generated/api"; import type { Id } from "./_generated/dataModel"; @@ -18,7 +18,10 @@ export const hasConnectedConvexProject = query({ chatId: v.string(), }, handler: async (ctx, args) => { - const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: args.chatId, sessionId: args.sessionId }); + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { + id: args.chatId, + sessionId: args.sessionId, + }); return chat?.convexProject !== undefined; }, }); @@ -48,7 +51,10 @@ export const loadConnectedConvexProjectCredentials = query({ v.null(), ), handler: async (ctx, args) => { - const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: args.chatId, sessionId: args.sessionId }); + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { + id: args.chatId, + sessionId: args.sessionId, + }); if (!chat) { return null; } @@ -64,7 +70,11 @@ export const loadConnectedConvexProjectCredentials = query({ } const credentials = await ctx.db .query("convexProjectCredentials") - .withIndex("bySlugs", (q) => q.eq("teamSlug", project.teamSlug).eq("projectSlug", project.projectSlug)) + .withIndex("bySlugs", (q) => + q + .eq("teamSlug", project.teamSlug) + .eq("projectSlug", project.projectSlug), + ) .first(); if (!credentials) { return null; @@ -110,7 +120,10 @@ export async function startProvisionConvexProjectHelper( }; }, ): Promise { - const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: args.chatId, sessionId: args.sessionId }); + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { + id: args.chatId, + sessionId: args.sessionId, + }); if (!chat) { throw new ConvexError({ code: "NotAuthorized", message: "Chat not found" }); } @@ -120,25 +133,43 @@ export async function startProvisionConvexProjectHelper( throw new ConvexError({ code: "NotAuthorized", message: "Chat not found" }); } if (session.memberId === undefined) { - throw new ConvexError({ code: "NotAuthorized", message: "Must be logged in to connect a project" }); + throw new ConvexError({ + code: "NotAuthorized", + message: "Must be logged in to connect a project", + }); } // OAuth flow if (args.projectInitParams === undefined) { - console.error(`Must provide projectInitParams for oauth: ${args.sessionId}`); - throw new ConvexError({ code: "NotAuthorized", message: "Invalid flow for connecting a project" }); + console.error( + `Must provide projectInitParams for oauth: ${args.sessionId}`, + ); + throw new ConvexError({ + code: "NotAuthorized", + message: "Invalid flow for connecting a project", + }); } - await ctx.scheduler.runAfter(0, internal.convexProjects.connectConvexProjectForOauth, { - sessionId: args.sessionId, - chatId: args.chatId, - accessToken: args.projectInitParams.auth0AccessToken, - teamSlug: args.projectInitParams.teamSlug, - }); - const jobId = await ctx.scheduler.runAfter(CHECK_CONNECTION_DEADLINE_MS, internal.convexProjects.checkConnection, { - sessionId: args.sessionId, - chatId: args.chatId, + await ctx.scheduler.runAfter( + 0, + internal.convexProjects.connectConvexProjectForOauth, + { + sessionId: args.sessionId, + chatId: args.chatId, + accessToken: args.projectInitParams.auth0AccessToken, + teamSlug: args.projectInitParams.teamSlug, + }, + ); + const jobId = await ctx.scheduler.runAfter( + CHECK_CONNECTION_DEADLINE_MS, + internal.convexProjects.checkConnection, + { + sessionId: args.sessionId, + chatId: args.chatId, + }, + ); + await ctx.db.patch(chat._id, { + convexProject: { kind: "connecting", checkConnectionJobId: jobId }, }); - await ctx.db.patch(chat._id, { convexProject: { kind: "connecting", checkConnectionJobId: jobId } }); return; } @@ -160,9 +191,14 @@ export const recordProvisionedConvexProjectCredentials = internalMutation({ teamSlug, projectDeployKey: args.projectDeployKey, }); - const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: args.chatId, sessionId: args.sessionId }); + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { + id: args.chatId, + sessionId: args.sessionId, + }); if (!chat) { - console.error(`Chat not found: ${args.chatId}, sessionId: ${args.sessionId}`); + console.error( + `Chat not found: ${args.chatId}, sessionId: ${args.sessionId}`, + ); return; } if (chat.convexProject?.kind === "connecting") { @@ -202,25 +238,34 @@ export const connectConvexProjectForOauth = internalAction({ teamSlug: args.teamSlug, }) .then(async (data) => { - await ctx.runMutation(internal.convexProjects.recordProvisionedConvexProjectCredentials, { - sessionId: args.sessionId, - chatId: args.chatId, - projectSlug: data.projectSlug, - teamSlug: args.teamSlug, - projectDeployKey: data.projectDeployKey, - deploymentUrl: data.deploymentUrl, - deploymentName: data.deploymentName, - warningMessage: data.warningMessage, - }); + await ctx.runMutation( + internal.convexProjects.recordProvisionedConvexProjectCredentials, + { + sessionId: args.sessionId, + chatId: args.chatId, + projectSlug: data.projectSlug, + teamSlug: args.teamSlug, + projectDeployKey: data.projectDeployKey, + deploymentUrl: data.deploymentUrl, + deploymentName: data.deploymentName, + warningMessage: data.warningMessage, + }, + ); }) .catch(async (error) => { console.error(`Error connecting convex project: ${error.message}`); - const errorMessage = error instanceof ConvexError ? error.data.message : "Unexpected error"; - await ctx.runMutation(internal.convexProjects.recordFailedConvexProjectConnection, { - sessionId: args.sessionId, - chatId: args.chatId, - errorMessage, - }); + const errorMessage = + error instanceof ConvexError + ? error.data.message + : "Unexpected error"; + await ctx.runMutation( + internal.convexProjects.recordFailedConvexProjectConnection, + { + sessionId: args.sessionId, + chatId: args.chatId, + errorMessage, + }, + ); }); }, }); @@ -288,7 +333,11 @@ async function _connectConvexProjectForMember( } // Special case this error since it's probably semi-common - if (data !== null && data.code === "ProjectQuotaReached" && typeof data.message === "string") { + if ( + data !== null && + data.code === "ProjectQuotaReached" && + typeof data.message === "string" + ) { throw new ConvexError({ code: "ProvisioningError", message: `Failed to create project: ProjectQuotaReached: ${data.message}`, @@ -308,18 +357,21 @@ async function _connectConvexProjectForMember( projectsRemaining: number; } = await response.json(); - const projectDeployKeyResponse = await fetch(`${bigBrainHost}/api/dashboard/authorize`, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${args.accessToken}`, + const projectDeployKeyResponse = await fetch( + `${bigBrainHost}/api/dashboard/authorize`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${args.accessToken}`, + }, + body: JSON.stringify({ + authn_token: args.accessToken, + projectId: data.projectId, + appName: ensureEnvVar("CHEF_OAUTH_APP_NAME"), + }), }, - body: JSON.stringify({ - authn_token: args.accessToken, - projectId: data.projectId, - appName: ensureEnvVar("CHEF_OAUTH_APP_NAME"), - }), - }); + ); if (!projectDeployKeyResponse.ok) { const text = await projectDeployKeyResponse.text(); throw new ConvexError({ @@ -328,10 +380,13 @@ async function _connectConvexProjectForMember( details: text, }); } - const projectDeployKeyData: { accessToken: string } = await projectDeployKeyResponse.json(); + const projectDeployKeyData: { accessToken: string } = + await projectDeployKeyResponse.json(); const projectDeployKey = `project:${args.teamSlug}:${data.projectSlug}|${projectDeployKeyData.accessToken}`; const warningMessage = - data.projectsRemaining <= 2 ? `You have ${data.projectsRemaining} projects remaining on this team.` : undefined; + data.projectsRemaining <= 2 + ? `You have ${data.projectsRemaining} projects remaining on this team.` + : undefined; return { projectSlug: data.projectSlug, @@ -350,9 +405,14 @@ export const recordFailedConvexProjectConnection = internalMutation({ errorMessage: v.string(), }, handler: async (ctx, args) => { - const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: args.chatId, sessionId: args.sessionId }); + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { + id: args.chatId, + sessionId: args.sessionId, + }); if (!chat) { - console.error(`Chat not found: ${args.chatId}, sessionId: ${args.sessionId}`); + console.error( + `Chat not found: ${args.chatId}, sessionId: ${args.sessionId}`, + ); return; } if (chat.convexProject?.kind === "connecting") { @@ -373,15 +433,25 @@ export const checkConnection = internalMutation({ chatId: v.string(), }, handler: async (ctx, args) => { - const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: args.chatId, sessionId: args.sessionId }); + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { + id: args.chatId, + sessionId: args.sessionId, + }); if (!chat) { - console.error(`Chat not found: ${args.chatId}, sessionId: ${args.sessionId}`); + console.error( + `Chat not found: ${args.chatId}, sessionId: ${args.sessionId}`, + ); return; } if (chat.convexProject?.kind !== "connecting") { return; } - await ctx.db.patch(chat._id, { convexProject: { kind: "failed", errorMessage: "Failed to connect to project" } }); + await ctx.db.patch(chat._id, { + convexProject: { + kind: "failed", + errorMessage: "Failed to connect to project", + }, + }); }, }); @@ -392,9 +462,15 @@ export const getProjectName = internalQuery({ }, returns: v.union(v.string(), v.null()), handler: async (ctx, args) => { - const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: args.chatId, sessionId: args.sessionId }); + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { + id: args.chatId, + sessionId: args.sessionId, + }); if (!chat) { - throw new ConvexError({ code: "NotAuthorized", message: "Chat not found" }); + throw new ConvexError({ + code: "NotAuthorized", + message: "Chat not found", + }); } return chat.urlId ?? null; }, @@ -406,9 +482,15 @@ export const disconnectConvexProject = mutation({ chatId: v.string(), }, handler: async (ctx, args) => { - const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: args.chatId, sessionId: args.sessionId }); + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { + id: args.chatId, + sessionId: args.sessionId, + }); if (!chat) { - throw new ConvexError({ code: "NotAuthorized", message: "Chat not found" }); + throw new ConvexError({ + code: "NotAuthorized", + message: "Chat not found", + }); } await ctx.db.patch(chat._id, { convexProject: undefined }); }, diff --git a/src/test/fake_chef/crons.ts b/src/test/fake_chef/crons.ts index 04a6a0ef..b3bd8fef 100644 --- a/src/test/fake_chef/crons.ts +++ b/src/test/fake_chef/crons.ts @@ -1,4 +1,4 @@ -import { cronJobs } from "convex/server"; +import { cronJobs } from "../../server"; import { internal } from "./_generated/api"; const crons = cronJobs(); diff --git a/src/test/fake_chef/debugPrompt.ts b/src/test/fake_chef/debugPrompt.ts index cfb5118d..032cd0ce 100644 --- a/src/test/fake_chef/debugPrompt.ts +++ b/src/test/fake_chef/debugPrompt.ts @@ -1,5 +1,5 @@ import { internalMutation, query } from "./_generated/server"; -import { v } from "convex/values"; +import { v } from "../../values"; import type { MutationCtx, QueryCtx } from "./_generated/server"; import { assertIsConvexAdmin } from "./admin"; import type { Id } from "./_generated/dataModel"; @@ -8,7 +8,9 @@ import { usageRecordValidator } from "./schema"; async function getChatByInitialId(ctx: QueryCtx, initialId: string) { const chatByInitialId = await ctx.db .query("chats") - .withIndex("byInitialId", (q: any) => q.eq("initialId", initialId).lt("isDeleted", true)) + .withIndex("byInitialId", (q: any) => + q.eq("initialId", initialId).lt("isDeleted", true), + ) .unique(); if (!chatByInitialId) { throw new Error(`No corresponding chat found for initial ID ${initialId}`); @@ -58,7 +60,10 @@ export const deleteDebugPrompt = internalMutation({ }, }); -async function _deleteDebugPrompt(ctx: MutationCtx, id: Id<"debugChatApiRequestLog">) { +async function _deleteDebugPrompt( + ctx: MutationCtx, + id: Id<"debugChatApiRequestLog">, +) { const record = await ctx.db.get(id); if (!record) { return; @@ -75,7 +80,9 @@ export const deleteAllDebugPrompts = internalMutation({ for (let i = 0; i < records.length; i += 10) { const chunk = records.slice(i, i + 10); - await Promise.all(chunk.map((record) => _deleteDebugPrompt(ctx, record._id))); + await Promise.all( + chunk.map((record) => _deleteDebugPrompt(ctx, record._id)), + ); } }, }); @@ -97,7 +104,9 @@ export const show = query({ const promptsWithUrls = await Promise.all( debugPrompts.map(async (prompt) => ({ ...prompt, - coreMessagesUrl: await ctx.storage.getUrl(prompt.promptCoreMessagesStorageId), + coreMessagesUrl: await ctx.storage.getUrl( + prompt.promptCoreMessagesStorageId, + ), })), ); diff --git a/src/test/fake_chef/deploy.ts b/src/test/fake_chef/deploy.ts index 766c4efd..d5f80434 100644 --- a/src/test/fake_chef/deploy.ts +++ b/src/test/fake_chef/deploy.ts @@ -1,4 +1,4 @@ -import { ConvexError, v } from "convex/values"; +import { ConvexError, v } from "../../values"; import { mutation, query } from "./_generated/server"; import { getChatByIdOrUrlIdEnsuringAccess } from "./messages"; diff --git a/src/test/fake_chef/dev.ts b/src/test/fake_chef/dev.ts index f54cc69a..72e80ae5 100644 --- a/src/test/fake_chef/dev.ts +++ b/src/test/fake_chef/dev.ts @@ -1,5 +1,9 @@ -import { internalMutation, internalAction, internalQuery } from "./_generated/server"; -import { v } from "convex/values"; +import { + internalMutation, + internalAction, + internalQuery, +} from "./_generated/server"; +import { v } from "../../values"; import schema from "./schema"; import { internal } from "./_generated/api"; @@ -23,7 +27,9 @@ export const clearAll = internalAction({ let isCleared = false; while (!isCleared) { - isCleared = await ctx.runMutation(internal.dev.deleteFromTable, { tableName }); + isCleared = await ctx.runMutation(internal.dev.deleteFromTable, { + tableName, + }); } } }, @@ -37,13 +43,18 @@ export const findSessionForUser = internalQuery({ if (!isNaN(parseInt(normalizedGithubMemberId))) { normalizedGithubMemberId = `github|${normalizedGithubMemberId}`; } else { - throw new Error("Invalid github member id -- these should look like github|1234567890"); + throw new Error( + "Invalid github member id -- these should look like github|1234567890", + ); } } const convexMember = await ctx.db .query("convexMembers") .withIndex("byTokenIdentifier", (q) => - q.eq("tokenIdentifier", `https://auth.convex.dev/|${normalizedGithubMemberId}`), + q.eq( + "tokenIdentifier", + `https://auth.convex.dev/|${normalizedGithubMemberId}`, + ), ) .first(); if (!convexMember) { diff --git a/src/test/fake_chef/http.ts b/src/test/fake_chef/http.ts index ba184f24..a3ac5d6e 100644 --- a/src/test/fake_chef/http.ts +++ b/src/test/fake_chef/http.ts @@ -1,27 +1,32 @@ -import { httpRouter } from "convex/server"; +import { httpRouter } from "../../server"; import { httpAction, type ActionCtx } from "./_generated/server"; import { internal } from "./_generated/api"; import type { Id } from "./_generated/dataModel"; -import { ConvexError } from "convex/values"; +import { ConvexError } from "../../values"; import { openaiProxy } from "./openaiProxy"; import { corsRouter } from "convex-helpers/server/cors"; import { resendProxy } from "./resendProxy"; const http = httpRouter(); -const httpWithCors = corsRouter(http, { +const httpWithCors = corsRouter(http as never, { allowedHeaders: ["Content-Type", "X-Chef-Admin-Token"], }); // This is particularly useful with CORS, where an unhandled error won't have CORS // headers applied to it. -function httpActionWithErrorHandling(handler: (ctx: ActionCtx, request: Request) => Promise) { +function httpActionWithErrorHandling( + handler: (ctx: ActionCtx, request: Request) => Promise, +) { return httpAction(async (ctx, request) => { try { return await handler(ctx, request); } catch (e) { console.error(e); return new Response( - JSON.stringify({ error: e instanceof ConvexError ? e.message : "An unknown error occurred" }), + JSON.stringify({ + error: + e instanceof ConvexError ? e.message : "An unknown error occurred", + }), { status: 500, headers: { @@ -89,10 +94,13 @@ httpWithCors.route({ if (!chatId) { throw new ConvexError("chatId is required"); } - const storageInfo = await ctx.runQuery(internal.messages.getInitialMessagesStorageInfo, { - sessionId, - chatId, - }); + const storageInfo = await ctx.runQuery( + internal.messages.getInitialMessagesStorageInfo, + { + sessionId, + chatId, + }, + ); if (!storageInfo) { return new Response(`Chat not found: ${chatId}`, { status: 404, @@ -156,7 +164,8 @@ http.route({ headers: { "Access-Control-Allow-Origin": request.headers.get("Origin") ?? "*", "Access-Control-Allow-Methods": "POST, OPTIONS", - "Access-Control-Allow-Headers": "Content-Type, Authorization, X-Chef-Admin-Token", + "Access-Control-Allow-Headers": + "Content-Type, Authorization, X-Chef-Admin-Token", "Access-Control-Allow-Credentials": "true", }, }); @@ -173,19 +182,28 @@ http.route({ const authHeader = request.headers.get("Authorization"); if (authHeader === null) { if (header !== process.env.CHEF_ADMIN_TOKEN) { - return new Response(JSON.stringify({ code: "Unauthorized", message: "Invalid admin token" }), { - status: 401, - headers: { - "Content-Type": "application/json", + return new Response( + JSON.stringify({ + code: "Unauthorized", + message: "Invalid admin token", + }), + { + status: 401, + headers: { + "Content-Type": "application/json", + }, }, - }); + ); } } const chatUuid = body.chatUuid; - const storageId = await ctx.runQuery(internal.messages.getMessagesByChatInitialIdBypassingAccessControl, { - id: chatUuid, - ensureAdmin: authHeader !== null, - }); + const storageId = await ctx.runQuery( + internal.messages.getMessagesByChatInitialIdBypassingAccessControl, + { + id: chatUuid, + ensureAdmin: authHeader !== null, + }, + ); if (!storageId) { return new Response(null, { status: 204, @@ -223,7 +241,10 @@ httpWithCors.route({ const promptCoreMessagesStorageId = await ctx.storage.store(messagesBlob); try { - await ctx.runMutation(internal.debugPrompt.storeDebugPrompt, { ...metadata, promptCoreMessagesStorageId }); + await ctx.runMutation(internal.debugPrompt.storeDebugPrompt, { + ...metadata, + promptCoreMessagesStorageId, + }); } catch (e) { await ctx.storage.delete(promptCoreMessagesStorageId); throw e; @@ -255,18 +276,28 @@ httpWithCors.route({ // Validate content type const contentType = imageBlob.type; if (!contentType.startsWith("image/")) { - return new Response(JSON.stringify({ error: "Invalid file type. Only images are allowed." }), { - status: 400, - headers: { "Content-Type": "application/json" }, - }); + return new Response( + JSON.stringify({ + error: "Invalid file type. Only images are allowed.", + }), + { + status: 400, + headers: { "Content-Type": "application/json" }, + }, + ); } const MAX_THUMBNAIL_SIZE = 5 * 1024 * 1024; if (imageBlob.size > MAX_THUMBNAIL_SIZE) { - return new Response(JSON.stringify({ error: "Thumbnail image exceeds maximum size of 5MB" }), { - status: 413, // Payload Too Large - headers: { "Content-Type": "application/json" }, - }); + return new Response( + JSON.stringify({ + error: "Thumbnail image exceeds maximum size of 5MB", + }), + { + status: 413, // Payload Too Large + headers: { "Content-Type": "application/json" }, + }, + ); } const storageId = await ctx.storage.store(imageBlob); diff --git a/src/test/fake_chef/lz4.ts b/src/test/fake_chef/lz4.ts index ec65f04e..f25e0181 100644 --- a/src/test/fake_chef/lz4.ts +++ b/src/test/fake_chef/lz4.ts @@ -20,7 +20,10 @@ export class Lz4 { private heap_next: number; constructor() { - this.textDecoder = new TextDecoder("utf-8", { ignoreBOM: true, fatal: true }); + this.textDecoder = new TextDecoder("utf-8", { + ignoreBOM: true, + fatal: true, + }); this.textDecoder.decode(); this.heap = new Array(32).fill(undefined); @@ -45,7 +48,10 @@ export class Lz4 { compress(input: Uint8Array) { try { const retptr = this.exports.__wbindgen_add_to_stack_pointer(-16); - const ptr0 = this.passArray8ToWasm0(input, this.exports.__wbindgen_malloc); + const ptr0 = this.passArray8ToWasm0( + input, + this.exports.__wbindgen_malloc, + ); const len0 = this.wasmVectorLen; this.exports.compress(retptr, ptr0, len0); var r0 = this.getInt32Memory0()[retptr / 4 + 0]; @@ -61,7 +67,10 @@ export class Lz4 { decompress(input: Uint8Array) { try { const retptr = this.exports.__wbindgen_add_to_stack_pointer(-16); - const ptr0 = this.passArray8ToWasm0(input, this.exports.__wbindgen_malloc); + const ptr0 = this.passArray8ToWasm0( + input, + this.exports.__wbindgen_malloc, + ); const len0 = this.wasmVectorLen; this.exports.decompress(retptr, ptr0, len0); var r0 = this.getInt32Memory0()[retptr / 4 + 0]; @@ -90,14 +99,19 @@ export class Lz4 { if (!this.instance) { throw new Error("Lz4 instance not initialized"); } - if (this.uint8Memory0 === null || this.uint8Memory0.buffer !== this.exports.memory.buffer) { + if ( + this.uint8Memory0 === null || + this.uint8Memory0.buffer !== this.exports.memory.buffer + ) { this.uint8Memory0 = new Uint8Array(this.exports.memory.buffer); } return this.uint8Memory0; } private getStringFromWasm0(ptr: number, len: number) { - return this.textDecoder.decode(this.getUint8Memory0().subarray(ptr, ptr + len)); + return this.textDecoder.decode( + this.getUint8Memory0().subarray(ptr, ptr + len), + ); } private passArray8ToWasm0(arg: any, malloc: any) { @@ -115,7 +129,10 @@ export class Lz4 { if (!this.instance) { throw new Error("Lz4 instance not initialized"); } - if (this.int32Memory0 === null || this.int32Memory0.buffer !== this.exports.memory.buffer) { + if ( + this.int32Memory0 === null || + this.int32Memory0.buffer !== this.exports.memory.buffer + ) { this.int32Memory0 = new Int32Array(this.exports.memory.buffer); } return this.int32Memory0; diff --git a/src/test/fake_chef/messages.ts b/src/test/fake_chef/messages.ts index 536db2ad..5309917f 100644 --- a/src/test/fake_chef/messages.ts +++ b/src/test/fake_chef/messages.ts @@ -8,11 +8,14 @@ import { type QueryCtx, } from "./_generated/server"; import type { Message as AIMessage } from "ai"; -import { ConvexError, v } from "convex/values"; -import type { Infer } from "convex/values"; +import { ConvexError, v } from "../../values"; +import type { Infer } from "../../values"; import { isValidSession } from "./sessions"; import type { Doc, Id } from "./_generated/dataModel"; -import { ensureEnvVar, startProvisionConvexProjectHelper } from "./convexProjects"; +import { + ensureEnvVar, + startProvisionConvexProjectHelper, +} from "./convexProjects"; import { internal } from "./_generated/api"; import { assertIsConvexAdmin } from "./admin"; @@ -35,7 +38,10 @@ export const initializeChat = mutation({ returns: v.null(), handler: async (ctx, args) => { const { id, sessionId, projectInitParams } = args; - let existing = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: args.id, sessionId: args.sessionId }); + let existing = await getChatByIdOrUrlIdEnsuringAccess(ctx, { + id: args.id, + sessionId: args.sessionId, + }); if (existing) { return; @@ -62,13 +68,19 @@ export const setUrlId = mutation({ }), handler: async (ctx, args) => { const { chatId, urlHint, description } = args; - const existing = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: chatId, sessionId: args.sessionId }); + const existing = await getChatByIdOrUrlIdEnsuringAccess(ctx, { + id: chatId, + sessionId: args.sessionId, + }); if (!existing) { throw new ConvexError({ code: "NotFound", message: "Chat not found" }); } if (existing.urlId === undefined) { - const urlId = await _allocateUrlId(ctx, { urlHint, sessionId: args.sessionId }); + const urlId = await _allocateUrlId(ctx, { + urlHint, + sessionId: args.sessionId, + }); await ctx.db.patch(existing._id, { urlId, description: existing.description ?? description, @@ -88,7 +100,10 @@ export const setDescription = mutation({ returns: v.null(), handler: async (ctx, args) => { const { id, description } = args; - const existing = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id, sessionId: args.sessionId }); + const existing = await getChatByIdOrUrlIdEnsuringAccess(ctx, { + id, + sessionId: args.sessionId, + }); if (!existing) { throw new ConvexError({ code: "NotFound", message: "Chat not found" }); @@ -100,7 +115,11 @@ export const setDescription = mutation({ }, }); -export async function getChat(ctx: QueryCtx, id: string, sessionId: Id<"sessions">) { +export async function getChat( + ctx: QueryCtx, + id: string, + sessionId: Id<"sessions">, +) { const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id, sessionId }); if (!chat) { @@ -152,7 +171,9 @@ export async function getLatestChatMessageStorageState( } return await ctx.db .query("chatMessagesStorageState") - .withIndex("byChatId", (q) => q.eq("chatId", chat._id).lte("lastMessageRank", lastMessageRank)) + .withIndex("byChatId", (q) => + q.eq("chatId", chat._id).lte("lastMessageRank", lastMessageRank), + ) .order("desc") .first(); } @@ -175,7 +196,10 @@ export const getInitialMessagesStorageInfo = internalQuery({ returns: v.union(v.null(), storageInfo), handler: async (ctx, args): Promise => { const { chatId, sessionId } = args; - const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: chatId, sessionId }); + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { + id: chatId, + sessionId, + }); if (!chat) { return null; } @@ -213,7 +237,10 @@ export const getMessagesByChatInitialIdBypassingAccessControl = internalQuery({ } const storageInfo = await getLatestChatMessageStorageState(ctx, chat); if (storageInfo === null) { - throw new ConvexError({ code: "NotFound", message: "Chat messages storage state not found" }); + throw new ConvexError({ + code: "NotFound", + message: "Chat messages storage state not found", + }); } return storageInfo.storageId; }, @@ -230,9 +257,19 @@ export const updateStorageState = internalMutation({ snapshotId: v.optional(v.union(v.id("_storage"), v.null())), }, handler: async (ctx, args): Promise => { - const { chatId, storageId, lastMessageRank, partIndex, snapshotId, sessionId } = args; + const { + chatId, + storageId, + lastMessageRank, + partIndex, + snapshotId, + sessionId, + } = args; const messageHistoryStorageId = storageId; - const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: chatId, sessionId }); + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { + id: chatId, + sessionId, + }); if (!chat) { throw new ConvexError({ code: "NotFound", message: "Chat not found" }); } @@ -247,14 +284,20 @@ export const updateStorageState = internalMutation({ ); return; } - if (previous.lastMessageRank === lastMessageRank && previous.partIndex > partIndex) { + if ( + previous.lastMessageRank === lastMessageRank && + previous.partIndex > partIndex + ) { console.warn( `Stale update -- stored parts in message ${previous.lastMessageRank} up to part ${previous.partIndex} but received update up to part ${partIndex}`, ); return; } - if (previous.lastMessageRank === lastMessageRank && previous.partIndex === partIndex) { + if ( + previous.lastMessageRank === lastMessageRank && + previous.partIndex === partIndex + ) { if (messageHistoryStorageId !== null && snapshotId === null) { // Should this error? console.warn( @@ -263,7 +306,9 @@ export const updateStorageState = internalMutation({ return; } if (snapshotId === null) { - throw new Error("Received null snapshotId for message that is already saved and has no storageId"); + throw new Error( + "Received null snapshotId for message that is already saved and has no storageId", + ); } await ctx.db.patch(previous._id, { snapshotId, @@ -277,7 +322,9 @@ export const updateStorageState = internalMutation({ if (previous.lastMessageRank === lastMessageRank) { if (previous.partIndex >= partIndex) { - throw new Error("Tried to update to a part that is already stored. Should have already returned."); + throw new Error( + "Tried to update to a part that is already stored. Should have already returned.", + ); } // This is a part update, so we can patch instead of inserting a new document, cleaning up the old stored state. // We do not support rewinding to parts. @@ -319,7 +366,9 @@ async function deletePreviousStorageStates( // Remove the storage state records for future messages on a different branch const storageStatesToDelete = await ctx.db .query("chatMessagesStorageState") - .withIndex("byChatId", (q) => q.eq("chatId", chat._id).gt("lastMessageRank", chatLastMessageRank)) + .withIndex("byChatId", (q) => + q.eq("chatId", chat._id).gt("lastMessageRank", chatLastMessageRank), + ) .collect(); for (const storageState of storageStatesToDelete) { await ctx.db.delete(storageState._id); @@ -333,11 +382,16 @@ async function deletePreviousStorageStates( // I don't think it's possible in the current data model to have a duplicate storageId // here because there should not be duplicate rows for the same chatId, lastMessageRank, // and partIndex. Newer snapshots should be patched. - console.warn("Unexpectedly found chatHistoryRef for storageId", chatStorageId); + console.warn( + "Unexpectedly found chatHistoryRef for storageId", + chatStorageId, + ); } const shareRef = await ctx.db .query("shares") - .withIndex("byChatHistoryId", (q) => q.eq("chatHistoryId", chatStorageId)) + .withIndex("byChatHistoryId", (q) => + q.eq("chatHistoryId", chatStorageId), + ) .first(); if (shareRef === null && chatHistoryRef === null) { await ctx.storage.delete(chatStorageId); @@ -371,7 +425,10 @@ export const earliestRewindableMessageRank = query({ returns: v.union(v.null(), v.number()), handler: async (ctx, args): Promise => { const { chatId, sessionId } = args; - const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: chatId, sessionId }); + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { + id: chatId, + sessionId, + }); if (!chat) { throw new ConvexError({ code: "NotFound", message: "Chat not found" }); } @@ -382,11 +439,17 @@ export const earliestRewindableMessageRank = query({ } const docs = await ctx.db .query("chatMessagesStorageState") - .withIndex("byChatId", (q) => q.eq("chatId", chat._id).lte("lastMessageRank", latestState.lastMessageRank)) + .withIndex("byChatId", (q) => + q + .eq("chatId", chat._id) + .lte("lastMessageRank", latestState.lastMessageRank), + ) .order("asc") .collect(); - const docWithSnapshot = docs.find((doc) => doc.snapshotId !== undefined && doc.snapshotId !== null); + const docWithSnapshot = docs.find( + (doc) => doc.snapshotId !== undefined && doc.snapshotId !== null, + ); if (!docWithSnapshot) { return null; @@ -403,19 +466,33 @@ export const rewindChat = mutation({ }, handler: async (ctx, args): Promise => { const { chatId, sessionId, lastMessageRank } = args; - const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: chatId, sessionId }); + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { + id: chatId, + sessionId, + }); if (!chat) { throw new ConvexError({ code: "NotFound", message: "Chat not found" }); } - const latestStorageState = await getLatestChatMessageStorageState(ctx, { _id: chat._id, lastMessageRank }); + const latestStorageState = await getLatestChatMessageStorageState(ctx, { + _id: chat._id, + lastMessageRank, + }); if (latestStorageState === null) { - throw new Error(`Storage state not found for lastMessageRank ${lastMessageRank} in chat ${chatId}`); + throw new Error( + `Storage state not found for lastMessageRank ${lastMessageRank} in chat ${chatId}`, + ); } if (latestStorageState.storageId === null) { - throw new ConvexError({ code: "NoMessagesSaved", message: "Cannot rewind to a chat with no messages saved" }); + throw new ConvexError({ + code: "NoMessagesSaved", + message: "Cannot rewind to a chat with no messages saved", + }); } - if (chat.lastMessageRank !== undefined && chat.lastMessageRank < lastMessageRank) { + if ( + chat.lastMessageRank !== undefined && + chat.lastMessageRank < lastMessageRank + ) { throw new ConvexError({ code: "RewindToFuture", message: "Cannot rewind to a future message", @@ -444,7 +521,9 @@ export const maybeCleanupStaleChatHistory = internalMutation({ const shareRef = await ctx.db .query("shares") - .withIndex("byChatHistoryId", (q) => q.eq("chatHistoryId", args.storageId)) + .withIndex("byChatHistoryId", (q) => + q.eq("chatHistoryId", args.storageId), + ) .first(); if (shareRef !== null) { return; @@ -495,10 +574,23 @@ export const remove = action({ accessToken: v.string(), }, handler: async (ctx, args) => { - const { accessToken, id, sessionId, teamSlug, projectSlug, shouldDeleteConvexProject } = args; - let projectDeletionResult: { kind: "success" } | { kind: "error"; error: string } = { kind: "success" }; + const { + accessToken, + id, + sessionId, + teamSlug, + projectSlug, + shouldDeleteConvexProject, + } = args; + let projectDeletionResult: + | { kind: "success" } + | { kind: "error"; error: string } = { kind: "success" }; if (shouldDeleteConvexProject) { - projectDeletionResult = await tryDeleteProject({ teamSlug, projectSlug, accessToken }); + projectDeletionResult = await tryDeleteProject({ + teamSlug, + projectSlug, + accessToken, + }); } await ctx.runMutation(internal.messages.removeChat, { @@ -523,16 +615,23 @@ async function tryDeleteProject(args: { }): Promise<{ kind: "success" } | { kind: "error"; error: string }> { const { teamSlug, projectSlug, accessToken } = args; if (teamSlug === undefined || projectSlug === undefined) { - return { kind: "error", error: "Team slug and project slug are required to delete a Convex project" }; + return { + kind: "error", + error: + "Team slug and project slug are required to delete a Convex project", + }; } const bigBrainHost = ensureEnvVar("BIG_BRAIN_HOST"); - const projectsResponse = await fetch(`${bigBrainHost}/api/teams/${teamSlug}/projects`, { - headers: { - Authorization: `Bearer ${accessToken}`, + const projectsResponse = await fetch( + `${bigBrainHost}/api/teams/${teamSlug}/projects`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + }, }, - }); + ); if (!projectsResponse.ok) { const text = await projectsResponse.text(); @@ -541,9 +640,15 @@ async function tryDeleteProject(args: { if (error.code === "TeamNotFound") { return { kind: "error", error: `Team not found: ${teamSlug}` }; } - return { kind: "error", error: `Failed to fetch team projects: ${projectsResponse.statusText} ${text}` }; + return { + kind: "error", + error: `Failed to fetch team projects: ${projectsResponse.statusText} ${text}`, + }; } catch (_e) { - return { kind: "error", error: `Failed to fetch team projects: ${projectsResponse.statusText} ${text}` }; + return { + kind: "error", + error: `Failed to fetch team projects: ${projectsResponse.statusText} ${text}`, + }; } } @@ -551,16 +656,22 @@ async function tryDeleteProject(args: { const project = projects.find((p: any) => p.slug === projectSlug); if (project) { - const response = await fetch(`${bigBrainHost}/api/dashboard/delete_project/${project.id}`, { - method: "POST", - headers: { - Authorization: `Bearer ${accessToken}`, + const response = await fetch( + `${bigBrainHost}/api/dashboard/delete_project/${project.id}`, + { + method: "POST", + headers: { + Authorization: `Bearer ${accessToken}`, + }, }, - }); + ); if (!response.ok) { const text = await response.text(); - return { kind: "error", error: `Failed to delete project: ${response.statusText} ${text}` }; + return { + kind: "error", + error: `Failed to delete project: ${response.statusText} ${text}`, + }; } } @@ -573,7 +684,10 @@ export const removeChat = internalMutation({ sessionId: v.id("sessions"), }, handler: async (ctx, args) => { - const existing = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: args.id, sessionId: args.sessionId }); + const existing = await getChatByIdOrUrlIdEnsuringAccess(ctx, { + id: args.id, + sessionId: args.sessionId, + }); if (!existing) { return; @@ -584,7 +698,9 @@ export const removeChat = internalMutation({ const credentials = await ctx.db .query("convexProjectCredentials") .withIndex("bySlugs", (q) => - q.eq("teamSlug", convexProject.teamSlug).eq("projectSlug", convexProject.projectSlug), + q + .eq("teamSlug", convexProject.teamSlug) + .eq("projectSlug", convexProject.projectSlug), ) .unique(); if (credentials !== null) { @@ -597,7 +713,10 @@ export const removeChat = internalMutation({ }, }); -async function _allocateUrlId(ctx: QueryCtx, { urlHint, sessionId }: { urlHint: string; sessionId: Id<"sessions"> }) { +async function _allocateUrlId( + ctx: QueryCtx, + { urlHint, sessionId }: { urlHint: string; sessionId: Id<"sessions"> }, +) { const existing = await getChatByUrlId(ctx, { id: urlHint, sessionId }); if (existing === null) { @@ -631,10 +750,16 @@ export async function createNewChat( }, ): Promise> { const { id, sessionId, projectInitParams } = args; - const existing = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id, sessionId }); + const existing = await getChatByIdOrUrlIdEnsuringAccess(ctx, { + id, + sessionId, + }); if (existing) { - throw new ConvexError({ code: "InvalidState", message: "Chat already exists" }); + throw new ConvexError({ + code: "InvalidState", + message: "Chat already exists", + }); } const session = await ctx.db.get(sessionId); @@ -666,17 +791,27 @@ export async function createNewChat( return chatId; } -function getChatByInitialId(ctx: QueryCtx, { id, sessionId }: { id: string; sessionId: Id<"sessions"> }) { +function getChatByInitialId( + ctx: QueryCtx, + { id, sessionId }: { id: string; sessionId: Id<"sessions"> }, +) { return ctx.db .query("chats") - .withIndex("byCreatorAndId", (q) => q.eq("creatorId", sessionId).eq("initialId", id).lt("isDeleted", true)) + .withIndex("byCreatorAndId", (q) => + q.eq("creatorId", sessionId).eq("initialId", id).lt("isDeleted", true), + ) .unique(); } -function getChatByUrlId(ctx: QueryCtx, { id, sessionId }: { id: string; sessionId: Id<"sessions"> }) { +function getChatByUrlId( + ctx: QueryCtx, + { id, sessionId }: { id: string; sessionId: Id<"sessions"> }, +) { return ctx.db .query("chats") - .withIndex("byCreatorAndUrlId", (q) => q.eq("creatorId", sessionId).eq("urlId", id).lt("isDeleted", true)) + .withIndex("byCreatorAndUrlId", (q) => + q.eq("creatorId", sessionId).eq("urlId", id).lt("isDeleted", true), + ) .unique(); } @@ -724,19 +859,26 @@ export const eraseMessageHistory = internalMutation({ .order("desc") .first(); if (mostRecentStorageState === null) { - throw new ConvexError({ code: "NotFound", message: "No storage state found for chat" }); + throw new ConvexError({ + code: "NotFound", + message: "No storage state found for chat", + }); } console.log("Most recent storage state is", mostRecentStorageState); const mostRecentFilesystemSnapshot = mostRecentStorageState.snapshotId; if (mostRecentFilesystemSnapshot === undefined) { - throw new ConvexError({ code: "NotFound", message: "No filesystem snapshot found for chat" }); + throw new ConvexError({ + code: "NotFound", + message: "No filesystem snapshot found for chat", + }); } const earliestMessages = await ctx.db .query("chatMessagesStorageState") .withIndex("byChatId", (q) => q.eq("chatId", share.chatId)) .order("asc") .take(100); - let earliestMessageWithSnapshot: Doc<"chatMessagesStorageState"> | null = null; + let earliestMessageWithSnapshot: Doc<"chatMessagesStorageState"> | null = + null; for (const storageState of earliestMessages) { if (storageState.snapshotId !== undefined) { earliestMessageWithSnapshot = storageState; @@ -746,7 +888,8 @@ export const eraseMessageHistory = internalMutation({ if (earliestMessageWithSnapshot === null) { throw new ConvexError({ code: "NotFound", - message: "No message with snapshot found for chat after looking at the first 100 message states", + message: + "No message with snapshot found for chat after looking at the first 100 message states", }); } @@ -754,7 +897,9 @@ export const eraseMessageHistory = internalMutation({ const latestEarlyStorageState = await ctx.db .query("chatMessagesStorageState") .withIndex("byChatId", (q) => - q.eq("chatId", share.chatId).eq("lastMessageRank", earliestMessageWithSnapshot.lastMessageRank), + q + .eq("chatId", share.chatId) + .eq("lastMessageRank", earliestMessageWithSnapshot.lastMessageRank), ) .order("desc") .first(); @@ -773,11 +918,16 @@ export const eraseMessageHistory = internalMutation({ mostRecentFilesystemSnapshot, ); if (!dryRun) { - await ctx.db.patch(latestEarlyStorageState._id, { snapshotId: mostRecentFilesystemSnapshot }); + await ctx.db.patch(latestEarlyStorageState._id, { + snapshotId: mostRecentFilesystemSnapshot, + }); } // Rewind the chat to look at the lastMessageRank of the earliestMessageWithSnapshot - console.log("Rewinding chat to lastMessageRank", earliestMessageWithSnapshot.lastMessageRank); + console.log( + "Rewinding chat to lastMessageRank", + earliestMessageWithSnapshot.lastMessageRank, + ); if (!dryRun) { await ctx.db.patch(share.chatId, { lastMessageRank: earliestMessageWithSnapshot.lastMessageRank, diff --git a/src/test/fake_chef/migrations.ts b/src/test/fake_chef/migrations.ts deleted file mode 100644 index 1ca5cb80..00000000 --- a/src/test/fake_chef/migrations.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { Migrations } from "@convex-dev/migrations"; -import { components, internal } from "./_generated/api.js"; - -export const migrations = new Migrations(components.migrations); -export const run = migrations.runner(); - -export const setDefaultDeletedFalse = migrations.define({ - table: "chats", - migrateOne: async (ctx, doc) => { - if (doc.isDeleted === undefined) { - await ctx.db.patch(doc._id, { isDeleted: false }); - } - }, -}); - -export const runSetDefaultDeletedFalse = migrations.runner(internal.migrations.setDefaultDeletedFalse); - -export const addLastSubchatIndex = migrations.define({ - table: "chats", - migrateOne: async (ctx, doc) => { - if (doc.lastSubchatIndex === undefined) { - await ctx.db.patch(doc._id, { lastSubchatIndex: 0 }); - } - }, -}); - -export const runAddLastSubchatIndex = migrations.runner(internal.migrations.addLastSubchatIndex); - -export const addSubchatIndex = migrations.define({ - table: "chatMessagesStorageState", - migrateOne: async (ctx, doc) => { - if (doc.subchatIndex === undefined) { - await ctx.db.patch(doc._id, { subchatIndex: 0 }); - } - }, -}); - -export const runAddSubchatIndex = migrations.runner(internal.migrations.addSubchatIndex); - -export const addLastSubchatIndexToShares = migrations.define({ - table: "shares", - migrateOne: async (ctx, doc) => { - if (doc.lastSubchatIndex === undefined) { - await ctx.db.patch(doc._id, { lastSubchatIndex: 0 }); - } - }, -}); - -export const runAddLastSubchatIndexToShares = migrations.runner(internal.migrations.addLastSubchatIndexToShares); diff --git a/src/test/fake_chef/openaiProxy.ts b/src/test/fake_chef/openaiProxy.ts index 510d4b48..ac8f5d62 100644 --- a/src/test/fake_chef/openaiProxy.ts +++ b/src/test/fake_chef/openaiProxy.ts @@ -1,4 +1,4 @@ -import { v } from "convex/values"; +import { v } from "../../values"; import { httpAction, internalMutation, mutation } from "./_generated/server"; import { getCurrentMember } from "./sessions"; import { internal } from "./_generated/api"; @@ -21,14 +21,18 @@ export const openaiProxy = httpAction(async (ctx, req) => { return new Response("Invalid authorization header", { status: 401 }); } const token = authHeader.slice(7); - const result = await ctx.runMutation(internal.openaiProxy.decrementToken, { token }); + const result = await ctx.runMutation(internal.openaiProxy.decrementToken, { + token, + }); if (!result.success) { return new Response(result.error, { status: 401 }); } const url = new URL(req.url); if (url.pathname != "/openai-proxy/chat/completions") { - return new Response("Only the /chat/completions API is supported", { status: 400 }); + return new Response("Only the /chat/completions API is supported", { + status: 400, + }); } let body: any; @@ -38,11 +42,15 @@ export const openaiProxy = httpAction(async (ctx, req) => { return new Response("Invalid request body", { status: 400 }); } if (!ALLOWED_MODELS.includes(body.model)) { - return new Response("Only gpt-4o-mini and gpt-4.1-nano are supported", { status: 400 }); + return new Response("Only gpt-4o-mini and gpt-4.1-nano are supported", { + status: 400, + }); } if (body.max_completion_tokens && body.max_completion_tokens > 16384) { - return new Response("max_completion_tokens must be <= 16384", { status: 400 }); + return new Response("max_completion_tokens must be <= 16384", { + status: 400, + }); } if (body.max_tokens && body.max_tokens > 16384) { return new Response("max_tokens must be <= 16384", { status: 400 }); diff --git a/src/test/fake_chef/rateLimiter.ts b/src/test/fake_chef/rateLimiter.ts index c9cba295..4951268e 100644 --- a/src/test/fake_chef/rateLimiter.ts +++ b/src/test/fake_chef/rateLimiter.ts @@ -6,7 +6,7 @@ function resendProxyEmailsPerMinute() { return fromEnv ? parseInt(fromEnv) : 20; } -export const rateLimiter = new RateLimiter(components.rateLimiter, { +export const rateLimiter = new RateLimiter(components.rateLimiter as never, { resendProxy: { kind: "token bucket", // Permit 20 requests per minute => ~900k emails per month => ~$650/month on Resend's scale plan. diff --git a/src/test/fake_chef/resendProxy.ts b/src/test/fake_chef/resendProxy.ts index 2234ade2..96298447 100644 --- a/src/test/fake_chef/resendProxy.ts +++ b/src/test/fake_chef/resendProxy.ts @@ -1,4 +1,4 @@ -import { v } from "convex/values"; +import { v } from "../../values"; import { httpAction, internalMutation, mutation } from "./_generated/server"; import { getCurrentMember } from "./sessions"; import { internal } from "./_generated/api"; @@ -8,7 +8,9 @@ const MAX_RATELIMITER_WAIT = 60 * 1000; export const resendProxy = httpAction(async (ctx, req) => { if (!resendProxyEnabled()) { - return new Response(JSON.stringify("Convex Resend proxy is disabled."), { status: 400 }); + return new Response(JSON.stringify("Convex Resend proxy is disabled."), { + status: 400, + }); } if (!process.env.RESEND_API_KEY) { throw new Error("RESEND_API_KEY is not set"); @@ -16,7 +18,9 @@ export const resendProxy = httpAction(async (ctx, req) => { const url = new URL(req.url); if (url.pathname != "/resend-proxy/emails") { - return new Response(JSON.stringify("Only the /emails API is supported"), { status: 400 }); + return new Response(JSON.stringify("Only the /emails API is supported"), { + status: 400, + }); } const headers = new Headers(req.headers); @@ -27,21 +31,33 @@ export const resendProxy = httpAction(async (ctx, req) => { recipientEmail = body.to; } else { if (!Array.isArray(body.to) || body.to.length !== 1) { - return new Response(JSON.stringify("Convex Resend proxy only supports one recipient."), { status: 400 }); + return new Response( + JSON.stringify("Convex Resend proxy only supports one recipient."), + { status: 400 }, + ); } recipientEmail = body.to[0]; } if (body.bcc || body.cc) { - return new Response(JSON.stringify("Convex Resend proxy does not support bcc or cc."), { status: 400 }); + return new Response( + JSON.stringify("Convex Resend proxy does not support bcc or cc."), + { status: 400 }, + ); } if (body.scheduled_at) { - return new Response(JSON.stringify("Convex Resend proxy does not support scheduled emails."), { status: 400 }); + return new Response( + JSON.stringify("Convex Resend proxy does not support scheduled emails."), + { status: 400 }, + ); } if (body.headers) { - return new Response(JSON.stringify("Convex Resend proxy does not support custom headers."), { status: 400 }); + return new Response( + JSON.stringify("Convex Resend proxy does not support custom headers."), + { status: 400 }, + ); } const authHeader = headers.get("Authorization"); @@ -49,10 +65,15 @@ export const resendProxy = httpAction(async (ctx, req) => { return new Response(JSON.stringify("Unauthorized"), { status: 401 }); } if (!authHeader.startsWith("Bearer ")) { - return new Response(JSON.stringify("Invalid authorization header"), { status: 401 }); + return new Response(JSON.stringify("Invalid authorization header"), { + status: 401, + }); } const token = authHeader.slice(7); - const result = await ctx.runMutation(internal.resendProxy.decrementToken, { token, recipientEmail }); + const result = await ctx.runMutation(internal.resendProxy.decrementToken, { + token, + recipientEmail, + }); if (!result.success) { return new Response(JSON.stringify(result.error), { status: 401 }); } @@ -67,15 +88,23 @@ export const resendProxy = httpAction(async (ctx, req) => { } const now = Date.now(); if (now > deadline) { - return new Response(JSON.stringify("Rate limit exceeded"), { status: 429 }); + return new Response(JSON.stringify("Rate limit exceeded"), { + status: 429, + }); } const remainingTime = deadline - now; - const waitTime = Math.min(status.retryAfter * (1 + Math.random()), remainingTime); + const waitTime = Math.min( + status.retryAfter * (1 + Math.random()), + remainingTime, + ); console.warn(`Rate limit exceeded, waiting ${waitTime}ms`); await new Promise((resolve) => setTimeout(resolve, waitTime)); } - const deploymentName = process.env.CONVEX_CLOUD_URL?.replace("https://", "").replace(".convex.cloud", ""); + const deploymentName = process.env.CONVEX_CLOUD_URL?.replace( + "https://", + "", + ).replace(".convex.cloud", ""); return await fetch("https://api.resend.com/emails", { method: "POST", headers: { @@ -113,7 +142,10 @@ export const decrementToken = internalMutation({ return { success: false, error: "Invalid RESEND_API_TOKEN" }; } if (token.requestsRemaining <= 0) { - return { success: false, error: "Resend API token has no requests remaining." }; + return { + success: false, + error: "Resend API token has no requests remaining.", + }; } if (token.verifiedEmail !== args.recipientEmail) { return { diff --git a/src/test/fake_chef/schema.ts b/src/test/fake_chef/schema.ts index 9ceb3be4..45b34a5b 100644 --- a/src/test/fake_chef/schema.ts +++ b/src/test/fake_chef/schema.ts @@ -1,6 +1,6 @@ -import { defineSchema, defineTable } from "convex/server"; -import { v } from "convex/values"; -import type { Infer, Validator } from "convex/values"; +import { defineSchema, defineTable } from "../../server"; +import { v } from "../../values"; +import type { Infer, Validator } from "../../values"; import type { CoreMessage } from "ai"; export const apiKeyValidator = v.object({ @@ -158,7 +158,11 @@ export default defineSchema({ code: v.string(), thumbnailImageStorageId: v.optional(v.id("_storage")), // Does the share link work. Three states so we can immediately share on opening the share dialog. - shared: v.union(v.literal("shared"), v.literal("expresslyUnshared"), v.literal("noPreferenceExpressed")), + shared: v.union( + v.literal("shared"), + v.literal("expresslyUnshared"), + v.literal("noPreferenceExpressed"), + ), // Allow others to fork this project at its most recent state. Always true for now. allowForkFromLatest: v.boolean(), // Allow to be shown in gallery (doesn't mean we actual show it). @@ -208,7 +212,9 @@ export default defineSchema({ // Such a loose type doesn't feel so bad since this is debugging data, but if we try // to display older versions of this we need to make any fields added to CoreMessage in // later versions of the Vercel AI SDK optional on the read path. - responseCoreMessages: v.array(v.any() as Validator), + responseCoreMessages: v.array( + v.any() as Validator, + ), promptCoreMessagesStorageId: v.id("_storage"), finishReason: v.string(), modelId: v.string(), diff --git a/src/test/fake_chef/sessions.ts b/src/test/fake_chef/sessions.ts index 0dadcaaf..b7c4cd81 100644 --- a/src/test/fake_chef/sessions.ts +++ b/src/test/fake_chef/sessions.ts @@ -1,6 +1,13 @@ -import { v } from "convex/values"; -import { action, internalMutation, mutation, query, type MutationCtx, type QueryCtx } from "./_generated/server"; -import { ConvexError } from "convex/values"; +import { v } from "../../values"; +import { + action, + internalMutation, + mutation, + query, + type MutationCtx, + type QueryCtx, +} from "./_generated/server"; +import { ConvexError } from "../../values"; import type { Id } from "./_generated/dataModel"; import { getChatByIdOrUrlIdEnsuringAccess } from "./messages"; import { internal } from "./_generated/api"; @@ -20,16 +27,25 @@ export const verifySession = query({ if (!session || !session.memberId) { return false; } - return isValidSessionForConvexOAuth(ctx, { sessionId, memberId: session.memberId }); + return isValidSessionForConvexOAuth(ctx, { + sessionId, + memberId: session.memberId, + }); }, }); -export async function isValidSession(ctx: QueryCtx, args: { sessionId: Id<"sessions"> }) { +export async function isValidSession( + ctx: QueryCtx, + args: { sessionId: Id<"sessions"> }, +) { const session = await ctx.db.get(args.sessionId); if (!session || !session.memberId) { return false; } - return await isValidSessionForConvexOAuth(ctx, { sessionId: args.sessionId, memberId: session.memberId }); + return await isValidSessionForConvexOAuth(ctx, { + sessionId: args.sessionId, + memberId: session.memberId, + }); } async function isValidSessionForConvexOAuth( @@ -65,11 +81,17 @@ export const registerConvexOAuthConnection = internalMutation({ sessionId: args.sessionId, }); if (!chat) { - throw new ConvexError({ code: "NotAuthorized", message: "Chat not found" }); + throw new ConvexError({ + code: "NotAuthorized", + message: "Chat not found", + }); } const session = await ctx.db.get(args.sessionId); if (!session || !session.memberId) { - throw new ConvexError({ code: "NotAuthorized", message: "Chat not found" }); + throw new ConvexError({ + code: "NotAuthorized", + message: "Chat not found", + }); } await ctx.db.patch(args.chatId, { convexProject: { @@ -82,7 +104,9 @@ export const registerConvexOAuthConnection = internalMutation({ }); const credentials = await ctx.db .query("convexProjectCredentials") - .withIndex("bySlugs", (q) => q.eq("teamSlug", args.teamSlug).eq("projectSlug", args.projectSlug)) + .withIndex("bySlugs", (q) => + q.eq("teamSlug", args.teamSlug).eq("projectSlug", args.projectSlug), + ) .collect(); if (credentials.length === 0) { await ctx.db.insert("convexProjectCredentials", { @@ -120,7 +144,9 @@ async function getOrCreateCurrentMember(ctx: MutationCtx) { } const existingMember = await ctx.db .query("convexMembers") - .withIndex("byTokenIdentifier", (q) => q.eq("tokenIdentifier", identity.tokenIdentifier)) + .withIndex("byTokenIdentifier", (q) => + q.eq("tokenIdentifier", identity.tokenIdentifier), + ) .unique(); if (existingMember) { return existingMember._id; @@ -137,7 +163,9 @@ export async function getCurrentMember(ctx: QueryCtx) { } const existingMember = await ctx.db .query("convexMembers") - .withIndex("byTokenIdentifier", (q) => q.eq("tokenIdentifier", identity.tokenIdentifier)) + .withIndex("byTokenIdentifier", (q) => + q.eq("tokenIdentifier", identity.tokenIdentifier), + ) .unique(); if (!existingMember) { throw new ConvexError({ code: "NotAuthorized", message: "Unauthorized" }); @@ -187,13 +215,16 @@ export const updateCachedProfile = action({ }); if (!response.ok) { const body = await response.text(); - throw new Error(`Failed to fetch profile: ${response.statusText}: ${body}`); + throw new Error( + `Failed to fetch profile: ${response.statusText}: ${body}`, + ); } const convexProfile: ConvexProfile = await response.json(); const profile = { - username: convexProfile.name || auth0Profile.name || auth0Profile.nickname || "", + username: + convexProfile.name || auth0Profile.name || auth0Profile.nickname || "", email: convexProfile.email || auth0Profile.email || "", avatar: auth0Profile.pictureUrl || "", id: convexProfile.id || auth0Profile.subject || "", diff --git a/src/test/fake_chef/share.ts b/src/test/fake_chef/share.ts index c7c95d3d..4e03e562 100644 --- a/src/test/fake_chef/share.ts +++ b/src/test/fake_chef/share.ts @@ -1,7 +1,10 @@ -import { ConvexError, v } from "convex/values"; +import { ConvexError, v } from "../../values"; import { mutation, query, type DatabaseReader } from "./_generated/server"; import type { MutationCtx } from "./_generated/server"; -import { getChatByIdOrUrlIdEnsuringAccess, getLatestChatMessageStorageState } from "./messages"; +import { + getChatByIdOrUrlIdEnsuringAccess, + getLatestChatMessageStorageState, +} from "./messages"; import { startProvisionConvexProjectHelper } from "./convexProjects"; import type { Id } from "./_generated/dataModel"; @@ -122,7 +125,11 @@ async function cloneShow( showCode, sessionId, projectInitParams, - }: { showCode: string; sessionId: Id<"sessions">; projectInitParams: { teamSlug: string; auth0AccessToken: string } }, + }: { + showCode: string; + sessionId: Id<"sessions">; + projectInitParams: { teamSlug: string; auth0AccessToken: string }; + }, ): Promise<{ id: string; description?: string }> { const show = await ctx.db .query("socialShares") @@ -206,7 +213,11 @@ export const clone = mutation({ .withIndex("byCode", (q) => q.eq("code", shareCode)) .first(); if (!getShare) { - return cloneShow(ctx, { showCode: shareCode, sessionId, projectInitParams }); + return cloneShow(ctx, { + showCode: shareCode, + sessionId, + projectInitParams, + }); } const parentChat = await ctx.db.get(getShare.chatId); @@ -231,7 +242,8 @@ export const clone = mutation({ if (!getShare.chatHistoryId) { throw new ConvexError({ code: "NotFound", - message: "The original chat history was not found. It may have been deleted.", + message: + "The original chat history was not found. It may have been deleted.", }); } await ctx.db.insert("chatMessagesStorageState", { diff --git a/src/test/fake_chef/snapshot.ts b/src/test/fake_chef/snapshot.ts index 998268c9..aeaafee1 100644 --- a/src/test/fake_chef/snapshot.ts +++ b/src/test/fake_chef/snapshot.ts @@ -1,6 +1,9 @@ import { internalMutation, query } from "./_generated/server"; -import { v } from "convex/values"; -import { getChatByIdOrUrlIdEnsuringAccess, getLatestChatMessageStorageState } from "./messages"; +import { v } from "../../values"; +import { + getChatByIdOrUrlIdEnsuringAccess, + getLatestChatMessageStorageState, +} from "./messages"; // Save the snapshot information after successful upload export const saveSnapshot = internalMutation({ @@ -10,7 +13,10 @@ export const saveSnapshot = internalMutation({ storageId: v.id("_storage"), }, handler: async (ctx, { sessionId, chatId, storageId }) => { - const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: chatId, sessionId }); + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { + id: chatId, + sessionId, + }); if (!chat) { throw new Error("Chat not found"); @@ -27,11 +33,17 @@ export const getSnapshotUrl = query({ chatId: v.string(), }, handler: async (ctx, { sessionId, chatId }) => { - const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: chatId, sessionId }); + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { + id: chatId, + sessionId, + }); if (!chat) { throw new Error("Chat not found"); } - const latestChatStorageState = await getLatestChatMessageStorageState(ctx, chat); + const latestChatStorageState = await getLatestChatMessageStorageState( + ctx, + chat, + ); if (latestChatStorageState?.snapshotId) { const url = await ctx.storage.getUrl(latestChatStorageState.snapshotId); return url; @@ -45,7 +57,9 @@ export const getSnapshotUrl = query({ } const snapshot = await ctx.storage.getUrl(snapshotId); if (!snapshot) { - throw new Error(`Expected to find a storageUrl for snapshot with id ${snapshotId}`); + throw new Error( + `Expected to find a storageUrl for snapshot with id ${snapshotId}`, + ); } return snapshot; }, diff --git a/src/test/fake_chef/socialShare.ts b/src/test/fake_chef/socialShare.ts index 3db4abaf..d2c85b1e 100644 --- a/src/test/fake_chef/socialShare.ts +++ b/src/test/fake_chef/socialShare.ts @@ -1,4 +1,4 @@ -import { ConvexError, v } from "convex/values"; +import { ConvexError, v } from "../../values"; import { mutation, query, internalMutation } from "./_generated/server"; import type { QueryCtx } from "./_generated/server"; import { getChatByIdOrUrlIdEnsuringAccess } from "./messages"; @@ -9,17 +9,26 @@ export const share = mutation({ args: { sessionId: v.id("sessions"), id: v.string(), - shared: v.union(v.literal("shared"), v.literal("expresslyUnshared"), v.literal("noPreferenceExpressed")), + shared: v.union( + v.literal("shared"), + v.literal("expresslyUnshared"), + v.literal("noPreferenceExpressed"), + ), allowForkFromLatest: v.boolean(), thumbnailImageStorageId: v.optional(v.id("_storage")), referralCode: v.optional(v.union(v.string(), v.null())), }, - handler: async (ctx, { sessionId, id, shared, allowForkFromLatest, referralCode }) => { + handler: async ( + ctx, + { sessionId, id, shared, allowForkFromLatest, referralCode }, + ) => { // Validate referral code if set if (referralCode !== undefined && referralCode !== null) { // Only allow alphanumeric, dashes, and underscores if (!/^[a-zA-Z0-9_-]+$/.test(referralCode)) { - throw new ConvexError("Invalid referral code: must be alphanumeric, dashes, or underscores only"); + throw new ConvexError( + "Invalid referral code: must be alphanumeric, dashes, or underscores only", + ); } } const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id, sessionId }); @@ -105,7 +114,9 @@ async function getSocialShareInner(ctx: QueryCtx, code: string) { } const session = await ctx.db.get(chat.creatorId); - const authorProfile = session?.memberId ? ((await ctx.db.get(session.memberId))?.cachedProfile ?? null) : null; + const authorProfile = session?.memberId + ? ((await ctx.db.get(session.memberId))?.cachedProfile ?? null) + : null; const chatHasBeenDeployed = !!chat.hasBeenDeployed; @@ -173,7 +184,10 @@ export const saveThumbnail = internalMutation({ storageId: v.id("_storage"), }, handler: async (ctx, { sessionId, urlId, storageId }) => { - const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { id: urlId, sessionId }); + const chat = await getChatByIdOrUrlIdEnsuringAccess(ctx, { + id: urlId, + sessionId, + }); if (!chat) { throw new ConvexError("Chat not found"); } @@ -224,7 +238,9 @@ export const createAdminShare = internalMutation({ .withIndex("byChatId", (q) => q.eq("chatId", chatId)) .unique(); if (existing) { - console.log(`Already have a share for chat ${chatId}: Go to https://chef.show/${existing.code}`); + console.log( + `Already have a share for chat ${chatId}: Go to https://chef.show/${existing.code}`, + ); return; } @@ -238,6 +254,8 @@ export const createAdminShare = internalMutation({ allowForkFromLatest: true, allowShowInGallery: false, }); - console.log(`Created admin share for chat ${chatId}. Go to https://chef.show/${code}`); + console.log( + `Created admin share for chat ${chatId}. Go to https://chef.show/${code}`, + ); }, }); diff --git a/tsconfig.json b/tsconfig.json index bb1e1f64..d8c252db 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,6 @@ { "include": ["src", ".eslintrc.cjs"], + "exclude": ["src/test"], "compileOnSave": true, "compilerOptions": { /* Basic Options */ From 5dd8580c06d2e845181b7621d8c056b541f9ff23 Mon Sep 17 00:00:00 2001 From: David Blass Date: Wed, 20 Aug 2025 19:41:41 -0400 Subject: [PATCH 04/12] begin optimizing ApiFromModules --- src/server/api.ts | 109 ++++++++++++------------- src/server/components/index.ts | 10 +-- src/test/fake_chef/_generated/api.d.ts | 22 ++--- src/test/types.bench.ts | 66 ++++++++++++++- 4 files changed, 130 insertions(+), 77 deletions(-) diff --git a/src/server/api.ts b/src/server/api.ts index f5c1aef5..1b23f037 100644 --- a/src/server/api.ts +++ b/src/server/api.ts @@ -1,15 +1,14 @@ +import { getFunctionAddress } from "./components/paths.js"; +import { functionName } from "./functionName.js"; +import { PaginationOptions, PaginationResult } from "./pagination.js"; import { - EmptyObject, DefaultFunctionArgs, + EmptyObject, FunctionVisibility, RegisteredAction, RegisteredMutation, RegisteredQuery, } from "./registration.js"; -import { Expand, UnionToIntersection } from "../type_utils.js"; -import { PaginationOptions, PaginationResult } from "./pagination.js"; -import { functionName } from "./functionName.js"; -import { getFunctionAddress } from "./components/paths.js"; /** * The type of a Convex function. @@ -63,6 +62,18 @@ export type FunctionReference< _componentPath: ComponentPath; }; +// useful pattern for a collection of utilities related to a type +// eslint-disable-next-line @typescript-eslint/no-redeclare +export declare namespace FunctionReference { + /** + * A {@link FunctionReference} of any type and any visibility with any + * arguments and any return type. + * + * @public + */ + export type Any = FunctionReference; +} + /** * Get the name of a function from a {@link FunctionReference}. * @@ -76,7 +87,7 @@ export type FunctionReference< * @public */ export function getFunctionName( - functionReference: AnyFunctionReference, + functionReference: FunctionReference.Any, ): string { const address = getFunctionAddress(functionReference); @@ -236,11 +247,18 @@ type FunctionReferencesInModule> = { type ApiForModule< ModulePath extends string, Module extends object, -> = ModulePath extends `${infer First}/${infer Second}` - ? { - [_ in First]: ApiForModule; - } - : { [_ in ModulePath]: FunctionReferencesInModule }; +> = OmitEmptyObjects< + ModulePath extends `${infer First}/${infer Second}` + ? { + [_ in First]: ApiForModule; + } + : FunctionReferencesInModule +>; + +type OmitEmptyObjects = { + [K in keyof O as {} extends O[K] ? never : K]: O[K]; + // equivalent to Expand without requiring another mapped type +} & unknown; /** * Given the types of all modules in the `convex/` directory, construct the type @@ -253,22 +271,18 @@ type ApiForModule< * @public */ export type ApiFromModules> = - FilterApi< - ApiFromModulesAllowEmptyNodes, - FunctionReference - >; + OmitEmptyObjects<{ + [ModulePath in keyof AllModules as FirstModuleSegment]: ApiForModule< + SecondModuleSegment, + AllModules[ModulePath] + >; + }>; -type ApiFromModulesAllowEmptyNodes> = - ExpandModulesAndDirs< - UnionToIntersection< - { - [ModulePath in keyof AllModules]: ApiForModule< - ModulePath & string, - AllModules[ModulePath] - >; - }[keyof AllModules] - > - >; +type FirstModuleSegment = + ModulePath extends `${infer First}/${string}` ? First : ModulePath & string; + +type SecondModuleSegment = + ModulePath extends `${string}/${infer Second}` ? Second : ModulePath & string; /** * @public @@ -276,17 +290,18 @@ type ApiFromModulesAllowEmptyNodes> = * Filter a Convex deployment api object for functions which meet criteria, * for example all public queries. */ -export type FilterApi = Expand<{ +export type FilterApi = { [mod in keyof API as API[mod] extends Predicate ? mod - : API[mod] extends FunctionReference + : API[mod] extends FunctionReference.Any ? never - : FilterApi extends Record + : {} extends FilterApi ? never : mod]: API[mod] extends Predicate ? API[mod] : FilterApi; -}>; + // equivalent to Expand without requiring another mapped type +} & unknown; /** * Given an api of type API and a FunctionReference subtype, return an api object @@ -360,29 +375,9 @@ export function justSchedulable( return api as any; } -/** - * Like {@link Expand}, this simplifies how TypeScript displays object types. - * The differences are: - * 1. This version is recursive. - * 2. This stops recursing when it hits a {@link FunctionReference}. - */ -type ExpandModulesAndDirs = ObjectType extends AnyFunctionReference - ? ObjectType - : { - [Key in keyof ObjectType]: ExpandModulesAndDirs; - }; - -/** - * A {@link FunctionReference} of any type and any visibility with any - * arguments and any return type. - * - * @public - */ -export type AnyFunctionReference = FunctionReference; - type AnyModuleDirOrFunc = { [key: string]: AnyModuleDirOrFunc; -} & AnyFunctionReference; +} & FunctionReference.Any; /** * The type that Convex api objects extend. If you were writing an api from @@ -399,7 +394,7 @@ export type AnyApi = Record>; * @public */ export type PartialApi = { - [mod in keyof API]?: API[mod] extends FunctionReference + [mod in keyof API]?: API[mod] extends FunctionReference.Any ? API[mod] : PartialApi; }; @@ -415,7 +410,7 @@ export type PartialApi = { * * This supports accessing any path regardless of what directories and modules * are in your project. All function references are typed as - * {@link AnyFunctionReference}. + * {@link FunctionReference.Any}. * * * If you're using code generation, use `api` from `convex/_generated/api` @@ -432,7 +427,7 @@ export const anyApi: AnyApi = createApi() as any; * This is represented as an object mapping argument names to values. * @public */ -export type FunctionArgs = +export type FunctionArgs = FuncRef["_args"]; /** @@ -443,7 +438,7 @@ export type FunctionArgs = * * @public */ -export type OptionalRestArgs = +export type OptionalRestArgs = FuncRef["_args"] extends EmptyObject ? [args?: EmptyObject] : [args: FuncRef["_args"]]; @@ -458,7 +453,7 @@ export type OptionalRestArgs = * @public */ export type ArgsAndOptions< - FuncRef extends AnyFunctionReference, + FuncRef extends FunctionReference.Any, Options, > = FuncRef["_args"] extends EmptyObject ? [args?: EmptyObject, options?: Options] @@ -469,7 +464,7 @@ export type ArgsAndOptions< * * @public */ -export type FunctionReturnType = +export type FunctionReturnType = FuncRef["_returnType"]; type UndefinedToNull = T extends void ? null : T; diff --git a/src/server/components/index.ts b/src/server/components/index.ts index 57deee61..3690242c 100644 --- a/src/server/components/index.ts +++ b/src/server/components/index.ts @@ -1,10 +1,6 @@ import { PropertyValidators, convexToJson } from "../../values/index.js"; import { version } from "../../index.js"; -import { - AnyFunctionReference, - FunctionReference, - FunctionType, -} from "../api.js"; +import { FunctionReference, FunctionType } from "../api.js"; import { performAsyncSyscall } from "../impl/syscall.js"; import { DefaultFunctionArgs } from "../registration.js"; import { @@ -71,7 +67,7 @@ export async function createFunctionHandle< } interface ComponentExports { - [key: string]: FunctionReference | ComponentExports; + [key: string]: FunctionReference.Any | ComponentExports; } /** @@ -396,7 +392,7 @@ export function defineApp(): AppDefinition { type AnyInterfaceType = { [key: string]: AnyInterfaceType; -} & AnyFunctionReference; +} & FunctionReference.Any; export type AnyComponentReference = Record; export type AnyChildComponents = Record; diff --git a/src/test/fake_chef/_generated/api.d.ts b/src/test/fake_chef/_generated/api.d.ts index 7fd42a97..f962c8c2 100644 --- a/src/test/fake_chef/_generated/api.d.ts +++ b/src/test/fake_chef/_generated/api.d.ts @@ -35,15 +35,7 @@ import type { FunctionReference, } from "../../../server"; -/** - * A utility for referencing Convex functions in your app's API. - * - * Usage: - * ```js - * const myFunctionReference = api.myModule.myFunction; - * ``` - */ -declare const fullApi: ApiFromModules<{ +export type Modules = { admin: typeof admin; apiKeys: typeof apiKeys; cleanup: typeof cleanup; @@ -64,7 +56,17 @@ declare const fullApi: ApiFromModules<{ share: typeof share; snapshot: typeof snapshot; socialShare: typeof socialShare; -}>; +}; + +/** + * A utility for referencing Convex functions in your app's API. + * + * Usage: + * ```js + * const myFunctionReference = api.myModule.myFunction; + * ``` + */ +declare const fullApi: ApiFromModules; declare const fullApiWithMounts: typeof fullApi; export declare const api: FilterApi< diff --git a/src/test/types.bench.ts b/src/test/types.bench.ts index 7ac2eac3..69fcd7a8 100644 --- a/src/test/types.bench.ts +++ b/src/test/types.bench.ts @@ -1,6 +1,66 @@ import { bench } from "@ark/attest"; -import type { api } from "./fake_chef/_generated/api.js"; +import type { ApiFromModules } from "../server/api.js"; + +import type * as admin from "./fake_chef/admin.js"; +import type * as apiKeys from "./fake_chef/apiKeys.js"; +import type * as cleanup from "./fake_chef/cleanup.js"; +import type * as compressMessages from "./fake_chef/compressMessages.js"; +import type * as convexProjects from "./fake_chef/convexProjects.js"; +import type * as crons from "./fake_chef/crons.js"; +import type * as debugPrompt from "./fake_chef/debugPrompt.js"; +import type * as deploy from "./fake_chef/deploy.js"; +import type * as dev from "./fake_chef/dev.js"; +import type * as http from "./fake_chef/http.js"; +import type * as lz4 from "./fake_chef/lz4.js"; +import type * as lz4Wasm from "./fake_chef/lz4Wasm.js"; +import type * as messages from "./fake_chef/messages.js"; +import type * as openaiProxy from "./fake_chef/openaiProxy.js"; +import type * as rateLimiter from "./fake_chef/rateLimiter.js"; +import type * as resendProxy from "./fake_chef/resendProxy.js"; +import type * as sessions from "./fake_chef/sessions.js"; +import type * as share from "./fake_chef/share.js"; +import type * as snapshot from "./fake_chef/snapshot.js"; +import type * as socialShare from "./fake_chef/socialShare.js"; + +export type Modules = { + admin: typeof admin; + apiKeys: typeof apiKeys; + cleanup: typeof cleanup; + compressMessages: typeof compressMessages; + convexProjects: typeof convexProjects; + crons: typeof crons; + debugPrompt: typeof debugPrompt; + deploy: typeof deploy; + dev: typeof dev; + http: typeof http; + lz4: typeof lz4; + lz4Wasm: typeof lz4Wasm; + messages: typeof messages; + openaiProxy: typeof openaiProxy; + rateLimiter: typeof rateLimiter; + resendProxy: typeof resendProxy; + sessions: typeof sessions; + share: typeof share; + snapshot: typeof snapshot; + socialShare: typeof socialShare; +}; + +// type ValueOf = T[keyof T]; + +// exclude overhead of loading first module to isolate +// scaling performance as number of modules increases +bench.baseline(() => { + type Value = TransformModules; + return {} as ApiFromModules<{ admin: typeof admin }> | Value; +}); + +type TransformModules = { + [K in keyof T]: { [K1 in keyof T[K]]: T[K][K1] }[keyof T[K]]; +}[keyof T]; bench("ApiFromModules", () => { - return {} as typeof api; -}).types([115837, "instantiations"]); + type T = ApiFromModules; + + return {} as T; + // original 24168 +}).types([24168, "instantiations"]); From 7d7edd95c97570f55ad1f4fa259fd25baf53ab6d Mon Sep 17 00:00:00 2001 From: David Blass Date: Thu, 21 Aug 2025 12:56:17 -0400 Subject: [PATCH 05/12] fix build --- src/test/fake_chef/messages.ts | 2 +- src/test/fake_chef/share.ts | 2 +- tsconfig.json | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/test/fake_chef/messages.ts b/src/test/fake_chef/messages.ts index 5309917f..dd7c9d2a 100644 --- a/src/test/fake_chef/messages.ts +++ b/src/test/fake_chef/messages.ts @@ -899,7 +899,7 @@ export const eraseMessageHistory = internalMutation({ .withIndex("byChatId", (q) => q .eq("chatId", share.chatId) - .eq("lastMessageRank", earliestMessageWithSnapshot.lastMessageRank), + .eq("lastMessageRank", earliestMessageWithSnapshot?.lastMessageRank!), ) .order("desc") .first(); diff --git a/src/test/fake_chef/share.ts b/src/test/fake_chef/share.ts index 4e03e562..ff0fcea4 100644 --- a/src/test/fake_chef/share.ts +++ b/src/test/fake_chef/share.ts @@ -70,7 +70,7 @@ export const isShareReady = query({ }); // Unique across shares and socialShares in case these two url namespaces are combined. -export async function generateUniqueCode(db: DatabaseReader) { +export async function generateUniqueCode(db: DatabaseReader): Promise { const code = crypto.randomUUID().replace(/-/g, "").substring(0, 6); let existing: { _id: any } | null = await db .query("shares") diff --git a/tsconfig.json b/tsconfig.json index d8c252db..bb1e1f64 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,5 @@ { "include": ["src", ".eslintrc.cjs"], - "exclude": ["src/test"], "compileOnSave": true, "compilerOptions": { /* Basic Options */ From 576491f841ac69e7e71c435ca3bacb387494aa52 Mon Sep 17 00:00:00 2001 From: David Blass Date: Thu, 21 Aug 2025 13:24:20 -0400 Subject: [PATCH 06/12] fix segmented inference --- src/server/api.ts | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/server/api.ts b/src/server/api.ts index 1b23f037..5e8b9fb1 100644 --- a/src/server/api.ts +++ b/src/server/api.ts @@ -247,12 +247,17 @@ type FunctionReferencesInModule> = { type ApiForModule< ModulePath extends string, Module extends object, + LastPathWasSegmented extends boolean, > = OmitEmptyObjects< - ModulePath extends `${infer First}/${infer Second}` + ModulePath extends SegmentedPath ? { - [_ in First]: ApiForModule; + [_ in First]: ApiForModule; } - : FunctionReferencesInModule + : LastPathWasSegmented extends true + ? { + [_ in ModulePath]: FunctionReferencesInModule; + } + : FunctionReferencesInModule >; type OmitEmptyObjects = { @@ -272,17 +277,21 @@ type OmitEmptyObjects = { */ export type ApiFromModules> = OmitEmptyObjects<{ - [ModulePath in keyof AllModules as FirstModuleSegment]: ApiForModule< - SecondModuleSegment, - AllModules[ModulePath] - >; + [ModulePath in keyof AllModules as FirstSegment]: ModulePath extends SegmentedPath< + string, + infer Rest + > + ? ApiForModule + : ApiForModule; }>; -type FirstModuleSegment = - ModulePath extends `${infer First}/${string}` ? First : ModulePath & string; +type SegmentedPath< + First extends string = string, + Rest extends string = string, +> = `${First}/${Rest}`; -type SecondModuleSegment = - ModulePath extends `${string}/${infer Second}` ? Second : ModulePath & string; +type FirstSegment = + ModulePath extends SegmentedPath ? First : ModulePath; /** * @public From b65131532d4f3352f5c2e3e676c099e195a3acdb Mon Sep 17 00:00:00 2001 From: David Blass Date: Thu, 21 Aug 2025 14:10:45 -0400 Subject: [PATCH 07/12] add fast path and benchmarks --- .attest/assertions/typescript.json | 20 +++++++++++ src/server/api.ts | 27 +++++++++------ src/test/types.bench.ts | 54 ++++++++++++++++++++++++------ 3 files changed, 81 insertions(+), 20 deletions(-) create mode 100644 .attest/assertions/typescript.json diff --git a/.attest/assertions/typescript.json b/.attest/assertions/typescript.json new file mode 100644 index 00000000..165e6cf8 --- /dev/null +++ b/.attest/assertions/typescript.json @@ -0,0 +1,20 @@ +{ + "updates": [ + { + "position": { + "method": "", + "file": "/home/ssalb/convex-js/src/test/types.bench.ts", + "line": 100, + "char": 4 + }, + "serializedValue": [ + 1796, + "instantiations" + ], + "snapFunctionName": "types", + "baselinePath": [ + "Segmented ApiFromModules" + ] + } + ] +} diff --git a/src/server/api.ts b/src/server/api.ts index 5e8b9fb1..b969a11d 100644 --- a/src/server/api.ts +++ b/src/server/api.ts @@ -233,11 +233,11 @@ export type FunctionReferenceFromExport = * This is written carefully to preserve jumping to function definitions using * cmd+click. If you edit it, please test that cmd+click still works. */ -type FunctionReferencesInModule> = { +export type FunctionReferencesInModule> = { -readonly [ExportName in keyof Module as Module[ExportName]["isConvexFunction"] extends true ? ExportName : never]: FunctionReferenceFromExport; -}; +} & unknown; /** * Given a path to a module and it's type, generate an API type for this module. @@ -276,14 +276,21 @@ type OmitEmptyObjects = { * @public */ export type ApiFromModules> = - OmitEmptyObjects<{ - [ModulePath in keyof AllModules as FirstSegment]: ModulePath extends SegmentedPath< - string, - infer Rest - > - ? ApiForModule - : ApiForModule; - }>; + keyof AllModules & SegmentedPath extends never + ? // fast path for cases with no segmented modules + OmitEmptyObjects<{ + [ModulePath in keyof AllModules]: FunctionReferencesInModule< + AllModules[ModulePath] + >; + }> + : OmitEmptyObjects<{ + [ModulePath in keyof AllModules as FirstSegment]: ModulePath extends SegmentedPath< + string, + infer Rest + > + ? ApiForModule + : ApiForModule; + }>; type SegmentedPath< First extends string = string, diff --git a/src/test/types.bench.ts b/src/test/types.bench.ts index 69fcd7a8..5ca67920 100644 --- a/src/test/types.bench.ts +++ b/src/test/types.bench.ts @@ -1,5 +1,8 @@ import { bench } from "@ark/attest"; -import type { ApiFromModules } from "../server/api.js"; +import type { + ApiFromModules, + FunctionReferencesInModule, +} from "../server/api.js"; import type * as admin from "./fake_chef/admin.js"; import type * as apiKeys from "./fake_chef/apiKeys.js"; @@ -21,6 +24,7 @@ import type * as sessions from "./fake_chef/sessions.js"; import type * as share from "./fake_chef/share.js"; import type * as snapshot from "./fake_chef/snapshot.js"; import type * as socialShare from "./fake_chef/socialShare.js"; +import type { Equals } from "./type_testing.js"; export type Modules = { admin: typeof admin; @@ -45,22 +49,52 @@ export type Modules = { socialShare: typeof socialShare; }; -// type ValueOf = T[keyof T]; - // exclude overhead of loading first module to isolate // scaling performance as number of modules increases bench.baseline(() => { + type TransformModules = { + [K in keyof T]: { [K1 in keyof T[K]]: T[K][K1] }[keyof T[K]]; + }[keyof T]; + type Value = TransformModules; return {} as ApiFromModules<{ admin: typeof admin }> | Value; }); -type TransformModules = { - [K in keyof T]: { [K1 in keyof T[K]]: T[K][K1] }[keyof T[K]]; -}[keyof T]; - -bench("ApiFromModules", () => { - type T = ApiFromModules; +bench("Flat ApiFromModules", () => { + type Actual = ApiFromModules; - return {} as T; + return {} as Actual; // original 24168 }).types([24168, "instantiations"]); + +export type SegmentedModules = { + "a/b/c": typeof admin; + "a/b/d": typeof apiKeys; + "b/c/d": typeof admin; + c: typeof cleanup; + // omitted + "b/c/e": typeof compressMessages; + d: typeof compressMessages; +}; + +type Expected = { + a: { + b: { + c: FunctionReferencesInModule; + d: FunctionReferencesInModule; + }; + }; + b: { + c: { + d: FunctionReferencesInModule; + }; + }; + c: FunctionReferencesInModule; +}; + +bench("Segmented ApiFromModules", () => { + type Actual = ApiFromModules; + const equal: true = {} as Equals; + + return {} as Actual; +}).types([1796, "instantiations"]); From 8838321fc20209b64b5a86d36ca04d269bb62014 Mon Sep 17 00:00:00 2001 From: David Blass Date: Thu, 21 Aug 2025 14:44:24 -0400 Subject: [PATCH 08/12] fix tests --- .attest/assertions/typescript.json | 20 -------------------- src/server/api.ts | 25 ++++++++++++++++--------- src/test/types.bench.ts | 6 +++--- 3 files changed, 19 insertions(+), 32 deletions(-) delete mode 100644 .attest/assertions/typescript.json diff --git a/.attest/assertions/typescript.json b/.attest/assertions/typescript.json deleted file mode 100644 index 165e6cf8..00000000 --- a/.attest/assertions/typescript.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "updates": [ - { - "position": { - "method": "", - "file": "/home/ssalb/convex-js/src/test/types.bench.ts", - "line": 100, - "char": 4 - }, - "serializedValue": [ - 1796, - "instantiations" - ], - "snapFunctionName": "types", - "baselinePath": [ - "Segmented ApiFromModules" - ] - } - ] -} diff --git a/src/server/api.ts b/src/server/api.ts index b969a11d..18a6dfab 100644 --- a/src/server/api.ts +++ b/src/server/api.ts @@ -1,3 +1,4 @@ +import type { UnionToIntersection } from "../type_utils.js"; import { getFunctionAddress } from "./components/paths.js"; import { functionName } from "./functionName.js"; import { PaginationOptions, PaginationResult } from "./pagination.js"; @@ -237,7 +238,7 @@ export type FunctionReferencesInModule> = { -readonly [ExportName in keyof Module as Module[ExportName]["isConvexFunction"] extends true ? ExportName : never]: FunctionReferenceFromExport; -} & unknown; +}; /** * Given a path to a module and it's type, generate an API type for this module. @@ -283,14 +284,20 @@ export type ApiFromModules> = AllModules[ModulePath] >; }> - : OmitEmptyObjects<{ - [ModulePath in keyof AllModules as FirstSegment]: ModulePath extends SegmentedPath< - string, - infer Rest - > - ? ApiForModule - : ApiForModule; - }>; + : OmitEmptyObjects< + IntersectUnionProperties<{ + [ModulePath in keyof AllModules as FirstSegment]: ModulePath extends SegmentedPath< + string, + infer Rest + > + ? ApiForModule + : ApiForModule; + }> + >; + +type IntersectUnionProperties = { + [K in keyof O]: UnionToIntersection; +}; type SegmentedPath< First extends string = string, diff --git a/src/test/types.bench.ts b/src/test/types.bench.ts index 5ca67920..94f48844 100644 --- a/src/test/types.bench.ts +++ b/src/test/types.bench.ts @@ -24,7 +24,6 @@ import type * as sessions from "./fake_chef/sessions.js"; import type * as share from "./fake_chef/share.js"; import type * as snapshot from "./fake_chef/snapshot.js"; import type * as socialShare from "./fake_chef/socialShare.js"; -import type { Equals } from "./type_testing.js"; export type Modules = { admin: typeof admin; @@ -92,9 +91,10 @@ type Expected = { c: FunctionReferencesInModule; }; +type Equals = [A, B] extends [B, A] ? true : false; + bench("Segmented ApiFromModules", () => { type Actual = ApiFromModules; - const equal: true = {} as Equals; - + const _equal: true = {} as Equals; return {} as Actual; }).types([1796, "instantiations"]); From c022064971df52f9024a6eb0fc99bbd189c63593 Mon Sep 17 00:00:00 2001 From: David Blass Date: Thu, 21 Aug 2025 15:10:32 -0400 Subject: [PATCH 09/12] simplify expansion --- src/server/api.ts | 54 ++++++++++++++-------- src/test/types.bench.ts | 100 ++++++++++++---------------------------- 2 files changed, 63 insertions(+), 91 deletions(-) diff --git a/src/server/api.ts b/src/server/api.ts index 18a6dfab..82c26658 100644 --- a/src/server/api.ts +++ b/src/server/api.ts @@ -249,7 +249,7 @@ type ApiForModule< ModulePath extends string, Module extends object, LastPathWasSegmented extends boolean, -> = OmitEmptyObjects< +> = ModulePath extends SegmentedPath ? { [_ in First]: ApiForModule; @@ -258,13 +258,7 @@ type ApiForModule< ? { [_ in ModulePath]: FunctionReferencesInModule; } - : FunctionReferencesInModule ->; - -type OmitEmptyObjects = { - [K in keyof O as {} extends O[K] ? never : K]: O[K]; - // equivalent to Expand without requiring another mapped type -} & unknown; + : FunctionReferencesInModule; /** * Given the types of all modules in the `convex/` directory, construct the type @@ -279,21 +273,24 @@ type OmitEmptyObjects = { export type ApiFromModules> = keyof AllModules & SegmentedPath extends never ? // fast path for cases with no segmented modules - OmitEmptyObjects<{ + { [ModulePath in keyof AllModules]: FunctionReferencesInModule< AllModules[ModulePath] >; - }> - : OmitEmptyObjects< - IntersectUnionProperties<{ - [ModulePath in keyof AllModules as FirstSegment]: ModulePath extends SegmentedPath< - string, - infer Rest - > - ? ApiForModule - : ApiForModule; - }> - >; + } + : ApiFromSegmentedModules; + +type ApiFromSegmentedModules> = + ExpandModulesAndDirs< + IntersectUnionProperties<{ + [ModulePath in keyof AllModules as FirstSegment]: ModulePath extends SegmentedPath< + string, + infer Rest + > + ? ApiForModule + : ApiForModule; + }> + >; type IntersectUnionProperties = { [K in keyof O]: UnionToIntersection; @@ -326,6 +323,23 @@ export type FilterApi = { // equivalent to Expand without requiring another mapped type } & unknown; +/** + * Like {@link Expand}, this simplifies how TypeScript displays object types. + * The differences are: + * 1. This version is recursive. + * 2. This stops recursing when it hits a {@link FunctionReference}. + * 3. This omits empty object properties + */ +type ExpandModulesAndDirs = ObjectType extends FunctionReference.Any + ? ObjectType + : { + [Key in keyof ObjectType as keyof ExpandModulesAndDirs< + ObjectType[Key] + > extends never + ? never + : Key]: ExpandModulesAndDirs; + }; + /** * Given an api of type API and a FunctionReference subtype, return an api object * containing only the function references that match. diff --git a/src/test/types.bench.ts b/src/test/types.bench.ts index 94f48844..cbca1867 100644 --- a/src/test/types.bench.ts +++ b/src/test/types.bench.ts @@ -3,98 +3,56 @@ import type { ApiFromModules, FunctionReferencesInModule, } from "../server/api.js"; +import type { Modules } from "./fake_chef/_generated/api.js"; -import type * as admin from "./fake_chef/admin.js"; -import type * as apiKeys from "./fake_chef/apiKeys.js"; -import type * as cleanup from "./fake_chef/cleanup.js"; -import type * as compressMessages from "./fake_chef/compressMessages.js"; -import type * as convexProjects from "./fake_chef/convexProjects.js"; -import type * as crons from "./fake_chef/crons.js"; -import type * as debugPrompt from "./fake_chef/debugPrompt.js"; -import type * as deploy from "./fake_chef/deploy.js"; -import type * as dev from "./fake_chef/dev.js"; -import type * as http from "./fake_chef/http.js"; -import type * as lz4 from "./fake_chef/lz4.js"; -import type * as lz4Wasm from "./fake_chef/lz4Wasm.js"; -import type * as messages from "./fake_chef/messages.js"; -import type * as openaiProxy from "./fake_chef/openaiProxy.js"; -import type * as rateLimiter from "./fake_chef/rateLimiter.js"; -import type * as resendProxy from "./fake_chef/resendProxy.js"; -import type * as sessions from "./fake_chef/sessions.js"; -import type * as share from "./fake_chef/share.js"; -import type * as snapshot from "./fake_chef/snapshot.js"; -import type * as socialShare from "./fake_chef/socialShare.js"; - -export type Modules = { - admin: typeof admin; - apiKeys: typeof apiKeys; - cleanup: typeof cleanup; - compressMessages: typeof compressMessages; - convexProjects: typeof convexProjects; - crons: typeof crons; - debugPrompt: typeof debugPrompt; - deploy: typeof deploy; - dev: typeof dev; - http: typeof http; - lz4: typeof lz4; - lz4Wasm: typeof lz4Wasm; - messages: typeof messages; - openaiProxy: typeof openaiProxy; - rateLimiter: typeof rateLimiter; - resendProxy: typeof resendProxy; - sessions: typeof sessions; - share: typeof share; - snapshot: typeof snapshot; - socialShare: typeof socialShare; -}; +type EvaluateModules = { + [K in keyof T]: { [K1 in keyof T[K]]: T[K][K1] }[keyof T[K]]; +}[keyof T]; // exclude overhead of loading first module to isolate // scaling performance as number of modules increases bench.baseline(() => { - type TransformModules = { - [K in keyof T]: { [K1 in keyof T[K]]: T[K][K1] }[keyof T[K]]; - }[keyof T]; - - type Value = TransformModules; - return {} as ApiFromModules<{ admin: typeof admin }> | Value; + type Value = EvaluateModules; + return {} as ApiFromModules<{ admin: Modules["admin"] }> | Value; }); bench("Flat ApiFromModules", () => { - type Actual = ApiFromModules; + type Actual = EvaluateModules>; return {} as Actual; // original 24168 }).types([24168, "instantiations"]); export type SegmentedModules = { - "a/b/c": typeof admin; - "a/b/d": typeof apiKeys; - "b/c/d": typeof admin; - c: typeof cleanup; + "a/b/c": Modules["admin"]; + "a/b/d": Modules["apiKeys"]; + "b/c/d": Modules["sessions"]; + c: Modules["cleanup"]; // omitted - "b/c/e": typeof compressMessages; - d: typeof compressMessages; -}; - -type Expected = { - a: { - b: { - c: FunctionReferencesInModule; - d: FunctionReferencesInModule; - }; - }; - b: { - c: { - d: FunctionReferencesInModule; - }; - }; - c: FunctionReferencesInModule; + "b/c/e": Modules["compressMessages"]; + d: Modules["compressMessages"]; }; type Equals = [A, B] extends [B, A] ? true : false; bench("Segmented ApiFromModules", () => { type Actual = ApiFromModules; + + type Expected = { + a: { + b: { + c: FunctionReferencesInModule; + d: FunctionReferencesInModule; + }; + }; + b: { + c: { + d: FunctionReferencesInModule; + }; + }; + c: FunctionReferencesInModule; + }; + const _equal: true = {} as Equals; return {} as Actual; }).types([1796, "instantiations"]); From 27ce9c4531ebd7fc54449071a926e6133e55c391 Mon Sep 17 00:00:00 2001 From: David Blass Date: Thu, 21 Aug 2025 15:55:30 -0400 Subject: [PATCH 10/12] finalize optimizations --- src/server/api.ts | 37 +++++++++++++++++++++---------------- src/test/types.bench.ts | 13 +++++++------ 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/src/server/api.ts b/src/server/api.ts index 82c26658..345425e7 100644 --- a/src/server/api.ts +++ b/src/server/api.ts @@ -238,7 +238,7 @@ export type FunctionReferencesInModule> = { -readonly [ExportName in keyof Module as Module[ExportName]["isConvexFunction"] extends true ? ExportName : never]: FunctionReferenceFromExport; -}; +} & unknown; /** * Given a path to a module and it's type, generate an API type for this module. @@ -272,14 +272,18 @@ type ApiForModule< */ export type ApiFromModules> = keyof AllModules & SegmentedPath extends never - ? // fast path for cases with no segmented modules - { - [ModulePath in keyof AllModules]: FunctionReferencesInModule< - AllModules[ModulePath] - >; - } + ? ApiFromNonSegmentedModules : ApiFromSegmentedModules; +// fast path for cases with no segmented modules +type ApiFromNonSegmentedModules> = { + [ModulePath in keyof AllModules as keyof FunctionReferencesInModule< + AllModules[ModulePath] + > extends never + ? never + : ModulePath]: FunctionReferencesInModule; +}; + type ApiFromSegmentedModules> = ExpandModulesAndDirs< IntersectUnionProperties<{ @@ -330,15 +334,16 @@ export type FilterApi = { * 2. This stops recursing when it hits a {@link FunctionReference}. * 3. This omits empty object properties */ -type ExpandModulesAndDirs = ObjectType extends FunctionReference.Any - ? ObjectType - : { - [Key in keyof ObjectType as keyof ExpandModulesAndDirs< - ObjectType[Key] - > extends never - ? never - : Key]: ExpandModulesAndDirs; - }; +type ExpandModulesAndDirs = + ObjectType extends Record + ? ObjectType + : { + [Key in keyof ObjectType as keyof ExpandModulesAndDirs< + ObjectType[Key] + > extends never + ? never + : Key]: ExpandModulesAndDirs; + }; /** * Given an api of type API and a FunctionReference subtype, return an api object diff --git a/src/test/types.bench.ts b/src/test/types.bench.ts index cbca1867..56e804cf 100644 --- a/src/test/types.bench.ts +++ b/src/test/types.bench.ts @@ -17,11 +17,11 @@ bench.baseline(() => { }); bench("Flat ApiFromModules", () => { - type Actual = EvaluateModules>; + type Actual = ApiFromModules; - return {} as Actual; - // original 24168 -}).types([24168, "instantiations"]); + return {} as EvaluateModules; + // was 27312 +}).types([20041, "instantiations"]); export type SegmentedModules = { "a/b/c": Modules["admin"]; @@ -54,5 +54,6 @@ bench("Segmented ApiFromModules", () => { }; const _equal: true = {} as Equals; - return {} as Actual; -}).types([1796, "instantiations"]); + return {} as EvaluateModules; + // was 7780 +}).types([6681, "instantiations"]); From fed91eb5a5e8d5e528f6eb4b8857d8028d38fbef Mon Sep 17 00:00:00 2001 From: David Blass Date: Thu, 21 Aug 2025 16:09:35 -0400 Subject: [PATCH 11/12] remove package.json manager --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index 87151d9a..f1cec69f 100644 --- a/package.json +++ b/package.json @@ -341,6 +341,5 @@ "engines": { "npm": ">=7.0.0", "node": ">=18.0.0" - }, - "packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39" + } } From a186ece62864c6583da65aed73df41d6d885f93b Mon Sep 17 00:00:00 2001 From: David Blass Date: Thu, 21 Aug 2025 16:11:02 -0400 Subject: [PATCH 12/12] remove autogenerated vscode settings --- .vscode/settings.json | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 0967ef42..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1 +0,0 @@ -{}