From 1869d808d926331d03934a7a970d6ecf56e55e41 Mon Sep 17 00:00:00 2001 From: Konstantin Tarkus Date: Mon, 8 Jan 2024 02:18:19 +0100 Subject: [PATCH] feat: Add tRPC application server and Firestore database --- .env | 7 + .vscode/settings.json | 5 + README.md | 2 + app/package.json | 2 +- db/README.md | 20 + db/firestore.indexes.json | 13 + db/firestore.rules | 19 + db/index.ts | 6 + db/models/index.ts | 4 + db/models/workspace.ts | 16 + db/package.json | 30 + db/scripts/seed.ts | 43 + db/seeds/01-users.ts | 87 ++ db/seeds/02-workspaces.ts | 63 ++ db/tsconfig.json | 14 + edge/package.json | 2 +- package.json | 12 +- scripts/bundle-yarn.js | 33 + scripts/package.json | 2 +- scripts/post-install.js | 31 + scripts/postinstall.js | 39 - server/Dockerfile | 34 + server/README.md | 9 + server/app.test.ts | 30 + server/app.ts | 54 ++ server/core/auth.test.ts | 52 + server/core/auth.ts | 188 ++++ server/core/env.ts | 21 + server/core/firestore.ts | 18 + server/core/logging.ts | 60 ++ server/core/openai.ts | 15 + server/core/trpc.ts | 102 ++ server/core/utils.ts | 13 + server/global.d.ts | 13 + server/index.ts | 65 ++ server/package.json | 52 + server/routes/index.ts | 15 + server/routes/workspace.test.ts | 63 ++ server/routes/workspace.ts | 39 + server/start.ts | 26 + server/test/context.ts | 80 ++ server/tsconfig.json | 15 + server/types.ts | 4 + server/vite.config.ts | 38 + tsconfig.json | 4 +- yarn.lock | 1594 ++++++++++++++++++++++++++++--- 46 files changed, 2877 insertions(+), 177 deletions(-) create mode 100644 db/README.md create mode 100644 db/firestore.indexes.json create mode 100644 db/firestore.rules create mode 100644 db/index.ts create mode 100644 db/models/index.ts create mode 100644 db/models/workspace.ts create mode 100644 db/package.json create mode 100644 db/scripts/seed.ts create mode 100644 db/seeds/01-users.ts create mode 100644 db/seeds/02-workspaces.ts create mode 100644 db/tsconfig.json create mode 100644 scripts/bundle-yarn.js create mode 100644 scripts/post-install.js delete mode 100644 scripts/postinstall.js create mode 100644 server/Dockerfile create mode 100644 server/README.md create mode 100644 server/app.test.ts create mode 100644 server/app.ts create mode 100644 server/core/auth.test.ts create mode 100644 server/core/auth.ts create mode 100644 server/core/env.ts create mode 100644 server/core/firestore.ts create mode 100644 server/core/logging.ts create mode 100644 server/core/openai.ts create mode 100644 server/core/trpc.ts create mode 100644 server/core/utils.ts create mode 100644 server/global.d.ts create mode 100644 server/index.ts create mode 100644 server/package.json create mode 100644 server/routes/index.ts create mode 100644 server/routes/workspace.test.ts create mode 100644 server/routes/workspace.ts create mode 100644 server/start.ts create mode 100644 server/test/context.ts create mode 100644 server/tsconfig.json create mode 100644 server/types.ts create mode 100644 server/vite.config.ts diff --git a/.env b/.env index 3e76ccab6..123e4bd74 100644 --- a/.env +++ b/.env @@ -11,11 +11,13 @@ APP_NAME=Acme Co. APP_HOSTNAME=localhost APP_ORIGIN=http://localhost:5173 API_ORIGIN=https://api-mcfytwakla-uc.a.run.app +APP_STORAGE_BUCKET=example.com # Google Cloud # https://console.cloud.google.com/ GOOGLE_CLOUD_PROJECT=kriasoft GOOGLE_CLOUD_REGION=us-central1 +GOOGLE_CLOUD_DATABASE="(default)" GOOGLE_CLOUD_CREDENTIALS={"type":"service_account","project_id":"example","private_key_id":"xxx","private_key":"-----BEGIN PRIVATE KEY-----\nxxxxx\n-----END PRIVATE KEY-----\n","client_email":"application@exmaple.iam.gserviceaccount.com","client_id":"xxxxx","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_x509_cert_url":"https://www.googleapis.com/robot/v1/metadata/x509/application%40example.iam.gserviceaccount.com"} # Firebase @@ -24,6 +26,11 @@ FIREBASE_APP_ID=1:736557952746:web:b5ee23841e24c0b883b193 FIREBASE_API_KEY=AIzaSyAZDmdeRWvlYgZpwm6LBCkYJM6ySIMF2Hw FIREBASE_AUTH_DOMAIN=kriasoft.web.app +# OpenAI +# https://platform.openai.com/ +OPENAI_ORGANIZATION=xxxxx +OPENAI_API_KEY=xxxxx + # Cloudflare # https://dash.cloudflare.com/ # https://developers.cloudflare.com/api/tokens/create diff --git a/.vscode/settings.json b/.vscode/settings.json index 5162a0e85..d2ac34e86 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -61,6 +61,7 @@ "firestore", "globby", "hono", + "identitytoolkit", "jamstack", "kriasoft", "localforage", @@ -68,16 +69,20 @@ "nodenext", "notistack", "oidc", + "openai", "pathinfo", + "pino", "pnpify", "reactstarter", "refetch", "refetchable", + "relyingparty", "sendgrid", "signup", "sourcemap", "spdx", "swapi", + "trpc", "tslib", "typechecking", "vite", diff --git a/README.md b/README.md index 0e53aa73a..ae0224eb3 100644 --- a/README.md +++ b/README.md @@ -31,9 +31,11 @@ Be sure to join our [Discord channel](https://discord.com/invite/2nKEnKq) for as `├──`[`.github`](.github) — GitHub configuration including CI/CD workflows
`├──`[`.vscode`](.vscode) — VSCode settings including code snippets, recommended extensions etc.
`├──`[`app`](./app) — Web application front-end built with [React](https://react.dev/) and [Joy UI](https://mui.com/joy-ui/getting-started/)
+`├──`[`db`](./db) — Firestore database schema, seed data, and admin tools
`├──`[`edge`](./edge) — Cloudflare Workers (CDN) edge endpoint
`├──`[`env`](./env) — Application settings, API keys, etc.
`├──`[`scripts`](./scripts) — Automation scripts such as `yarn deploy`
+`├──`[`server`](./server) — Node.js application server built with [tRPC](https://trpc.io/)
`├──`[`tsconfig.base.json`](./tsconfig.base.json) — The common/shared TypeScript configuration
`└──`[`tsconfig.json`](./tsconfig.json) — The root TypeScript configuration
diff --git a/app/package.json b/app/package.json index 524f4bcb9..50fde8331 100644 --- a/app/package.json +++ b/app/package.json @@ -36,7 +36,7 @@ "devDependencies": { "@babel/core": "^7.23.7", "@emotion/babel-plugin": "^11.11.0", - "@types/node": "^20.10.6", + "@types/node": "^20.10.7", "@types/react": "^18.2.47", "@types/react-dom": "^18.2.18", "@vitejs/plugin-react": "^4.2.1", diff --git a/db/README.md b/db/README.md new file mode 100644 index 000000000..12d758756 --- /dev/null +++ b/db/README.md @@ -0,0 +1,20 @@ +# Firestore Database + +Database schema, security rules, indexes, and seed data for the [Firestore](https://cloud.google.com/firestore) database. + +## Directory Structure + +- [`/models`](./models/) — Database schema definitions using [Zod](https://zod.dev/). +- [`/seeds`](./seeds/) — Sample / reference data for the database. +- [`/scripts`](./scripts/) — Scripts for managing the database. +- [`/firestore.indexes.json`](./firestore.indexes.json) — Firestore indexes. +- [`/firestore.rules`](./firestore.rules) — Firestore security rules. + +## Scripts + +- `yarn workspace db seed` - Seed the database with data from [`/seeds`](./seeds/). + +## References + +- https://zod.dev/ +- https://cloud.google.com/firestore diff --git a/db/firestore.indexes.json b/db/firestore.indexes.json new file mode 100644 index 000000000..19272370d --- /dev/null +++ b/db/firestore.indexes.json @@ -0,0 +1,13 @@ +{ + "indexes": [ + { + "collectionGroup": "workspace", + "queryScope": "COLLECTION", + "fields": [ + { "fieldPath": "ownerId", "order": "ASCENDING" }, + { "fieldPath": "archived", "order": "DESCENDING" } + ] + } + ], + "fieldOverrides": [] +} diff --git a/db/firestore.rules b/db/firestore.rules new file mode 100644 index 000000000..23b616177 --- /dev/null +++ b/db/firestore.rules @@ -0,0 +1,19 @@ +// Firestore security rules. +// https://cloud.google.com/firestore/docs/security/get-started + +rules_version = '2'; + +service cloud.firestore { + match /databases/{database}/documents { + match /workspace/{id} { + allow read: if request.auth != null && ( + resource.data.ownerId = request.auth.uid || + request.auth.token.admin == true + ); + } + + match /{document=**} { + allow read, write: if false; + } + } +} diff --git a/db/index.ts b/db/index.ts new file mode 100644 index 000000000..a4c50fa4b --- /dev/null +++ b/db/index.ts @@ -0,0 +1,6 @@ +/* SPDX-FileCopyrightText: 2014-present Kriasoft */ +/* SPDX-License-Identifier: MIT */ + +export * from "./models"; +export { testUsers } from "./seeds/01-users"; +export { testWorkspaces } from "./seeds/02-workspaces"; diff --git a/db/models/index.ts b/db/models/index.ts new file mode 100644 index 000000000..0690c018f --- /dev/null +++ b/db/models/index.ts @@ -0,0 +1,4 @@ +/* SPDX-FileCopyrightText: 2014-present Kriasoft */ +/* SPDX-License-Identifier: MIT */ + +export * from "./workspace"; diff --git a/db/models/workspace.ts b/db/models/workspace.ts new file mode 100644 index 000000000..7794839ed --- /dev/null +++ b/db/models/workspace.ts @@ -0,0 +1,16 @@ +/* SPDX-FileCopyrightText: 2014-present Kriasoft */ +/* SPDX-License-Identifier: MIT */ + +import { Timestamp } from "@google-cloud/firestore"; +import { z } from "zod"; + +export const WorkspaceSchema = z.object({ + name: z.string().max(100), + ownerId: z.string().max(50), + created: z.instanceof(Timestamp), + updated: z.instanceof(Timestamp), + archived: z.instanceof(Timestamp).nullable(), +}); + +export type Workspace = z.output; +export type WorkspaceInput = z.input; diff --git a/db/package.json b/db/package.json new file mode 100644 index 000000000..c8c41afa7 --- /dev/null +++ b/db/package.json @@ -0,0 +1,30 @@ +{ + "name": "db", + "version": "0.0.0", + "private": true, + "type": "module", + "exports": { + ".": { + "default": "./index.ts" + }, + "./package.json": "./package.json" + }, + "scripts": { + "seed": "vite-node ./scripts/seed.ts", + "test": "vitest" + }, + "dependencies": { + "@google-cloud/firestore": "^7.1.0", + "@googleapis/identitytoolkit": "^8.0.0", + "zod": "^3.22.4" + }, + "devDependencies": { + "@types/node": "^20.10.7", + "dotenv": "^16.3.1", + "ora": "^8.0.1", + "typescript": "~5.3.3", + "vite": "~5.0.11", + "vite-node": "~1.1.3", + "vitest": "~1.1.3" + } +} diff --git a/db/scripts/seed.ts b/db/scripts/seed.ts new file mode 100644 index 000000000..5fa0eafda --- /dev/null +++ b/db/scripts/seed.ts @@ -0,0 +1,43 @@ +/* SPDX-FileCopyrightText: 2014-present Kriasoft */ +/* SPDX-License-Identifier: MIT */ + +import { Firestore } from "@google-cloud/firestore"; +import { configDotenv } from "dotenv"; +import { relative, resolve } from "node:path"; +import { oraPromise } from "ora"; + +const rootDir = resolve(__dirname, "../.."); + +// Load environment variables from .env files. +configDotenv({ path: resolve(rootDir, ".env.local") }); +configDotenv({ path: resolve(rootDir, ".env") }); + +let db: Firestore | null = null; + +// Seed the database with test / sample data. +try { + db = new Firestore({ + projectId: process.env.GOOGLE_CLOUD_PROJECT, + databaseId: process.env.GOOGLE_CLOUD_DATABASE, + }); + + // Import all seed modules from the `/seeds` folder. + const files = import.meta.glob("../seeds/*.ts"); + + // Sequentially seed the database with data from each module. + for (const [path, load] of Object.entries(files)) { + const message = `Seeding ${relative("../seeds", path)}`; + const action = (async () => { + const { seed } = await load(); + await seed(db); + })(); + + await oraPromise(action, message); + } +} finally { + await db?.terminate(); +} + +type SeedModule = { + seed: (db: Firestore) => Promise; +}; diff --git a/db/seeds/01-users.ts b/db/seeds/01-users.ts new file mode 100644 index 000000000..09f23fb58 --- /dev/null +++ b/db/seeds/01-users.ts @@ -0,0 +1,87 @@ +/* SPDX-FileCopyrightText: 2014-present Kriasoft */ +/* SPDX-License-Identifier: MIT */ + +import { + AuthPlus, + identitytoolkit, + identitytoolkit_v3, +} from "@googleapis/identitytoolkit"; + +/** + * Test user accounts generated by https://randomuser.me/. + */ +export const testUsers: identitytoolkit_v3.Schema$UserInfo[] = [ + { + localId: "test-erika", + screenName: "erika", + email: "erika.pearson@example.com", + emailVerified: true, + phoneNumber: "+14788078434", + displayName: "Erika Pearson", + photoUrl: "https://randomuser.me/api/portraits/women/29.jpg", + rawPassword: "paloma", + createdAt: new Date("2024-01-01T12:00:00Z").getTime().toString(), + lastLoginAt: new Date("2024-01-01T12:00:00Z").getTime().toString(), + }, + { + localId: "test-ryan", + screenName: "ryan", + email: "ryan.hunt@example.com", + emailVerified: true, + phoneNumber: "+16814758216", + displayName: "Ryan Hunt", + photoUrl: "https://randomuser.me/api/portraits/men/20.jpg", + rawPassword: "baggins", + createdAt: new Date("2024-01-02T12:00:00Z").getTime().toString(), + lastLoginAt: new Date("2024-01-02T12:00:00Z").getTime().toString(), + }, + { + localId: "test-marian", + screenName: "marian", + email: "marian.stone@example.com", + emailVerified: true, + phoneNumber: "+19243007975", + displayName: "Marian Stone", + photoUrl: "https://randomuser.me/api/portraits/women/2.jpg", + rawPassword: "winter1", + createdAt: new Date("2024-01-03T12:00:00Z").getTime().toString(), + lastLoginAt: new Date("2024-01-03T12:00:00Z").getTime().toString(), + }, + { + localId: "test-kurt", + screenName: "kurt", + email: "kurt.howward@example.com", + emailVerified: true, + phoneNumber: "+19243007975", + displayName: "Kurt Howard", + photoUrl: "https://randomuser.me/api/portraits/men/23.jpg", + rawPassword: "mayday", + createdAt: new Date("2024-01-04T12:00:00Z").getTime().toString(), + lastLoginAt: new Date("2024-01-04T12:00:00Z").getTime().toString(), + }, + { + localId: "test-dan", + screenName: "dan", + email: "dan.day@example.com", + emailVerified: true, + phoneNumber: "+12046748092", + displayName: "Dan Day", + photoUrl: "https://randomuser.me/api/portraits/men/65.jpg", + rawPassword: "teresa", + createdAt: new Date("2024-01-05T12:00:00Z").getTime().toString(), + lastLoginAt: new Date("2024-01-05T12:00:00Z").getTime().toString(), + customAttributes: JSON.stringify({ admin: true }), + }, +]; + +/** + * Seeds the Google Identity Platform (Firebase Auth) with test user accounts. + * + * @see https://randomuser.me/ + * @see https://cloud.google.com/identity-platform + */ +export async function seed() { + const auth = new AuthPlus(); + const { relyingparty } = identitytoolkit({ version: "v3", auth }); + await relyingparty.uploadAccount({ requestBody: { users: testUsers } }); +} diff --git a/db/seeds/02-workspaces.ts b/db/seeds/02-workspaces.ts new file mode 100644 index 000000000..1455ec2d7 --- /dev/null +++ b/db/seeds/02-workspaces.ts @@ -0,0 +1,63 @@ +/* SPDX-FileCopyrightText: 2014-present Kriasoft */ +/* SPDX-License-Identifier: MIT */ + +import { Firestore, Timestamp } from "@google-cloud/firestore"; +import { WorkspaceInput } from "../models"; +import { testUsers as users } from "./01-users"; + +/** + * Test workspaces. + */ +export const testWorkspaces: (WorkspaceInput & { id: string })[] = [ + { + id: "DwYchGFGpk", + ownerId: users[0].localId!, + name: "Personal workspace", + created: Timestamp.fromDate(new Date(+users[0].createdAt!)), + updated: Timestamp.fromDate(new Date(+users[0].createdAt!)), + archived: null, + }, + { + id: "YfYKTcO9q9", + ownerId: users[1].localId!, + name: "Personal workspace", + created: Timestamp.fromDate(new Date(+users[1].createdAt!)), + updated: Timestamp.fromDate(new Date(+users[1].createdAt!)), + archived: null, + }, + { + id: "c2OsmUvFMY", + ownerId: users[2].localId!, + name: "Personal workspace", + created: Timestamp.fromDate(new Date(+users[2].createdAt!)), + updated: Timestamp.fromDate(new Date(+users[2].createdAt!)), + archived: null, + }, + { + id: "uTqcGw4qn7", + ownerId: users[3].localId!, + name: "Personal workspace", + created: Timestamp.fromDate(new Date(+users[3].createdAt!)), + updated: Timestamp.fromDate(new Date(+users[3].createdAt!)), + archived: null, + }, + { + id: "vBHHgg5ydn", + ownerId: users[4].localId!, + name: "Personal workspace", + created: Timestamp.fromDate(new Date(+users[4].createdAt!)), + updated: Timestamp.fromDate(new Date(+users[4].createdAt!)), + archived: null, + }, +]; + +export async function seed(db: Firestore) { + const batch = db.batch(); + + for (const { id, ...workspace } of testWorkspaces) { + const ref = db.doc(`workspace/${id}`); + batch.set(ref, workspace, { merge: true }); + } + + await batch.commit(); +} diff --git a/db/tsconfig.json b/db/tsconfig.json new file mode 100644 index 000000000..22d7921eb --- /dev/null +++ b/db/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "emitDeclarationOnly": true, + "module": "ESNext", + "moduleResolution": "Bundler", + "noEmit": false, + "outDir": "../.cache/ts-db", + "types": ["node", "vite/client"] + }, + "include": ["**/*.ts", "**/*.json"], + "exclude": ["**/dist/**/*", "**/node_modules/**/*"] +} diff --git a/edge/package.json b/edge/package.json index 2bb7a65cf..5af88ab69 100644 --- a/edge/package.json +++ b/edge/package.json @@ -26,7 +26,7 @@ }, "devDependencies": { "@cloudflare/workers-types": "^4.20231218.0", - "@types/node": "^20.10.6", + "@types/node": "^20.10.7", "happy-dom": "^12.10.3", "toml": "^3.0.0", "typescript": "~5.3.3", diff --git a/package.json b/package.json index c9191ffb9..70e0fb324 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,18 @@ { "name": "root", "version": "0.0.0", - "private": true, "packageManager": "yarn@4.0.2", + "private": true, "type": "module", "workspaces": [ "app", + "db", "edge", - "scripts" + "scripts", + "server" ], "scripts": { - "postinstall": "husky install && node ./scripts/postinstall.js", + "postinstall": "husky install && node ./scripts/post-install.js", "start": "yarn workspace app start", "lint": "eslint --cache --report-unused-disable-directives .", "test": "vitest", @@ -21,8 +23,8 @@ "@emotion/babel-plugin": "^11.11.0", "@emotion/eslint-plugin": "^11.11.0", "@types/eslint": "^8.56.1", - "@typescript-eslint/eslint-plugin": "^6.17.0", - "@typescript-eslint/parser": "^6.17.0", + "@typescript-eslint/eslint-plugin": "^6.18.0", + "@typescript-eslint/parser": "^6.18.0", "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", "eslint-import-resolver-typescript": "^3.6.1", diff --git a/scripts/bundle-yarn.js b/scripts/bundle-yarn.js new file mode 100644 index 000000000..81f85e741 --- /dev/null +++ b/scripts/bundle-yarn.js @@ -0,0 +1,33 @@ +/* SPDX-FileCopyrightText: 2014-present Kriasoft */ +/* SPDX-License-Identifier: MIT */ + +import { execa } from "execa"; +import { copyFile, readFile, writeFile } from "node:fs/promises"; +import { resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const rootDir = resolve(fileURLToPath(import.meta.url), "../.."); +const rootPkg = JSON.parse(await readFile(`${rootDir}/package.json`, "utf-8")); +const pkg = JSON.parse(await readFile("./package.json", "utf-8")); + +pkg.packageManager = rootPkg.packageManager; +delete pkg.scripts; +delete pkg.devDependencies; +delete pkg.dependencies.db; + +// Create ./dist/package.json +await writeFile("./dist/package.json", JSON.stringify(pkg, null, 2), "utf-8"); + +// Create ./dist/yarn.lock +await copyFile(`${rootDir}/yarn.lock`, "./dist/yarn.lock"); + +// Install production dependencies +await execa("yarn", ["install", "--mode=update-lockfile"], { + env: { + ...process.env, + NODE_OPTIONS: undefined, + YARN_ENABLE_IMMUTABLE_INSTALLS: "false", + }, + cwd: "./dist", + stdio: "inherit", +}); diff --git a/scripts/package.json b/scripts/package.json index a479911a0..95f50a766 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -7,7 +7,7 @@ "dotenv": "^16.3.1", "execa": "^8.0.1", "get-port": "^7.0.0", - "got": "^14.0.0", + "got": "^13.0.0", "graphql": "^16.8.1", "lodash-es": "^4.17.21", "miniflare": "^3.20231218.1", diff --git a/scripts/post-install.js b/scripts/post-install.js new file mode 100644 index 000000000..447844f92 --- /dev/null +++ b/scripts/post-install.js @@ -0,0 +1,31 @@ +/* SPDX-FileCopyrightText: 2014-present Kriasoft */ +/* SPDX-License-Identifier: MIT */ + +import { execa } from "execa"; +import fs from "node:fs"; +import { EOL } from "node:os"; + +// Create Git-ignored files for environment variable overrides +if (!fs.existsSync("./.env.local")) { + await fs.writeFile( + "./.env.local", + [ + `# Overrides for the ``.env`` file in the root folder.`, + "#", + "# CLOUDFLARE_API_TOKEN=xxxxx", + "# GOOGLE_CLOUD_CREDENTIALS=xxxxx", + "# SENDGRID_API_KEY=xxxxx", + "#", + "", + "API_URL=http://localhost:8080", + "", + ].join(EOL), + "utf-8", + ); +} + +try { + await execa("yarn", ["tsc", "--build"], { stdin: "inherit" }); +} catch (err) { + console.error(err); +} diff --git a/scripts/postinstall.js b/scripts/postinstall.js deleted file mode 100644 index 51cd15cb4..000000000 --- a/scripts/postinstall.js +++ /dev/null @@ -1,39 +0,0 @@ -/* SPDX-FileCopyrightText: 2014-present Kriasoft */ -/* SPDX-License-Identifier: MIT */ - -import { execa } from "execa"; -import fs from "node:fs"; -import { EOL } from "node:os"; - -const environments = [ - { name: "local", description: "development" }, - { name: "test", description: "staging/QA" }, - { name: "prod", description: "production" }, -]; - -// Create Git-ignored files for environment variable overrides -for (const env of environments) { - const filename = `./env/.${env.name}.override.env`; - - if (!fs.existsSync(filename)) { - await fs.writeFile( - filename, - [ - `# Overrides for the "${env.name}" (${env.description}) environment`, - "#", - "# CLOUDFLARE_API_TOKEN=xxxxx", - "# GOOGLE_CLOUD_CREDENTIALS=xxxxx", - "# SENDGRID_API_KEY=xxxxx", - "#", - "", - ].join(EOL), - "utf-8", - ); - } -} - -try { - await execa("yarn", ["tsc", "--build"], { stdin: "inherit" }); -} catch (err) { - console.error(err); -} diff --git a/server/Dockerfile b/server/Dockerfile new file mode 100644 index 000000000..66b13e51a --- /dev/null +++ b/server/Dockerfile @@ -0,0 +1,34 @@ +# SPDX-FileCopyrightText: 2014-present Kriasoft +# SPDX-License-Identifier: MIT + +# Docker image for a Cloud Run service. +# +# https://cloud.google.com/run/docs/container-contract +# https://cloud.google.com/run/docs/quickstarts/build-and-deploy/nodejs +# https://github.com/GoogleCloudPlatform/cloud-run-microservice-template-nodejs/blob/main/Dockerfile + +# Use the official lightweight Node.js image. +# https://hub.docker.com/_/node +FROM node:20.10.0-slim + +# Upgrade OS packages. +RUN apt-get update && apt-get upgrade -y + +# Set environment variables. +ENV NODE_ENV=production + +# Create and change to the app directory. +WORKDIR /usr/src/app + +# Copy application dependency manifests to the container image. +# Copying this separately prevents re-running npm install on every code change. +COPY dist/package.json dist/yarn.lock ./ + +# Install dependencies. +RUN corepack enable && yarn config set nodeLinker node-modules && yarn install --immutable + +# Copy compiled code to the container image. +COPY ./dist . + +# Run the web service on container startup. +CMD [ "node", "index.js" ] diff --git a/server/README.md b/server/README.md new file mode 100644 index 000000000..6fa39a911 --- /dev/null +++ b/server/README.md @@ -0,0 +1,9 @@ +# Application Server + +Node.js application server for web and mobile clients using [tRPC](https://trpc.io/) with HTTP and WebSocket transports. + +## Scripts + +- `yarn workspace server start` — Start the server in development mode. +- `yarn workspace server build` — Build the server for production. +- `yarn workspace server test` — Run tests. diff --git a/server/app.test.ts b/server/app.test.ts new file mode 100644 index 000000000..b8c7854ac --- /dev/null +++ b/server/app.test.ts @@ -0,0 +1,30 @@ +/* SPDX-FileCopyrightText: 2014-present Kriasoft */ +/* SPDX-License-Identifier: MIT */ + +import supertest from "supertest"; +import { describe, expect, it } from "vitest"; +import { app } from "./app"; + +describe("GET /trpc", () => { + it("returns NOT_FOUND response for unknown requests", async () => { + const res = await supertest(app).get("/trpc"); + + expect({ + statusCode: res.statusCode, + body: res.body, + }).toEqual({ + statusCode: 404, + body: { + error: { + code: -32004, + data: { + code: "NOT_FOUND", + httpStatus: 404, + path: "", + }, + message: 'No "query"-procedure on path ""', + }, + }, + }); + }); +}); diff --git a/server/app.ts b/server/app.ts new file mode 100644 index 000000000..ddf6d6f90 --- /dev/null +++ b/server/app.ts @@ -0,0 +1,54 @@ +/* SPDX-FileCopyrightText: 2014-present Kriasoft */ +/* SPDX-License-Identifier: MIT */ + +import { createExpressMiddleware } from "@trpc/server/adapters/express"; +import express from "express"; +import { createProxyMiddleware } from "http-proxy-middleware"; +import { sessionMiddleware } from "./core/auth"; +import { env } from "./core/env"; +import { loggerMiddleware } from "./core/logging"; +import { createContext } from "./core/trpc"; +import { router } from "./routes/index"; + +export const app = express(); + +app.disable("x-powered-by"); +app.set("trust proxy", 1); // Trust first proxy + +app.use(loggerMiddleware); +app.use(sessionMiddleware); + +/** + * tRPC HTTP handler for Express.js. + * + * @see https://trpc.io/docs/getting-started + * @see https://trpc.io/docs/server/adapters/express + */ +app.use("/trpc", createExpressMiddleware({ router, createContext })); + +/** + * Proxy auth requests to Google Identity Platform. + * + * @see https://firebase.google.com/docs/auth/web/redirect-best-practices + */ +app.use( + createProxyMiddleware("/__", { + target: `https://${env.GOOGLE_CLOUD_PROJECT}.firebaseapp.com`, + changeOrigin: true, + logLevel: "warn", + }), +); + +/** + * Proxy static assets to Google Cloud Storage. + */ +app.use( + createProxyMiddleware("/", { + target: "https://c.storage.googleapis.com", + changeOrigin: true, + logLevel: "warn", + onProxyReq(proxyReq) { + proxyReq.setHeader("host", env.APP_STORAGE_BUCKET); + }, + }), +); diff --git a/server/core/auth.test.ts b/server/core/auth.test.ts new file mode 100644 index 000000000..248d4f757 --- /dev/null +++ b/server/core/auth.test.ts @@ -0,0 +1,52 @@ +/* SPDX-FileCopyrightText: 2014-present Kriasoft */ +/* SPDX-License-Identifier: MIT */ + +import express from "express"; +import supertest from "supertest"; +import { describe, expect, it } from "vitest"; +import { fetchCertificates, sessionMiddleware } from "./auth"; +import { env } from "./env"; + +describe("fetchCertificates()", () => { + it("should fetch certificates from Google", async () => { + const certs = await fetchCertificates(); + expect(certs).toEqual(expect.any(Object)); + expect(Object.values(certs)).toEqual( + expect.arrayContaining([ + expect.stringMatching(/^-----BEGIN CERTIFICATE-----/), + ]), + ); + }); +}); + +describe("sessionMiddleware()", () => { + it.skip("should verify a valid ID token", async () => { + const app = express(); + app.get("/", sessionMiddleware, (req, res) => { + res.type("json"); + res.send(JSON.stringify(req.token)); + }); + + const idToken = "eyJhbGciOiJSUzI1NiIsImtpZCI6ImQxNjg5NDE1ZWMyM2EzMzdlMmJiYWE1ZTNlNjhiNjZkYzk5MzY4ODQiLCJ0eXAiOiJKV1QifQ.eyJuYW1lIjoiS3JpYXNvZnQiLCJwaWN0dXJlIjoiaHR0cHM6Ly9saDMuZ29vZ2xldXNlcmNvbnRlbnQuY29tL2EvQUNnOG9jS0hBRVhvRXpJMnpoMWNpN3lna0FVZXFiRWppQ1ZxUE96VkZQYnZmUGtXcGc9czk2LWMiLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20va3JpYXNvZnQiLCJhdWQiOiJrcmlhc29mdCIsImF1dGhfdGltZSI6MTcwNDU0MjYyOCwidXNlcl9pZCI6IkpRdWxWbjhaMUhZelRIR0J5YnhlcndGRUhsNzIiLCJzdWIiOiJKUXVsVm44WjFIWXpUSEdCeWJ4ZXJ3RkVIbDcyIiwiaWF0IjoxNzA0NTQyNjI4LCJleHAiOjE3MDQ1NDYyMjgsImVtYWlsIjoia3JpYXNvZnRAZ21haWwuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZ29vZ2xlLmNvbSI6WyIxMDI2NTQwODA3NzUxNTYyOTI2ODYiXSwiZW1haWwiOlsia3JpYXNvZnRAZ21haWwuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoiZ29vZ2xlLmNvbSJ9fQ.E1sOoIeLH0269m4K4DXfXOJk97cxc8h3D62u3q9Kqyk0AsmwQfKURRl34IiOtNEzizjesLex6EHSetFr8kS1GSgVrW6yxHowmJZCY8tsgSWfunZ7Vj8l1kG4y0iM7hdFw3t0dhilK8-vDlKpLeRfLVHG8qgt46qI7Rxmdb928llJoa7H6NuuS5heavNJadLfiYItJyUq7i5kjys6-WfndQQcRb7kTt07arHb_1w2jtZnyjZE_S3ErZcIgwnE9M_gqXZ4y1MucpGPR2_nHzicRBBYOMwZDVG7Y0tI9IWRaTyTF3Psd7XKisE6GorZ_X1cDwkaT5ffoXZ1tkBOjeMjfw"; // prettier-ignore + const res = await supertest(app).get("/").auth(idToken, { type: "bearer" }); + + expect({ status: res.status, body: res.body }).toEqual({ + status: 200, + body: expect.objectContaining({ + name: expect.any(String), + picture: expect.any(String), + iss: `https://securetoken.google.com/${env.GOOGLE_CLOUD_PROJECT}`, + aud: env.GOOGLE_CLOUD_PROJECT, + auth_time: expect.any(Number), + sub: expect.any(String), + iat: expect.any(Number), + exp: expect.any(Number), + email: expect.any(String), + email_verified: true, + firebase: expect.objectContaining({ + sign_in_provider: "google.com", + }), + }), + }); + }); +}); diff --git a/server/core/auth.ts b/server/core/auth.ts new file mode 100644 index 000000000..9affeaea2 --- /dev/null +++ b/server/core/auth.ts @@ -0,0 +1,188 @@ +/* SPDX-FileCopyrightText: 2014-present Kriasoft */ +/* SPDX-License-Identifier: MIT */ + +import { NextFunction, Request, Response } from "express"; +import { + Certificates, + GoogleAuth, + IdTokenClient, + TokenPayload, +} from "google-auth-library"; +import { got } from "got"; +import { env } from "./env"; + +export const auth = new GoogleAuth({ + scopes: ["https://www.googleapis.com/auth/cloud-platform"], +}); + +const certificatesURL = "https://www.googleapis.com/service_accounts/v1/metadata/x509/securetoken@system.gserviceaccount.com"; // prettier-ignore +const certificatesCache = new Map(); + +/** + * Fetches the latest Google Cloud Identity Platform certificates. + */ +export function fetchCertificates(options?: { signal: AbortSignal }) { + return got.get(certificatesURL, { + cache: certificatesCache, + resolveBodyOnly: true, + responseType: "json", + signal: options?.signal, + }); +} + +// Refresh certificates every 6 hours. +const cleanup = (() => { + const ac = new AbortController(); + const int = setInterval(() => fetchCertificates(), 2.16e7); + fetchCertificates({ signal: ac.signal }); + return () => { + clearInterval(int); + ac.abort(); + }; +})(); + +process.on("SIGTERM", cleanup); +process.on("SIGINT", cleanup); + +const idTokenClients = new Map(); + +/** + * Express middleware that verifies that the request has a valid Firebase ID + * token attached, and adds the decoded token to `req.token`. + */ +export async function sessionMiddleware( + req: Request, + res: Response, + next: NextFunction, +) { + try { + req.token = null; + const idToken = req.headers.authorization?.replace(/^Bearer /i, ""); + + if (idToken) { + const certificatesPromise = fetchCertificates(); + const audience = env.GOOGLE_CLOUD_PROJECT; + let idTokenClient = idTokenClients.get(audience); + + if (!idTokenClient) { + idTokenClient = await auth.getIdTokenClient(audience); + idTokenClients.set(audience, idTokenClient); + } + + const ticket = await idTokenClient.verifySignedJwtWithCertsAsync( + idToken, + await certificatesPromise, + audience, + [`https://securetoken.google.com/${env.GOOGLE_CLOUD_PROJECT}`], + ); + + const token = ticket.getPayload(); + + if (token) { + if ("user_id" in token) delete token.user_id; + Object.assign(token, { uid: token.sub }); + req.token = token as DecodedIdToken; + } + } + + next(); + } catch (err) { + req.log?.warn(err); + next(); + } +} + +// #region Types + +/** + * Interface representing a decoded Firebase ID token, returned from the + * {@link verifyIdToken} method. + * + * Firebase ID tokens are OpenID Connect spec-compliant JSON Web Tokens (JWTs). + * See the + * [ID Token section of the OpenID Connect spec](http://openid.net/specs/openid-connect-core-1_0.html#IDToken) + * for more information about the specific properties below. + */ +export interface DecodedIdToken extends TokenPayload { + /** + * Time, in seconds since the Unix epoch, when the end-user authentication + * occurred. + * + * This value is not set when this particular ID token was created, but when the + * user initially logged in to this session. In a single session, the Firebase + * SDKs will refresh a user's ID tokens every hour. Each ID token will have a + * different [`iat`](#iat) value, but the same `auth_time` value. + */ + auth_time: number; + + /** + * Information about the sign in event, including which sign in provider was + * used and provider-specific identity details. + * + * This data is provided by the Firebase Authentication service and is a + * reserved claim in the ID token. + */ + firebase: { + /** + * Provider-specific identity details corresponding + * to the provider used to sign in the user. + */ + identities: { + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ + [key: string]: any; + }; + + /** + * The ID of the provider used to sign in the user. + * One of `"anonymous"`, `"password"`, `"facebook.com"`, `"github.com"`, + * `"google.com"`, `"twitter.com"`, `"apple.com"`, `"microsoft.com"`, + * `"yahoo.com"`, `"phone"`, `"playgames.google.com"`, `"gc.apple.com"`, + * or `"custom"`. + * + * Additional Identity Platform provider IDs include `"linkedin.com"`, + * OIDC and SAML identity providers prefixed with `"saml."` and `"oidc."` + * respectively. + */ + sign_in_provider: string; + + /** + * The type identifier or `factorId` of the second factor, provided the + * ID token was obtained from a multi-factor authenticated user. + * For phone, this is `"phone"`. + */ + sign_in_second_factor?: string; + + /** + * The `uid` of the second factor used to sign in, provided the + * ID token was obtained from a multi-factor authenticated user. + */ + second_factor_identifier?: string; + + /** + * The ID of the tenant the user belongs to, if available. + */ + tenant?: string; + /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ + [key: string]: any; + }; + + /** + * The phone number of the user to whom the ID token belongs, if available. + */ + phone_number?: string; + + /** + * The `uid` corresponding to the user who the ID token belonged to. + * + * This value is not actually in the JWT token claims itself. It is added as a + * convenience, and is set as the value of the [`sub`](#sub) property. + */ + uid: string; + + /** + * Indicates whether or not the user is an admin. + */ + admin?: boolean; +} + +// #endregion diff --git a/server/core/env.ts b/server/core/env.ts new file mode 100644 index 000000000..6feccc153 --- /dev/null +++ b/server/core/env.ts @@ -0,0 +1,21 @@ +/* SPDX-FileCopyrightText: 2014-present Kriasoft */ +/* SPDX-License-Identifier: MIT */ + +import { cleanEnv, str } from "envalid"; + +/** + * Environment variables that has been validated and sanitized. + * + * @see https://github.com/ilyakaznacheev/cleanenv#readme + */ +export const env = cleanEnv(process.env, { + VERSION: str({ default: "latest" }), + + APP_STORAGE_BUCKET: str(), + + GOOGLE_CLOUD_PROJECT: str(), + GOOGLE_CLOUD_DATABASE: str(), + + OPENAI_ORGANIZATION: str(), + OPENAI_API_KEY: str(), +}); diff --git a/server/core/firestore.ts b/server/core/firestore.ts new file mode 100644 index 000000000..5c1965721 --- /dev/null +++ b/server/core/firestore.ts @@ -0,0 +1,18 @@ +/* SPDX-FileCopyrightText: 2014-present Kriasoft */ +/* SPDX-License-Identifier: MIT */ + +import { Firestore } from "@google-cloud/firestore"; +import { env } from "./env"; + +let db: Firestore | undefined; + +export function getFirestore() { + if (!db) { + db = new Firestore({ + projectId: env.GOOGLE_CLOUD_PROJECT, + databaseId: env.GOOGLE_CLOUD_DATABASE, + }); + } + + return db; +} diff --git a/server/core/logging.ts b/server/core/logging.ts new file mode 100644 index 000000000..b8ca332db --- /dev/null +++ b/server/core/logging.ts @@ -0,0 +1,60 @@ +/* SPDX-FileCopyrightText: 2014-present Kriasoft */ +/* SPDX-License-Identifier: MIT */ + +import { Request, Response } from "express"; +import { pino } from "pino"; +import { pinoHttp } from "pino-http"; +import { env } from "./env"; + +/** + * Low overhead Node.js logger. + * + * @see https://github.com/pinojs/pino + */ +export const logger = pino({ + // Custom formatter to set the "severity" property in the JSON payload + // to the log level to be automatically parsed. + // https://cloud.google.com/run/docs/logging#special-fields + formatters: { + level(label) { + return { severity: label }; + }, + }, + transport: { + // Enable pretty printing in development. + // https://github.com/pinojs/pino-pretty#readme + target: env.isProduction ? "pino/file" : "pino-pretty", + options: { + ...(!env.isProduction && { colorize: true }), + ignore: env.isProduction + ? "pid,hostname" + : "pid,hostname,req.headers,req.remoteAddress,req.remotePort,res.headers", + }, + }, +}); + +/** + * Creates a request-based logger with trace ID field for logging correlation. + * + * @see https://cloud.google.com/run/docs/logging#correlate-logs + */ +export const loggerMiddleware = pinoHttp({ + logger, + customProps(req) { + const traceHeader = req.header("X-Cloud-Trace-Context"); + + let trace; + + if (traceHeader) { + const [traceId] = traceHeader.split("/"); + trace = `projects/${env.GOOGLE_CLOUD_PROJECT}/traces/${traceId}`; + } + + return { + "logging.googleapis.com/trace": trace, + }; + }, + redact: { + paths: ["req.headers.authorization", "req.headers.cookie"], + }, +}); diff --git a/server/core/openai.ts b/server/core/openai.ts new file mode 100644 index 000000000..c3fe75560 --- /dev/null +++ b/server/core/openai.ts @@ -0,0 +1,15 @@ +/* SPDX-FileCopyrightText: 2014-present Kriasoft */ +/* SPDX-License-Identifier: MIT */ + +import { OpenAI } from "openai"; +import { env } from "./env"; + +/** + * OpenAI API client. + * + * @see https://github.com/openai/openai-node#readme + */ +export const openai = new OpenAI({ + organization: env.OPENAI_ORGANIZATION, + apiKey: env.OPENAI_API_KEY, +}); diff --git a/server/core/trpc.ts b/server/core/trpc.ts new file mode 100644 index 000000000..31c67c604 --- /dev/null +++ b/server/core/trpc.ts @@ -0,0 +1,102 @@ +/* SPDX-FileCopyrightText: 2014-present Kriasoft */ +/* SPDX-License-Identifier: MIT */ + +import { Firestore } from "@google-cloud/firestore"; +import { TRPCError, initTRPC } from "@trpc/server"; +import { CreateExpressContextOptions } from "@trpc/server/adapters/express"; +import { Logger } from "pino"; +import { SetNonNullable } from "type-fest"; +import { ZodError } from "zod"; +import { DecodedIdToken } from "./auth"; +import { env } from "./env"; +import { getFirestore } from "./firestore"; +import { logger } from "./logging"; + +/** + * tRPC instance. + * + * @see https://trpc.io/docs/quickstart + */ +export const t = initTRPC.context().create({ + isDev: env.isDev, + + // https://trpc.io/docs/server/error-formatting + errorFormatter(opts) { + const { shape, error } = opts; + return { + ...shape, + data: { + ...shape.data, + ...(error.code === "BAD_REQUEST" && + error.cause instanceof ZodError && { + zodError: error.cause.flatten(), + }), + }, + }; + }, +}); + +/** + * Creates a tRPC context for an incoming HTTP request. + */ +export async function createContext( + ctx: CreateExpressContextOptions, +): Promise { + return new HttpContext(getFirestore(), ctx.req.log, ctx.req.token); +} + +/** + * Creates a tRPC context for an incoming WebSocket request. + */ +export async function createWsContext(): Promise { + return new WsContext(getFirestore(), logger); +} + +class HttpContext { + constructor( + readonly db: Firestore, + readonly log: Logger, + readonly token: DecodedIdToken | null, + ) {} +} + +class WsContext { + constructor( + readonly db: Firestore, + readonly log: Logger, + ) {} + + get token(): DecodedIdToken | null { + throw new Error("ID token is not available in WebSocket context."); + } +} + +/** + * Ensures that the user is authenticated. + */ +export const authorize = t.middleware((opts) => { + if (!opts.ctx.token) { + throw new TRPCError({ code: "UNAUTHORIZED" }); + } + + return opts.next({ + ...opts, + ctx: opts.ctx as SetNonNullable, + }); +}); + +/** + * Ensures that the user is an admin. + */ +export const authorizeAdmin = authorize.unstable_pipe((opts) => { + if (!opts.ctx.token.admin) { + throw new TRPCError({ code: "FORBIDDEN" }); + } + + return opts.next(opts); +}); + +/** + * tRPC context. + */ +export type Context = HttpContext; diff --git a/server/core/utils.ts b/server/core/utils.ts new file mode 100644 index 000000000..ce7480e6b --- /dev/null +++ b/server/core/utils.ts @@ -0,0 +1,13 @@ +/* SPDX-FileCopyrightText: 2014-present Kriasoft */ +/* SPDX-License-Identifier: MIT */ + +import { customAlphabet, urlAlphabet } from "nanoid"; + +const shortIdAlphabet = urlAlphabet.replace(/[-_]/g, ""); + +/** + * Generates secure unique ID using URL friendly characters. + * + * @see https://github.com/ai/nanoid#readme + */ +export const newId = customAlphabet(shortIdAlphabet, 10); diff --git a/server/global.d.ts b/server/global.d.ts new file mode 100644 index 000000000..4d3c00163 --- /dev/null +++ b/server/global.d.ts @@ -0,0 +1,13 @@ +/* SPDX-FileCopyrightText: 2014-present Kriasoft */ +/* SPDX-License-Identifier: MIT */ + +import "express"; +import { DecodedIdToken } from "./core/auth"; + +declare global { + namespace Express { + interface Request { + token: DecodedIdToken | null; + } + } +} diff --git a/server/index.ts b/server/index.ts new file mode 100644 index 000000000..6000ae8f9 --- /dev/null +++ b/server/index.ts @@ -0,0 +1,65 @@ +/* SPDX-FileCopyrightText: 2014-present Kriasoft */ +/* SPDX-License-Identifier: MIT */ + +import { applyWSSHandler } from "@trpc/server/adapters/ws"; +import { WebSocketServer } from "ws"; +import { app } from "./app"; +import { logger } from "./core/logging"; +import { createWsContext as createContext } from "./core/trpc"; +import { router } from "./routes"; + +// Detect if running in Google Cloud environment +const isCloudRun = !!process.env.K_SERVICE; + +/** + * Starts the HTTP and WebSocket servers. + */ +export function listen(port: number) { + const server = app.listen(port, () => { + logger.info(`API listening on ${port}`); + }); + + const wss = new WebSocketServer({ server, path: "/trpc" }); + const handler = applyWSSHandler({ wss, router, createContext }); + + wss.on("connection", (ws) => { + logger.info({ clients: wss.clients.size }, "wss:connection"); + ws.once("close", () => { + logger.info({ clients: wss.clients.size }, "wss:close"); + }); + }); + + return function dispose(cb?: () => void) { + handler.broadcastReconnectNotification(); + wss.close((err) => { + if (err) logger.error(err); + if (isCloudRun) logger.info("WebSocket server closed"); + server.close((err) => { + if (err) logger.error(err); + if (isCloudRun) logger.info("HTTP server closed"); + logger.flush((err) => { + if (err) console.error(err); + if (isCloudRun) { + process.exit(0); + } else { + cb?.(); + } + }); + }); + }); + }; +} + +if (process.env.PORT && process.env.K_SERVICE?.startsWith("server")) { + const port = parseInt(process.env.PORT); + const dispose = listen(port); + + /* eslint-disable-next-line no-inner-declarations */ + function handleClose(code: NodeJS.Signals) { + logger.info(`${code} signal received`); + dispose(); + } + + process.on("SIGINT", handleClose); + process.on("SIGTERM", handleClose); +} diff --git a/server/package.json b/server/package.json new file mode 100644 index 000000000..92bdcaa12 --- /dev/null +++ b/server/package.json @@ -0,0 +1,52 @@ +{ + "name": "server", + "version": "0.0.0", + "private": true, + "type": "module", + "exports": { + ".": { + "types": "./types.ts" + }, + "./package.json": "./package.json" + }, + "scripts": { + "start": "vite-node --watch ./start.ts", + "build": "vite build && yarn node ../scripts/bundle-yarn.js", + "test": "vitest" + }, + "dependencies": { + "@google-cloud/firestore": "^7.1.0", + "@googleapis/identitytoolkit": "^8.0.0", + "@trpc/server": "^10.45.0", + "db": "workspace:*", + "envalid": "^8.0.0", + "express": "^4.18.2", + "google-auth-library": "^9.4.1", + "got": "^13.0.0", + "http-errors": "^2.0.0", + "http-proxy-middleware": "^2.0.6", + "nanoid": "^5.0.4", + "openai": "^4.24.1", + "pino": "^8.17.2", + "pino-http": "^9.0.0", + "pino-pretty": "^10.3.1", + "type-fest": "^4.9.0", + "ws": "^8.16.0", + "zod": "^3.22.4" + }, + "devDependencies": { + "@types/express": "^4.17.21", + "@types/http-errors": "^2.0.4", + "@types/node": "^20.10.7", + "@types/supertest": "^6.0.2", + "@types/ws": "^8.5.10", + "envars": "^1.0.2", + "get-port": "^7.0.0", + "supertest": "^6.3.3", + "type-fest": "^4.9.0", + "typescript": "~5.3.3", + "vite": "~5.0.11", + "vite-node": "~1.1.3", + "vitest": "~1.1.3" + } +} diff --git a/server/routes/index.ts b/server/routes/index.ts new file mode 100644 index 000000000..93a7b37e6 --- /dev/null +++ b/server/routes/index.ts @@ -0,0 +1,15 @@ +/* SPDX-FileCopyrightText: 2014-present Kriasoft */ +/* SPDX-License-Identifier: MIT */ + +import { t } from "../core/trpc"; +import { workspace } from "./workspace"; + +/** + * The root tRPC router. + * @see https://trpc.io/docs/quickstart + */ +export const router = t.router({ + workspace, +}); + +export type AppRouter = typeof router; diff --git a/server/routes/workspace.test.ts b/server/routes/workspace.test.ts new file mode 100644 index 000000000..6eea0ea0e --- /dev/null +++ b/server/routes/workspace.test.ts @@ -0,0 +1,63 @@ +/* SPDX-FileCopyrightText: 2014-present Kriasoft */ +/* SPDX-License-Identifier: MIT */ + +import { TRPCError } from "@trpc/server"; +import { testWorkspaces } from "db"; +import { describe, expect, it } from "vitest"; +import { createClient } from "../test/context"; +import * as router from "./workspace"; + +describe("workspace.update()", () => { + it("requires authentication", async () => { + const [client] = createClient(router.workspace); + const action = client.update({ + id: "xxxxxxxxxx", + name: "My Workspace", + }); + + await expect(action).rejects.toThrowError( + new TRPCError({ code: "UNAUTHORIZED" }), + ); + }); + + it("workspace must exist", async () => { + const [client] = createClient(router.workspace, { user: "erika" }); + const action = client.update({ + id: "xxxxxxxxxx", // Non-existent workspace ID + name: "My Workspace", + }); + + await expect(action).rejects.toThrowError( + new TRPCError({ code: "NOT_FOUND" }), + ); + }); + + it("workspace must belong to the user", async () => { + const [client, ctx] = createClient(router.workspace, { user: "erika" }); + const id = testWorkspaces.find((x) => x.ownerId !== ctx.token?.uid)?.id; + expect(id).toEqual(expect.any(String)); + + const action = client.update({ + id: id!, // Workspace ID that belongs to another user + name: "My Workspace", + }); + + await expect(action).rejects.toThrowError( + new TRPCError({ code: "NOT_FOUND" }), + ); + }); + + it("updates the workspace", async () => { + const [client, ctx] = createClient(router.workspace, { user: "erika" }); + const id = testWorkspaces.find((x) => x.ownerId === ctx.token?.uid)?.id; + expect(id).toEqual(expect.any(String)); + + await client.update({ id: id!, name: "My Workspace" }); + const doc1 = await ctx.db.doc(`workspace/${id}`).get(); + expect(doc1.data()?.name).toBe("My Workspace"); + + await client.update({ id: id!, name: "Personal workspace" }); + const doc2 = await ctx.db.doc(`workspace/${id}`).get(); + expect(doc2.data()?.name).toBe("Personal workspace"); + }); +}); diff --git a/server/routes/workspace.ts b/server/routes/workspace.ts new file mode 100644 index 000000000..c98eb4835 --- /dev/null +++ b/server/routes/workspace.ts @@ -0,0 +1,39 @@ +/* SPDX-FileCopyrightText: 2014-present Kriasoft */ +/* SPDX-License-Identifier: MIT */ + +import { Timestamp } from "@google-cloud/firestore"; +import { TRPCError } from "@trpc/server"; +import { z } from "zod"; +import { authorize, t } from "../core/trpc"; + +/** + * Workspace API. + */ +export const workspace = t.router({ + /** + * Updates the workspace. + */ + update: t.procedure + .use(authorize) + .input( + z.object({ + id: z.string(), + name: z.string().max(100), + }), + ) + .query(async ({ input, ctx }) => { + const { db } = ctx; + const doc = await db.doc(`workspace/${input.id}`).get(); + + if (!doc.exists || doc.data()?.ownerId !== ctx.token.uid) { + throw new TRPCError({ code: "NOT_FOUND" }); + } + + await doc.ref.update({ + name: input.name, + updated: Timestamp.now(), + }); + }), +}); + +export type WorkspaceRouter = typeof workspace; diff --git a/server/start.ts b/server/start.ts new file mode 100644 index 000000000..6d73dbb02 --- /dev/null +++ b/server/start.ts @@ -0,0 +1,26 @@ +/* SPDX-FileCopyrightText: 2014-present Kriasoft */ +/* SPDX-License-Identifier: MIT */ + +import getPort, { portNumbers } from "get-port"; +import { listen } from "./index"; + +const port = await getPort({ port: portNumbers(8080, 9000) }); + +let dispose = listen(port); + +if (import.meta.hot) { + import.meta.hot.accept("/index.ts", () => { + dispose(() => { + import("./index").then(({ listen }) => { + dispose = listen(port); + }); + }); + }); +} + +function cleanUp() { + dispose(() => process.exit()); +} + +process.on("SIGINT", cleanUp); +process.on("SIGTERM", cleanUp); diff --git a/server/test/context.ts b/server/test/context.ts new file mode 100644 index 000000000..c74b7eeac --- /dev/null +++ b/server/test/context.ts @@ -0,0 +1,80 @@ +/* SPDX-FileCopyrightText: 2014-present Kriasoft */ +/* SPDX-License-Identifier: MIT */ + +import { Firestore } from "@google-cloud/firestore"; +import { + AnyRootConfig, + AnyRouterDef, + Router, + createCallerFactory, +} from "@trpc/server"; +import { testUsers } from "db"; +import { IdTokenClient } from "google-auth-library"; +import { auth } from "../core/auth"; +import { env } from "../core/env"; +import { getFirestore } from "../core/firestore"; +import { logger } from "../core/logging"; +import { Context } from "../core/trpc"; + +let idTokenClient: IdTokenClient | undefined; + +export async function getIdToken(audience: string) { + idTokenClient = idTokenClient ?? (await auth.getIdTokenClient(audience)); + return await idTokenClient.idTokenProvider.fetchIdToken(audience); +} + +/** + * Create a tRPC context for unit testing. + */ +export function createContext(options?: ContextOptions): Context { + const user = testUsers.find((u) => u.screenName === options?.user); + const now = Math.floor(Date.now() / 1000); + + if (options?.user && !user) { + throw new Error(`User not found: ${options.user}`); + } + + return { + db: options?.db ?? getFirestore(), + log: logger, + token: user + ? { + uid: user.localId, + sub: user.localId, + email: user.email, + email_verified: user.emailVerified, + aud: env.GOOGLE_CLOUD_PROJECT, + iss: `https://securetoken.google.com/${env.GOOGLE_CLOUD_PROJECT}`, + iat: now, + exp: now + 3600, + auth_time: parseInt(user.lastLoginAt!, 10), + firebase: { + identities: {}, + sign_in_provider: "google.com", + }, + ...(user.customAttributes && JSON.parse(user.customAttributes)), + } + : null, + }; +} + +export function createClient< + TRouter extends Router>, +>(router: TRouter, options?: ContextOptions) { + const createCaller = createCallerFactory(); + const ctx = createContext(options); + const client = createCaller(router)(ctx); + return [client, ctx] as const; +} + +// #region Types + +type ContextOptions = { + /** + * The user to impersonate. Use `dan` if you need admin permissions. + */ + user?: "erika" | "ryan" | "marian" | "kurt" | "dan"; + db?: Firestore; +}; + +// #endregion Types diff --git a/server/tsconfig.json b/server/tsconfig.json new file mode 100644 index 000000000..74dd02c1a --- /dev/null +++ b/server/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "emitDeclarationOnly": true, + "module": "ESNext", + "moduleResolution": "Bundler", + "noEmit": false, + "outDir": "../.cache/ts-server", + "types": ["node", "vite/client"] + }, + "include": ["**/*.ts", "**/*.json"], + "exclude": ["**/dist/**/*", "**/node_modules/**/*"], + "references": [{ "path": "../db" }] +} diff --git a/server/types.ts b/server/types.ts new file mode 100644 index 000000000..97af82379 --- /dev/null +++ b/server/types.ts @@ -0,0 +1,4 @@ +/* SPDX-FileCopyrightText: 2014-present Kriasoft */ +/* SPDX-License-Identifier: MIT */ + +export type { AppRouter } from "./routes/index"; diff --git a/server/vite.config.ts b/server/vite.config.ts new file mode 100644 index 000000000..34c038937 --- /dev/null +++ b/server/vite.config.ts @@ -0,0 +1,38 @@ +/* SPDX-FileCopyrightText: 2014-present Kriasoft */ +/* SPDX-License-Identifier: MIT */ + +import { loadEnv } from "envars"; +import { defineConfig } from "vitest/config"; + +/** + * Vite configuration for the server-side bundle. + * + * @see https://vitejs.dev/config/ + */ +export default defineConfig(async ({ mode }) => { + await loadEnv(mode, { + root: "..", + schema: "./core/env.ts", + mergeTo: process.env, + }); + + return { + cacheDir: "../.cache/vite-server", + + build: { + ssr: "./index.ts", + sourcemap: true, + }, + + ssr: { + ...(mode === "production" && { + noExternal: ["http-errors"], + }), + }, + + test: { + environment: "node", + testTimeout: 20000, + }, + }; +}); diff --git a/tsconfig.json b/tsconfig.json index 8c8c9f712..7e02dcc24 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,9 @@ "files": [], "references": [ { "path": "./app" }, + { "path": "./db" }, { "path": "./edge" }, - { "path": "./scripts" } + { "path": "./scripts" }, + { "path": "./server" } ] } diff --git a/yarn.lock b/yarn.lock index cfcd7bde8..3322766c8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1443,11 +1443,21 @@ __metadata: linkType: hard "@floating-ui/utils@npm:^0.2.0": - version: 0.2.0 - resolution: "@floating-ui/utils@npm:0.2.0" - peerDependencies: - react: ">=16.8.0" - checksum: 892a7a272a350f32ceab68bf0be7773378bd0d7ccf1c27385f935f18dc38327fc44633e8286191f9a836ae063684386d48e43ee6026f17be149bf02ca20433d7 + version: 0.2.1 + resolution: "@floating-ui/utils@npm:0.2.1" + checksum: 33c9ab346e7b05c5a1e6a95bc902aafcfc2c9d513a147e2491468843bd5607531b06d0b9aa56aa491cbf22a6c2495c18ccfc4c0344baec54a689a7bb8e4898d6 + languageName: node + linkType: hard + +"@google-cloud/firestore@npm:^7.1.0": + version: 7.1.0 + resolution: "@google-cloud/firestore@npm:7.1.0" + dependencies: + fast-deep-equal: "npm:^3.1.1" + functional-red-black-tree: "npm:^1.0.1" + google-gax: "npm:^4.0.4" + protobufjs: "npm:^7.2.5" + checksum: 141c03bec6ba55c80308ea105ca1319d29a7fbea1ecb95a5c2f58aa454d7f1d44112b67440a960639344b6562f0c89ffbda206fd74d875023e7addc4cdfa876f languageName: node linkType: hard @@ -1460,6 +1470,15 @@ __metadata: languageName: node linkType: hard +"@googleapis/identitytoolkit@npm:^8.0.0": + version: 8.0.0 + resolution: "@googleapis/identitytoolkit@npm:8.0.0" + dependencies: + googleapis-common: "npm:^7.0.0" + checksum: 59b20c6c130d00cbeeaaca0bdf2efc0e8a06ebb20725c96b5a63efbee24b50d4d7ced9f032c691fce1acb13cc906eaf698b6e21b03d63a17fb98287a9e42a135 + languageName: node + linkType: hard + "@grpc/grpc-js@npm:~1.9.0, @grpc/grpc-js@npm:~1.9.6": version: 1.9.13 resolution: "@grpc/grpc-js@npm:1.9.13" @@ -2260,10 +2279,10 @@ __metadata: languageName: node linkType: hard -"@sindresorhus/is@npm:^6.1.0": - version: 6.1.0 - resolution: "@sindresorhus/is@npm:6.1.0" - checksum: e15c4abdca53a938e177d70c4e0562234a7ae32fa7c09ca318919d6ad28aa4ba68f58bd5339c3c38f2e912d40a201df169b9ed84579f2ef6f25d9da2d7b05334 +"@sindresorhus/is@npm:^5.2.0": + version: 5.6.0 + resolution: "@sindresorhus/is@npm:5.6.0" + checksum: b077c325acec98e30f7d86df158aaba2e7af2acb9bb6a00fda4b91578539fbff4ecebe9b934e24fec0e6950de3089d89d79ec02d9062476b20ce185be0e01bd6 languageName: node linkType: hard @@ -2283,6 +2302,13 @@ __metadata: languageName: node linkType: hard +"@trpc/server@npm:^10.45.0": + version: 10.45.0 + resolution: "@trpc/server@npm:10.45.0" + checksum: 87f3c0f0169d73e7a4ce4df0cbbe5a7dd18a67a29e8a932db135bfa72301adbc656558e9bc8478317ec4007b32d6988f456aefd37a66624041714333d64d81e0 + languageName: node + linkType: hard + "@types/babel__core@npm:^7.20.5": version: 7.20.5 resolution: "@types/babel__core@npm:7.20.5" @@ -2333,6 +2359,16 @@ __metadata: languageName: node linkType: hard +"@types/body-parser@npm:*": + version: 1.19.5 + resolution: "@types/body-parser@npm:1.19.5" + dependencies: + "@types/connect": "npm:*" + "@types/node": "npm:*" + checksum: 1e251118c4b2f61029cc43b0dc028495f2d1957fe8ee49a707fb940f86a9bd2f9754230805598278fe99958b49e9b7e66eec8ef6a50ab5c1f6b93e1ba2aaba82 + languageName: node + linkType: hard + "@types/caseless@npm:*": version: 0.12.5 resolution: "@types/caseless@npm:0.12.5" @@ -2340,6 +2376,22 @@ __metadata: languageName: node linkType: hard +"@types/connect@npm:*": + version: 3.4.38 + resolution: "@types/connect@npm:3.4.38" + dependencies: + "@types/node": "npm:*" + checksum: 7eb1bc5342a9604facd57598a6c62621e244822442976c443efb84ff745246b10d06e8b309b6e80130026a396f19bf6793b7cecd7380169f369dac3bfc46fb99 + languageName: node + linkType: hard + +"@types/cookiejar@npm:^2.1.5": + version: 2.1.5 + resolution: "@types/cookiejar@npm:2.1.5" + checksum: 04d5990e87b6387532d15a87d9ec9b2eb783039291193863751dcfd7fc723a3b3aa30ce4c06b03975cba58632e933772f1ff031af23eaa3ac7f94e71afa6e073 + languageName: node + linkType: hard + "@types/eslint@npm:^8.56.1": version: 8.56.1 resolution: "@types/eslint@npm:8.56.1" @@ -2357,6 +2409,30 @@ __metadata: languageName: node linkType: hard +"@types/express-serve-static-core@npm:^4.17.33": + version: 4.17.41 + resolution: "@types/express-serve-static-core@npm:4.17.41" + dependencies: + "@types/node": "npm:*" + "@types/qs": "npm:*" + "@types/range-parser": "npm:*" + "@types/send": "npm:*" + checksum: 7647e19d9c3d57ddd18947d2b161b90ef0aedd15875140e5b824209be41c1084ae942d4fb43cd5f2051a6a5f8c044519ef6c9ac1b2ad86b9aa546b4f1f023303 + languageName: node + linkType: hard + +"@types/express@npm:^4.17.21": + version: 4.17.21 + resolution: "@types/express@npm:4.17.21" + dependencies: + "@types/body-parser": "npm:*" + "@types/express-serve-static-core": "npm:^4.17.33" + "@types/qs": "npm:*" + "@types/serve-static": "npm:*" + checksum: 7a6d26cf6f43d3151caf4fec66ea11c9d23166e4f3102edfe45a94170654a54ea08cf3103d26b3928d7ebcc24162c90488e33986b7e3a5f8941225edd5eb18c7 + languageName: node + linkType: hard + "@types/fs-extra@npm:^11.0.1": version: 11.0.4 resolution: "@types/fs-extra@npm:11.0.4" @@ -2374,6 +2450,22 @@ __metadata: languageName: node linkType: hard +"@types/http-errors@npm:*, @types/http-errors@npm:^2.0.4": + version: 2.0.4 + resolution: "@types/http-errors@npm:2.0.4" + checksum: 1f3d7c3b32c7524811a45690881736b3ef741bf9849ae03d32ad1ab7062608454b150a4e7f1351f83d26a418b2d65af9bdc06198f1c079d75578282884c4e8e3 + languageName: node + linkType: hard + +"@types/http-proxy@npm:^1.17.8": + version: 1.17.14 + resolution: "@types/http-proxy@npm:1.17.14" + dependencies: + "@types/node": "npm:*" + checksum: aa1a3e66cd43cbf06ea5901bf761d2031200a0ab42ba7e462a15c752e70f8669f21fb3be7c2f18fefcb83b95132dfa15740282e7421b856745598fbaea8e3a42 + languageName: node + linkType: hard + "@types/json-schema@npm:*, @types/json-schema@npm:^7.0.12": version: 7.0.15 resolution: "@types/json-schema@npm:7.0.15" @@ -2404,6 +2496,27 @@ __metadata: languageName: node linkType: hard +"@types/methods@npm:^1.1.4": + version: 1.1.4 + resolution: "@types/methods@npm:1.1.4" + checksum: ad2a7178486f2fd167750f3eb920ab032a947ff2e26f55c86670a6038632d790b46f52e5b6ead5823f1e53fc68028f1e9ddd15cfead7903e04517c88debd72b1 + languageName: node + linkType: hard + +"@types/mime@npm:*": + version: 3.0.4 + resolution: "@types/mime@npm:3.0.4" + checksum: a6139c8e1f705ef2b064d072f6edc01f3c099023ad7c4fce2afc6c2bf0231888202adadbdb48643e8e20da0ce409481a49922e737eca52871b3dc08017455843 + languageName: node + linkType: hard + +"@types/mime@npm:^1": + version: 1.3.5 + resolution: "@types/mime@npm:1.3.5" + checksum: e29a5f9c4776f5229d84e525b7cd7dd960b51c30a0fb9a028c0821790b82fca9f672dab56561e2acd9e8eed51d431bde52eafdfef30f643586c4162f1aecfc78 + languageName: node + linkType: hard + "@types/minimist@npm:^1.2.2": version: 1.2.5 resolution: "@types/minimist@npm:1.2.5" @@ -2411,6 +2524,16 @@ __metadata: languageName: node linkType: hard +"@types/node-fetch@npm:^2.6.4": + version: 2.6.10 + resolution: "@types/node-fetch@npm:2.6.10" + dependencies: + "@types/node": "npm:*" + form-data: "npm:^4.0.0" + checksum: 121c0c2d4d28e784006cbff26383b31538aa4f113475dcf05a2744d18b4f44d6b30184fb2460a6ef8ec66b289abc6d9b80bf54518f8a5803fec614514b022c6b + languageName: node + linkType: hard + "@types/node-forge@npm:^1.3.0": version: 1.3.11 resolution: "@types/node-forge@npm:1.3.11" @@ -2420,21 +2543,21 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:*, @types/node@npm:>=12.12.47, @types/node@npm:>=13.7.0, @types/node@npm:^20.10.6": - version: 20.10.6 - resolution: "@types/node@npm:20.10.6" +"@types/node@npm:*, @types/node@npm:>=12.12.47, @types/node@npm:>=13.7.0, @types/node@npm:^20.10.7": + version: 20.10.7 + resolution: "@types/node@npm:20.10.7" dependencies: undici-types: "npm:~5.26.4" - checksum: 08471220d3cbbb6669835c4b78541edf5eface8f2c2e36c550cfa4ff73da73071c90e200a06359fac25d6564127597c23e178128058fb676824ec23d5178a017 + checksum: 4a1ba3fb7fc0a262c6b2d5ec763cec7abf5b5d6b2d277798ddbf05227f5778f9a806987821fd4d3eacd6f37c946ac61a9c64fb79ae4b84daaec12354158835a3 languageName: node linkType: hard -"@types/node@npm:^18.16.3": - version: 18.19.4 - resolution: "@types/node@npm:18.19.4" +"@types/node@npm:^18.11.18, @types/node@npm:^18.16.3": + version: 18.19.5 + resolution: "@types/node@npm:18.19.5" dependencies: undici-types: "npm:~5.26.4" - checksum: 4eaf6e1bb588da960c7e5974e8a0112633138b5468f0e48fc959d5585863baca549bc8c5f5f7b751c61dd282b8c2879842220f6eeed33aa54c255e876ec16536 + checksum: 215bc65e464e5c9f042dc2e475298851dc4ba9cb68a3648ad816075b515fd1051bc19dcfab49f661938a64f40d62cc462e9c469c7aff971d1cda7095b52be9ec languageName: node linkType: hard @@ -2466,6 +2589,20 @@ __metadata: languageName: node linkType: hard +"@types/qs@npm:*": + version: 6.9.11 + resolution: "@types/qs@npm:6.9.11" + checksum: 620ca1628bf3da65662c54ed6ebb120b18a3da477d0bfcc872b696685a9bb1893c3c92b53a1190a8f54d52eaddb6af8b2157755699ac83164604329935e8a7f2 + languageName: node + linkType: hard + +"@types/range-parser@npm:*": + version: 1.2.7 + resolution: "@types/range-parser@npm:1.2.7" + checksum: 95640233b689dfbd85b8c6ee268812a732cf36d5affead89e806fe30da9a430767af8ef2cd661024fd97e19d61f3dec75af2df5e80ec3bea000019ab7028629a + languageName: node + linkType: hard + "@types/react-dom@npm:^18.2.18": version: 18.2.18 resolution: "@types/react-dom@npm:18.2.18" @@ -2521,6 +2658,48 @@ __metadata: languageName: node linkType: hard +"@types/send@npm:*": + version: 0.17.4 + resolution: "@types/send@npm:0.17.4" + dependencies: + "@types/mime": "npm:^1" + "@types/node": "npm:*" + checksum: 28320a2aa1eb704f7d96a65272a07c0bf3ae7ed5509c2c96ea5e33238980f71deeed51d3631927a77d5250e4091b3e66bce53b42d770873282c6a20bb8b0280d + languageName: node + linkType: hard + +"@types/serve-static@npm:*": + version: 1.15.5 + resolution: "@types/serve-static@npm:1.15.5" + dependencies: + "@types/http-errors": "npm:*" + "@types/mime": "npm:*" + "@types/node": "npm:*" + checksum: 49aa21c367fffe4588fc8c57ea48af0ea7cbadde7418bc53cde85d8bd57fd2a09a293970d9ea86e79f17a87f8adeb3e20da76aab38e1c4d1567931fa15c8af38 + languageName: node + linkType: hard + +"@types/superagent@npm:^8.1.0": + version: 8.1.1 + resolution: "@types/superagent@npm:8.1.1" + dependencies: + "@types/cookiejar": "npm:^2.1.5" + "@types/methods": "npm:^1.1.4" + "@types/node": "npm:*" + checksum: 02b987833cf0d85da9b137fd296fe8ad25a470d60f7e9d81a6ed3f8f8a5d6bace8780816bd35885e2928f467e819a4aa509879a7da0f28018ab1453845eb91e2 + languageName: node + linkType: hard + +"@types/supertest@npm:^6.0.2": + version: 6.0.2 + resolution: "@types/supertest@npm:6.0.2" + dependencies: + "@types/methods": "npm:^1.1.4" + "@types/superagent": "npm:^8.1.0" + checksum: 4b67fb2d1bfbb7ff0a7dfaaf190cdf2e0014522615fb2dc53c214bdac95b4ee42696dd1df13332c90a7765cc52934c9cc0c428bf0f9e8189167aef01042e7448 + languageName: node + linkType: hard + "@types/tough-cookie@npm:*": version: 4.0.5 resolution: "@types/tough-cookie@npm:4.0.5" @@ -2535,15 +2714,24 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:^6.17.0": - version: 6.17.0 - resolution: "@typescript-eslint/eslint-plugin@npm:6.17.0" +"@types/ws@npm:^8.5.10": + version: 8.5.10 + resolution: "@types/ws@npm:8.5.10" + dependencies: + "@types/node": "npm:*" + checksum: 9b414dc5e0b6c6f1ea4b1635b3568c58707357f68076df9e7cd33194747b7d1716d5189c0dbdd68c8d2521b148e88184cf881bac7429eb0e5c989b001539ed31 + languageName: node + linkType: hard + +"@typescript-eslint/eslint-plugin@npm:^6.18.0": + version: 6.18.0 + resolution: "@typescript-eslint/eslint-plugin@npm:6.18.0" dependencies: "@eslint-community/regexpp": "npm:^4.5.1" - "@typescript-eslint/scope-manager": "npm:6.17.0" - "@typescript-eslint/type-utils": "npm:6.17.0" - "@typescript-eslint/utils": "npm:6.17.0" - "@typescript-eslint/visitor-keys": "npm:6.17.0" + "@typescript-eslint/scope-manager": "npm:6.18.0" + "@typescript-eslint/type-utils": "npm:6.18.0" + "@typescript-eslint/utils": "npm:6.18.0" + "@typescript-eslint/visitor-keys": "npm:6.18.0" debug: "npm:^4.3.4" graphemer: "npm:^1.4.0" ignore: "npm:^5.2.4" @@ -2556,44 +2744,44 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: f2a5774e9cc03e491a5a488501e5622c7eebd766f5a4fc2c30642864a3b89b0807946bde33a678f326ba7032f3f6a51aa0bf9c2d10adc823804fc9fb47db55a6 + checksum: 7810a84f6d9cb735f6848aa4a7ef64c77740812afc0248fda63ec182910a1d045cd9a32d8e49b0e5323266db6da12a2fe50171147927b3186815a56f12c16ee7 languageName: node linkType: hard -"@typescript-eslint/parser@npm:^6.17.0": - version: 6.17.0 - resolution: "@typescript-eslint/parser@npm:6.17.0" +"@typescript-eslint/parser@npm:^6.18.0": + version: 6.18.0 + resolution: "@typescript-eslint/parser@npm:6.18.0" dependencies: - "@typescript-eslint/scope-manager": "npm:6.17.0" - "@typescript-eslint/types": "npm:6.17.0" - "@typescript-eslint/typescript-estree": "npm:6.17.0" - "@typescript-eslint/visitor-keys": "npm:6.17.0" + "@typescript-eslint/scope-manager": "npm:6.18.0" + "@typescript-eslint/types": "npm:6.18.0" + "@typescript-eslint/typescript-estree": "npm:6.18.0" + "@typescript-eslint/visitor-keys": "npm:6.18.0" debug: "npm:^4.3.4" peerDependencies: eslint: ^7.0.0 || ^8.0.0 peerDependenciesMeta: typescript: optional: true - checksum: 2ed0ed4a5b30e953430ce3279df3655af09fa1caed2abf11804d239717daefc32a22864f6620ef57bb9c684c74a99a13241384fea5096e961385e3678fc2e920 + checksum: 6798332819f839454a8405e31cfaa0fe908d5966be929bef55e78ac51a0ff3868feb42b38e7772cedf88277f6b2841b3d91f6c573eacb945e2741ecae94047c7 languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:6.17.0": - version: 6.17.0 - resolution: "@typescript-eslint/scope-manager@npm:6.17.0" +"@typescript-eslint/scope-manager@npm:6.18.0": + version: 6.18.0 + resolution: "@typescript-eslint/scope-manager@npm:6.18.0" dependencies: - "@typescript-eslint/types": "npm:6.17.0" - "@typescript-eslint/visitor-keys": "npm:6.17.0" - checksum: fe09c628553c9336e6a36d32c1d34e78ebd20aa02130a6bf535329621ba5a98aaac171f607bc6e4d17b3478c42e7de6476376636897ce3f227c754eb99acd07e + "@typescript-eslint/types": "npm:6.18.0" + "@typescript-eslint/visitor-keys": "npm:6.18.0" + checksum: c2c465e6803f78d3300142167a8a79dd2613c64cf464a40a9cf6b13a2c10c3d82ca30bb9c2d26aba7f054b8740b38e1d25f377fcdf917aba489d5b5ea2550858 languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:6.17.0": - version: 6.17.0 - resolution: "@typescript-eslint/type-utils@npm:6.17.0" +"@typescript-eslint/type-utils@npm:6.18.0": + version: 6.18.0 + resolution: "@typescript-eslint/type-utils@npm:6.18.0" dependencies: - "@typescript-eslint/typescript-estree": "npm:6.17.0" - "@typescript-eslint/utils": "npm:6.17.0" + "@typescript-eslint/typescript-estree": "npm:6.18.0" + "@typescript-eslint/utils": "npm:6.18.0" debug: "npm:^4.3.4" ts-api-utils: "npm:^1.0.1" peerDependencies: @@ -2601,23 +2789,23 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: dc7938429193acfda61b7282197ec046039e2c4da41cdcddf4daaf300d10229e4e23bb0fcf0503b19c0b99a874849c8a9f5bb35ce106260f56a14106d2b41d8c + checksum: 9f4a392fe7b7f6b1fb02acbdaa331e764775f6404c29ca66774419e39552523d878227020526ca9c438d997555a99600f8d711496e9a435fb14a779e25ed094e languageName: node linkType: hard -"@typescript-eslint/types@npm:6.17.0": - version: 6.17.0 - resolution: "@typescript-eslint/types@npm:6.17.0" - checksum: 87ab1b5a3270ab34b917c22a2fb90a9ad7d9f3b19d73a337bc9efbe65f924da13482c97e8ccbe3bd3d081aa96039eeff50de41c1da2a2128066429b931cdb21d +"@typescript-eslint/types@npm:6.18.0": + version: 6.18.0 + resolution: "@typescript-eslint/types@npm:6.18.0" + checksum: fc507ca7a1bfec04467087165ff6722f7b9aa9a089ecf0c17656824a951b92ca014766e480122de850057c63a3066628985eb0681c5bbb80ab41d94e7bb52288 languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:6.17.0": - version: 6.17.0 - resolution: "@typescript-eslint/typescript-estree@npm:6.17.0" +"@typescript-eslint/typescript-estree@npm:6.18.0": + version: 6.18.0 + resolution: "@typescript-eslint/typescript-estree@npm:6.18.0" dependencies: - "@typescript-eslint/types": "npm:6.17.0" - "@typescript-eslint/visitor-keys": "npm:6.17.0" + "@typescript-eslint/types": "npm:6.18.0" + "@typescript-eslint/visitor-keys": "npm:6.18.0" debug: "npm:^4.3.4" globby: "npm:^11.1.0" is-glob: "npm:^4.0.3" @@ -2627,34 +2815,34 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 1671b0d2f2fdf07074fb1e2524d61935cec173bd8db6e482cc5b2dcc77aed3ffa831396736ffa0ee2fdbddd8585ae9ca8d6c97bcaea1385b23907a1ec0508f83 + checksum: b65392e944baba97ed98e99a1e7122b7b05fa0d9a082b48d0190b377ae9e2ae4ed72d505a2f0f05f2ca78908f0e4b0f1acd44d345c7f4f4415fcec6bb2c57791 languageName: node linkType: hard -"@typescript-eslint/utils@npm:6.17.0": - version: 6.17.0 - resolution: "@typescript-eslint/utils@npm:6.17.0" +"@typescript-eslint/utils@npm:6.18.0": + version: 6.18.0 + resolution: "@typescript-eslint/utils@npm:6.18.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.4.0" "@types/json-schema": "npm:^7.0.12" "@types/semver": "npm:^7.5.0" - "@typescript-eslint/scope-manager": "npm:6.17.0" - "@typescript-eslint/types": "npm:6.17.0" - "@typescript-eslint/typescript-estree": "npm:6.17.0" + "@typescript-eslint/scope-manager": "npm:6.18.0" + "@typescript-eslint/types": "npm:6.18.0" + "@typescript-eslint/typescript-estree": "npm:6.18.0" semver: "npm:^7.5.4" peerDependencies: eslint: ^7.0.0 || ^8.0.0 - checksum: 37c63afcf66124bf84808699997953b8c84a378aa2c490a771b611d82cdac8499c58fac8eeb8378528e97660b59563d99297bfec4b982cd68760b0ffe54aa714 + checksum: f91798069e337ed42b7e415aabded833d540dab9adf66ae88183003428892584946f20a5496aae2dc65fc0f65b66496baed785a3470ee782b6e2dd25b9200c6c languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:6.17.0": - version: 6.17.0 - resolution: "@typescript-eslint/visitor-keys@npm:6.17.0" +"@typescript-eslint/visitor-keys@npm:6.18.0": + version: 6.18.0 + resolution: "@typescript-eslint/visitor-keys@npm:6.18.0" dependencies: - "@typescript-eslint/types": "npm:6.17.0" + "@typescript-eslint/types": "npm:6.18.0" eslint-visitor-keys: "npm:^3.4.1" - checksum: a2aed0e1437fdab8858ab9c7c8e355f8b72a5fa4b0adc54f28b8a2bbc29d4bb93214968ee940f83d013d0a4b83d00cd4eeeb05fb4c2c7d0ead324c6793f7d6d4 + checksum: bf34a357549d515607c761f385b7c7c82aaa07795cada0be2e1e3860c5103fbf731408ac07eaeb0517b51426d77ef9b194dfb94f205c776107a46e0d0027b452 languageName: node linkType: hard @@ -2750,6 +2938,16 @@ __metadata: languageName: node linkType: hard +"accepts@npm:~1.3.8": + version: 1.3.8 + resolution: "accepts@npm:1.3.8" + dependencies: + mime-types: "npm:~2.1.34" + negotiator: "npm:0.6.3" + checksum: 67eaaa90e2917c58418e7a9b89392002d2b1ccd69bcca4799135d0c632f3b082f23f4ae4ddeedbced5aa59bcc7bdf4699c69ebed4593696c922462b7bc5744d6 + languageName: node + linkType: hard + "acorn-jsx@npm:^5.3.2": version: 5.3.2 resolution: "acorn-jsx@npm:5.3.2" @@ -2793,6 +2991,15 @@ __metadata: languageName: node linkType: hard +"agentkeepalive@npm:^4.2.1": + version: 4.5.0 + resolution: "agentkeepalive@npm:4.5.0" + dependencies: + humanize-ms: "npm:^1.2.1" + checksum: dd210ba2a2e2482028f027b1156789744aadbfd773a6c9dd8e4e8001930d5af82382abe19a69240307b1d8003222ce6b0542935038313434b900e351914fc15f + languageName: node + linkType: hard + "aggregate-error@npm:^3.0.0": version: 3.1.0 resolution: "aggregate-error@npm:3.1.0" @@ -2885,7 +3092,7 @@ __metadata: "@mui/joy": "npm:^5.0.0-beta.21" "@mui/lab": "npm:^5.0.0-alpha.159" "@mui/material": "npm:^5.15.3" - "@types/node": "npm:^20.10.6" + "@types/node": "npm:^20.10.7" "@types/react": "npm:^18.2.47" "@types/react-dom": "npm:^18.2.18" "@vitejs/plugin-react": "npm:^4.2.1" @@ -2940,6 +3147,13 @@ __metadata: languageName: node linkType: hard +"array-flatten@npm:1.1.1": + version: 1.1.1 + resolution: "array-flatten@npm:1.1.1" + checksum: e13c9d247241be82f8b4ec71d035ed7204baa82fae820d4db6948d30d3c4a9f2b3905eb2eec2b937d4aa3565200bd3a1c500480114cff649fa748747d2a50feb + languageName: node + linkType: hard + "array-includes@npm:^3.1.6, array-includes@npm:^3.1.7": version: 3.1.7 resolution: "array-includes@npm:3.1.7" @@ -3034,6 +3248,13 @@ __metadata: languageName: node linkType: hard +"asap@npm:^2.0.0": + version: 2.0.6 + resolution: "asap@npm:2.0.6" + checksum: b244c0458c571945e4b3be0b14eb001bea5596f9868cc50cc711dc03d58a7e953517d3f0dad81ccde3ff37d1f074701fa76a6f07d41aaa992d7204a37b915dda + languageName: node + linkType: hard + "assertion-error@npm:^1.1.0": version: 1.1.0 resolution: "assertion-error@npm:1.1.0" @@ -3064,6 +3285,13 @@ __metadata: languageName: node linkType: hard +"atomic-sleep@npm:^1.0.0": + version: 1.0.0 + resolution: "atomic-sleep@npm:1.0.0" + checksum: 3ab6d2cf46b31394b4607e935ec5c1c3c4f60f3e30f0913d35ea74b51b3585e84f590d09e58067f11762eec71c87d25314ce859030983dc0e4397eed21daa12e + languageName: node + linkType: hard + "available-typed-arrays@npm:^1.0.5": version: 1.0.5 resolution: "available-typed-arrays@npm:1.0.5" @@ -3105,7 +3333,14 @@ __metadata: languageName: node linkType: hard -"base64-js@npm:^1.3.0": +"base-64@npm:^0.1.0": + version: 0.1.0 + resolution: "base-64@npm:0.1.0" + checksum: 5a42938f82372ab5392cbacc85a5a78115cbbd9dbef9f7540fa47d78763a3a8bd7d598475f0d92341f66285afd377509851a9bb5c67bbecb89686e9255d5b3eb + languageName: node + linkType: hard + +"base64-js@npm:^1.3.0, base64-js@npm:^1.3.1": version: 1.5.1 resolution: "base64-js@npm:1.5.1" checksum: 669632eb3745404c2f822a18fc3a0122d2f9a7a13f7fb8b5823ee19d1d2ff9ee5b52c53367176ea4ad093c332fd5ab4bd0ebae5a8e27917a4105a4cfc86b1005 @@ -3133,6 +3368,26 @@ __metadata: languageName: node linkType: hard +"body-parser@npm:1.20.1": + version: 1.20.1 + resolution: "body-parser@npm:1.20.1" + dependencies: + bytes: "npm:3.1.2" + content-type: "npm:~1.0.4" + debug: "npm:2.6.9" + depd: "npm:2.0.0" + destroy: "npm:1.2.0" + http-errors: "npm:2.0.0" + iconv-lite: "npm:0.4.24" + on-finished: "npm:2.4.1" + qs: "npm:6.11.0" + raw-body: "npm:2.5.1" + type-is: "npm:~1.6.18" + unpipe: "npm:1.0.0" + checksum: 5f8d128022a2fb8b6e7990d30878a0182f300b70e46b3f9d358a9433ad6275f0de46add6d63206da3637c01c3b38b6111a7480f7e7ac2e9f7b989f6133fe5510 + languageName: node + linkType: hard + "brace-expansion@npm:^1.1.7": version: 1.1.11 resolution: "brace-expansion@npm:1.1.11" @@ -3182,6 +3437,16 @@ __metadata: languageName: node linkType: hard +"buffer@npm:^6.0.3": + version: 6.0.3 + resolution: "buffer@npm:6.0.3" + dependencies: + base64-js: "npm:^1.3.1" + ieee754: "npm:^1.2.1" + checksum: b6bc68237ebf29bdacae48ce60e5e28fc53ae886301f2ad9496618efac49427ed79096750033e7eab1897a4f26ae374ace49106a5758f38fb70c78c9fda2c3b1 + languageName: node + linkType: hard + "builtins@npm:^5.0.0": version: 5.0.1 resolution: "builtins@npm:5.0.1" @@ -3200,6 +3465,13 @@ __metadata: languageName: node linkType: hard +"bytes@npm:3.1.2": + version: 3.1.2 + resolution: "bytes@npm:3.1.2" + checksum: a10abf2ba70c784471d6b4f58778c0beeb2b5d405148e66affa91f23a9f13d07603d0a0354667310ae1d6dc141474ffd44e2a074be0f6e2254edb8fc21445388 + languageName: node + linkType: hard + "cac@npm:^6.7.14": version: 6.7.14 resolution: "cac@npm:6.7.14" @@ -3234,7 +3506,7 @@ __metadata: languageName: node linkType: hard -"cacheable-request@npm:^10.2.14": +"cacheable-request@npm:^10.2.8": version: 10.2.14 resolution: "cacheable-request@npm:10.2.14" dependencies: @@ -3300,9 +3572,9 @@ __metadata: linkType: hard "caniuse-lite@npm:^1.0.30001565": - version: 1.0.30001574 - resolution: "caniuse-lite@npm:1.0.30001574" - checksum: 159ebd04d9bbef11bd08499f058f70bf795a55641929be5efadf0f6b17216d4b923506778e59bbb939246834304b753b2e88ff1e2430f6a5aef0a86971f98bd3 + version: 1.0.30001576 + resolution: "caniuse-lite@npm:1.0.30001576" + checksum: 51632942733593f310e581bd91c9558b8d75fbf67160a39f8036d2976cd7df9183e96d4c9d9e6f18e0205950b940d9c761bcfb7810962d7899f8a1179fde6e3f languageName: node linkType: hard @@ -3352,13 +3624,20 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^5.2.0": +"chalk@npm:^5.2.0, chalk@npm:^5.3.0": version: 5.3.0 resolution: "chalk@npm:5.3.0" checksum: 6373caaab21bd64c405bfc4bd9672b145647fc9482657b5ea1d549b3b2765054e9d3d928870cdf764fb4aad67555f5061538ff247b8310f110c5c888d92397ea languageName: node linkType: hard +"charenc@npm:0.0.2": + version: 0.0.2 + resolution: "charenc@npm:0.0.2" + checksum: 81dcadbe57e861d527faf6dd3855dc857395a1c4d6781f4847288ab23cffb7b3ee80d57c15bba7252ffe3e5e8019db767757ee7975663ad2ca0939bb8fcaf2e5 + languageName: node + linkType: hard + "check-error@npm:^1.0.3": version: 1.0.3 resolution: "check-error@npm:1.0.3" @@ -3401,6 +3680,22 @@ __metadata: languageName: node linkType: hard +"cli-cursor@npm:^4.0.0": + version: 4.0.0 + resolution: "cli-cursor@npm:4.0.0" + dependencies: + restore-cursor: "npm:^4.0.0" + checksum: ab3f3ea2076e2176a1da29f9d64f72ec3efad51c0960898b56c8a17671365c26e67b735920530eaf7328d61f8bd41c27f46b9cf6e4e10fe2fa44b5e8c0e392cc + languageName: node + linkType: hard + +"cli-spinners@npm:^2.9.2": + version: 2.9.2 + resolution: "cli-spinners@npm:2.9.2" + checksum: a0a863f442df35ed7294424f5491fa1756bd8d2e4ff0c8736531d886cec0ece4d85e8663b77a5afaf1d296e3cbbebff92e2e99f52bbea89b667cbe789b994794 + languageName: node + linkType: hard + "cliui@npm:^8.0.1": version: 8.0.1 resolution: "cliui@npm:8.0.1" @@ -3458,7 +3753,14 @@ __metadata: languageName: node linkType: hard -"combined-stream@npm:^1.0.6": +"colorette@npm:^2.0.7": + version: 2.0.20 + resolution: "colorette@npm:2.0.20" + checksum: 0b8de48bfa5d10afc160b8eaa2b9938f34a892530b2f7d7897e0458d9535a066e3998b49da9d21161c78225b272df19ae3a64d6df28b4c9734c0e55bbd02406f + languageName: node + linkType: hard + +"combined-stream@npm:^1.0.6, combined-stream@npm:^1.0.8": version: 1.0.8 resolution: "combined-stream@npm:1.0.8" dependencies: @@ -3467,6 +3769,13 @@ __metadata: languageName: node linkType: hard +"component-emitter@npm:^1.3.0": + version: 1.3.1 + resolution: "component-emitter@npm:1.3.1" + checksum: 94550aa462c7bd5a61c1bc480e28554aa306066930152d1b1844a0dd3845d4e5db7e261ddec62ae184913b3e59b55a2ad84093b9d3596a8f17c341514d6c483d + languageName: node + linkType: hard + "concat-map@npm:0.0.1": version: 0.0.1 resolution: "concat-map@npm:0.0.1" @@ -3474,6 +3783,22 @@ __metadata: languageName: node linkType: hard +"content-disposition@npm:0.5.4": + version: 0.5.4 + resolution: "content-disposition@npm:0.5.4" + dependencies: + safe-buffer: "npm:5.2.1" + checksum: b7f4ce176e324f19324be69b05bf6f6e411160ac94bc523b782248129eb1ef3be006f6cff431aaea5e337fe5d176ce8830b8c2a1b721626ead8933f0cbe78720 + languageName: node + linkType: hard + +"content-type@npm:~1.0.4": + version: 1.0.5 + resolution: "content-type@npm:1.0.5" + checksum: 585847d98dc7fb8035c02ae2cb76c7a9bd7b25f84c447e5ed55c45c2175e83617c8813871b4ee22f368126af6b2b167df655829007b21aa10302873ea9c62662 + languageName: node + linkType: hard + "convert-source-map@npm:^1.5.0": version: 1.9.0 resolution: "convert-source-map@npm:1.9.0" @@ -3488,13 +3813,27 @@ __metadata: languageName: node linkType: hard -"cookie@npm:^0.5.0": +"cookie-signature@npm:1.0.6": + version: 1.0.6 + resolution: "cookie-signature@npm:1.0.6" + checksum: f4e1b0a98a27a0e6e66fd7ea4e4e9d8e038f624058371bf4499cfcd8f3980be9a121486995202ba3fca74fbed93a407d6d54d43a43f96fd28d0bd7a06761591a + languageName: node + linkType: hard + +"cookie@npm:0.5.0, cookie@npm:^0.5.0": version: 0.5.0 resolution: "cookie@npm:0.5.0" checksum: aae7911ddc5f444a9025fbd979ad1b5d60191011339bce48e555cb83343d0f98b865ff5c4d71fecdfb8555a5cafdc65632f6fce172f32aaf6936830a883a0380 languageName: node linkType: hard +"cookiejar@npm:^2.1.4": + version: 2.1.4 + resolution: "cookiejar@npm:2.1.4" + checksum: 4a184f5a0591df8b07d22a43ea5d020eacb4572c383e853a33361a99710437eaa0971716c688684075bbf695b484f5872e9e3f562382e46858716cb7fc8ce3f4 + languageName: node + linkType: hard + "cosmiconfig@npm:^5.0.5": version: 5.2.1 resolution: "cosmiconfig@npm:5.2.1" @@ -3531,6 +3870,13 @@ __metadata: languageName: node linkType: hard +"crypt@npm:0.0.2": + version: 0.0.2 + resolution: "crypt@npm:0.0.2" + checksum: 2c72768de3d28278c7c9ffd81a298b26f87ecdfe94415084f339e6632f089b43fe039f2c93f612bcb5ffe447238373d93b2e8c90894cba6cfb0ac7a74616f8b9 + languageName: node + linkType: hard + "css.escape@npm:^1.5.1": version: 1.5.1 resolution: "css.escape@npm:1.5.1" @@ -3566,6 +3912,39 @@ __metadata: languageName: node linkType: hard +"dateformat@npm:^4.6.3": + version: 4.6.3 + resolution: "dateformat@npm:4.6.3" + checksum: 5c149c91bf9ce2142c89f84eee4c585f0cb1f6faf2536b1af89873f862666a28529d1ccafc44750aa01384da2197c4f76f4e149a3cc0c1cb2c46f5cc45f2bcb5 + languageName: node + linkType: hard + +"db@workspace:*, db@workspace:db": + version: 0.0.0-use.local + resolution: "db@workspace:db" + dependencies: + "@google-cloud/firestore": "npm:^7.1.0" + "@googleapis/identitytoolkit": "npm:^8.0.0" + "@types/node": "npm:^20.10.7" + dotenv: "npm:^16.3.1" + ora: "npm:^8.0.1" + typescript: "npm:~5.3.3" + vite: "npm:~5.0.11" + vite-node: "npm:~1.1.3" + vitest: "npm:~1.1.3" + zod: "npm:^3.22.4" + languageName: unknown + linkType: soft + +"debug@npm:2.6.9": + version: 2.6.9 + resolution: "debug@npm:2.6.9" + dependencies: + ms: "npm:2.0.0" + checksum: e07005f2b40e04f1bd14a3dd20520e9c4f25f60224cb006ce9d6781732c917964e9ec029fc7f1a151083cd929025ad5133814d4dc624a9aaf020effe4914ed14 + languageName: node + linkType: hard + "debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4": version: 4.3.4 resolution: "debug@npm:4.3.4" @@ -3648,6 +4027,13 @@ __metadata: languageName: node linkType: hard +"depd@npm:2.0.0": + version: 2.0.0 + resolution: "depd@npm:2.0.0" + checksum: c0c8ff36079ce5ada64f46cc9d6fd47ebcf38241105b6e0c98f412e8ad91f084bcf906ff644cc3a4bd876ca27a62accb8b0fff72ea6ed1a414b89d8506f4a5ca + languageName: node + linkType: hard + "dequal@npm:^2.0.3": version: 2.0.3 resolution: "dequal@npm:2.0.3" @@ -3655,6 +4041,23 @@ __metadata: languageName: node linkType: hard +"destroy@npm:1.2.0": + version: 1.2.0 + resolution: "destroy@npm:1.2.0" + checksum: 0acb300b7478a08b92d810ab229d5afe0d2f4399272045ab22affa0d99dbaf12637659411530a6fcd597a9bdac718fc94373a61a95b4651bbc7b83684a565e38 + languageName: node + linkType: hard + +"dezalgo@npm:^1.0.4": + version: 1.0.4 + resolution: "dezalgo@npm:1.0.4" + dependencies: + asap: "npm:^2.0.0" + wrappy: "npm:1" + checksum: 895389c6aead740d2ab5da4d3466d20fa30f738010a4d3f4dcccc9fc645ca31c9d10b7e1804ae489b1eb02c7986f9f1f34ba132d409b043082a86d9a4e745624 + languageName: node + linkType: hard + "diff-sequences@npm:^29.6.3": version: 29.6.3 resolution: "diff-sequences@npm:29.6.3" @@ -3662,6 +4065,16 @@ __metadata: languageName: node linkType: hard +"digest-fetch@npm:^1.3.0": + version: 1.3.0 + resolution: "digest-fetch@npm:1.3.0" + dependencies: + base-64: "npm:^0.1.0" + md5: "npm:^2.3.0" + checksum: 5a90f350ed19600aab44753ad19e8b2e5409b0a07a2a6949c9eed589bfc7a735308be020d63c6ddff97ad83d0b5d56405e0577ed4c291baccadec3e61ebca189 + languageName: node + linkType: hard + "dir-glob@npm:^3.0.1": version: 3.0.1 resolution: "dir-glob@npm:3.0.1" @@ -3761,7 +4174,7 @@ __metadata: dependencies: "@cloudflare/workers-types": "npm:^4.20231218.0" "@hono/zod-validator": "npm:^0.1.11" - "@types/node": "npm:^20.10.6" + "@types/node": "npm:^20.10.7" happy-dom: "npm:^12.10.3" hono: "npm:^3.12.0" jose: "npm:^5.2.0" @@ -3775,6 +4188,13 @@ __metadata: languageName: unknown linkType: soft +"ee-first@npm:1.1.1": + version: 1.1.1 + resolution: "ee-first@npm:1.1.1" + checksum: 1b4cac778d64ce3b582a7e26b218afe07e207a0f9bfe13cc7395a6d307849cfe361e65033c3251e00c27dd060cab43014c2d6b2647676135e18b77d2d05b3f4f + languageName: node + linkType: hard + "electron-to-chromium@npm:^1.4.601": version: 1.4.623 resolution: "electron-to-chromium@npm:1.4.623" @@ -3782,6 +4202,13 @@ __metadata: languageName: node linkType: hard +"emoji-regex@npm:^10.3.0": + version: 10.3.0 + resolution: "emoji-regex@npm:10.3.0" + checksum: b9b084ebe904f13bb4b66ee4c29fb41a7a4a1165adcc33c1ce8056c0194b882cc91ebdc782f1a779b5d7ea7375c5064643a7734893d7c657b44c5c6b9d7bf1e7 + languageName: node + linkType: hard + "emoji-regex@npm:^8.0.0": version: 8.0.0 resolution: "emoji-regex@npm:8.0.0" @@ -3796,6 +4223,13 @@ __metadata: languageName: node linkType: hard +"encodeurl@npm:~1.0.2": + version: 1.0.2 + resolution: "encodeurl@npm:1.0.2" + checksum: e50e3d508cdd9c4565ba72d2012e65038e5d71bdc9198cb125beb6237b5b1ade6c0d343998da9e170fb2eae52c1bed37d4d6d98a46ea423a0cddbed5ac3f780c + languageName: node + linkType: hard + "encoding@npm:^0.1.13": version: 0.1.13 resolution: "encoding@npm:0.1.13" @@ -3805,7 +4239,7 @@ __metadata: languageName: node linkType: hard -"end-of-stream@npm:^1.4.1": +"end-of-stream@npm:^1.1.0, end-of-stream@npm:^1.4.1": version: 1.4.4 resolution: "end-of-stream@npm:1.4.4" dependencies: @@ -3838,6 +4272,15 @@ __metadata: languageName: node linkType: hard +"envalid@npm:^8.0.0": + version: 8.0.0 + resolution: "envalid@npm:8.0.0" + dependencies: + tslib: "npm:2.6.2" + checksum: cf2159a070c7335ecc9cd01523654fb7c8fdc63b92bef47d18027c406dfcf2b4a72663e70ddd91fa91967fa71c7716fe643dcd4a652f9ab0f93722feddb9e1b2 + languageName: node + linkType: hard + "envars@npm:^1.0.2": version: 1.0.2 resolution: "envars@npm:1.0.2" @@ -4350,6 +4793,13 @@ __metadata: languageName: node linkType: hard +"escape-html@npm:~1.0.3": + version: 1.0.3 + resolution: "escape-html@npm:1.0.3" + checksum: 6213ca9ae00d0ab8bccb6d8d4e0a98e76237b2410302cf7df70aaa6591d509a2a37ce8998008cbecae8fc8ffaadf3fb0229535e6a145f3ce0b211d060decbb24 + languageName: node + linkType: hard + "escape-string-regexp@npm:^1.0.5": version: 1.0.5 resolution: "escape-string-regexp@npm:1.0.5" @@ -4638,6 +5088,13 @@ __metadata: languageName: node linkType: hard +"etag@npm:~1.8.1": + version: 1.8.1 + resolution: "etag@npm:1.8.1" + checksum: 571aeb3dbe0f2bbd4e4fadbdb44f325fc75335cd5f6f6b6a091e6a06a9f25ed5392f0863c5442acb0646787446e816f13cbfc6edce5b07658541dff573cab1ff + languageName: node + linkType: hard + "event-stream@npm:=3.3.4": version: 3.3.4 resolution: "event-stream@npm:3.3.4" @@ -4660,6 +5117,20 @@ __metadata: languageName: node linkType: hard +"eventemitter3@npm:^4.0.0": + version: 4.0.7 + resolution: "eventemitter3@npm:4.0.7" + checksum: 8030029382404942c01d0037079f1b1bc8fed524b5849c237b80549b01e2fc49709e1d0c557fa65ca4498fc9e24cff1475ef7b855121fcc15f9d61f93e282346 + languageName: node + linkType: hard + +"events@npm:^3.3.0": + version: 3.3.0 + resolution: "events@npm:3.3.0" + checksum: a3d47e285e28d324d7180f1e493961a2bbb4cad6412090e4dec114f4db1f5b560c7696ee8e758f55e23913ede856e3689cd3aa9ae13c56b5d8314cd3b3ddd1be + languageName: node + linkType: hard + "execa@npm:^6.1.0": version: 6.1.0 resolution: "execa@npm:6.1.0" @@ -4708,6 +5179,45 @@ __metadata: languageName: node linkType: hard +"express@npm:^4.18.2": + version: 4.18.2 + resolution: "express@npm:4.18.2" + dependencies: + accepts: "npm:~1.3.8" + array-flatten: "npm:1.1.1" + body-parser: "npm:1.20.1" + content-disposition: "npm:0.5.4" + content-type: "npm:~1.0.4" + cookie: "npm:0.5.0" + cookie-signature: "npm:1.0.6" + debug: "npm:2.6.9" + depd: "npm:2.0.0" + encodeurl: "npm:~1.0.2" + escape-html: "npm:~1.0.3" + etag: "npm:~1.8.1" + finalhandler: "npm:1.2.0" + fresh: "npm:0.5.2" + http-errors: "npm:2.0.0" + merge-descriptors: "npm:1.0.1" + methods: "npm:~1.1.2" + on-finished: "npm:2.4.1" + parseurl: "npm:~1.3.3" + path-to-regexp: "npm:0.1.7" + proxy-addr: "npm:~2.0.7" + qs: "npm:6.11.0" + range-parser: "npm:~1.2.1" + safe-buffer: "npm:5.2.1" + send: "npm:0.18.0" + serve-static: "npm:1.15.0" + setprototypeof: "npm:1.2.0" + statuses: "npm:2.0.1" + type-is: "npm:~1.6.18" + utils-merge: "npm:1.0.1" + vary: "npm:~1.1.2" + checksum: 869ae89ed6ff4bed7b373079dc58e5dddcf2915a2669b36037ff78c99d675ae930e5fe052b35c24f56557d28a023bb1cbe3e2f2fb87eaab96a1cedd7e597809d + languageName: node + linkType: hard + "extend@npm:^3.0.2": version: 3.0.2 resolution: "extend@npm:3.0.2" @@ -4715,6 +5225,13 @@ __metadata: languageName: node linkType: hard +"fast-copy@npm:^3.0.0": + version: 3.0.1 + resolution: "fast-copy@npm:3.0.1" + checksum: 2f655f1e84440f990cddf7895f0acce38b2eb090a27dc0f97f1654cd6f2e38f67d9603471856c2af13e0bfbdf04c2c0b8d446fee1dd1f6f485992e4cc4693c7a + languageName: node + linkType: hard + "fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3": version: 3.1.3 resolution: "fast-deep-equal@npm:3.1.3" @@ -4749,12 +5266,26 @@ __metadata: languageName: node linkType: hard -"fastq@npm:^1.6.0": - version: 1.16.0 - resolution: "fastq@npm:1.16.0" - dependencies: - reusify: "npm:^1.0.4" - checksum: de151543aab9d91900ed5da88860c46987ece925c628df586fac664235f25e020ec20729e1c032edb5fd2520fd4aa5b537d69e39b689e65e82112cfbecb4479e +"fast-redact@npm:^3.1.1": + version: 3.3.0 + resolution: "fast-redact@npm:3.3.0" + checksum: a69c5cb52396eafc4f466f46864406cbd4a6ead6782caf74750ce817794829048baaa933ad98543e744dd54ffb4cddff71f3e75e465a86e3d887894e281ec154 + languageName: node + linkType: hard + +"fast-safe-stringify@npm:^2.1.1": + version: 2.1.1 + resolution: "fast-safe-stringify@npm:2.1.1" + checksum: dc1f063c2c6ac9533aee14d406441f86783a8984b2ca09b19c2fe281f9ff59d315298bc7bc22fd1f83d26fe19ef2f20e2ddb68e96b15040292e555c5ced0c1e4 + languageName: node + linkType: hard + +"fastq@npm:^1.6.0": + version: 1.16.0 + resolution: "fastq@npm:1.16.0" + dependencies: + reusify: "npm:^1.0.4" + checksum: de151543aab9d91900ed5da88860c46987ece925c628df586fac664235f25e020ec20729e1c032edb5fd2520fd4aa5b537d69e39b689e65e82112cfbecb4479e languageName: node linkType: hard @@ -4795,6 +5326,21 @@ __metadata: languageName: node linkType: hard +"finalhandler@npm:1.2.0": + version: 1.2.0 + resolution: "finalhandler@npm:1.2.0" + dependencies: + debug: "npm:2.6.9" + encodeurl: "npm:~1.0.2" + escape-html: "npm:~1.0.3" + on-finished: "npm:2.4.1" + parseurl: "npm:~1.3.3" + statuses: "npm:2.0.1" + unpipe: "npm:~1.0.0" + checksum: 635718cb203c6d18e6b48dfbb6c54ccb08ea470e4f474ddcef38c47edcf3227feec316f886dd701235997d8af35240cae49856721ce18f539ad038665ebbf163 + languageName: node + linkType: hard + "find-root@npm:^1.1.0": version: 1.1.0 resolution: "find-root@npm:1.1.0" @@ -4874,6 +5420,16 @@ __metadata: languageName: node linkType: hard +"follow-redirects@npm:^1.0.0": + version: 1.15.4 + resolution: "follow-redirects@npm:1.15.4" + peerDependenciesMeta: + debug: + optional: true + checksum: 2e8f5f259a6b02dfa8dc199e08431848a7c3beed32eb4c19945966164a52c89f07b86c3afcc32ebe4279cf0a960520e45a63013d6350309c5ec90133c5d9351a + languageName: node + linkType: hard + "for-each@npm:^0.3.3": version: 0.3.3 resolution: "for-each@npm:0.3.3" @@ -4893,10 +5449,17 @@ __metadata: languageName: node linkType: hard -"form-data-encoder@npm:^4.0.2": - version: 4.0.2 - resolution: "form-data-encoder@npm:4.0.2" - checksum: 71a00a1e954267f8b87d4301936dda7177cfe18714a92930c87ebc132acb8b19ed12e9b9f8ad47af52c1cc582fd45c1771f66f850a6d319147731160f6b9b3fa +"form-data-encoder@npm:1.7.2": + version: 1.7.2 + resolution: "form-data-encoder@npm:1.7.2" + checksum: 227bf2cea083284411fd67472ccc22f5cb354ca92c00690e11ff5ed942d993c13ac99dea365046306200f8bd71e1a7858d2d99e236de694b806b1f374a4ee341 + languageName: node + linkType: hard + +"form-data-encoder@npm:^2.1.2": + version: 2.1.4 + resolution: "form-data-encoder@npm:2.1.4" + checksum: 3778e7db3c21457296e6fdbc4200642a6c01e8be9297256e845ee275f9ddaecb5f49bfb0364690ad216898c114ec59bf85f01ec823a70670b8067273415d62f6 languageName: node linkType: hard @@ -4911,6 +5474,27 @@ __metadata: languageName: node linkType: hard +"form-data@npm:^4.0.0": + version: 4.0.0 + resolution: "form-data@npm:4.0.0" + dependencies: + asynckit: "npm:^0.4.0" + combined-stream: "npm:^1.0.8" + mime-types: "npm:^2.1.12" + checksum: 7264aa760a8cf09482816d8300f1b6e2423de1b02bba612a136857413fdc96d7178298ced106817655facc6b89036c6e12ae31c9eb5bdc16aabf502ae8a5d805 + languageName: node + linkType: hard + +"formdata-node@npm:^4.3.2": + version: 4.4.1 + resolution: "formdata-node@npm:4.4.1" + dependencies: + node-domexception: "npm:1.0.0" + web-streams-polyfill: "npm:4.0.0-beta.3" + checksum: 29622f75533107c1bbcbe31fda683e6a55859af7f48ec354a9800591ce7947ed84cd3ef2b2fcb812047a884f17a1bac75ce098ffc17e23402cd373e49c1cd335 + languageName: node + linkType: hard + "formdata-polyfill@npm:^4.0.10": version: 4.0.10 resolution: "formdata-polyfill@npm:4.0.10" @@ -4920,6 +5504,32 @@ __metadata: languageName: node linkType: hard +"formidable@npm:^2.1.2": + version: 2.1.2 + resolution: "formidable@npm:2.1.2" + dependencies: + dezalgo: "npm:^1.0.4" + hexoid: "npm:^1.0.0" + once: "npm:^1.4.0" + qs: "npm:^6.11.0" + checksum: d385180e0461f65e6f7b70452859fe1c32aa97a290c2ca33f00cdc33145ef44fa68bbc9b93af2c3af73ae726e42c3477c6619c49f3c34b49934e9481275b7b4c + languageName: node + linkType: hard + +"forwarded@npm:0.2.0": + version: 0.2.0 + resolution: "forwarded@npm:0.2.0" + checksum: 29ba9fd347117144e97cbb8852baae5e8b2acb7d1b591ef85695ed96f5b933b1804a7fac4a15dd09ca7ac7d0cdc104410e8102aae2dd3faa570a797ba07adb81 + languageName: node + linkType: hard + +"fresh@npm:0.5.2": + version: 0.5.2 + resolution: "fresh@npm:0.5.2" + checksum: 64c88e489b5d08e2f29664eb3c79c705ff9a8eb15d3e597198ef76546d4ade295897a44abb0abd2700e7ef784b2e3cbf1161e4fbf16f59129193fd1030d16da1 + languageName: node + linkType: hard + "from@npm:~0": version: 0.1.7 resolution: "from@npm:0.1.7" @@ -5001,6 +5611,13 @@ __metadata: languageName: node linkType: hard +"functional-red-black-tree@npm:^1.0.1": + version: 1.0.1 + resolution: "functional-red-black-tree@npm:1.0.1" + checksum: debe73e92204341d1fa5f89614e44284d3add26dee660722978d8c50829170f87d1c74768f68c251d215ae461c11db7bac13101c77f4146ff051da75466f7a12 + languageName: node + linkType: hard + "functions-have-names@npm:^1.2.3": version: 1.2.3 resolution: "functions-have-names@npm:1.2.3" @@ -5017,7 +5634,7 @@ __metadata: languageName: node linkType: hard -"gaxios@npm:^6.0.0, gaxios@npm:^6.1.1": +"gaxios@npm:^6.0.0, gaxios@npm:^6.0.3, gaxios@npm:^6.1.1": version: 6.1.1 resolution: "gaxios@npm:6.1.1" dependencies: @@ -5053,6 +5670,13 @@ __metadata: languageName: node linkType: hard +"get-east-asian-width@npm:^1.0.0": + version: 1.2.0 + resolution: "get-east-asian-width@npm:1.2.0" + checksum: c9b280e7c7c67fb89fa17e867c4a9d1c9f1321aba2a9ee27bff37fb6ca9552bccda328c70a80c1f83a0e39ba1b7e3427e60f47823402d19e7a41b83417ec047a + languageName: node + linkType: hard + "get-func-name@npm:^2.0.1, get-func-name@npm:^2.0.2": version: 2.0.2 resolution: "get-func-name@npm:2.0.2" @@ -5237,7 +5861,7 @@ __metadata: languageName: node linkType: hard -"google-auth-library@npm:^9.0.0": +"google-auth-library@npm:^9.0.0, google-auth-library@npm:^9.4.1": version: 9.4.1 resolution: "google-auth-library@npm:9.4.1" dependencies: @@ -5251,7 +5875,7 @@ __metadata: languageName: node linkType: hard -"google-gax@npm:^4.0.3": +"google-gax@npm:^4.0.3, google-gax@npm:^4.0.4": version: 4.0.5 resolution: "google-gax@npm:4.0.5" dependencies: @@ -5270,6 +5894,20 @@ __metadata: languageName: node linkType: hard +"googleapis-common@npm:^7.0.0": + version: 7.0.1 + resolution: "googleapis-common@npm:7.0.1" + dependencies: + extend: "npm:^3.0.2" + gaxios: "npm:^6.0.3" + google-auth-library: "npm:^9.0.0" + qs: "npm:^6.7.0" + url-template: "npm:^2.0.8" + uuid: "npm:^9.0.0" + checksum: ae44d20c81183d72bf3ce85bfca3915b5370310ce5387b7b8c5b3e9348b1eb14c5f2b8e187aa6d088cd7a64153b3f7b797e0e2ee2894068f0c295287edeaafe3 + languageName: node + linkType: hard + "gopd@npm:^1.0.1": version: 1.0.1 resolution: "gopd@npm:1.0.1" @@ -5279,22 +5917,22 @@ __metadata: languageName: node linkType: hard -"got@npm:^14.0.0": - version: 14.0.0 - resolution: "got@npm:14.0.0" +"got@npm:^13.0.0": + version: 13.0.0 + resolution: "got@npm:13.0.0" dependencies: - "@sindresorhus/is": "npm:^6.1.0" + "@sindresorhus/is": "npm:^5.2.0" "@szmarczak/http-timer": "npm:^5.0.1" cacheable-lookup: "npm:^7.0.0" - cacheable-request: "npm:^10.2.14" + cacheable-request: "npm:^10.2.8" decompress-response: "npm:^6.0.0" - form-data-encoder: "npm:^4.0.2" - get-stream: "npm:^8.0.1" - http2-wrapper: "npm:^2.2.1" + form-data-encoder: "npm:^2.1.2" + get-stream: "npm:^6.0.1" + http2-wrapper: "npm:^2.1.10" lowercase-keys: "npm:^3.0.0" - p-cancelable: "npm:^4.0.1" + p-cancelable: "npm:^3.0.0" responselike: "npm:^3.0.0" - checksum: 3afcf60c8313cacb3bdc1a153b9b48e6c974241dab09ceb9155bfd4f2aad161a32e9b2f541584c78e8ac2e9615b241e453d06cfa2a4518b7e3899b46948ab9f3 + checksum: 35ac9fe37daca3d0a4f90305d8e64626268ef5a42584f5bcb42eea3cb9bbeb691cf9041d5ea72133a7295d1291684789a3148ff89a95f3d3ce3d0ebb6fb2f680 languageName: node linkType: hard @@ -5419,6 +6057,20 @@ __metadata: languageName: node linkType: hard +"help-me@npm:^5.0.0": + version: 5.0.0 + resolution: "help-me@npm:5.0.0" + checksum: 5f99bd91dae93d02867175c3856c561d7e3a24f16999b08f5fc79689044b938d7ed58457f4d8c8744c01403e6e0470b7896baa344d112b2355842fd935a75d69 + languageName: node + linkType: hard + +"hexoid@npm:^1.0.0": + version: 1.0.0 + resolution: "hexoid@npm:1.0.0" + checksum: f2271b8b6b0e13fb5a1eccf740f53ce8bae689c80b9498b854c447f9dc94f75f44e0de064c0e4660ecdbfa8942bb2b69973fdcb080187b45bbb409a3c71f19d4 + languageName: node + linkType: hard + "hoist-non-react-statics@npm:^3.3.1": version: 3.3.2 resolution: "hoist-non-react-statics@npm:3.3.2" @@ -5458,6 +6110,19 @@ __metadata: languageName: node linkType: hard +"http-errors@npm:2.0.0, http-errors@npm:^2.0.0": + version: 2.0.0 + resolution: "http-errors@npm:2.0.0" + dependencies: + depd: "npm:2.0.0" + inherits: "npm:2.0.4" + setprototypeof: "npm:1.2.0" + statuses: "npm:2.0.1" + toidentifier: "npm:1.0.1" + checksum: 0e7f76ee8ff8a33e58a3281a469815b893c41357378f408be8f6d4aa7d1efafb0da064625518e7078381b6a92325949b119dc38fcb30bdbc4e3a35f78c44c439 + languageName: node + linkType: hard + "http-parser-js@npm:>=0.5.1": version: 0.5.8 resolution: "http-parser-js@npm:0.5.8" @@ -5486,7 +6151,36 @@ __metadata: languageName: node linkType: hard -"http2-wrapper@npm:^2.2.1": +"http-proxy-middleware@npm:^2.0.6": + version: 2.0.6 + resolution: "http-proxy-middleware@npm:2.0.6" + dependencies: + "@types/http-proxy": "npm:^1.17.8" + http-proxy: "npm:^1.18.1" + is-glob: "npm:^4.0.1" + is-plain-obj: "npm:^3.0.0" + micromatch: "npm:^4.0.2" + peerDependencies: + "@types/express": ^4.17.13 + peerDependenciesMeta: + "@types/express": + optional: true + checksum: 768e7ae5a422bbf4b866b64105b4c2d1f468916b7b0e9c96750551c7732383069b411aa7753eb7b34eab113e4f77fb770122cb7fb9c8ec87d138d5ddaafda891 + languageName: node + linkType: hard + +"http-proxy@npm:^1.18.1": + version: 1.18.1 + resolution: "http-proxy@npm:1.18.1" + dependencies: + eventemitter3: "npm:^4.0.0" + follow-redirects: "npm:^1.0.0" + requires-port: "npm:^1.0.0" + checksum: 2489e98aba70adbfd8b9d41ed1ff43528be4598c88616c558b109a09eaffe4bb35e551b6c75ac42ed7d948bb7530a22a2be6ef4f0cecacb5927be139f4274594 + languageName: node + linkType: hard + +"http2-wrapper@npm:^2.1.10": version: 2.2.1 resolution: "http2-wrapper@npm:2.2.1" dependencies: @@ -5530,6 +6224,15 @@ __metadata: languageName: node linkType: hard +"humanize-ms@npm:^1.2.1": + version: 1.2.1 + resolution: "humanize-ms@npm:1.2.1" + dependencies: + ms: "npm:^2.0.0" + checksum: 9c7a74a2827f9294c009266c82031030eae811ca87b0da3dceb8d6071b9bde22c9f3daef0469c3c533cc67a97d8a167cd9fc0389350e5f415f61a79b171ded16 + languageName: node + linkType: hard + "husky@npm:^8.0.3": version: 8.0.3 resolution: "husky@npm:8.0.3" @@ -5539,6 +6242,15 @@ __metadata: languageName: node linkType: hard +"iconv-lite@npm:0.4.24": + version: 0.4.24 + resolution: "iconv-lite@npm:0.4.24" + dependencies: + safer-buffer: "npm:>= 2.1.2 < 3" + checksum: 6d3a2dac6e5d1fb126d25645c25c3a1209f70cceecc68b8ef51ae0da3cdc078c151fade7524a30b12a3094926336831fca09c666ef55b37e2c69638b5d6bd2e3 + languageName: node + linkType: hard + "iconv-lite@npm:0.6.3, iconv-lite@npm:^0.6.2, iconv-lite@npm:^0.6.3": version: 0.6.3 resolution: "iconv-lite@npm:0.6.3" @@ -5562,6 +6274,13 @@ __metadata: languageName: node linkType: hard +"ieee754@npm:^1.2.1": + version: 1.2.1 + resolution: "ieee754@npm:1.2.1" + checksum: d9f2557a59036f16c282aaeb107832dc957a93d73397d89bbad4eb1130560560eb695060145e8e6b3b498b15ab95510226649a0b8f52ae06583575419fe10fc4 + languageName: node + linkType: hard + "ignore@npm:^5.2.0, ignore@npm:^5.2.4": version: 5.3.0 resolution: "ignore@npm:5.3.0" @@ -5620,7 +6339,7 @@ __metadata: languageName: node linkType: hard -"inherits@npm:2, inherits@npm:^2.0.3": +"inherits@npm:2, inherits@npm:2.0.4, inherits@npm:^2.0.3": version: 2.0.4 resolution: "inherits@npm:2.0.4" checksum: cd45e923bee15186c07fa4c89db0aace24824c482fb887b528304694b2aa6ff8a898da8657046a5dcf3e46cd6db6c61629551f9215f208d7c3f157cf9b290521 @@ -5645,6 +6364,13 @@ __metadata: languageName: node linkType: hard +"ipaddr.js@npm:1.9.1": + version: 1.9.1 + resolution: "ipaddr.js@npm:1.9.1" + checksum: 864d0cced0c0832700e9621913a6429ccdc67f37c1bd78fb8c6789fff35c9d167cb329134acad2290497a53336813ab4798d2794fd675d5eb33b5fdf0982b9ca + languageName: node + linkType: hard + "is-array-buffer@npm:^3.0.1, is-array-buffer@npm:^3.0.2": version: 3.0.2 resolution: "is-array-buffer@npm:3.0.2" @@ -5700,6 +6426,13 @@ __metadata: languageName: node linkType: hard +"is-buffer@npm:~1.1.6": + version: 1.1.6 + resolution: "is-buffer@npm:1.1.6" + checksum: f63da109e74bbe8947036ed529d43e4ae0c5fcd0909921dce4917ad3ea212c6a87c29f525ba1d17c0858c18331cf1046d4fc69ef59ed26896b25c8288a627133 + languageName: node + linkType: hard + "is-callable@npm:^1.1.3, is-callable@npm:^1.1.4, is-callable@npm:^1.2.7": version: 1.2.7 resolution: "is-callable@npm:1.2.7" @@ -5773,6 +6506,13 @@ __metadata: languageName: node linkType: hard +"is-interactive@npm:^2.0.0": + version: 2.0.0 + resolution: "is-interactive@npm:2.0.0" + checksum: e8d52ad490bed7ae665032c7675ec07732bbfe25808b0efbc4d5a76b1a1f01c165f332775c63e25e9a03d319ebb6b24f571a9e902669fc1e40b0a60b5be6e26c + languageName: node + linkType: hard + "is-lambda@npm:^1.0.1": version: 1.0.1 resolution: "is-lambda@npm:1.0.1" @@ -5817,6 +6557,13 @@ __metadata: languageName: node linkType: hard +"is-plain-obj@npm:^3.0.0": + version: 3.0.0 + resolution: "is-plain-obj@npm:3.0.0" + checksum: a6ebdf8e12ab73f33530641972a72a4b8aed6df04f762070d823808303e4f76d87d5ea5bd76f96a7bbe83d93f04ac7764429c29413bd9049853a69cb630fb21c + languageName: node + linkType: hard + "is-regex@npm:^1.1.4": version: 1.1.4 resolution: "is-regex@npm:1.1.4" @@ -5884,6 +6631,20 @@ __metadata: languageName: node linkType: hard +"is-unicode-supported@npm:^1.3.0": + version: 1.3.0 + resolution: "is-unicode-supported@npm:1.3.0" + checksum: 20a1fc161afafaf49243551a5ac33b6c4cf0bbcce369fcd8f2951fbdd000c30698ce320de3ee6830497310a8f41880f8066d440aa3eb0a853e2aa4836dd89abc + languageName: node + linkType: hard + +"is-unicode-supported@npm:^2.0.0": + version: 2.0.0 + resolution: "is-unicode-supported@npm:2.0.0" + checksum: 000b80639dedaf59a385f1c0a57f97a4d1435e0723716f24cc19ad94253a7a0a9f838bdc9ac49b10a29ac93b01f52ae9b2ed358a8876caf1eb74d73b4ede92b2 + languageName: node + linkType: hard + "is-weakmap@npm:^2.0.1": version: 2.0.1 resolution: "is-weakmap@npm:2.0.1" @@ -5995,6 +6756,13 @@ __metadata: languageName: node linkType: hard +"joycon@npm:^3.1.1": + version: 3.1.1 + resolution: "joycon@npm:3.1.1" + checksum: 4b36e3479144ec196425f46b3618f8a96ce7e1b658f091a309cd4906215f5b7a402d7df331a3e0a09681381a658d0c5f039cb3cf6907e0a1e17ed847f5d37775 + languageName: node + linkType: hard + "js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0": version: 4.0.0 resolution: "js-tokens@npm:4.0.0" @@ -6281,6 +7049,16 @@ __metadata: languageName: node linkType: hard +"log-symbols@npm:^6.0.0": + version: 6.0.0 + resolution: "log-symbols@npm:6.0.0" + dependencies: + chalk: "npm:^5.3.0" + is-unicode-supported: "npm:^1.3.0" + checksum: 510cdda36700cbcd87a2a691ea08d310a6c6b449084018f7f2ec4f732ca5e51b301ff1327aadd96f53c08318e616276c65f7fe22f2a16704fb0715d788bc3c33 + languageName: node + linkType: hard + "long@npm:^5.0.0": version: 5.2.3 resolution: "long@npm:5.2.3" @@ -6384,6 +7162,31 @@ __metadata: languageName: node linkType: hard +"md5@npm:^2.3.0": + version: 2.3.0 + resolution: "md5@npm:2.3.0" + dependencies: + charenc: "npm:0.0.2" + crypt: "npm:0.0.2" + is-buffer: "npm:~1.1.6" + checksum: 88dce9fb8df1a084c2385726dcc18c7f54e0b64c261b5def7cdfe4928c4ee1cd68695c34108b4fab7ecceb05838c938aa411c6143df9fdc0026c4ddb4e4e72fa + languageName: node + linkType: hard + +"media-typer@npm:0.3.0": + version: 0.3.0 + resolution: "media-typer@npm:0.3.0" + checksum: 38e0984db39139604756903a01397e29e17dcb04207bb3e081412ce725ab17338ecc47220c1b186b6bbe79a658aad1b0d41142884f5a481f36290cdefbe6aa46 + languageName: node + linkType: hard + +"merge-descriptors@npm:1.0.1": + version: 1.0.1 + resolution: "merge-descriptors@npm:1.0.1" + checksum: 5abc259d2ae25bb06d19ce2b94a21632583c74e2a9109ee1ba7fd147aa7362b380d971e0251069f8b3eb7d48c21ac839e21fa177b335e82c76ec172e30c31a26 + languageName: node + linkType: hard + "merge-stream@npm:^2.0.0": version: 2.0.0 resolution: "merge-stream@npm:2.0.0" @@ -6398,7 +7201,14 @@ __metadata: languageName: node linkType: hard -"micromatch@npm:^4.0.4": +"methods@npm:^1.1.2, methods@npm:~1.1.2": + version: 1.1.2 + resolution: "methods@npm:1.1.2" + checksum: a385dd974faa34b5dd021b2bbf78c722881bf6f003bfe6d391d7da3ea1ed625d1ff10ddd13c57531f628b3e785be38d3eed10ad03cebd90b76932413df9a1820 + languageName: node + linkType: hard + +"micromatch@npm:^4.0.2, micromatch@npm:^4.0.4": version: 4.0.5 resolution: "micromatch@npm:4.0.5" dependencies: @@ -6415,7 +7225,7 @@ __metadata: languageName: node linkType: hard -"mime-types@npm:^2.1.12": +"mime-types@npm:^2.1.12, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34": version: 2.1.35 resolution: "mime-types@npm:2.1.35" dependencies: @@ -6424,6 +7234,24 @@ __metadata: languageName: node linkType: hard +"mime@npm:1.6.0": + version: 1.6.0 + resolution: "mime@npm:1.6.0" + bin: + mime: cli.js + checksum: b7d98bb1e006c0e63e2c91b590fe1163b872abf8f7ef224d53dd31499c2197278a6d3d0864c45239b1a93d22feaf6f9477e9fc847eef945838150b8c02d03170 + languageName: node + linkType: hard + +"mime@npm:2.6.0": + version: 2.6.0 + resolution: "mime@npm:2.6.0" + bin: + mime: cli.js + checksum: 7da117808b5cd0203bb1b5e33445c330fe213f4d8ee2402a84d62adbde9716ca4fb90dd6d9ab4e77a4128c6c5c24a9c4c9f6a4d720b095b1b342132d02dba58d + languageName: node + linkType: hard + "mime@npm:^3.0.0": version: 3.0.0 resolution: "mime@npm:3.0.0" @@ -6433,6 +7261,13 @@ __metadata: languageName: node linkType: hard +"mimic-fn@npm:^2.1.0": + version: 2.1.0 + resolution: "mimic-fn@npm:2.1.0" + checksum: d2421a3444848ce7f84bd49115ddacff29c15745db73f54041edc906c14b131a38d05298dae3081667627a59b2eb1ca4b436ff2e1b80f69679522410418b478a + languageName: node + linkType: hard + "mimic-fn@npm:^4.0.0": version: 4.0.0 resolution: "mimic-fn@npm:4.0.0" @@ -6616,6 +7451,13 @@ __metadata: languageName: node linkType: hard +"ms@npm:2.0.0": + version: 2.0.0 + resolution: "ms@npm:2.0.0" + checksum: 0e6a22b8b746d2e0b65a430519934fefd41b6db0682e3477c10f60c76e947c4c0ad06f63ffdf1d78d335f83edee8c0aa928aa66a36c7cd95b69b26f468d527f4 + languageName: node + linkType: hard + "ms@npm:2.1.2": version: 2.1.2 resolution: "ms@npm:2.1.2" @@ -6623,7 +7465,7 @@ __metadata: languageName: node linkType: hard -"ms@npm:^2.1.1": +"ms@npm:2.1.3, ms@npm:^2.0.0, ms@npm:^2.1.1": version: 2.1.3 resolution: "ms@npm:2.1.3" checksum: aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d @@ -6648,6 +7490,15 @@ __metadata: languageName: node linkType: hard +"nanoid@npm:^5.0.4": + version: 5.0.4 + resolution: "nanoid@npm:5.0.4" + bin: + nanoid: bin/nanoid.js + checksum: cf09cca3774f3147100948f7478f75f4c9ee97a4af65c328dd9abbd83b12f8bb35cf9f89a21c330f3b759d667a4cd0140ed84aa5fdd522c61e0d341aeaa7fb6f + languageName: node + linkType: hard + "natural-compare@npm:^1.4.0": version: 1.4.0 resolution: "natural-compare@npm:1.4.0" @@ -6655,14 +7506,14 @@ __metadata: languageName: node linkType: hard -"negotiator@npm:^0.6.3": +"negotiator@npm:0.6.3, negotiator@npm:^0.6.3": version: 0.6.3 resolution: "negotiator@npm:0.6.3" checksum: 2723fb822a17ad55c93a588a4bc44d53b22855bf4be5499916ca0cab1e7165409d0b288ba2577d7b029f10ce18cf2ed8e703e5af31c984e1e2304277ef979837 languageName: node linkType: hard -"node-domexception@npm:^1.0.0": +"node-domexception@npm:1.0.0, node-domexception@npm:^1.0.0": version: 1.0.0 resolution: "node-domexception@npm:1.0.0" checksum: e332522f242348c511640c25a6fc7da4f30e09e580c70c6b13cb0be83c78c3e71c8d4665af2527e869fc96848924a4316ae7ec9014c091e2156f41739d4fa233 @@ -6680,7 +7531,7 @@ __metadata: languageName: node linkType: hard -"node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.9": +"node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.7, node-fetch@npm:^2.6.9": version: 2.7.0 resolution: "node-fetch@npm:2.7.0" dependencies: @@ -6894,7 +7745,23 @@ __metadata: languageName: node linkType: hard -"once@npm:^1.3.0, once@npm:^1.4.0": +"on-exit-leak-free@npm:^2.1.0": + version: 2.1.2 + resolution: "on-exit-leak-free@npm:2.1.2" + checksum: f7b4b7200026a08f6e4a17ba6d72e6c5cbb41789ed9cf7deaf9d9e322872c7dc5a7898549a894651ee0ee9ae635d34a678115bf8acdfba8ebd2ba2af688b563c + languageName: node + linkType: hard + +"on-finished@npm:2.4.1": + version: 2.4.1 + resolution: "on-finished@npm:2.4.1" + dependencies: + ee-first: "npm:1.1.1" + checksum: 8e81472c5028125c8c39044ac4ab8ba51a7cdc19a9fbd4710f5d524a74c6d8c9ded4dd0eed83f28d3d33ac1d7a6a439ba948ccb765ac6ce87f30450a26bfe2ea + languageName: node + linkType: hard + +"once@npm:^1.3.0, once@npm:^1.3.1, once@npm:^1.4.0": version: 1.4.0 resolution: "once@npm:1.4.0" dependencies: @@ -6903,6 +7770,15 @@ __metadata: languageName: node linkType: hard +"onetime@npm:^5.1.0": + version: 5.1.2 + resolution: "onetime@npm:5.1.2" + dependencies: + mimic-fn: "npm:^2.1.0" + checksum: e9fd0695a01cf226652f0385bf16b7a24153dbbb2039f764c8ba6d2306a8506b0e4ce570de6ad99c7a6eb49520743afdb66edd95ee979c1a342554ed49a9aadd + languageName: node + linkType: hard + "onetime@npm:^6.0.0": version: 6.0.0 resolution: "onetime@npm:6.0.0" @@ -6912,6 +7788,25 @@ __metadata: languageName: node linkType: hard +"openai@npm:^4.24.1": + version: 4.24.1 + resolution: "openai@npm:4.24.1" + dependencies: + "@types/node": "npm:^18.11.18" + "@types/node-fetch": "npm:^2.6.4" + abort-controller: "npm:^3.0.0" + agentkeepalive: "npm:^4.2.1" + digest-fetch: "npm:^1.3.0" + form-data-encoder: "npm:1.7.2" + formdata-node: "npm:^4.3.2" + node-fetch: "npm:^2.6.7" + web-streams-polyfill: "npm:^3.2.1" + bin: + openai: bin/cli + checksum: 30769c66e62129bfe0e5caedb760d9885019cf69bea9d8bcd370e88b6c32311c81a4d895e84ada96da035b6710dd880d67b747a630369c4e3c2b2a08bc8484e9 + languageName: node + linkType: hard + "optionator@npm:^0.9.3": version: 0.9.3 resolution: "optionator@npm:0.9.3" @@ -6926,10 +7821,27 @@ __metadata: languageName: node linkType: hard -"p-cancelable@npm:^4.0.1": - version: 4.0.1 - resolution: "p-cancelable@npm:4.0.1" - checksum: 64de7b0be4c8bacc006488e0e90aa66fbcceb4da4f6fb84584573145f015f9650fe6ac26470897b3e82a3b528f6c60ea276b84cc315e35c45e9f12dec062a295 +"ora@npm:^8.0.1": + version: 8.0.1 + resolution: "ora@npm:8.0.1" + dependencies: + chalk: "npm:^5.3.0" + cli-cursor: "npm:^4.0.0" + cli-spinners: "npm:^2.9.2" + is-interactive: "npm:^2.0.0" + is-unicode-supported: "npm:^2.0.0" + log-symbols: "npm:^6.0.0" + stdin-discarder: "npm:^0.2.1" + string-width: "npm:^7.0.0" + strip-ansi: "npm:^7.1.0" + checksum: 3d37bb3f53e965e5176004af319f82feef7323ee0b2428db5ee6f689b9b9ba939d7b1e81691d4614333c4fb9e294790eb049db9c1e990b14b9bbe150c6f09993 + languageName: node + linkType: hard + +"p-cancelable@npm:^3.0.0": + version: 3.0.0 + resolution: "p-cancelable@npm:3.0.0" + checksum: a5eab7cf5ac5de83222a014eccdbfde65ecfb22005ee9bc242041f0b4441e07fac7629432c82f48868aa0f8413fe0df6c6067c16f76bf9217cd8dc651923c93d languageName: node linkType: hard @@ -7047,6 +7959,13 @@ __metadata: languageName: node linkType: hard +"parseurl@npm:~1.3.3": + version: 1.3.3 + resolution: "parseurl@npm:1.3.3" + checksum: 407cee8e0a3a4c5cd472559bca8b6a45b82c124e9a4703302326e9ab60fc1081442ada4e02628efef1eb16197ddc7f8822f5a91fd7d7c86b51f530aedb17dfa2 + languageName: node + linkType: hard + "path-exists@npm:^4.0.0": version: 4.0.0 resolution: "path-exists@npm:4.0.0" @@ -7099,6 +8018,13 @@ __metadata: languageName: node linkType: hard +"path-to-regexp@npm:0.1.7": + version: 0.1.7 + resolution: "path-to-regexp@npm:0.1.7" + checksum: 701c99e1f08e3400bea4d701cf6f03517474bb1b608da71c78b1eb261415b645c5670dfae49808c89e12cea2dccd113b069f040a80de012da0400191c6dbd1c8 + languageName: node + linkType: hard + "path-to-regexp@npm:^6.2.0": version: 6.2.1 resolution: "path-to-regexp@npm:6.2.1" @@ -7150,6 +8076,80 @@ __metadata: languageName: node linkType: hard +"pino-abstract-transport@npm:^1.0.0, pino-abstract-transport@npm:v1.1.0": + version: 1.1.0 + resolution: "pino-abstract-transport@npm:1.1.0" + dependencies: + readable-stream: "npm:^4.0.0" + split2: "npm:^4.0.0" + checksum: 39b4496c9e4289e8d44a1d01adfa8dfeebb374e14b7a6451a4f3713561aeb9e181c64ff0272921667abcb95aceb312ab2761b82e253db23a456ab3dd35a42675 + languageName: node + linkType: hard + +"pino-http@npm:^9.0.0": + version: 9.0.0 + resolution: "pino-http@npm:9.0.0" + dependencies: + get-caller-file: "npm:^2.0.5" + pino: "npm:^8.17.1" + pino-std-serializers: "npm:^6.2.2" + process-warning: "npm:^3.0.0" + checksum: 12a0b016c62c1f4e7d506f6a690d10b7ccdbb5868fba8156a1bf515143e522087f87ce83a732615773e903271468c308d5f5eb908ca5446208803da119025fdf + languageName: node + linkType: hard + +"pino-pretty@npm:^10.3.1": + version: 10.3.1 + resolution: "pino-pretty@npm:10.3.1" + dependencies: + colorette: "npm:^2.0.7" + dateformat: "npm:^4.6.3" + fast-copy: "npm:^3.0.0" + fast-safe-stringify: "npm:^2.1.1" + help-me: "npm:^5.0.0" + joycon: "npm:^3.1.1" + minimist: "npm:^1.2.6" + on-exit-leak-free: "npm:^2.1.0" + pino-abstract-transport: "npm:^1.0.0" + pump: "npm:^3.0.0" + readable-stream: "npm:^4.0.0" + secure-json-parse: "npm:^2.4.0" + sonic-boom: "npm:^3.0.0" + strip-json-comments: "npm:^3.1.1" + bin: + pino-pretty: bin.js + checksum: 4284f125f7e8a5a10e856c8fd591ba34c30c0a0071a0b265a9eda43c3e447ba11d40b06cc67108675586358a5d1213a6ac3a92f6abd2896abfbab9a5b4c17072 + languageName: node + linkType: hard + +"pino-std-serializers@npm:^6.0.0, pino-std-serializers@npm:^6.2.2": + version: 6.2.2 + resolution: "pino-std-serializers@npm:6.2.2" + checksum: a00cdff4e1fbc206da9bed047e6dc400b065f43e8b4cef1635b0192feab0e8f932cdeb0faaa38a5d93d2e777ba4cda939c2ed4c1a70f6839ff25f9aef97c27ff + languageName: node + linkType: hard + +"pino@npm:^8.17.1, pino@npm:^8.17.2": + version: 8.17.2 + resolution: "pino@npm:8.17.2" + dependencies: + atomic-sleep: "npm:^1.0.0" + fast-redact: "npm:^3.1.1" + on-exit-leak-free: "npm:^2.1.0" + pino-abstract-transport: "npm:v1.1.0" + pino-std-serializers: "npm:^6.0.0" + process-warning: "npm:^3.0.0" + quick-format-unescaped: "npm:^4.0.3" + real-require: "npm:^0.2.0" + safe-stable-stringify: "npm:^2.3.1" + sonic-boom: "npm:^3.7.0" + thread-stream: "npm:^2.0.0" + bin: + pino: bin.js + checksum: 90b74e4db3b3f8664b13def3eb4e39585a842396fd7a000ef00f2329076c889126f91bdaff0f653b9b5dd5a612dddde6ff87599ad046b47a264e8f7bfabb0ea8 + languageName: node + linkType: hard + "pkg-types@npm:^1.0.3": version: 1.0.3 resolution: "pkg-types@npm:1.0.3" @@ -7213,6 +8213,20 @@ __metadata: languageName: node linkType: hard +"process-warning@npm:^3.0.0": + version: 3.0.0 + resolution: "process-warning@npm:3.0.0" + checksum: 2d82fa641e50a5789eaf0f2b33453760996e373d4591aac576a22d696186ab7e240a0592db86c264d4f28a46c2abbe9b94689752017db7dadc90f169f12b0924 + languageName: node + linkType: hard + +"process@npm:^0.11.10": + version: 0.11.10 + resolution: "process@npm:0.11.10" + checksum: dbaa7e8d1d5cf375c36963ff43116772a989ef2bb47c9bdee20f38fd8fc061119cf38140631cf90c781aca4d3f0f0d2c834711952b728953f04fd7d238f59f5b + languageName: node + linkType: hard + "promise-retry@npm:^2.0.1": version: 2.0.1 resolution: "promise-retry@npm:2.0.1" @@ -7243,7 +8257,7 @@ __metadata: languageName: node linkType: hard -"protobufjs@npm:7.2.5, protobufjs@npm:^7.0.0, protobufjs@npm:^7.2.4": +"protobufjs@npm:7.2.5, protobufjs@npm:^7.0.0, protobufjs@npm:^7.2.4, protobufjs@npm:^7.2.5": version: 7.2.5 resolution: "protobufjs@npm:7.2.5" dependencies: @@ -7263,6 +8277,16 @@ __metadata: languageName: node linkType: hard +"proxy-addr@npm:~2.0.7": + version: 2.0.7 + resolution: "proxy-addr@npm:2.0.7" + dependencies: + forwarded: "npm:0.2.0" + ipaddr.js: "npm:1.9.1" + checksum: f24a0c80af0e75d31e3451398670d73406ec642914da11a2965b80b1898ca6f66a0e3e091a11a4327079b2b268795f6fa06691923fef91887215c3d0e8ea3f68 + languageName: node + linkType: hard + "ps-tree@npm:^1.2.0": version: 1.2.0 resolution: "ps-tree@npm:1.2.0" @@ -7274,6 +8298,16 @@ __metadata: languageName: node linkType: hard +"pump@npm:^3.0.0": + version: 3.0.0 + resolution: "pump@npm:3.0.0" + dependencies: + end-of-stream: "npm:^1.1.0" + once: "npm:^1.3.1" + checksum: e42e9229fba14732593a718b04cb5e1cfef8254544870997e0ecd9732b189a48e1256e4e5478148ecb47c8511dca2b09eae56b4d0aad8009e6fac8072923cfc9 + languageName: node + linkType: hard + "punycode@npm:^2.1.0": version: 2.3.1 resolution: "punycode@npm:2.3.1" @@ -7281,6 +8315,24 @@ __metadata: languageName: node linkType: hard +"qs@npm:6.11.0": + version: 6.11.0 + resolution: "qs@npm:6.11.0" + dependencies: + side-channel: "npm:^1.0.4" + checksum: 5a3bfea3e2f359ede1bfa5d2f0dbe54001aa55e40e27dc3e60fab814362d83a9b30758db057c2011b6f53a2d4e4e5150194b5bac45372652aecb3e3c0d4b256e + languageName: node + linkType: hard + +"qs@npm:^6.11.0, qs@npm:^6.7.0": + version: 6.11.2 + resolution: "qs@npm:6.11.2" + dependencies: + side-channel: "npm:^1.0.4" + checksum: f2321d0796664d0f94e92447ccd3bdfd6b6f3a50b6b762aa79d7f5b1ea3a7a9f94063ba896b82bc2a877ed6a7426d4081e4f16568fdb04f0ee188cca9d8505b4 + languageName: node + linkType: hard + "queue-microtask@npm:^1.2.2": version: 1.2.3 resolution: "queue-microtask@npm:1.2.3" @@ -7288,6 +8340,13 @@ __metadata: languageName: node linkType: hard +"quick-format-unescaped@npm:^4.0.3": + version: 4.0.4 + resolution: "quick-format-unescaped@npm:4.0.4" + checksum: 591eca457509a99368b623db05248c1193aa3cedafc9a077d7acab09495db1231017ba3ad1b5386e5633271edd0a03b312d8640a59ee585b8516a42e15438aa7 + languageName: node + linkType: hard + "quick-lru@npm:^5.1.1": version: 5.1.1 resolution: "quick-lru@npm:5.1.1" @@ -7295,6 +8354,25 @@ __metadata: languageName: node linkType: hard +"range-parser@npm:~1.2.1": + version: 1.2.1 + resolution: "range-parser@npm:1.2.1" + checksum: ce21ef2a2dd40506893157970dc76e835c78cf56437e26e19189c48d5291e7279314477b06ac38abd6a401b661a6840f7b03bd0b1249da9b691deeaa15872c26 + languageName: node + linkType: hard + +"raw-body@npm:2.5.1": + version: 2.5.1 + resolution: "raw-body@npm:2.5.1" + dependencies: + bytes: "npm:3.1.2" + http-errors: "npm:2.0.0" + iconv-lite: "npm:0.4.24" + unpipe: "npm:1.0.0" + checksum: 280bedc12db3490ecd06f740bdcf66093a07535374b51331242382c0e130bb273ebb611b7bc4cba1b4b4e016cc7b1f4b05a6df885a6af39c2bc3b94c02291c84 + languageName: node + linkType: hard + "react-dom@npm:^18.2.0": version: 18.2.0 resolution: "react-dom@npm:18.2.0" @@ -7410,6 +8488,19 @@ __metadata: languageName: node linkType: hard +"readable-stream@npm:^4.0.0": + version: 4.5.2 + resolution: "readable-stream@npm:4.5.2" + dependencies: + abort-controller: "npm:^3.0.0" + buffer: "npm:^6.0.3" + events: "npm:^3.3.0" + process: "npm:^0.11.10" + string_decoder: "npm:^1.3.0" + checksum: 01b128a559c5fd76a898495f858cf0a8839f135e6a69e3409f986e88460134791657eb46a2ff16826f331682a3c4d0c5a75cef5e52ef259711021ba52b1c2e82 + languageName: node + linkType: hard + "readdirp@npm:~3.6.0": version: 3.6.0 resolution: "readdirp@npm:3.6.0" @@ -7419,6 +8510,13 @@ __metadata: languageName: node linkType: hard +"real-require@npm:^0.2.0": + version: 0.2.0 + resolution: "real-require@npm:0.2.0" + checksum: ddf44ee76301c774e9c9f2826da8a3c5c9f8fc87310f4a364e803ef003aa1a43c378b4323051ced212097fff1af459070f4499338b36a7469df1d4f7e8c0ba4c + languageName: node + linkType: hard + "reflect.getprototypeof@npm:^1.0.4": version: 1.0.4 resolution: "reflect.getprototypeof@npm:1.0.4" @@ -7467,6 +8565,13 @@ __metadata: languageName: node linkType: hard +"requires-port@npm:^1.0.0": + version: 1.0.0 + resolution: "requires-port@npm:1.0.0" + checksum: 878880ee78ccdce372784f62f52a272048e2d0827c29ae31e7f99da18b62a2b9463ea03a75f277352f4697c100183debb0532371ad515a2d49d4bfe596dd4c20 + languageName: node + linkType: hard + "resolve-alpn@npm:^1.2.0": version: 1.2.1 resolution: "resolve-alpn@npm:1.2.1" @@ -7563,6 +8668,16 @@ __metadata: languageName: node linkType: hard +"restore-cursor@npm:^4.0.0": + version: 4.0.0 + resolution: "restore-cursor@npm:4.0.0" + dependencies: + onetime: "npm:^5.1.0" + signal-exit: "npm:^3.0.2" + checksum: 5b675c5a59763bf26e604289eab35711525f11388d77f409453904e1e69c0d37ae5889295706b2c81d23bd780165084d040f9b68fffc32cc921519031c4fa4af + languageName: node + linkType: hard + "retry-request@npm:^7.0.0": version: 7.0.1 resolution: "retry-request@npm:7.0.1" @@ -7697,8 +8812,8 @@ __metadata: "@emotion/babel-plugin": "npm:^11.11.0" "@emotion/eslint-plugin": "npm:^11.11.0" "@types/eslint": "npm:^8.56.1" - "@typescript-eslint/eslint-plugin": "npm:^6.17.0" - "@typescript-eslint/parser": "npm:^6.17.0" + "@typescript-eslint/eslint-plugin": "npm:^6.18.0" + "@typescript-eslint/parser": "npm:^6.18.0" eslint: "npm:^8.56.0" eslint-config-prettier: "npm:^9.1.0" eslint-import-resolver-typescript: "npm:^3.6.1" @@ -7739,7 +8854,7 @@ __metadata: languageName: node linkType: hard -"safe-buffer@npm:>=5.1.0, safe-buffer@npm:^5.0.1, safe-buffer@npm:~5.2.0": +"safe-buffer@npm:5.2.1, safe-buffer@npm:>=5.1.0, safe-buffer@npm:^5.0.1, safe-buffer@npm:~5.2.0": version: 5.2.1 resolution: "safe-buffer@npm:5.2.1" checksum: 32872cd0ff68a3ddade7a7617b8f4c2ae8764d8b7d884c651b74457967a9e0e886267d3ecc781220629c44a865167b61c375d2da6c720c840ecd73f45d5d9451 @@ -7757,7 +8872,14 @@ __metadata: languageName: node linkType: hard -"safer-buffer@npm:>= 2.1.2 < 3.0.0": +"safe-stable-stringify@npm:^2.3.1": + version: 2.4.3 + resolution: "safe-stable-stringify@npm:2.4.3" + checksum: a6c192bbefe47770a11072b51b500ed29be7b1c15095371c1ee1dc13e45ce48ee3c80330214c56764d006c485b88bd0b24940d868948170dddc16eed312582d8 + languageName: node + linkType: hard + +"safer-buffer@npm:>= 2.1.2 < 3, safer-buffer@npm:>= 2.1.2 < 3.0.0": version: 2.1.2 resolution: "safer-buffer@npm:2.1.2" checksum: 7eaf7a0cf37cc27b42fb3ef6a9b1df6e93a1c6d98c6c6702b02fe262d5fcbd89db63320793b99b21cb5348097d0a53de81bd5f4e8b86e20cc9412e3f1cfb4e83 @@ -7780,7 +8902,7 @@ __metadata: dotenv: "npm:^16.3.1" execa: "npm:^8.0.1" get-port: "npm:^7.0.0" - got: "npm:^14.0.0" + got: "npm:^13.0.0" graphql: "npm:^16.8.1" lodash-es: "npm:^4.17.21" miniflare: "npm:^3.20231218.1" @@ -7792,6 +8914,13 @@ __metadata: languageName: unknown linkType: soft +"secure-json-parse@npm:^2.4.0": + version: 2.7.0 + resolution: "secure-json-parse@npm:2.7.0" + checksum: 974386587060b6fc5b1ac06481b2f9dbbb0d63c860cc73dc7533f27835fdb67b0ef08762dbfef25625c15bc0a0c366899e00076cb0d556af06b71e22f1dede4c + languageName: node + linkType: hard + "selfsigned@npm:^2.0.1": version: 2.4.1 resolution: "selfsigned@npm:2.4.1" @@ -7811,7 +8940,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.0.0, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.5.4": +"semver@npm:^7.0.0, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.4": version: 7.5.4 resolution: "semver@npm:7.5.4" dependencies: @@ -7822,6 +8951,76 @@ __metadata: languageName: node linkType: hard +"send@npm:0.18.0": + version: 0.18.0 + resolution: "send@npm:0.18.0" + dependencies: + debug: "npm:2.6.9" + depd: "npm:2.0.0" + destroy: "npm:1.2.0" + encodeurl: "npm:~1.0.2" + escape-html: "npm:~1.0.3" + etag: "npm:~1.8.1" + fresh: "npm:0.5.2" + http-errors: "npm:2.0.0" + mime: "npm:1.6.0" + ms: "npm:2.1.3" + on-finished: "npm:2.4.1" + range-parser: "npm:~1.2.1" + statuses: "npm:2.0.1" + checksum: ec66c0ad109680ad8141d507677cfd8b4e40b9559de23191871803ed241718e99026faa46c398dcfb9250676076573bd6bfe5d0ec347f88f4b7b8533d1d391cb + languageName: node + linkType: hard + +"serve-static@npm:1.15.0": + version: 1.15.0 + resolution: "serve-static@npm:1.15.0" + dependencies: + encodeurl: "npm:~1.0.2" + escape-html: "npm:~1.0.3" + parseurl: "npm:~1.3.3" + send: "npm:0.18.0" + checksum: 699b2d4c29807a51d9b5e0f24955346911437aebb0178b3c4833ad30d3eca93385ff9927254f5c16da345903cad39d9cd4a532198c95a5129cc4ed43911b15a4 + languageName: node + linkType: hard + +"server@workspace:server": + version: 0.0.0-use.local + resolution: "server@workspace:server" + dependencies: + "@google-cloud/firestore": "npm:^7.1.0" + "@googleapis/identitytoolkit": "npm:^8.0.0" + "@trpc/server": "npm:^10.45.0" + "@types/express": "npm:^4.17.21" + "@types/http-errors": "npm:^2.0.4" + "@types/node": "npm:^20.10.7" + "@types/supertest": "npm:^6.0.2" + "@types/ws": "npm:^8.5.10" + db: "workspace:*" + envalid: "npm:^8.0.0" + envars: "npm:^1.0.2" + express: "npm:^4.18.2" + get-port: "npm:^7.0.0" + google-auth-library: "npm:^9.4.1" + got: "npm:^13.0.0" + http-errors: "npm:^2.0.0" + http-proxy-middleware: "npm:^2.0.6" + nanoid: "npm:^5.0.4" + openai: "npm:^4.24.1" + pino: "npm:^8.17.2" + pino-http: "npm:^9.0.0" + pino-pretty: "npm:^10.3.1" + supertest: "npm:^6.3.3" + type-fest: "npm:^4.9.0" + typescript: "npm:~5.3.3" + vite: "npm:~5.0.11" + vite-node: "npm:~1.1.3" + vitest: "npm:~1.1.3" + ws: "npm:^8.16.0" + zod: "npm:^3.22.4" + languageName: unknown + linkType: soft + "set-cookie-parser@npm:^2.4.8": version: 2.6.0 resolution: "set-cookie-parser@npm:2.6.0" @@ -7852,6 +9051,13 @@ __metadata: languageName: node linkType: hard +"setprototypeof@npm:1.2.0": + version: 1.2.0 + resolution: "setprototypeof@npm:1.2.0" + checksum: fde1630422502fbbc19e6844346778f99d449986b2f9cdcceb8326730d2f3d9964dbcb03c02aaadaefffecd0f2c063315ebea8b3ad895914bf1afc1747fc172e + languageName: node + linkType: hard + "shebang-command@npm:^2.0.0": version: 2.0.0 resolution: "shebang-command@npm:2.0.0" @@ -7886,7 +9092,7 @@ __metadata: languageName: node linkType: hard -"signal-exit@npm:^3.0.7": +"signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.7": version: 3.0.7 resolution: "signal-exit@npm:3.0.7" checksum: a2f098f247adc367dffc27845853e9959b9e88b01cb301658cfe4194352d8d2bb32e18467c786a7fe15f1d44b233ea35633d076d5e737870b7139949d1ab6318 @@ -7942,6 +9148,15 @@ __metadata: languageName: node linkType: hard +"sonic-boom@npm:^3.0.0, sonic-boom@npm:^3.7.0": + version: 3.8.0 + resolution: "sonic-boom@npm:3.8.0" + dependencies: + atomic-sleep: "npm:^1.0.0" + checksum: 470a82cb1af3ab99fcd3003bbecb2ce79a6b243d0f6012c59e5f567f71cbe039c8cd810752748b5820ee20d72c8da81aa298e510eec9e41a4ca05c7f419825ff + languageName: node + linkType: hard + "source-map-js@npm:^1.0.2": version: 1.0.2 resolution: "source-map-js@npm:1.0.2" @@ -8004,6 +9219,13 @@ __metadata: languageName: node linkType: hard +"split2@npm:^4.0.0": + version: 4.2.0 + resolution: "split2@npm:4.2.0" + checksum: 09bbefc11bcf03f044584c9764cd31a252d8e52cea29130950b26161287c11f519807c5e54bd9e5804c713b79c02cefe6a98f4688630993386be353e03f534ab + languageName: node + linkType: hard + "split@npm:0.3": version: 0.3.3 resolution: "split@npm:0.3.3" @@ -8046,6 +9268,13 @@ __metadata: languageName: node linkType: hard +"statuses@npm:2.0.1": + version: 2.0.1 + resolution: "statuses@npm:2.0.1" + checksum: 18c7623fdb8f646fb213ca4051be4df7efb3484d4ab662937ca6fbef7ced9b9e12842709872eb3020cc3504b93bde88935c9f6417489627a7786f24f8031cbcb + languageName: node + linkType: hard + "std-env@npm:^3.5.0": version: 3.7.0 resolution: "std-env@npm:3.7.0" @@ -8053,6 +9282,13 @@ __metadata: languageName: node linkType: hard +"stdin-discarder@npm:^0.2.1": + version: 0.2.2 + resolution: "stdin-discarder@npm:0.2.2" + checksum: 642ffd05bd5b100819d6b24a613d83c6e3857c6de74eb02fc51506fa61dc1b0034665163831873868157c4538d71e31762bcf319be86cea04c3aba5336470478 + languageName: node + linkType: hard + "stoppable@npm:^1.1.0": version: 1.1.0 resolution: "stoppable@npm:1.1.0" @@ -8114,6 +9350,17 @@ __metadata: languageName: node linkType: hard +"string-width@npm:^7.0.0": + version: 7.0.0 + resolution: "string-width@npm:7.0.0" + dependencies: + emoji-regex: "npm:^10.3.0" + get-east-asian-width: "npm:^1.0.0" + strip-ansi: "npm:^7.1.0" + checksum: bc0de5700a2690895169fce447ec4ed44bc62de80312c2093d5606bfd48319bb88e48a99e97f269dff2bc9577448b91c26b3804c16e7d9b389699795e4655c3b + languageName: node + linkType: hard + "string.prototype.matchall@npm:^4.0.8": version: 4.0.10 resolution: "string.prototype.matchall@npm:4.0.10" @@ -8164,7 +9411,7 @@ __metadata: languageName: node linkType: hard -"string_decoder@npm:^1.1.1": +"string_decoder@npm:^1.1.1, string_decoder@npm:^1.3.0": version: 1.3.0 resolution: "string_decoder@npm:1.3.0" dependencies: @@ -8182,7 +9429,7 @@ __metadata: languageName: node linkType: hard -"strip-ansi@npm:^7.0.1": +"strip-ansi@npm:^7.0.1, strip-ansi@npm:^7.1.0": version: 7.1.0 resolution: "strip-ansi@npm:7.1.0" dependencies: @@ -8235,6 +9482,34 @@ __metadata: languageName: node linkType: hard +"superagent@npm:^8.0.5": + version: 8.1.2 + resolution: "superagent@npm:8.1.2" + dependencies: + component-emitter: "npm:^1.3.0" + cookiejar: "npm:^2.1.4" + debug: "npm:^4.3.4" + fast-safe-stringify: "npm:^2.1.1" + form-data: "npm:^4.0.0" + formidable: "npm:^2.1.2" + methods: "npm:^1.1.2" + mime: "npm:2.6.0" + qs: "npm:^6.11.0" + semver: "npm:^7.3.8" + checksum: 33d0072e051baf91c7d68131c70682a0650dd1bd0b8dfb6f88e5bdfcb02e18cc2b42a66e44b32fd405ac6bcf5fd57c6e267bf80e2a8ce57a18166a9d3a78f57d + languageName: node + linkType: hard + +"supertest@npm:^6.3.3": + version: 6.3.3 + resolution: "supertest@npm:6.3.3" + dependencies: + methods: "npm:^1.1.2" + superagent: "npm:^8.0.5" + checksum: 7a7da05bf0f43bc891fd377f6ee192c6fa115f01034f522d4f53c6305ab8866c760a5b0e7aedb3e422224e35be00a75b283613c446651cc1d9e35f80d9b461b3 + languageName: node + linkType: hard + "supports-color@npm:^5.3.0": version: 5.5.0 resolution: "supports-color@npm:5.5.0" @@ -8301,6 +9576,15 @@ __metadata: languageName: node linkType: hard +"thread-stream@npm:^2.0.0": + version: 2.4.1 + resolution: "thread-stream@npm:2.4.1" + dependencies: + real-require: "npm:^0.2.0" + checksum: baac5bf555912f216a2494bf3f66377733a843306cddd233b1c5ad63084307266f61af35d6122e3936c657836d5db4a14da34300cd25930e013943b807a29c9b + languageName: node + linkType: hard + "through@npm:2, through@npm:~2.3, through@npm:~2.3.1": version: 2.3.8 resolution: "through@npm:2.3.8" @@ -8345,6 +9629,13 @@ __metadata: languageName: node linkType: hard +"toidentifier@npm:1.0.1": + version: 1.0.1 + resolution: "toidentifier@npm:1.0.1" + checksum: 952c29e2a85d7123239b5cfdd889a0dde47ab0497f0913d70588f19c53f7e0b5327c95f4651e413c74b785147f9637b17410ac8c846d5d4a20a5a33eb6dc3a45 + languageName: node + linkType: hard + "toml@npm:^3.0.0": version: 3.0.0 resolution: "toml@npm:3.0.0" @@ -8380,7 +9671,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.1.0, tslib@npm:^2.2.0": +"tslib@npm:2.6.2, tslib@npm:^2.1.0, tslib@npm:^2.2.0": version: 2.6.2 resolution: "tslib@npm:2.6.2" checksum: bd26c22d36736513980091a1e356378e8b662ded04204453d353a7f34a4c21ed0afc59b5f90719d4ba756e581a162ecbf93118dc9c6be5acf70aa309188166ca @@ -8417,13 +9708,23 @@ __metadata: languageName: node linkType: hard -"type-fest@npm:^4.2.0": +"type-fest@npm:^4.2.0, type-fest@npm:^4.9.0": version: 4.9.0 resolution: "type-fest@npm:4.9.0" checksum: 49acfb67999566a24d5604435c8cff786dfc26ebea5a2a343e14d437d34f30a55248f8e597b8f64446c344bb68ce14af68899f562cf66ca66c1e1a856b393259 languageName: node linkType: hard +"type-is@npm:~1.6.18": + version: 1.6.18 + resolution: "type-is@npm:1.6.18" + dependencies: + media-typer: "npm:0.3.0" + mime-types: "npm:~2.1.24" + checksum: 0bd9eeae5efd27d98fd63519f999908c009e148039d8e7179a074f105362d4fcc214c38b24f6cda79c87e563cbd12083a4691381ed28559220d4a10c2047bed4 + languageName: node + linkType: hard + "typed-array-buffer@npm:^1.0.0": version: 1.0.0 resolution: "typed-array-buffer@npm:1.0.0" @@ -8569,6 +9870,13 @@ __metadata: languageName: node linkType: hard +"unpipe@npm:1.0.0, unpipe@npm:~1.0.0": + version: 1.0.0 + resolution: "unpipe@npm:1.0.0" + checksum: 4fa18d8d8d977c55cb09715385c203197105e10a6d220087ec819f50cb68870f02942244f1017565484237f1f8c5d3cd413631b1ae104d3096f24fdfde1b4aa2 + languageName: node + linkType: hard + "update-browserslist-db@npm:^1.0.13": version: 1.0.13 resolution: "update-browserslist-db@npm:1.0.13" @@ -8592,6 +9900,13 @@ __metadata: languageName: node linkType: hard +"url-template@npm:^2.0.8": + version: 2.0.8 + resolution: "url-template@npm:2.0.8" + checksum: fc6a4cf6c3c3c3d7f0a0bb4405c41b81934e583b454e52ace7b2e5d7ed32ec9c2970ff1826d240c5823955fcb13531a1fc4ff6ba4569b1886a2976665353e952 + languageName: node + linkType: hard + "urlpattern-polyfill@npm:^4.0.3": version: 4.0.3 resolution: "urlpattern-polyfill@npm:4.0.3" @@ -8606,6 +9921,13 @@ __metadata: languageName: node linkType: hard +"utils-merge@npm:1.0.1": + version: 1.0.1 + resolution: "utils-merge@npm:1.0.1" + checksum: 5d6949693d58cb2e636a84f3ee1c6e7b2f9c16cb1d42d0ecb386d8c025c69e327205aa1c69e2868cc06a01e5e20681fbba55a4e0ed0cce913d60334024eae798 + languageName: node + linkType: hard + "uuid@npm:^9.0.0": version: 9.0.1 resolution: "uuid@npm:9.0.1" @@ -8634,7 +9956,14 @@ __metadata: languageName: node linkType: hard -"vite-node@npm:1.1.3": +"vary@npm:~1.1.2": + version: 1.1.2 + resolution: "vary@npm:1.1.2" + checksum: 31389debef15a480849b8331b220782230b9815a8e0dbb7b9a8369559aed2e9a7800cd904d4371ea74f4c3527db456dc8e7ac5befce5f0d289014dbdf47b2242 + languageName: node + linkType: hard + +"vite-node@npm:1.1.3, vite-node@npm:~1.1.3": version: 1.1.3 resolution: "vite-node@npm:1.1.3" dependencies: @@ -8765,7 +10094,14 @@ __metadata: languageName: node linkType: hard -"web-streams-polyfill@npm:^3.0.3": +"web-streams-polyfill@npm:4.0.0-beta.3": + version: 4.0.0-beta.3 + resolution: "web-streams-polyfill@npm:4.0.0-beta.3" + checksum: dcdef67de57d83008f9dc330662b65ba4497315555dd0e4e7bcacb132ffdf8a830eaab8f74ad40a4a44f542461f51223f406e2a446ece1cc29927859b1405853 + languageName: node + linkType: hard + +"web-streams-polyfill@npm:^3.0.3, web-streams-polyfill@npm:^3.2.1": version: 3.3.2 resolution: "web-streams-polyfill@npm:3.3.2" checksum: 0466050f40c3487a96dedb07c60581b07c334daedd1955a7380b9405b77934a5bf9e178411133abca5365f3533543e1876fa5a5b7082382b866d1e5d07385466 @@ -9026,7 +10362,7 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.11.0, ws@npm:^8.2.2": +"ws@npm:^8.11.0, ws@npm:^8.16.0, ws@npm:^8.2.2": version: 8.16.0 resolution: "ws@npm:8.16.0" peerDependencies: