From de9c80034af95f381d5e34d92fdf3e2bf5f01ede Mon Sep 17 00:00:00 2001 From: fennifith Date: Mon, 21 Sep 2026 18:03:26 -0400 Subject: [PATCH 01/20] implement same-image e2e testing using mockhttp + self-signed cert --- .dockerignore | 15 +- Dockerfile | 5 +- README.md | 8 - apps/e2e/package.json | 8 +- apps/e2e/project.json | 34 +- apps/e2e/scripts/generate-cert.ts | 14 + apps/e2e/scripts/typegen.ts | 4 +- .../e2e/src/{smoke.spec.ts => health.spec.ts} | 5 +- apps/e2e/src/lib/spawn-app.ts | 47 - apps/e2e/src/url-metadata.spec.ts | 49 + apps/e2e/test-utils/env.ts | 11 + apps/e2e/test-utils/setup.ts | 48 + apps/e2e/tsconfig.json | 17 +- apps/e2e/vite.config.ts | 21 - apps/e2e/vitest.config.ts | 10 + docker-compose.yml | 37 +- knip.json | 6 +- package.json | 1 - .../20260917034659_misty_stick/migration.sql | 1 + packages/db/scripts/migrate.ts | 6 +- packages/db/src/client.ts | 2 +- .../github-api/src/getAuthorGitHubStats.ts | 6 +- pnpm-lock.yaml | 2307 ++++++++++++++++- pnpm-workspace.yaml | 9 +- tsconfig.json | 5 + 25 files changed, 2476 insertions(+), 200 deletions(-) create mode 100644 apps/e2e/scripts/generate-cert.ts rename apps/e2e/src/{smoke.spec.ts => health.spec.ts} (67%) delete mode 100644 apps/e2e/src/lib/spawn-app.ts create mode 100644 apps/e2e/src/url-metadata.spec.ts create mode 100644 apps/e2e/test-utils/env.ts create mode 100644 apps/e2e/test-utils/setup.ts delete mode 100644 apps/e2e/vite.config.ts create mode 100644 apps/e2e/vitest.config.ts diff --git a/.dockerignore b/.dockerignore index 9a75fc0a..c95239b7 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,15 +1,22 @@ +.devcontainer +.env +.env.example .git .gitignore .github .husky +.lintstagedrc +.nvmrc .nx +.pnpm-store +.prettier* +.husky **/node_modules **/dist npm-debug.log docker-compose.yml Dockerfile -.lintstagedrc -.prettier* -.env -.env.example +fly.toml +knip.json +README.md services diff --git a/Dockerfile b/Dockerfile index a5c5aaab..73c546b1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,8 +23,7 @@ COPY --parents apps/*/package.json . COPY --parents packages/*/package.json . RUN pnpm install -# Copy & build the app +# Copy the app COPY . . -RUN pnpm build:all -CMD [ "/bin/sh", "-c", "parallel --jobs 2 --line-buffer --halt now,done=1 node --experimental-strip-types {} ::: apps/api/src/index.ts apps/worker/src/index.ts" ] +CMD [ "/bin/sh", "-c", "parallel --jobs 2 --line-buffer --halt now,done=1 node {} ::: apps/api/src/index.ts apps/worker/src/index.ts" ] diff --git a/README.md b/README.md index 12e3cf45..22464bb0 100644 --- a/README.md +++ b/README.md @@ -59,14 +59,6 @@ pnpm dev Once it's running, you should see the API documentation hosted at `http://localhost:3000/docs/`. This can also be used to test the endpoints! -#### Testing in Docker - -To test the server in its docker container, instead of running `pnpm dev`, you can build & start the docker image with: - -```bash -docker compose --profile=app watch -``` - ## Roadmap - Manage tasks for post image & metadata generation (initial deployment) diff --git a/apps/e2e/package.json b/apps/e2e/package.json index 48f8376b..a704b570 100644 --- a/apps/e2e/package.json +++ b/apps/e2e/package.json @@ -4,12 +4,14 @@ "private": true, "type": "module", "scripts": { - "typegen": "node --experimental-strip-types scripts/typegen.ts", + "typegen": "node scripts/typegen.ts", "test": "vitest run" }, "dependencies": { - "@playfulprogramming/api": "workspace:*", - "openapi-fetch": "catalog:" + "@jaredwray/mockhttp": "^1.9.0", + "openapi-fetch": "catalog:", + "selfsigned": "^5.5.0", + "typebox": "^1.3.30" }, "devDependencies": { "openapi-typescript": "catalog:", diff --git a/apps/e2e/project.json b/apps/e2e/project.json index b964510f..44c26987 100644 --- a/apps/e2e/project.json +++ b/apps/e2e/project.json @@ -15,21 +15,41 @@ "typegen": { "executor": "nx:run-commands", "options": { - "command": "node --experimental-strip-types scripts/typegen.ts", + "command": "node scripts/typegen.ts", "cwd": "apps/e2e" }, "outputs": ["{projectRoot}/src/generated/api-schema.d.ts"], "cache": true, "inputs": ["{workspaceRoot}/apps/api/**/*"], - "dependsOn": ["^build"] + "dependsOn": ["test:docker"] }, - "test": { - "executor": "@nx/vitest:test", - "outputs": ["{options.reportsDirectory}"], + "build:cert": { + "executor": "nx:run-commands", "options": { - "reportsDirectory": "coverage" + "command": "node scripts/generate-cert.ts", + "cwd": "apps/e2e" }, - "dependsOn": ["typegen"] + "cache": true, + "inputs": ["{projectRoot}/scripts/generate-cert.ts"], + "outputs": ["{projectRoot}/src/generated/*.pem"] + }, + "build:docker": { + "dependsOn": ["build:cert"], + "command": "docker compose --profile e2e build" + }, + "test:docker:clean": { + "executor": "nx:run-commands", + "options": { + "commands": [ + "docker compose down -v", + "docker compose up -d --wait" + ], + "parallel": false + } + }, + "test": { + "dependsOn": ["build:docker", "test:docker:clean"], + "command": "docker compose --profile e2e up e2e --abort-on-container-exit --attach-dependencies" } } } diff --git a/apps/e2e/scripts/generate-cert.ts b/apps/e2e/scripts/generate-cert.ts new file mode 100644 index 00000000..ba9d7de7 --- /dev/null +++ b/apps/e2e/scripts/generate-cert.ts @@ -0,0 +1,14 @@ +import selfsigned from "selfsigned"; +import fs from "fs/promises"; +import path from "path"; + +const outputDir = path.join(import.meta.dirname, "../src/generated"); + +const attrs = [{ name: "commonName", value: "example.com" }]; +const pems = await selfsigned.generate(attrs); + +await fs.writeFile(path.join(outputDir, "key.pem"), pems.private, "utf8"); +console.log("Private key saved to key.pem"); + +await fs.writeFile(path.join(outputDir, "cert.pem"), pems.cert, "utf8"); +console.log("Certificate saved to cert.pem"); diff --git a/apps/e2e/scripts/typegen.ts b/apps/e2e/scripts/typegen.ts index 9d210d05..3693fe75 100644 --- a/apps/e2e/scripts/typegen.ts +++ b/apps/e2e/scripts/typegen.ts @@ -2,7 +2,6 @@ import fs from "fs/promises"; import path from "path"; import { fileURLToPath } from "url"; import { execSync } from "child_process"; -import { spawnApp } from "../src/lib/spawn-app.ts"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -13,8 +12,7 @@ const outputFile = path.join(outputDir, "api-schema.d.ts"); export default async function generateTypes() { await fs.mkdir(outputDir, { recursive: true }); - await using app = await spawnApp(); - const swaggerUrl = `${app.baseUrl}/openapi.json`; + const swaggerUrl = `http://localhost:3000/openapi.json`; const response = await fetch(swaggerUrl); if (!response.ok) { diff --git a/apps/e2e/src/smoke.spec.ts b/apps/e2e/src/health.spec.ts similarity index 67% rename from apps/e2e/src/smoke.spec.ts rename to apps/e2e/src/health.spec.ts index a8811f5b..2411f6ac 100644 --- a/apps/e2e/src/smoke.spec.ts +++ b/apps/e2e/src/health.spec.ts @@ -1,13 +1,10 @@ import { describe, it, expect } from "vitest"; -import { spawnAppWithClient } from "./lib/spawn-app.ts"; describe("E2E: Health Check", () => { it.each(["/", "/health/postgres", "/health/redis"] as const)( "should respond 200 for %s", async (path) => { - await using app = await spawnAppWithClient(); - - const res = await app.client.GET(path, { + const res = await client.GET(path, { parseAs: "text", }); expect(res.response.status).toBe(200); diff --git a/apps/e2e/src/lib/spawn-app.ts b/apps/e2e/src/lib/spawn-app.ts deleted file mode 100644 index 75ce16aa..00000000 --- a/apps/e2e/src/lib/spawn-app.ts +++ /dev/null @@ -1,47 +0,0 @@ -import createClient, { type Client } from "openapi-fetch"; -import type { paths } from "../generated/api-schema.d.ts"; -import { createApp } from "@playfulprogramming/api/src/createApp.ts"; - -type TestApp = { - baseUrl: string; - port: number; -} & AsyncDisposable; - -type TestAppWithClient = TestApp & { - client: Client; -} & AsyncDisposable; - -export async function spawnApp(): Promise { - const app = createApp(); - - await app.listen({ port: 0, host: "127.0.0.1" }); - - const address = app.server.address(); - const port = typeof address === "string" ? address : address?.port; - if (!port) throw new Error("Failed to get server port"); - - const baseUrl = `http://127.0.0.1:${port}`; - return { - baseUrl, - port: Number(port), - [Symbol.asyncDispose]: async () => { - await app.close(); - }, - }; -} - -export async function spawnAppWithClient(): Promise { - const app = await spawnApp(); - - const client = createClient({ - baseUrl: app.baseUrl, - }); - - return { - ...app, - client, - [Symbol.asyncDispose]: async () => { - await app[Symbol.asyncDispose](); - }, - }; -} diff --git a/apps/e2e/src/url-metadata.spec.ts b/apps/e2e/src/url-metadata.spec.ts new file mode 100644 index 00000000..2201b6d9 --- /dev/null +++ b/apps/e2e/src/url-metadata.spec.ts @@ -0,0 +1,49 @@ +import { describe, test, expect } from "vitest"; +import { setTimeout } from "node:timers/promises"; + +describe("url-metadata", () => { + test("returns url-metadata", async () => { + const tap = mock.taps.inject( + { + response: ` + + + + Example Title + + + `, + statusCode: 200, + headers: { "Content-Type": "text/html" }, + }, + { + url: "/", + hostname: "example.com", + method: "GET", + }, + ); + + const url = `https://example.com/`; + + // Enqueue the URL metadata task + const enqueueRes = await client.POST("/tasks/url-metadata", { + body: { url }, + }); + expect(enqueueRes.response.status).toBe(201); + + // Wait for the task to be processed + await setTimeout(2000); + + // Fetch the processed result + const res = await client.POST("/tasks/url-metadata", { + body: { url }, + }); + expect(res.response.status).toBe(200); + expect(res.data).toStrictEqual({ + title: "Example Title", + error: false, + }); + + mock.taps.removeInjection(tap); + }); +}); diff --git a/apps/e2e/test-utils/env.ts b/apps/e2e/test-utils/env.ts new file mode 100644 index 00000000..ddbbf2b4 --- /dev/null +++ b/apps/e2e/test-utils/env.ts @@ -0,0 +1,11 @@ +import { Type } from "typebox"; +import { Settings } from "typebox/system"; +import { Value } from "typebox/value"; + +const EnvSchema = Type.Object({ + MOCK_PROXY_PORT: Type.Integer(), + API_URL: Type.String(), +}); + +Settings.Set({ correctiveParse: true }); +export const env = Value.Parse(EnvSchema, { ...process.env }); diff --git a/apps/e2e/test-utils/setup.ts b/apps/e2e/test-utils/setup.ts new file mode 100644 index 00000000..fd1e4021 --- /dev/null +++ b/apps/e2e/test-utils/setup.ts @@ -0,0 +1,48 @@ +import MockHttp from "@jaredwray/mockhttp"; +import path from "path"; +import http from "http"; +import net from "net"; +import { env } from "./env.ts"; +import createClient from "openapi-fetch"; +import type { paths } from "../src/generated/api-schema.js"; + +const apiClient = createClient({ + baseUrl: env.API_URL, +}); + +declare global { + var client: typeof apiClient; + var mock: MockHttp; +} + +globalThis.client = apiClient; + +const MOCK_HTTP_PORT = 4000; + +globalThis.mock = new MockHttp({ + port: MOCK_HTTP_PORT, + https: { + cert: path.join(import.meta.dirname, "../src/generated/cert.pem"), + key: path.join(import.meta.dirname, "../src/generated/key.pem"), + }, +}); + +await mock.start(); + +const server = http.createServer((req, res) => { + mock.server.server.emit("request", req, res); +}); + +server.on("connect", (_req, clientSocket, head) => { + const serverSocket = net.connect(MOCK_HTTP_PORT, "localhost", () => { + clientSocket.write("HTTP/1.1 200 Connection Established\r\n\r\n"); + serverSocket.write(head); + serverSocket.pipe(clientSocket); + clientSocket.pipe(serverSocket); + }); + + serverSocket.on("error", () => clientSocket.end()); + clientSocket.on("error", () => serverSocket.end()); +}); + +server.listen(env.MOCK_PROXY_PORT, "0.0.0.0"); diff --git a/apps/e2e/tsconfig.json b/apps/e2e/tsconfig.json index 6d333ece..010d9b21 100644 --- a/apps/e2e/tsconfig.json +++ b/apps/e2e/tsconfig.json @@ -1,19 +1,4 @@ { "extends": "../../tsconfig.json", - "compilerOptions": { - "module": "NodeNext", - "forceConsistentCasingInFileNames": true, - "strict": true, - "noImplicitOverride": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "noPropertyAccessFromIndexSignature": true, - "verbatimModuleSyntax": true, - "isolatedModules": true, - "moduleDetection": "force", - "noUncheckedIndexedAccess": true, - "allowJs": true, - "resolveJsonModule": true - }, - "include": ["src/**/*", "scripts/**/*"] + "include": ["src/**/*", "scripts/**/*", "test-utils/*"] } diff --git a/apps/e2e/vite.config.ts b/apps/e2e/vite.config.ts deleted file mode 100644 index 196a1918..00000000 --- a/apps/e2e/vite.config.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { defineConfig } from "vite"; -import { nxViteTsPaths } from "@nx/vite/plugins/nx-tsconfig-paths.plugin"; -import { nxCopyAssetsPlugin } from "@nx/vite/plugins/nx-copy-assets.plugin"; - -export default defineConfig(() => ({ - root: import.meta.dirname, - cacheDir: "../../node_modules/.vite/apps/e2e", - plugins: [nxViteTsPaths(), nxCopyAssetsPlugin(["*.md"])], - test: { - name: "e2e", - watch: false, - globals: true, - environment: "node", - include: ["src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"], - reporters: ["default"], - coverage: { - reportsDirectory: "coverage", - provider: "v8" as const, - }, - }, -})); diff --git a/apps/e2e/vitest.config.ts b/apps/e2e/vitest.config.ts new file mode 100644 index 00000000..50c0ed84 --- /dev/null +++ b/apps/e2e/vitest.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + setupFiles: ["test-utils/setup.ts"], + watch: false, + globals: true, + fileParallelism: false, + }, +}); diff --git a/docker-compose.yml b/docker-compose.yml index 95704dad..183ed703 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,4 @@ -name: playful-programming-cms +name: playful-programming-hoof services: pgsql: @@ -52,27 +52,44 @@ services: app: build: . + image: playful-programming-hoof:latest env_file: - .env environment: + NODE_USE_ENV_PROXY: 1 + NODE_EXTRA_CA_CERTS: /var/app/apps/e2e/src/generated/cert.pem + HTTP_PROXY: http://e2e:3999 + HTTPS_PROXY: http://e2e:3999 + NO_PROXY: "localhost, 127.0.0.1" S3_ENDPOINT: http://rustfs:9000 POSTGRES_URL: postgresql://postgres:unicorn@pgsql:5432/postgres REDIS_URL: redis://redis:6379 - ports: - - "3000:3000" - depends_on: [pgsql, redis, rustfs] - profiles: [app] + depends_on: + pgsql: + condition: service_healthy + redis: + condition: service_healthy + rustfs: + condition: service_healthy + profiles: [e2e] healthcheck: test: wget --no-verbose --tries=1 --spider http://0.0.0.0:3000 && wget --no-verbose --tries=1 --spider http://0.0.0.0:3001 || exit 1 interval: 10s timeout: 5s retries: 5 - develop: - watch: - - path: . - action: rebuild post_start: - - command: node --experimental-strip-types packages/db/scripts/migrate.ts + - command: node packages/db/scripts/migrate.ts + + e2e: + image: playful-programming-hoof:latest + environment: + MOCK_PROXY_PORT: 3999 + API_URL: http://app:3000 + depends_on: + app: + condition: service_healthy + profiles: [e2e] + command: pnpm -C apps/e2e vitest volumes: pgsql_storage: {} diff --git a/knip.json b/knip.json index 501ec7c3..0edde293 100644 --- a/knip.json +++ b/knip.json @@ -1,11 +1,13 @@ { - "$schema": "https://unpkg.com/knip@5/schema.json", - "ignore": ["*/*/scripts/*"], + "$schema": "./node_modules/knip/schema.json", "ignoreDependencies": ["@aws-sdk/types", "openapi-typescript"], "workspaces": { "apps/worker": { "entry": ["src/tasks/*/*.ts"] }, + "packages/db": { + "entry": ["scripts/migrate.ts"] + }, "packages/*": { "includeEntryExports": true } diff --git a/package.json b/package.json index bdfaf8aa..c57ccb88 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,6 @@ "packageManager": "pnpm@12.5.1+sha512.e3f305bc784a2bc89f5ad3b6138889470fae8d2af5f36b61216ec91c2c3d64089775f9de38aac331044ea40f245cb0d5666392dfdf65824e1907ef6a2c62de5f", "devDependencies": { "@eslint/js": "^10.0.1", - "@nx/vite": "22.7.2", "@nx/vitest": "22.7.2", "@types/node": "^26.4.1", "@vitest/coverage-v8": "catalog:", diff --git a/packages/db/drizzle/20260917034659_misty_stick/migration.sql b/packages/db/drizzle/20260917034659_misty_stick/migration.sql index 16f7b94e..529e3c18 100644 --- a/packages/db/drizzle/20260917034659_misty_stick/migration.sql +++ b/packages/db/drizzle/20260917034659_misty_stick/migration.sql @@ -34,6 +34,7 @@ ALTER TABLE "authors" ADD PRIMARY KEY ("id");--> statement-breakpoint ALTER TABLE "collection_authors" DROP COLUMN "collection_slug";--> statement-breakpoint ALTER TABLE "collection_tags" DROP COLUMN "collection_slug";--> statement-breakpoint ALTER TABLE "collection_tags" ADD PRIMARY KEY ("collection_id","tag");--> statement-breakpoint +ALTER TABLE "collections" DROP CONSTRAINT IF EXISTS "collections_pkey";--> statement-breakpoint ALTER TABLE "collections" ADD PRIMARY KEY ("id");--> statement-breakpoint ALTER TABLE "authors" ADD CONSTRAINT "authors_slug_branch_unique" UNIQUE("slug","branch");--> statement-breakpoint ALTER TABLE "collections" ADD CONSTRAINT "collections_slug_locale_branch_unique" UNIQUE("slug","locale","branch");--> statement-breakpoint diff --git a/packages/db/scripts/migrate.ts b/packages/db/scripts/migrate.ts index 4fb27cfe..da17a049 100644 --- a/packages/db/scripts/migrate.ts +++ b/packages/db/scripts/migrate.ts @@ -1,6 +1,6 @@ import { fileURLToPath } from "node:url"; import { migrate } from "drizzle-orm/node-postgres/migrator"; -import { db } from "../src/client.ts"; +import { db, pool } from "../src/client.ts"; const migrationsFolder = fileURLToPath(new URL("../drizzle", import.meta.url)); console.log(`Running migrations from ${migrationsFolder}`); @@ -8,3 +8,7 @@ console.log(`Running migrations from ${migrationsFolder}`); await migrate(db, { migrationsFolder, }); + +console.log("Completed migrations."); +await pool.end(); +console.log("Closed pool."); diff --git a/packages/db/src/client.ts b/packages/db/src/client.ts index a0d22654..907aba8e 100644 --- a/packages/db/src/client.ts +++ b/packages/db/src/client.ts @@ -4,7 +4,7 @@ import pg from "pg"; import { env } from "@playfulprogramming/common"; import { relations } from "./relations.ts"; -const pool = new pg.Pool({ +export const pool = new pg.Pool({ connectionString: env.POSTGRES_URL, idleTimeoutMillis: 60_000, query_timeout: 30_000, diff --git a/packages/github-api/src/getAuthorGitHubStats.ts b/packages/github-api/src/getAuthorGitHubStats.ts index a9b01e5d..bc43e516 100644 --- a/packages/github-api/src/getAuthorGitHubStats.ts +++ b/packages/github-api/src/getAuthorGitHubStats.ts @@ -52,12 +52,12 @@ export async function getAuthorGitHubStats( client: Octokit, githubLogin: string, ): Promise { - const userResult = (await client.graphql>( + const userResult = await client.graphql>( `query($login: String!) { user(login: $login) { id } }`, { login: githubLogin }, - )) as Record; + ); - const userId = userResult?.user?.id; + const userId = userResult?.["user"]?.id; if (!userId) return undefined; const response = await client.graphql(statsQuery, { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index aeae1917..3400aa60 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -214,9 +214,6 @@ importers: '@eslint/js': specifier: ^10.0.1 version: 10.0.1(eslint@10.10.0(jiti@2.7.0)(supports-color@7.2.0)) - '@nx/vite': - specifier: 22.7.2 - version: 22.7.2(@babel/traverse@7.29.0(supports-color@7.2.0))(debug@4.4.3(supports-color@7.2.0))(nx@23.1.2)(supports-color@7.2.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@4.1.7) '@nx/vitest': specifier: 22.7.2 version: 22.7.2(@babel/traverse@7.29.0(supports-color@7.2.0))(debug@4.4.3(supports-color@7.2.0))(nx@23.1.2)(supports-color@7.2.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@4.1.7) @@ -267,7 +264,7 @@ importers: version: 8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0) vitest: specifier: 'catalog:' - version: 4.1.7(@types/node@26.5.1)(@vitest/coverage-v8@4.1.7)(vite@8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.0)(@types/node@26.5.1)(@vitest/coverage-v8@4.1.7)(vite@8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0)) apps/api: dependencies: @@ -300,7 +297,7 @@ importers: version: 1.57.2 drizzle-orm: specifier: 'catalog:' - version: 1.0.0-beta.20(@types/mssql@9.1.9(supports-color@10.2.2))(@types/pg@8.20.0)(mssql@11.0.1(supports-color@10.2.2))(pg@8.21.0)(typebox@1.1.38)(zod@4.4.3) + version: 1.0.0-beta.20(@opentelemetry/api@1.9.0)(@types/mssql@9.1.9(supports-color@10.2.2))(@types/pg@8.20.0)(mssql@11.0.1(supports-color@10.2.2))(pg@8.21.0)(typebox@1.1.38)(zod@4.4.3) fastify: specifier: ^5.12.4 version: 5.12.4 @@ -316,23 +313,29 @@ importers: version: link:../../packages/test-fixtures vitest: specifier: 'catalog:' - version: 4.1.7(@types/node@26.5.1)(@vitest/coverage-v8@4.1.7)(vite@8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.0)(@types/node@26.5.1)(@vitest/coverage-v8@4.1.7)(vite@8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0)) apps/e2e: dependencies: - '@playfulprogramming/api': - specifier: workspace:* - version: link:../api + '@jaredwray/mockhttp': + specifier: ^1.9.0 + version: 1.9.0(axios@1.18.1(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))(change-case@5.4.4)(supports-color@10.2.2)(tailwindcss@4.3.3)(typescript@6.0.3)(zod@4.4.3) openapi-fetch: specifier: 'catalog:' version: 0.17.0 + selfsigned: + specifier: ^5.5.0 + version: 5.5.0 + typebox: + specifier: ^1.3.30 + version: 1.3.34 devDependencies: openapi-typescript: specifier: 'catalog:' version: 7.13.0(typescript@6.0.3) vitest: specifier: 'catalog:' - version: 4.1.7(@types/node@26.5.1)(@vitest/coverage-v8@4.1.7)(vite@8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.0)(@types/node@26.5.1)(@vitest/coverage-v8@4.1.7)(vite@8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0)) apps/worker: dependencies: @@ -362,7 +365,7 @@ importers: version: 5.76.10(supports-color@10.2.2) drizzle-orm: specifier: 'catalog:' - version: 1.0.0-beta.20(@types/mssql@9.1.9(supports-color@10.2.2))(@types/pg@8.20.0)(mssql@11.0.1(supports-color@10.2.2))(pg@8.21.0)(typebox@1.1.38)(zod@4.4.3) + version: 1.0.0-beta.20(@opentelemetry/api@1.9.0)(@types/mssql@9.1.9(supports-color@10.2.2))(@types/pg@8.20.0)(mssql@11.0.1(supports-color@10.2.2))(pg@8.21.0)(typebox@1.1.38)(zod@4.4.3) gray-matter: specifier: 'catalog:' version: 4.0.3 @@ -420,7 +423,7 @@ importers: version: 4.0.4 vitest: specifier: 'catalog:' - version: 4.1.7(@types/node@26.5.1)(@vitest/coverage-v8@4.1.7)(vite@8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.0)(@types/node@26.5.1)(@vitest/coverage-v8@4.1.7)(vite@8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0)) packages/bullmq: dependencies: @@ -436,7 +439,7 @@ importers: devDependencies: vitest: specifier: 'catalog:' - version: 4.1.7(@types/node@26.5.1)(@vitest/coverage-v8@4.1.7)(vite@8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.0)(@types/node@26.5.1)(@vitest/coverage-v8@4.1.7)(vite@8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0)) packages/common: dependencies: @@ -451,7 +454,7 @@ importers: version: link:../common drizzle-orm: specifier: 'catalog:' - version: 1.0.0-beta.20(@types/mssql@9.1.9(supports-color@10.2.2))(@types/pg@8.20.0)(mssql@11.0.1(supports-color@10.2.2))(pg@8.21.0)(typebox@1.1.38)(zod@4.4.3) + version: 1.0.0-beta.20(@opentelemetry/api@1.9.0)(@types/mssql@9.1.9(supports-color@10.2.2))(@types/pg@8.20.0)(mssql@11.0.1(supports-color@10.2.2))(pg@8.21.0)(typebox@1.1.38)(zod@4.4.3) pg: specifier: ^8.21.0 version: 8.21.0 @@ -486,7 +489,7 @@ importers: version: link:../test-fixtures vitest: specifier: 'catalog:' - version: 4.1.7(@types/node@26.5.1)(@vitest/coverage-v8@4.1.7)(vite@8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.0)(@types/node@26.5.1)(@vitest/coverage-v8@4.1.7)(vite@8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0)) packages/post-images: dependencies: @@ -538,7 +541,7 @@ importers: version: 10.0.1 vitest: specifier: 'catalog:' - version: 4.1.7(@types/node@26.5.1)(@vitest/coverage-v8@4.1.7)(vite@8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.0)(@types/node@26.5.1)(@vitest/coverage-v8@4.1.7)(vite@8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0)) packages/redis: dependencies: @@ -569,10 +572,32 @@ importers: dependencies: vitest: specifier: 'catalog:' - version: 4.1.7(@types/node@26.5.1)(@vitest/coverage-v8@4.1.7)(vite@8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0)) + version: 4.1.7(@opentelemetry/api@1.9.0)(@types/node@26.5.1)(@vitest/coverage-v8@4.1.7)(vite@8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0)) packages: + '@ai-sdk/gateway@3.0.13': + resolution: {integrity: sha512-g7nE4PFtngOZNZSy1lOPpkC+FAiHxqBJXqyRMEG7NUrEVZlz5goBdtHg1YgWRJIX776JTXAmbOI5JreAKVAsVA==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/provider-utils@4.0.5': + resolution: {integrity: sha512-Ow/X/SEkeExTTc1x+nYLB9ZHK2WUId8+9TlkamAx7Tl9vxU+cKzWx2dwjgMHeCN6twrgwkLrrtqckQeO4mxgVA==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + + '@ai-sdk/provider@3.0.2': + resolution: {integrity: sha512-HrEmNt/BH/hkQ7zpi2o6N3k1ZR1QTb7z85WYhYygiTxOQuaml4CMtHCWRbric5WPU+RNsYI7r1EpyVQMKO1pYw==} + engines: {node: '>=18'} + + '@ai-sdk/vue@3.0.33': + resolution: {integrity: sha512-czM9Js3a7f+Eo35gjEYEeJYUoPvMg5Dfi4bOLyDBghLqn0gaVg8yTmTaSuHCg+3K/+1xPjyXd4+2XcQIohWWiQ==} + engines: {node: '>=18'} + peerDependencies: + vue: ^3.3.4 + '@aws-crypto/crc32@5.2.0': resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} engines: {node: '>=16.0.0'} @@ -857,10 +882,18 @@ packages: resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.28.5': resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.27.1': resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} @@ -878,6 +911,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.29.8': + resolution: {integrity: sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5': resolution: {integrity: sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==} engines: {node: '>=6.9.0'} @@ -1313,6 +1351,10 @@ packages: resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} + engines: {node: '>=6.9.0'} + '@bcoe/v8-coverage@1.0.2': resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} @@ -1323,6 +1365,42 @@ packages: '@cacheable/utils@2.5.0': resolution: {integrity: sha512-buipgOVDkkPXNR5+xBpDw7Zk2n1EvU7qBJCNUcL7rhQ//kfpOXPAvQ511Os0vpLYJ1pZnvudNytkQt2hst3wqA==} + '@codemirror/autocomplete@6.20.3': + resolution: {integrity: sha512-tlosUqb+3BbxCxZdu4tKeRghPFC+QM7q4X5YhKV2eCmPG+1r2F3f4AaSz5sCrFqUtX4Jh20VFTKecl16MgiV9g==} + + '@codemirror/commands@6.11.0': + resolution: {integrity: sha512-/K4Rl5BN0OtTiPWmJCdqODu38XnDMsDxKY5rgrPnCkutPTJf2wVbkoixLfealF5Kwse/s8P8M5jAiURiwSwnFA==} + + '@codemirror/lang-css@6.3.1': + resolution: {integrity: sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==} + + '@codemirror/lang-html@6.4.12': + resolution: {integrity: sha512-pw2ReWKUqSkbvh76RAT4NYxiogRu+PWkR2ukAwO9uOgrm8uipkzjtKKtNpyeAQwHOqxEeSvAXZ6vr3AfyB9y/w==} + + '@codemirror/lang-javascript@6.2.5': + resolution: {integrity: sha512-zD4e5mS+50htS7F+TYjBPsiIFGanfVqg4HyUz6WNFikgOPf2BgKlx+TQedI1w6n/IqRBVBbBWmGFdLB/7uxO4A==} + + '@codemirror/lang-json@6.0.2': + resolution: {integrity: sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==} + + '@codemirror/lang-xml@6.1.0': + resolution: {integrity: sha512-3z0blhicHLfwi2UgkZYRPioSgVTo9PV5GP5ducFH6FaHy0IAJRg+ixj5gTR1gnT/glAIC8xv4w2VL1LoZfs+Jg==} + + '@codemirror/lang-yaml@6.1.3': + resolution: {integrity: sha512-AZ8DJBuXGVHybpBQhmZtgew5//4hv3tdkXnr3vDmOUMJRuB6vn/uuwtmTOTlqEaQFg3hQSVeA90NmvIQyUV6FQ==} + + '@codemirror/language@6.12.4': + resolution: {integrity: sha512-1q4PaT+o6PbgpkJt4Q8Fv5XJxTy4FUZ4MWETtyiDw3J0Pyr9E2vqcKL+k9wcvjNTIsauxvE7OfmWj3FRPHQ76A==} + + '@codemirror/lint@6.9.7': + resolution: {integrity: sha512-28/+iWLYxKxsvGYhSYL7zaCZqLz5+FFFDq9tVsvGv9kv8RY4fFAchJ5WX9M3YrrRlTIsECjsXPqeNgnSmNP2dg==} + + '@codemirror/state@6.7.4': + resolution: {integrity: sha512-QhQIVRY+xHZDxwOSFrJ1eUMapJBUID3IdeAjf7dHO7zBUzSkyooHiodnalz5MG3iHzwixKMlAAyn7244y537EA==} + + '@codemirror/view@6.43.11': + resolution: {integrity: sha512-2+esucbQX6wB2JYi1eDvdCPFTA31BN8oSy6xCmk3G6CloV11yOvEjYk+gH7kLrP0MuHG94E8WDhjs5oMiu3+Wg==} + '@drizzle-team/brocli@0.11.0': resolution: {integrity: sha512-hD3pekGiPg0WPCCGAZmusBBJsDqGUR66Y452YgQsZOnkdQ7ViEPKuyP4huUGEZQefp8g34RRodXYmJ2TbCH+tg==} @@ -1542,9 +1620,15 @@ packages: resolution: {integrity: sha512-IkO+/KEUvwbVpiURZg+P7zF74z5Jxe0UgJxVni+RtoHQ6IZieXaO02kmadomap/q+l6bc/jdPGGqTjhuZnuz1Q==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} + '@fastify/accept-negotiator@2.1.0': + resolution: {integrity: sha512-F3EVbzWt+xcnVaOHmWyIlpuFtbxOln7HDZQsh09MtMmMm/CipMayNt8hnIL8VQi54u2ZociDbf+iluGYkf7B1A==} + '@fastify/ajv-compiler@4.0.6': resolution: {integrity: sha512-NtuzM0SfaMJbGlnjr9LWQUN5LzgSrbB8tf/wRZNas+4E1O/Nmzl53e7ruT61HDZyRCJGC6FxIogmNZO1c5ETBA==} + '@fastify/cookie@11.1.2': + resolution: {integrity: sha512-Dtrpk/YOGUsbRMvP/8ZqPpwnMRv0qSqodFdoQ2B589Obc7jw4s4Qla+cV72Bsm7WsZJnqlYFX/i7uSBq0xzg6g==} + '@fastify/error@4.2.0': resolution: {integrity: sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ==} @@ -1554,6 +1638,9 @@ packages: '@fastify/forwarded@3.0.2': resolution: {integrity: sha512-NE8HgKLgYejV9lDpqkEFaDKMLYelJBVfHekhB0UKvX0ghagXRJqg68feg8er1NPXxG4N9i6vPxzt8E+3wHfcmA==} + '@fastify/helmet@13.1.1': + resolution: {integrity: sha512-bSat5DTq8geASv8G6P0KW1UbltZ+xGD/zyd9S72pT7ogAHehcsWL85GdjMRCjDsExJvaEvgEZ52qU/2HXirVCw==} + '@fastify/merge-json-schemas@0.2.1': resolution: {integrity: sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==} @@ -1563,12 +1650,54 @@ packages: '@fastify/rate-limit@11.2.0': resolution: {integrity: sha512-X7osJd4XSvMoejYrnJkSZYYjY1eNYoBqhjlzf1RakC2204qExFqZFTKj5+T7VuzA/iUI9Z3UoSqQRkB2HpG0oQ==} + '@fastify/send@4.1.1': + resolution: {integrity: sha512-BYo+EiaKwlxH+WetGk6hAs1d39iP0y1gqB8lGF/qwkJ9ZZ/cBY1vx5NvExb9Sc3yRMFjD5X4Eyh4e4+TzRkzdw==} + '@fastify/sensible@6.0.5': resolution: {integrity: sha512-v4mGejwT6EJA2zd2rS6XwlyUSva2ILsqzG0aGkyrlS7l2jIpUgFACclSPN3kDijX//cf8+GutRLhJVKx3ae2Cw==} + '@fastify/static@10.1.3': + resolution: {integrity: sha512-W6jqajYS974XjPjB5hQWoxPM8NKM4+p8YmQT6G5IbCa4uhdWSVadZUv75siy1wEA/3ty8RYdpBydfWeu9AqAqQ==} + + '@fastify/swagger-ui@6.1.1': + resolution: {integrity: sha512-RKCLSHASlzS2JZvHWn14NmEpHyl0yNosGvqzhUumm/LGPG6RWQBf4oscTFt83QDvc5O5Tol3Beup8inAl/k4EA==} + '@fastify/swagger@9.7.0': resolution: {integrity: sha512-Vp1SC1GC2Hrkd3faFILv86BzUNyFz5N4/xdExqtCgkGASOzn/x+eMe4qXIGq7cdT6wif/P/oa6r1Ruqx19paZA==} + '@fastify/swagger@9.8.1': + resolution: {integrity: sha512-VpHMnqZTY8iBZYJE8WWkbKPrXIYWy2rDfIf5qLr6DzZSpQYZ+KxQVcJFiq/AMlvNwI4gCBd66++iUlxXXGT0IQ==} + + '@floating-ui/core@1.8.0': + resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==} + + '@floating-ui/dom@1.8.0': + resolution: {integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==} + + '@floating-ui/utils@0.2.10': + resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + + '@floating-ui/utils@0.2.12': + resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} + + '@floating-ui/vue@1.1.11': + resolution: {integrity: sha512-HzHKCNVxnGS35r9fCHBc3+uCnjw9IWIlCPL683cGgM9Kgj2BiAl8x1mS7vtvP6F9S/e/q4O6MApwSHj8hNLGfw==} + + '@floating-ui/vue@1.1.9': + resolution: {integrity: sha512-BfNqNW6KA83Nexspgb9DZuz578R7HT8MZw1CfK9I6Ah4QReNWEJsXWHN+SdmOVLNGmTPDi+fDT535Df5PzMLbQ==} + + '@headlessui/tailwindcss@0.2.2': + resolution: {integrity: sha512-xNe42KjdyA4kfUKLLPGzME9zkH7Q3rOZ5huFihWNWOQFxnItxPB3/67yBI8/qBfY8nwBRx5GHn4VprsoluVMGw==} + engines: {node: '>=10'} + peerDependencies: + tailwindcss: ^3.0 || ^4.0 + + '@headlessui/vue@1.7.23': + resolution: {integrity: sha512-JzdCNqurrtuu0YW6QaDtR2PIYCKPUWq28csDyMvN4zmGccmE7lz40Is6hc3LA4HFeCI7sekZ/PQMTNmn9I/4Wg==} + engines: {node: '>=10'} + peerDependencies: + vue: ^3.2.0 + '@humanfs/core@0.19.2': resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} engines: {node: '>=18.18.0'} @@ -1751,9 +1880,19 @@ packages: cpu: [x64] os: [win32] + '@internationalized/date@3.12.4': + resolution: {integrity: sha512-M1dEn4c1U1HsSlaVR8upZtSqvXrTkHDfv18H01uCSJyjVLDxnBR38v/fMxecmlwXKR4i9HeZcmgQAPE6A+aGJQ==} + + '@internationalized/number@3.6.8': + resolution: {integrity: sha512-8UmMFia46DUt+k97zKd9fKWXcWHR+k8ae3eYzILETuT2KbIvLyOfac7zesw+sJdRAAZ7Q9pM1Mk22aXp2LD0Ig==} + '@ioredis/commands@1.5.1': resolution: {integrity: sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw==} + '@jaredwray/mockhttp@1.9.0': + resolution: {integrity: sha512-KqT40wMA5sghoCr2t57sHwAjFoeaxslAQJCkECBMVqcO6L/85ow0xSZyezOoYSr3EPMLHfteJu6CqcHP0/TXUA==} + engines: {node: '>=22.18.0'} + '@jest/diff-sequences@30.0.1': resolution: {integrity: sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -1796,10 +1935,40 @@ packages: '@keyv/serialize@1.1.1': resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==} + '@lezer/common@1.5.2': + resolution: {integrity: sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ==} + + '@lezer/css@1.3.8': + resolution: {integrity: sha512-EJn1zcL9qoDptief6ipWKZKLiOpXkxSe0+t8CH9oiMVcZlq7NBWrjCqnc/41EIjeo/ITj1gFFiATdTkaJDL+Og==} + + '@lezer/highlight@1.2.3': + resolution: {integrity: sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==} + + '@lezer/html@1.3.13': + resolution: {integrity: sha512-oI7n6NJml729m7pjm9lvLvmXbdoMoi2f+1pwSDJkl9d68zGr7a9Btz8NdHTGQZtW2DA25ybeuv/SyDb9D5tseg==} + + '@lezer/javascript@1.5.6': + resolution: {integrity: sha512-sEvOqpl4vJQca0NugSQK02vQPbc5KrvoojnYGoqqFgFO0MtcYhwGiHKVEB+LuXYJnfyXo4QPA8ztbrEa4Fw5NA==} + + '@lezer/json@1.0.3': + resolution: {integrity: sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==} + + '@lezer/lr@1.4.10': + resolution: {integrity: sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A==} + + '@lezer/xml@1.0.6': + resolution: {integrity: sha512-CdDwirL0OEaStFue/66ZmFSeppuL6Dwjlk8qk153mSQwiSH/Dlri4GNymrNWnUmPl2Um7QfV1FO9KFUyX3Twww==} + + '@lezer/yaml@1.0.4': + resolution: {integrity: sha512-2lrrHqxalACEbxIbsjhqGpSW8kWpUKuY6RHgnSAFZa6qK62wvnPxA8hGOwOoDbwHcOFs5M4o27mjGu+P7TvBmw==} + '@lukeed/ms@2.0.2': resolution: {integrity: sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==} engines: {node: '>=8'} + '@marijn/find-cluster-break@1.0.4': + resolution: {integrity: sha512-Wy0V7+SGUjnF9/TkiM1hKVDPj7jKXduPNboMVtHTA8dySMURWqfg/JZ9E2Sq8JgSJmkl7k7Qe9FLeMSrSraWmQ==} + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': resolution: {integrity: sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==} cpu: [arm64] @@ -1840,6 +2009,10 @@ packages: '@emnapi/core': ^1.7.1 || ^2.0.0-alpha.4 '@emnapi/runtime': ^1.7.1 || ^2.0.0-alpha.4 + '@noble/hashes@1.4.0': + resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} + engines: {node: '>= 16'} + '@nodable/entities@2.1.0': resolution: {integrity: sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==} @@ -1964,12 +2137,6 @@ packages: cpu: [x64] os: [win32] - '@nx/vite@22.7.2': - resolution: {integrity: sha512-EsFOp00f3/eFL4GirRBuHktFj/Bm7VL7kvMFru73bCqqQZsdFSGZT8puwYm77NBZB0if5cxkaynysiEmNpVCpw==} - peerDependencies: - vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 - vitest: ^1.3.1 || ^2.0.0 || ^3.0.0 || ^4.0.0 - '@nx/vitest@22.7.2': resolution: {integrity: sha512-i3Tl3tgxi8Gr6DGodvbZSF9cJE2Uno5/HOQENn5DfWnXa1KDyBR4+w4lZhf2T3TCeTOFghZd1pESWVCDfgCjvA==} peerDependencies: @@ -2094,6 +2261,10 @@ packages: resolution: {integrity: sha512-da6KbdNCV5sr1/txD896V+6W0iamFWrvVl8cHkBSPT+YlvmT3DwXa4jxZnQc+gnuTEqSWbBeoSZYTayXH9wXcw==} engines: {node: '>= 20'} + '@opentelemetry/api@1.9.0': + resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} + engines: {node: '>=8.0.0'} + '@oxc-parser/binding-android-arm-eabi@0.130.0': resolution: {integrity: sha512-h/xYU8/7ADWzVSf5I+YalLpj33LOy9CI/zgbJNIZ5eunRBG+Czqa3lZsvuPHHf3rOt6z1c5+UzoxjbAzAvhwVw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2335,11 +2506,61 @@ packages: cpu: [x64] os: [win32] + '@peculiar/asn1-cms@2.9.4': + resolution: {integrity: sha512-cben7oxmQsUGZqotus7yt0srYdncOT6RNWcTQ77T2RFOXejYVYkXadrfePdRcrVpO9K95IRLKKglG2k38jKXuw==} + engines: {node: '>=14'} + + '@peculiar/asn1-csr@2.9.4': + resolution: {integrity: sha512-xd4YN4vpRjkDAQWVfZZkeu12IEND7DOpkqaHSIHxZl1uggUNa9Ju0QxY2jHvDAS9pP0zhRBytg8ifsnGo3V0jw==} + engines: {node: '>=14'} + + '@peculiar/asn1-ecc@2.9.4': + resolution: {integrity: sha512-JJXefFshRAuVAjWQo/39bkg1ywc1VaiO44S8RRC+Ykvf/u2KDmYffoDb0ZBPCR5uJy4AGKQhl8mX+Q8ShcWaXQ==} + engines: {node: '>=14'} + + '@peculiar/asn1-pfx@2.9.4': + resolution: {integrity: sha512-khuGzHTzNzk4GDlIBEILyIs6Lce0yn0ZBdoI9v93kmNncfZRhD+AQ5ODFqdhvoE8cMJF/JMTQ8yA+t1D14kqCw==} + engines: {node: '>=14'} + + '@peculiar/asn1-pkcs8@2.9.4': + resolution: {integrity: sha512-duRdotlUx9eDZe6QrQpQKl61RbWykCHBCkKayP8V8XdEFwlKHZ8qGGDMyS6Pye7OX7nLFttTTpRkJeet78ckwQ==} + engines: {node: '>=14'} + + '@peculiar/asn1-pkcs9@2.9.4': + resolution: {integrity: sha512-kaL4cNxBpdQE2dKlyZBqz4ygCrwffO+8wfoxTEqM1Z8RadvCeELBRzcv0dzM8aY9azHMwODO5nxU65zXmhToOQ==} + engines: {node: '>=14'} + + '@peculiar/asn1-rsa@2.9.4': + resolution: {integrity: sha512-pZ96eD1PptovcWQ/GSmuNFXd/7EQJNlKfDaNCyE2rx3W0v6QFelkzquVqRSRyyDXXCYD69ZXJDzZ8GhIiQzKoA==} + engines: {node: '>=14'} + + '@peculiar/asn1-schema@2.9.4': + resolution: {integrity: sha512-GjzePcT9Iw8NzeOPf73iNS9xM+TBhd/FilAfP+RQGkTMQJTVWtytN3JHJACCjf/ABNau5S7mS3g+DcuxmRgYEg==} + engines: {node: '>=14'} + + '@peculiar/asn1-x509-attr@2.9.4': + resolution: {integrity: sha512-ehQXbpQaQYycgu8OrvigwSPTFfVRcu0ECNYCWw+yzBp02Lw5paRqzzhUpfOgO2K38+WfFZuEz/0RPtam5g0OMg==} + engines: {node: '>=14'} + + '@peculiar/asn1-x509@2.9.4': + resolution: {integrity: sha512-CxhBo/RdEbMMob7T31ZdQjGuoyRFLVwrDzTn25bihzBasRg9kRm/0IxIPvhgQtcK/9dNcO1XQL2fuPugwELL0Q==} + engines: {node: '>=14'} + + '@peculiar/utils@2.0.3': + resolution: {integrity: sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ==} + + '@peculiar/x509@1.14.3': + resolution: {integrity: sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==} + engines: {node: '>=20.0.0'} + '@phenomnomnominal/tsquery@6.2.0': resolution: {integrity: sha512-Vo9nkhfZxDB/sBiqIY3pjDC4mOSyure+AFlEW5hcy/tRE82MqCXjRN4InnVNMldinRt0dLYqg4HAU2XPq5e1LA==} peerDependencies: typescript: '>3.0.0' + '@phosphor-icons/core@2.1.1': + resolution: {integrity: sha512-v4ARvrip4qBCImOE5rmPUylOEK4iiED9ZyKjcvzuezqMaiRASCHKcRIuvvxL/twvLpkfnEODCOJp5dM4eZilxQ==} + '@pinojs/redact@0.4.0': resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} @@ -2357,6 +2578,13 @@ packages: resolution: {integrity: sha512-2+O+riuIUgVSuLl3Lyh5AplWZyVMNuG2F98/o6NrutKJfW4/GTZdPpZlIphS0HGgcOHgmWcCSHj+dWFlZaGSHw==} engines: {node: '>=18.17.0', npm: '>=9.5.0'} + '@replit/codemirror-css-color-picker@6.3.0': + resolution: {integrity: sha512-19biDANghUm7Fz7L1SNMIhK48tagaWuCOHj4oPPxc7hxPGkTVY2lU/jVZ8tsbTKQPVG7BO2CBDzs7CBwb20t4A==} + peerDependencies: + '@codemirror/language': ^6.0.0 + '@codemirror/state': ^6.0.0 + '@codemirror/view': ^6.0.0 + '@resvg/resvg-js-android-arm-eabi@2.6.2': resolution: {integrity: sha512-FrJibrAk6v29eabIPgcTUMPXiEz8ssrAk7TXxsiZzww9UTQ1Z5KAbFJs+Z0Ez+VZTYgnE5IQJqBcoSiMebtPHA==} engines: {node: '>= 10'} @@ -2536,22 +2764,66 @@ packages: '@rolldown/pluginutils@1.0.1': resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + '@scalar/agent-chat@0.12.30': + resolution: {integrity: sha512-1+09CTY/eVLg6ygdrrKyNkhWXnbHcVGrr4Jq4HYr7XogrkIsTCuHCBcpPbYRWOFJ2zRpToPFu5M3R7eLE810zA==} + engines: {node: '>=22'} + + '@scalar/api-client@3.18.0': + resolution: {integrity: sha512-FlseC6xWfx0ganh4s0IAOelJ5kF+aJ07BJfjUriwKDdK8Y4oAFkRlneKQNoT/S+4mrNCXdQ/T5VPEjYFlx4Zxw==} + engines: {node: '>=22'} + + '@scalar/api-reference@1.68.0': + resolution: {integrity: sha512-rY43w3REwCxp+rDDx/0CncZxmlzISnGTK9zZ8moq0Ij2vRHhLQCJ0/BXut9pBAupVrOZF7MoqKXcG+gISgTu5g==} + engines: {node: '>=22'} + + '@scalar/asyncapi-upgrader@0.1.9': + resolution: {integrity: sha512-+kK4dp1J8GvOcTU2OCMFwj62VMP3Y0lppjQ2mdfYsczhTTP9saf12vkRzuHQ+ILkLJJXo98km5zj7pEVMdOSPA==} + engines: {node: '>=22'} + + '@scalar/blocks@0.1.16': + resolution: {integrity: sha512-k72Dxwj2Bh9jhJkIyTlrpja6QRnvnT5Cb+LH++BGMV70lSu22cyzJBmhtKORTfQVb671s2I7WLUBU6zirxkhsA==} + engines: {node: '>=22'} + '@scalar/client-side-rendering@0.1.9': resolution: {integrity: sha512-Gv3CK0X+BqXYkVJLTQXNM8YgdquhuGV4hKlsM8S9k5GGonj8LBDSAiuU3W48JGX1pY7hRotBGcIIU+1a1X1mcw==} engines: {node: '>=22'} + '@scalar/code-highlight@0.4.5': + resolution: {integrity: sha512-OCZvBYwodCR1cWemO3MXMwXumI18kFOy8k2nwP+Ic8zFLK1ObyXECRoM+UH+GTw+O4TJsrjNIdLjsnXPO1MvyA==} + engines: {node: '>=22'} + + '@scalar/components@0.29.1': + resolution: {integrity: sha512-mxlJ/3Pv1YqqaXSBU2SLAiTZSGdam3ZyoYW7CAITSoVlruDBIEfSm6a8eABpM9mWSsoNQhmscLEq2QSC0szUPA==} + engines: {node: '>=22'} + '@scalar/fastify-api-reference@1.57.2': resolution: {integrity: sha512-m9CpGNQdSlmjdCpTUl/elhuGm5L9pGg6CXY9m1qvKo54Bk+neq+UDGqn8hll6hs2Is4+xrYQeF8rjIoQY1TNAA==} engines: {node: '>=22'} + '@scalar/helpers@0.11.3': + resolution: {integrity: sha512-4zPzuNTXObDUtZS93xzAoK83ddHDgBGifv/vKFe6bHqEl5i+IMLTTtEHOaaOZK4dusPEXcXsU5TBSaY/I6Mi+A==} + engines: {node: '>=22'} + '@scalar/helpers@0.8.0': resolution: {integrity: sha512-gmOC6VravNB9VDl6wnt/GOj4K/hn48tj5bpW4AM4MhH8Ubil6uu7g1DSoKHwltu8Ks79KEtR6JmOrROi9R7jaQ==} engines: {node: '>=22'} + '@scalar/icons@0.7.6': + resolution: {integrity: sha512-UIQ7K/xNDo7Mgez/Z+ArG7JZnS8QRZNOl+s9frdS1T8uEJYuTqT1lnS7R0ECwIw3y6dn6EIfjaZKPihjYC7+qw==} + engines: {node: '>=22'} + '@scalar/json-magic@0.12.14': resolution: {integrity: sha512-dWrCy3ew1r7OQ1pu2r4ZjiKEVy0yVd66kXdmsl41bteOG2F2I2IBlPjmPV6p8ckjImQHxtNBIntFaQfNrdBhJg==} engines: {node: '>=22'} + '@scalar/json-magic@0.13.4': + resolution: {integrity: sha512-pOZdlzkgLB+/4OlIlzMToV/cr4vsvWy/MtbtJoRcNHIzDnT8sfNtv/cH8MR2pNDg/29sPpTV1HaE4SZ8b9jUOQ==} + engines: {node: '>=22'} + + '@scalar/oas-utils@0.19.16': + resolution: {integrity: sha512-0u0/vd62lEektF9u6d7ywAYwamkrG1xTfxMf5gOkRGTVZJ7jV+J9LoSfUv+NCR3mmQpeGyKSaiD/3/Psa4OwRA==} + engines: {node: '>=22'} + '@scalar/openapi-parser@0.28.4': resolution: {integrity: sha512-OMrMzffQ7bfLKaIPdjQzVm1JE5Vlj781CA+qAo9YoxzGyoqLwI+YbctBbOlTgT5vNCY8qtP7vXkWWICl9E0Awg==} engines: {node: '>=22'} @@ -2560,6 +2832,14 @@ packages: resolution: {integrity: sha512-WmaxVSfvY5K/TwcG2B2TU1WOe1As1uc2s7myswtP6dBlcjU3hM08SApxv/jmyGaCE8t4gO5BBhmHY4pDUfmr2g==} engines: {node: '>=22'} + '@scalar/openapi-types@0.9.5': + resolution: {integrity: sha512-czrz/zkVm1oPzrpYo3hI/iymfiw1s4dgJiQtwNi2U77Sqf3EQOGKsif4VNRa5suWMYRuPaFx5EiFM1FNEV4Whg==} + engines: {node: '>=22'} + + '@scalar/openapi-upgrader@0.2.15': + resolution: {integrity: sha512-yqROK9U96ElasEL4Wl/+PIjQZlqrQXVUUpXk9PA6xBnrp8KdEv7at8pR5gsZkvddJfYVwLILPlctyqUg4u4YZA==} + engines: {node: '>=22'} + '@scalar/openapi-upgrader@0.2.7': resolution: {integrity: sha512-sC/uLQOivfX+Oef2QhUpgmERL7KZc1z+hiYkcwZQaUyVOHh5e6OscW0skfzbxKPyJfQ6Ocv0Iom9wMToCGaAPw==} engines: {node: '>=22'} @@ -2568,14 +2848,57 @@ packages: resolution: {integrity: sha512-FOpNecNoEKo8SogHEsdWlVRN4Q4PH3dzuOBWMA5tGt1xLZu5iFnPG2X/h6+Z/mOR34c7iW+hiKTqdUZoDgT9CA==} engines: {node: '>=22'} + '@scalar/schemas@0.9.0': + resolution: {integrity: sha512-yYRlIWzw+7HuIX4z7rk7tg4y1nERwvvWKbolZOm7LveSTrppllGKyjtnIqS5uXsmJddERxuurSgDW224gGJlFQ==} + engines: {node: '>=22'} + + '@scalar/sidebar@0.11.1': + resolution: {integrity: sha512-xXgB0WYWG4WJTFL93WkvoAVdD1H4k+A2n2jgwa/PrUM6RO8TTh+7BLcekpwvPLvXd5muWxzWDTcR413saocphw==} + engines: {node: '>=22'} + + '@scalar/snippetz@0.9.30': + resolution: {integrity: sha512-mDluVSGZet1Go8NgJK9s9Z8zNKqePG7zNn8PMCphAzwXNomvMy6j8WRuLDln+Dz33jILfiKlUtv4cnLkmzB+7g==} + engines: {node: '>=22'} + + '@scalar/themes@0.17.4': + resolution: {integrity: sha512-tSCtGLb0noijR8GzgH6H/tlbSuZoLupe66ta6I9FzZxdvgj4SdPM+2Q7E8k1uwPRfrE5NvMyp61s1BsA3DTFsg==} + engines: {node: '>=22'} + + '@scalar/typebox@0.1.3': + resolution: {integrity: sha512-lU055AUccECZMIfGA0z/C1StYmboAYIPJLDFBzOO81yXBi35Pxdq+I4fWX6iUZ8qcoHneiLGk9jAUM1rA93iEg==} + '@scalar/types@0.11.0': resolution: {integrity: sha512-TGZR8sys1jRlaWxLYfBo8y6D1W4UClYuDmkJ6Hmsb7oNuqokEAAK+AVYIzascVdBmaly0D1fqoqK7CzuGYHgyg==} engines: {node: '>=22'} + '@scalar/types@0.19.0': + resolution: {integrity: sha512-EKeoWgUlP+uepbM/zEHKbsdpBNyOmSrw6DdU/WC0p53gz5uRzI7d/IEecIP9SQMUkResPR9DmWeWfFQftG7vqg==} + engines: {node: '>=22'} + + '@scalar/use-codemirror@0.14.15': + resolution: {integrity: sha512-Uvjx0qvOsWPs+I32FL+3v//+VKwtp8ROXgbIAMauNypuw52VeF7R4RM7YoN2Xg1EAEiMtM8ZhWVzCoYTFr6AKQ==} + engines: {node: '>=22'} + + '@scalar/use-hooks@0.4.11': + resolution: {integrity: sha512-wCUn9WWKv4abiFZOcjBp8nIcyZ30t7QNPgHbcYu5MXgLgPclYwKb/A+TZiyp63TORWJOK8sp1RzrNGw2CT+m/Q==} + engines: {node: '>=22'} + + '@scalar/use-toasts@0.10.5': + resolution: {integrity: sha512-5a8qTVd9eXvMDeD0ifqHvnzg0i2v15clhyFToXDHYmInQD51oBRwByCFqFO+m9ymxSLOHZ7/FiEcU6F90/QTlg==} + engines: {node: '>=22'} + '@scalar/validation@0.5.0': resolution: {integrity: sha512-48CS1B0C7im57RQCHhS0GKt+qDLxB34IX8rO91jZj9D+Y/OosQZG3Gy57gJdHqZoD7l0sPUZtF8tVYry3BxRtw==} engines: {node: '>=20'} + '@scalar/validation@0.6.3': + resolution: {integrity: sha512-j3s9XPv8Wo1EG5/naBi4XGF0uXYQ2A3QZNI0NKWgCMxRHVrDJGlZEYymcHDHY7fquRm73P8U3c8xqJqB5yad6w==} + engines: {node: '>=20'} + + '@scalar/workspace-store@0.60.0': + resolution: {integrity: sha512-O3Zp6Olq7+L2Yp6xd7Z9sIQ4VG5SwR2oHkiGTagZBSrqEuvuce5Z93q3NVPux6j3cID/geW0vC0sPJ5LxNTnBA==} + engines: {node: '>=22'} + '@shuding/opentype.js@1.4.0-beta.0': resolution: {integrity: sha512-3NgmNyH3l/Hv6EvsWJbsvpcpUba6R8IREQ83nH83cyakCw7uM1arZKNfHwv1Wz6jgqrF/j4x5ELvR6PnK9nTcA==} engines: {node: '>= 8.0.0'} @@ -2624,6 +2947,17 @@ packages: '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + '@swc/helpers@0.5.23': + resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==} + + '@tanstack/virtual-core@3.17.10': + resolution: {integrity: sha512-eMorKWIi2nekElu+8DgSMJR4iR+sTDeh+W8VjKiMH79yUfEDJ3W9aL/96/wwbxNjyV6EfN39HKszSxjd90zAqQ==} + + '@tanstack/vue-virtual@3.13.38': + resolution: {integrity: sha512-9GE1RY5EBmA5w+0AR7AIL940EnGKik7o69QLDDcZg2sBLbitkjFRUD1BKxgKPD6WsCS/ptOPndARtDTkShH1jA==} + peerDependencies: + vue: ^2.7.0 || ^3.0.0 + '@tediousjs/connection-string@0.5.0': resolution: {integrity: sha512-7qSgZbincDDDFyRweCIEvZULFAw5iz/DeunhvuxpL31nfntX3P4Yd4HkHBRg9H8CdqY1e5WFN1PZIz/REL9MVQ==} @@ -2654,6 +2988,9 @@ packages: '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + '@types/har-format@1.2.16': + resolution: {integrity: sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==} + '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} @@ -2669,6 +3006,9 @@ packages: '@types/mssql@9.1.9': resolution: {integrity: sha512-P0nCgw6vzY23UxZMnbI4N7fnLGANt4LI4yvxze1paPj+LuN28cFv5EI+QidP8udnId/BKhkcRhm/BleNsjK65A==} + '@types/node@24.13.4': + resolution: {integrity: sha512-YJ7EqCstVTzIr0fMr7qul/977en+pQHrfmuKIo6Zr9i75Be21dr3MovcfvGtyvi2HAUrRerWps5sMO9I7WaxDw==} + '@types/node@26.5.1': resolution: {integrity: sha512-CzNm2FezW4VR/LjG6yUdiEgLE/rAQ9Slj5gCu/C2VrdcW7I0ahNZ8DRbHT7zOZ6r3ONgd/bsQIeSaoDGrd1C6g==} @@ -2684,6 +3024,12 @@ packages: '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + '@types/web-bluetooth@0.0.20': + resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==} + + '@types/web-bluetooth@0.0.21': + resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==} + '@typescript-eslint/eslint-plugin@8.59.4': resolution: {integrity: sha512-PegsU+XfyJJNjd4+u/k6f9yTyp0lEXXiPopUNobZcIAUJFGICFLN+sP0Rb3JehVmiij1Ph0dFGYqODoRo/2+6A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2751,6 +3097,15 @@ packages: resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} deprecated: Potential CWE-502 - Update to 1.3.1 or higher + '@unhead/vue@2.1.17': + resolution: {integrity: sha512-pnC8x9HLV3qQXdvWfylUEU25uhfCAy3ly9nmpQz84j9py818DRfU8jOsQ5wjdWtxyU1vX/fW2udfm4jtxUK8Bg==} + peerDependencies: + vue: '>=3.5.18' + + '@vercel/oidc@3.1.0': + resolution: {integrity: sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w==} + engines: {node: '>= 20'} + '@vitest/coverage-v8@4.1.7': resolution: {integrity: sha512-qsYPeXc5Q9dFLd1i8Ap+Bx8sQgcp+rFVQo4R0dDsWNBzl26ldVF1qOO+RL24K7FDrR6pA+50XedRLSoSG24bVQ==} peerDependencies: @@ -2789,6 +3144,97 @@ packages: '@vitest/utils@4.1.7': resolution: {integrity: sha512-T532WBu791cBxJlCl6SO+J14l81DQx6uQHm1bQbmCDY7nqlEIgkza/UFnSBNaUtSf41unldDFjdOBYEQC4b5Hw==} + '@vue/compiler-core@3.5.42': + resolution: {integrity: sha512-2Ye1ilMtKXxl8qZUrQ5j0CdgenFp/HFQmta6rfRyfEsTG69L6Wk+tWuNoHYHMx9E8tF2Slvdg1FuwDvAXdy1LQ==} + + '@vue/compiler-dom@3.5.42': + resolution: {integrity: sha512-qbhQZEFmycr+ni/qyuccS4sucNN7VAbDfbkvNxWOX2VfgFm90MNs3/UhRNKoPMEIVn0F8gdlYjLPvqxHwHeQOA==} + + '@vue/compiler-sfc@3.5.42': + resolution: {integrity: sha512-fkCAFB4okcAANGMThboWnScp/gzWjU0ZSkVnjTIiplmMDq2uq0tIB3j+xVu4rhv5rvOgBySCysudmbMd6xRRqw==} + + '@vue/compiler-ssr@3.5.42': + resolution: {integrity: sha512-xmLk3wLkbizPAiLyomjgFFosf2ys9b5Ghb+oh/k2tnvipNz8OFrQOiTcWCzyK7MpBp9KkyGtfvgfLUivbmuGYA==} + + '@vue/reactivity@3.5.42': + resolution: {integrity: sha512-TzNNfKpb7hDxbQltwAut8VDQA5YP+BuRlxntHUuRjyKwlMvmAPbs3unhCvieijifY6vFfVBwsS7wG/C7uq+bEQ==} + + '@vue/runtime-core@3.5.42': + resolution: {integrity: sha512-9uACtuHs7vJGkm5Bp3xu4xRDLFTIYy5DgxpToVjqGIAhAEKwQfsaLvKINhM6nFVp6bZPRFGdDqd1g52MqKsotA==} + + '@vue/runtime-dom@3.5.42': + resolution: {integrity: sha512-rsCmhiWLaRxGltLwhlCWyYkFn7WAbKRh0q17eZ1A6Dq6eqc2ACQ61IIryxz0LrsvCzHSilLA9JHovVwM8CNE2g==} + + '@vue/server-renderer@3.5.42': + resolution: {integrity: sha512-2++5dUyYS4gvo7xQXSECUDhB7TS0aOl5SeVfC5qSq1Jgfhjvegw1zqhwTIR3imZ+QYPJQw9gfcFvXGAjGZ7ajQ==} + + '@vue/shared@3.5.42': + resolution: {integrity: sha512-2rPxex1jQf4jvl9MOHl6YaXCPcrNqz/FstMOEh3QWY+/OME9nQTvl9WYeCwhW7AFjaR0SnngZGlp/wkR6rkI6g==} + + '@vueuse/core@10.11.1': + resolution: {integrity: sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==} + + '@vueuse/core@13.9.0': + resolution: {integrity: sha512-ts3regBQyURfCE2BcytLqzm8+MmLlo5Ln/KLoxDVcsZ2gzIwVNnQpQOL/UKV8alUqjSZOlpFZcRNsLRqj+OzyA==} + peerDependencies: + vue: ^3.5.0 + + '@vueuse/integrations@13.9.0': + resolution: {integrity: sha512-SDobKBbPIOe0cVL7QxMzGkuUGHvWTdihi9zOrrWaWUgFKe15cwEcwfWmgrcNzjT6kHnNmWuTajPHoIzUjYNYYQ==} + peerDependencies: + async-validator: ^4 + axios: ^1 + change-case: ^5 + drauu: ^0.4 + focus-trap: ^7 + fuse.js: ^7 + idb-keyval: ^6 + jwt-decode: ^4 + nprogress: ^0.2 + qrcode: ^1.5 + sortablejs: ^1 + universal-cookie: ^7 || ^8 + vue: ^3.5.0 + peerDependenciesMeta: + async-validator: + optional: true + axios: + optional: true + change-case: + optional: true + drauu: + optional: true + focus-trap: + optional: true + fuse.js: + optional: true + idb-keyval: + optional: true + jwt-decode: + optional: true + nprogress: + optional: true + qrcode: + optional: true + sortablejs: + optional: true + universal-cookie: + optional: true + + '@vueuse/metadata@10.11.1': + resolution: {integrity: sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw==} + + '@vueuse/metadata@13.9.0': + resolution: {integrity: sha512-1AFRvuiGphfF7yWixZa0KwjYH8ulyjDCC0aFgrGRz8+P4kvDFSdXLVfTk5xAN9wEuD1J6z4/myMoYbnHoX07zg==} + + '@vueuse/shared@10.11.1': + resolution: {integrity: sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==} + + '@vueuse/shared@13.9.0': + resolution: {integrity: sha512-e89uuTLMh0U5cZ9iDpEI2senqPGfbPRTHM/0AaQkcxnpqjkZqDYP8rpfm7edOz8s+pOCOROEy1PIveSW8+fL5g==} + peerDependencies: + vue: ^3.5.0 + '@yarnpkg/lockfile@1.1.0': resolution: {integrity: sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==} @@ -2825,6 +3271,12 @@ packages: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} + ai@6.0.33: + resolution: {integrity: sha512-bVokbmy2E2QF6Efl+5hOJx5MRWoacZ/CZY/y1E+VcewknvGlgaiCzMu8Xgddz6ArFJjiMFNUPHKxAhIePE4rmg==} + engines: {node: '>=18'} + peerDependencies: + zod: ^3.25.76 || ^4.1.8 + ajv-draft-04@1.0.0: resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} peerDependencies: @@ -2877,6 +3329,14 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + aria-hidden@1.2.6: + resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} + engines: {node: '>=10'} + + asn1js@3.0.10: + resolution: {integrity: sha512-S2s3aOytiKdFRdulw2qPE51MzjzVOisppcVv7jVFR+Kw0kxwvFrDcYA0h7Ndqbmj0HkMIXYWaoj7fli8kgx1eg==} + engines: {node: '>=12.0.0'} + assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -3029,6 +3489,10 @@ packages: resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} engines: {node: '>=18'} + bytestreamjs@2.0.1: + resolution: {integrity: sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==} + engines: {node: '>=6.0.0'} + cacheable@2.5.0: resolution: {integrity: sha512-60cyAOytib/OzBw1JNSoSV/boK1AtHryDIjvVBk7XbN4ugfkM3+Sry7fEjNgPMGgOjuaZPAp8ruZ0Cxafwyq9g==} @@ -3057,9 +3521,19 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + change-case@5.4.4: resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==} + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + character-entities@2.0.2: resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} @@ -3087,6 +3561,10 @@ packages: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + cluster-key-slot@1.1.2: resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} engines: {node: '>=0.10.0'} @@ -3107,6 +3585,9 @@ packages: colorette@1.4.0: resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==} + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + columnify@1.6.0: resolution: {integrity: sha512-lomjuFZKfM6MSAnV9aCZC9sc0qGbmZdfygNv+nCpqVkSKdCxCklLtd16O0EILGkImHw9ZpHkAnHaB+8Zxq5W6Q==} engines: {node: '>=8.0.0'} @@ -3122,10 +3603,18 @@ packages: resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} engines: {node: '>=16'} + content-disposition@2.0.1: + resolution: {integrity: sha512-e+H0ZXHSWYrENhQzw1LPuP4oF5MzVKmDU6d3hxlvaPEYLLg62MxtQNPRx4SYSuYJSBUgnQIG4HIN2tEtNv7Dog==} + engines: {node: '>=18'} + content-type@1.0.5: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} + convert-hrtime@5.0.0: + resolution: {integrity: sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==} + engines: {node: '>=12'} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -3133,6 +3622,10 @@ packages: resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} engines: {node: '>=18'} + cookie@2.0.1: + resolution: {integrity: sha512-yuToqVvRrj6pfDXREyQAAv8SkAEk/8GS3jQRTiUMm66TVtBYmqQeoEjL2Lmq8Rpo6271vH76InTChTitEAm65w==} + engines: {node: '>=22'} + core-js-compat@3.49.0: resolution: {integrity: sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==} @@ -3140,6 +3633,9 @@ packages: resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} engines: {node: '>=10'} + crelt@1.0.7: + resolution: {integrity: sha512-aK6BbWfhf4U/wCcLHKPJl/xa6VkVstRaPywWtMKGwuOLc/wZTyQYuoxgvZnNsBvv7Kg3YTBQYYBCggcviQczuA==} + cron-parser@4.9.0: resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==} engines: {node: '>=12.0.0'} @@ -3185,6 +3681,20 @@ packages: resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + cva@1.0.0-beta.4: + resolution: {integrity: sha512-F/JS9hScapq4DBVQXcK85l9U91M6ePeXoBMSp7vypzShoefUBxjQTo3g3935PUHgQd+IW77DjbPRIxugy4/GCQ==} + peerDependencies: + typescript: '>= 4.5.5' + peerDependenciesMeta: + typescript: + optional: true + + dateformat@4.6.3: + resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} + dayjs@1.11.23: resolution: {integrity: sha512-QDTCU0M0MxR3hQfnlDJfwekQiaanm1ubOD231u73WBckQ/fsamwRLiE2GBz6D3a/xF1NgfiDLJjXBa1hYOYTtQ==} @@ -3230,6 +3740,9 @@ packages: resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} engines: {node: '>=12'} + defu@6.1.7: + resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} + delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -3444,6 +3957,10 @@ packages: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} + environment@1.1.0: resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} engines: {node: '>=18'} @@ -3537,6 +4054,9 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} @@ -3555,6 +4075,10 @@ packages: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} + eventsource-parser@3.1.1: + resolution: {integrity: sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==} + engines: {node: '>=18.0.0'} + expect-type@1.3.0: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} @@ -3569,6 +4093,9 @@ packages: fast-content-type-parse@3.0.0: resolution: {integrity: sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==} + fast-copy@4.1.1: + resolution: {integrity: sha512-A4QTJmuiztpGtr6AMeJts9R4hbj2ZBUwtOaKrG6rw2y7t6+IaJKjz5M3XDs8BUznxDH43FVc6A0y/gWlMl4UtA==} + fast-decode-uri-component@1.0.1: resolution: {integrity: sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==} @@ -3587,6 +4114,9 @@ packages: fast-querystring@1.1.2: resolution: {integrity: sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==} + fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + fast-uri@3.1.0: resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} @@ -3612,6 +4142,9 @@ packages: fastify-plugin@6.0.0: resolution: {integrity: sha512-fZOty7z3O7vOliF6d8bHE3wiEh1KcNnKEQensSgTk9C1DvN6nRLS++XVd86v33Hw/8u9Un8A1zDrQ8ujcQDHEg==} + fastify@5.12.1: + resolution: {integrity: sha512-FWi+tQvwxR/PeRX7Z2mhfEF5ozJ3jn9asiiclzKXNSzJRHAYcU924aIOKAdHFJ+YIKieh3cqr1IwCOvTr41B3Q==} + fastify@5.12.4: resolution: {integrity: sha512-zATD676fP6tBMsn+jQ7Q5/cO/Qo+149TXH0dUqUKel+1/hcZxwIbnflqtPa9EjlrvpY8eCZcL3M1UYrMc31Y7g==} @@ -3661,6 +4194,9 @@ packages: flatted@3.4.2: resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + focus-trap@7.8.0: + resolution: {integrity: sha512-/yNdlIkpWbM0ptxno3ONTuf+2g318kh2ez3KSeZN5dZ8YC6AAmgeWz+GasYYiBJPFaYcSAPeu4GfhUaChzIJXA==} + follow-redirects@1.16.0: resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} engines: {node: '>=4.0'} @@ -3698,6 +4234,14 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + function-timeout@1.0.2: + resolution: {integrity: sha512-939eZS4gJ3htTHAldmyyuzlrD58P03fHG49v2JfFXbV6OhvZKRC9j2yAtdHw/zrp2zXHuv05zMIy40F0ge7spA==} + engines: {node: '>=18'} + + fuse.js@7.5.0: + resolution: {integrity: sha512-sQtrEfA+ez/3G0cCZecF70oqpCRttCexYUG4mUrtWL49ULUzUyxokt5kyqwtKzj1270RaKih+hcP3qLcumccow==} + engines: {node: '>=10'} + gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} @@ -3714,6 +4258,10 @@ packages: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} + get-own-enumerable-keys@1.0.0: + resolution: {integrity: sha512-PKsK2FSrQCyxcGHsGrLDcK0lx+0Ke+6e8KFFozA9/fIQLhQzPaRvJFdcz7+Axg3jUH/Mq+NI4xa5u/UT2tQskA==} + engines: {node: '>=14.16'} + get-proto@1.0.1: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} @@ -3731,6 +4279,10 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} + glob@13.0.6: + resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} + engines: {node: 18 || 20 || >=22} + globals@17.6.0: resolution: {integrity: sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA==} engines: {node: '>=18'} @@ -3743,6 +4295,10 @@ packages: resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==} engines: {node: '>=6.0'} + guess-json-indent@3.0.1: + resolution: {integrity: sha512-LWZ3Vr8BG7DHE3TzPYFqkhjNRw4vYgFSsv2nfMuHklAlOfiy54/EwiDQuQfFVLxENCVv20wpbjfTayooQHrEhQ==} + engines: {node: '>=18.18.0'} + harfbuzzjs@0.10.0: resolution: {integrity: sha512-SN8LVwCOzvTq3OPNd0+EAghgXugItz56wst567D1vs7LnnKGWprhi3EG58aVOBwhN8HEbQxL4I9NDWkcXUutRw==} @@ -3770,34 +4326,104 @@ packages: resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} engines: {node: '>= 0.4'} + hast-util-embedded@3.0.0: + resolution: {integrity: sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA==} + + hast-util-format@1.1.0: + resolution: {integrity: sha512-yY1UDz6bC9rDvCWHpx12aIBGRG7krurX0p0Fm6pT547LwDIZZiNr8a+IHDogorAdreULSEzP82Nlv5SZkHZcjA==} + hast-util-from-html@2.0.3: resolution: {integrity: sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==} hast-util-from-parse5@8.0.3: resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==} + hast-util-has-property@3.0.0: + resolution: {integrity: sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA==} + + hast-util-is-body-ok-link@3.0.1: + resolution: {integrity: sha512-0qpnzOBLztXHbHQenVB8uNuxTnm/QBFUOmdOSsEn7GnBtyY07+ENTWVFBAnXd/zEgd9/SUG3lRY7hSIBWRgGpQ==} + + hast-util-is-element@3.0.0: + resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==} + + hast-util-minify-whitespace@1.0.1: + resolution: {integrity: sha512-L96fPOVpnclQE0xzdWb/D12VT5FabA7SnZOUMtL1DbXmYiHJMXZvFkIZfiMmTCNJHUeO2K9UYNXoVyfz+QHuOw==} + hast-util-parse-selector@4.0.0: resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} + hast-util-phrasing@3.0.1: + resolution: {integrity: sha512-6h60VfI3uBQUxHqTyMymMZnEbNl1XmEGtOxxKYL7stY2o601COo62AWAYBQR9lZbYXYSBoxag8UpPRXK+9fqSQ==} + + hast-util-raw@9.1.0: + resolution: {integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==} + + hast-util-sanitize@5.0.2: + resolution: {integrity: sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg==} + + hast-util-to-html@9.0.5: + resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + + hast-util-to-parse5@8.0.1: + resolution: {integrity: sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==} + hast-util-to-string@3.0.1: resolution: {integrity: sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==} + hast-util-to-text@4.0.2: + resolution: {integrity: sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + hastscript@9.0.1: resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==} + helmet@8.3.0: + resolution: {integrity: sha512-Qgpiaws3Sm30Av8Eah6sjMCZZwjlBu+E68rhpCWBshY1lb09HtLwj5GviX0OyQIn+ulUS0iX0AxN5n3tLZzz1w==} + engines: {node: '>=18.0.0'} + + help-me@5.0.0: + resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} + hex-rgb@4.3.0: resolution: {integrity: sha512-Ox1pJVrDCyGHMG9CFg1tmrRUMRPRsAWYc/PinY0XzJU4K7y7vjNoLKIQ7BR5UJMCxNN8EM1MNDmHWA/B3aZUuw==} engines: {node: '>=6'} + highlight.js@11.11.2: + resolution: {integrity: sha512-oaXMACAU0kzOMXBjWpNcX+vlwSBCIAiZ9BHa7gA15NOTtT2L/l8OSZDuqS2XppOhZBPJ7hm4o8ep2kyuip2uEQ==} + engines: {node: '>=12.0.0'} + + highlight.js@11.12.0: + resolution: {integrity: sha512-nbfWpyRMcMrPMmDwJB+dhX/eiaPKtc2RB+0QZskqJ3WjRA/FDS0e9hZrx8EC/lbEv8gXy98FcDbNa/dspAaJMg==} + engines: {node: '>=12.0.0'} + + hookable@6.1.1: + resolution: {integrity: sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==} + hookified@1.15.1: resolution: {integrity: sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==} hookified@2.2.0: resolution: {integrity: sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA==} + hookified@3.0.3: + resolution: {integrity: sha512-ZcbRytYgERoKtosiaXolUIPIWMgUCrSiWQEzFZ0kc8etD/LPgpy4/KOrtssFdK7ZXlTTyb4cob2veqj7osv8yA==} + engines: {node: '>=22.18.0'} + html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + html-escaper@3.0.3: + resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==} + + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + + html-whitespace-sensitive-tag-names@3.0.1: + resolution: {integrity: sha512-q+310vW8zmymYHALr1da4HyXUQ0zgiIwIicEfotYPWGN0OJVEN/58IJ3A4GBYcEq3LGAZqKb+ugvP0GNB9CEAA==} + http-errors@2.0.1: resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} engines: {node: '>= 0.8'} @@ -3827,6 +4453,10 @@ packages: resolution: {integrity: sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==} engines: {node: '>=0.10.0'} + identifier-regex@1.1.0: + resolution: {integrity: sha512-SLX4H/vtcYlYnL7XqnuJKHU7Z8517TgsW9nmQiGOgMCjQ8V/deLYu6bEmbGoXe7WMMhc9+EUGyFFneHja8KabA==} + engines: {node: '>=18'} + ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -3869,6 +4499,10 @@ packages: resolution: {integrity: sha512-aq+t5NAc+cS6rZQQVWC2x98CPqGtKKTMDd4Gaodv0wShnItdKg/51djkGJ1hqH+Oy0ivDftCbSLCQob8zso01w==} engines: {node: '>= 10'} + is-absolute-url@4.0.1: + resolution: {integrity: sha512-/51/TKE88Lmm7Gc4/8btclNXWS+g50wXhYJq8HWIBAGUBnoAdRu1aXeh364t/O7wXDAcTJDP8PNuNKWUDWie+A==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} @@ -3906,6 +4540,10 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} + is-identifier@1.1.0: + resolution: {integrity: sha512-NhOds0mDx9lJu+1lBRO0xbwFo5nobA7GCk/0e5xjr6+6XugX985+0OyGX35BNrTkPAsdLcIKg02HUQJOK8D8kw==} + engines: {node: '>=18'} + is-inside-container@1.0.0: resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} engines: {node: '>=14.16'} @@ -3915,10 +4553,18 @@ packages: resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} engines: {node: '>=8'} + is-obj@3.0.0: + resolution: {integrity: sha512-IlsXEHOjtKhpN8r/tRFj2nDyTmHvcfNeu/nrRIcXE17ROeatXchkojffa1SpdqW4cr/Fj6QkEf/Gn4zf6KKvEQ==} + engines: {node: '>=12'} + is-plain-obj@4.1.0: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} + is-regexp@3.1.0: + resolution: {integrity: sha512-rbku49cWloU5bSMI+zaRaXdQHXnthP6DZ/vLnfdSKyL4zUzuWnomtOEiZZOd+ioQ+avFo/qau3KPTc7Fjy1uPA==} + engines: {node: '>=12'} + is-unicode-supported@0.1.0: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} engines: {node: '>=10'} @@ -3958,6 +4604,13 @@ packages: resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true + joycon@3.1.1: + resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} + engines: {node: '>=10'} + + js-base64@3.9.3: + resolution: {integrity: sha512-uwYQp+VJ38FVvtim6qNbit6e9uT6dwWQ4Y1+H9TxhW5hcHjpHwoxlR0nMpqUmIFOmu4VqMxwdJA88gIVuZJQ/g==} + js-levenshtein@1.1.6: resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==} engines: {node: '>=0.10.0'} @@ -4003,6 +4656,9 @@ packages: json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-schema@0.4.0: + resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} + json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -4206,6 +4862,9 @@ packages: resolution: {integrity: sha512-MHTGwSbifNjQIKkjAr9aOCDxg+E06wtdLBdSmjJSnFxl7u5rkQqTfx+FYK+Fv6sK2MKJFlBaTYN6WOLdm5JSGA==} engines: {node: '>= 18.0.0'} + lowlight@3.3.0: + resolution: {integrity: sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ==} + lru-cache@11.5.2: resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==} engines: {node: 20 || >=22} @@ -4223,6 +4882,10 @@ packages: magicast@0.5.3: resolution: {integrity: sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==} + make-asynchronous@1.1.0: + resolution: {integrity: sha512-ayF7iT+44LXdxJLTrTd3TLQpFDDvPCBxXxbv+pMUSuHA5Q8zyAfwkRP6aHHwNVFBUFWtxAHqwNJxF8vMZLAbVg==} + engines: {node: '>=18'} + make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} @@ -4280,6 +4943,9 @@ packages: resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} engines: {node: '>= 0.8'} + microdiff@1.6.0: + resolution: {integrity: sha512-w7JWt8Bno6I8h0rEqlxr4lNG4UbT1FVtWo42wWosIiaJ+rP3pXh7shCYDnyqBrYx36LJ1CZ64p8A62aVp0sJpQ==} + micromark-core-commonmark@2.0.3: resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} @@ -4380,6 +5046,11 @@ packages: resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} engines: {node: '>=18'} + mime@3.0.0: + resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} + engines: {node: '>=10.0.0'} + hasBin: true + mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -4403,6 +5074,10 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} @@ -4441,6 +5116,9 @@ packages: nested-error-stacks@2.1.1: resolution: {integrity: sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==} + neverpanic@0.0.8: + resolution: {integrity: sha512-vVdkelrLxaow/fdWDumzNBO+jwm6X8bxeLJc34THtpj70u0C5QBkcV6CRCu2X726km7XD45N0A3QtYCla4RvKw==} + node-abort-controller@3.1.1: resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} @@ -4550,6 +5228,10 @@ packages: oxc-resolver@11.19.1: resolution: {integrity: sha512-qE/CIg/spwrTBFt5aKmwe3ifeDdLfA2NESN30E42X/lII5ClF8V7Wt6WIJhcGZjp0/Q+nQ+9vgxGk//xZNX2hg==} + p-event@6.0.1: + resolution: {integrity: sha512-Q6Bekk5wpzW5qIyUP4gdMEujObYstZl6DMMOSenwBvV0BlE5LkDwkjs5yHbZmdCEq2o4RJx4tE1vwxFVf2FG1w==} + engines: {node: '>=16.17'} + p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -4558,6 +5240,10 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} + p-timeout@6.1.4: + resolution: {integrity: sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==} + engines: {node: '>=14.16'} + package-manager-detector@1.6.0: resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==} @@ -4582,6 +5268,10 @@ packages: resolution: {integrity: sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==} engines: {node: '>=18'} + parse-ms@4.0.0: + resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} + engines: {node: '>=18'} + parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} @@ -4600,6 +5290,10 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-scurry@2.0.2: + resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} + engines: {node: 18 || 20 || >=22} + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -4655,6 +5349,10 @@ packages: pino-abstract-transport@3.0.0: resolution: {integrity: sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==} + pino-pretty@13.1.3: + resolution: {integrity: sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==} + hasBin: true + pino-std-serializers@7.1.0: resolution: {integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==} @@ -4662,6 +5360,10 @@ packages: resolution: {integrity: sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==} hasBin: true + pkijs@3.4.0: + resolution: {integrity: sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw==} + engines: {node: '>=16.0.0'} + pluralize@8.0.0: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} @@ -4698,6 +5400,10 @@ packages: engines: {node: '>=14'} hasBin: true + pretty-ms@9.3.1: + resolution: {integrity: sha512-HzMy3Geq23nVALD/M2LliU+F+M+gVNsvkQWWqeBZ8HDiCgzo6YPJ/Omrmtq24EFrIsk0a3EkQGEd7bDOo+IhGA==} + engines: {node: '>=18'} + process-warning@4.0.1: resolution: {integrity: sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==} @@ -4720,10 +5426,20 @@ packages: engines: {node: '>=18'} hasBin: true + pump@3.0.4: + resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + pvtsutils@1.3.6: + resolution: {integrity: sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==} + + pvutils@1.2.0: + resolution: {integrity: sha512-BbubeCEyTuQjVMakvJQ/Sxbc93F2pwmbsxONT/ZRrwU7Ua38d8unYTwXpTVLAKJ4BDuH9IGztCjQcd/N/39Dvg==} + engines: {node: '>=16.0.0'} + qified@0.10.1: resolution: {integrity: sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA==} engines: {node: '>=20'} @@ -4731,6 +5447,11 @@ packages: quick-format-unescaped@4.0.4: resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} + radix-vue@1.9.17: + resolution: {integrity: sha512-mVCu7I2vXt1L2IUYHTt0sZMz7s1K2ZtqKeTIxG3yC5mMFfLBG4FtE1FDeRMpDd+Hhg/ybi9+iXmAP1ISREndoQ==} + peerDependencies: + vue: '>= 3.2.0' + readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} @@ -4754,6 +5475,9 @@ packages: resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} engines: {node: '>=4'} + reflect-metadata@0.2.2: + resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} + regenerate-unicode-properties@10.2.2: resolution: {integrity: sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==} engines: {node: '>=4'} @@ -4772,6 +5496,24 @@ packages: resolution: {integrity: sha512-dLsljMd9sqwRkby8zhO1gSg3PnJIBFid8f4CQj/sXx+7cKx+E7u0PKhZ+U4wmhx7EfmtvnA318oVaIkAB1lRJw==} hasBin: true + rehype-external-links@3.0.0: + resolution: {integrity: sha512-yp+e5N9V3C6bwBeAC4n796kc86M4gJCdlVhiMTxIrJG5UHDMh+PJANf9heqORJbt1nrCbDwIlAZKjANIaVBbvw==} + + rehype-format@5.0.1: + resolution: {integrity: sha512-zvmVru9uB0josBVpr946OR8ui7nJEdzZobwLOOqHb/OOD88W0Vk2SqLwoVOj0fM6IPCCO6TaV9CvQvJMWwukFQ==} + + rehype-parse@9.0.1: + resolution: {integrity: sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==} + + rehype-raw@7.0.0: + resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} + + rehype-sanitize@6.0.0: + resolution: {integrity: sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg==} + + rehype-stringify@10.0.1: + resolution: {integrity: sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==} + remark-gfm@4.0.1: resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} @@ -4792,6 +5534,10 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} + reserved-identifiers@1.2.0: + resolution: {integrity: sha512-yE7KUfFvaBFzGPs5H3Ops1RevfUEsDc5Iz65rOwWg4lE8HJSYtle77uul3+573457oHvBKuHYDl/xqUkKpEEdw==} + engines: {node: '>=18'} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -4873,6 +5619,10 @@ packages: secure-json-parse@4.1.0: resolution: {integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==} + selfsigned@5.5.0: + resolution: {integrity: sha512-ftnu3TW4+3eBfLRFnDEkzGxSF/10BJBkaLJuBHZX0kiPS7bRdlpZGu6YGt4KngMkdTwJE6MbjavFpqHvqVt+Ew==} + engines: {node: '>=18'} + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -4900,6 +5650,9 @@ packages: set-cookie-parser@2.7.2: resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + set-cookie-parser@3.1.0: + resolution: {integrity: sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==} + setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} @@ -5033,6 +5786,14 @@ packages: resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} engines: {node: '>=0.6.19'} + string-byte-length@3.0.1: + resolution: {integrity: sha512-yJ8vP0HMwZ54CcA8S8mKoXbkezpZHANFtmafFo8lGxZThCQcAwRHjdFabuSLgOzxj9OFJcmssmiAvmcOK4O2Hw==} + engines: {node: '>=18.18.0'} + + string-byte-slice@3.0.1: + resolution: {integrity: sha512-GWv2K4lYyd2+AhmKH3BV+OVx62xDX+99rSLfKpaqFiQU7uOMaUY1tDjdrRD4gsrCr9lTyjMgjna7tZcCOw+Smg==} + engines: {node: '>=18.18.0'} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -5051,6 +5812,13 @@ packages: string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + + stringify-object@6.0.0: + resolution: {integrity: sha512-6f94vIED6vmJJfh3lyVsVWxCYSfI5uM+16ntED/Ql37XIyV6kj0mRAAiTeMMc/QLYIaizC3bUprQ8pQnDDrKfA==} + engines: {node: '>=20'} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -5074,6 +5842,13 @@ packages: strnum@2.3.0: resolution: {integrity: sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q==} + style-mod@4.1.3: + resolution: {integrity: sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==} + + super-regex@1.1.0: + resolution: {integrity: sha512-WHkws2ZflZe41zj6AolvvmaTrWds/VuyeYr9iPVv/oQeaIoVxMKaushfFWpOGDT+GuBrM/sVqF8KUCYQlSSTdQ==} + engines: {node: '>=18'} + supports-color@10.2.2: resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} engines: {node: '>=18'} @@ -5091,10 +5866,24 @@ packages: engines: {node: '>=16'} hasBin: true + swrv@1.2.0: + resolution: {integrity: sha512-lH/g4UcNyj+7lzK4eRGT4C68Q4EhQ6JtM9otPRIASfhhzfLWtbZPHcMuhuba7S9YVYuxkMUGImwMyGpfbkH07A==} + peerDependencies: + vue: '>=3.2.26 < 4' + + tabbable@6.5.0: + resolution: {integrity: sha512-wieBHXygIm7OyQOu5hQlkk62/WyCFYGlWg7L6/ZCUZwx0o398Zkn4pVmMyfYhfMG8kGrj/Krt8eIk6UKC6VzwA==} + tagged-tag@1.0.0: resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} engines: {node: '>=20'} + tailwind-merge@3.5.0: + resolution: {integrity: sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==} + + tailwindcss@4.3.3: + resolution: {integrity: sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==} + tar-stream@2.2.0: resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} engines: {node: '>=6'} @@ -5115,6 +5904,10 @@ packages: resolution: {integrity: sha512-e2zZ96wSChazBsbENf/Pcm/4swHt2cEKQ92rhUjkL9GCKiTDJIaTBenjE/m9DXi0QBmTMDkFDdOomUy20A1tDQ==} engines: {node: '>=20'} + time-span@5.1.0: + resolution: {integrity: sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==} + engines: {node: '>=12'} + tiny-inflate@1.0.3: resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==} @@ -5163,6 +5956,10 @@ packages: trough@2.2.0: resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + truncate-json@3.0.1: + resolution: {integrity: sha512-QVsbr1WhGLq2F0oDyYbqtOXcf3gcnL8C9H5EX8bBwAr8ZWvWGJzukpPrDrWgJMrNtgDbo74BIjI4kJu3q2xQWw==} + engines: {node: '>=18.18.0'} + ts-api-utils@2.5.0: resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} engines: {node: '>=18.12'} @@ -5173,9 +5970,16 @@ packages: resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} engines: {node: '>=6'} + tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tsyringe@4.10.0: + resolution: {integrity: sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==} + engines: {node: '>= 6.0.0'} + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -5188,6 +5992,10 @@ packages: resolution: {integrity: sha512-8ZiHFm91orbSAe2PSAiSVBVko18pbhbiB3U9GglSzF/zCGkR+rxpHx6sEMCUm4kxY4LjDIUGgCfUMtwfZfjfUA==} engines: {node: '>=20'} + type-fest@5.9.0: + resolution: {integrity: sha512-yANm3Jr3GiJ1qgJlxGAVxTOIcEOk1rhQHamlXtnrCK7EHP4HeM9OGxtMg/W7HFdrVzw/ZWJKGVIJusVH85sLtw==} + engines: {node: '>=20'} + type-is@2.0.1: resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} engines: {node: '>= 0.6'} @@ -5195,6 +6003,9 @@ packages: typebox@1.1.38: resolution: {integrity: sha512-pZ0aQPmMmXoUvSbeuWf/Hzsc+avNw/Zd6VeE8CFgkVGWyuHPJvqeJJDeJqLve+K70LvjYIoleGcoJHPT17cWoA==} + typebox@1.3.34: + resolution: {integrity: sha512-wbnzrXXDW8xEFHDZZs2jo1MkhaYlKAY4FRhpBc1+2LF1fZVBGCXGdLEhA/Z/NBbgzJMFfeM8m7elKPa/+KxaUQ==} + typescript-eslint@8.59.4: resolution: {integrity: sha512-Rw6+44QNFaXtgHSjPy+Kw8hrJniMYzR85E9yLmOLcfZ91/rz+JXQbDTCmc6ccxMPY6K6PgAq26f0JCBfR7LIPQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -5211,6 +6022,9 @@ packages: resolution: {integrity: sha512-FeFPZ/WFT0mbRCuydiZzpPFlrYN8ZUpphQKoq4EeElVIYjYyGzPMxQR/simUwCOJIyVhpFk4RbtyO7RuMpMnHA==} engines: {node: '>=14'} + undici-types@7.18.2: + resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + undici-types@8.9.0: resolution: {integrity: sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==} @@ -5218,6 +6032,9 @@ packages: resolution: {integrity: sha512-/y4/bH9YNU5hi9NIrpOuvGXFcxrj3CMrV+/AYpowAYTpHn8gX/XPFjNy766FPoYY0miQhdW977JFWKGNhBdwyQ==} engines: {node: '>=22.19.0'} + unhead@2.1.17: + resolution: {integrity: sha512-HLMKXOszRhAPBrr6VlqCeVeJq2kbC4kXwzGLEZvvojPLWNYTJw22xG7Bfwhsvs31+IBet3Wl8ADg9dwYdyphfQ==} + unicode-canonical-property-names-ecmascript@2.0.1: resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} engines: {node: '>=4'} @@ -5240,6 +6057,9 @@ packages: unified@11.0.5: resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + unist-util-find-after@5.0.0: + resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==} + unist-util-find@3.0.0: resolution: {integrity: sha512-T7ZqS7immLjYyC4FCp2hDo3ksZ1v+qcbb+e5+iWxc2jONgHOLXPCpms1L8VV4hVxCXgWTxmBHDztuEZFVwC+Gg==} @@ -5373,6 +6193,34 @@ packages: jsdom: optional: true + vue-component-type-helpers@3.3.11: + resolution: {integrity: sha512-LwcxzeliO9fkQcpJG0PoX8X5kmAhKmH9wkpDLxNabwzkQ9Zeib2YVHwFV4pcWmMLfXVfjr/dSV+DaJ3cIPgSNA==} + + vue-demi@0.14.10: + resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==} + engines: {node: '>=12'} + hasBin: true + peerDependencies: + '@vue/composition-api': ^1.0.0-rc.1 + vue: ^3.0.0-0 || ^2.6.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + + vue-sonner@1.3.2: + resolution: {integrity: sha512-UbZ48E9VIya3ToiRHAZUbodKute/z/M1iT8/3fU8zEbwBRE11AKuHikssv18LMk2gTTr6eMQT4qf6JoLHWuj/A==} + + vue@3.5.42: + resolution: {integrity: sha512-4RyHQTbQvOPs3MfvUO1Sg0YRrKNnA0mAVtvpd12Tg1fKDN7OHBUl1IqSn8zGJjK9nI3NkNp8cgTpVrSZC5TTcA==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + w3c-keyname@2.2.8: + resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} + walk-up-path@4.0.0: resolution: {integrity: sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==} engines: {node: 20 || >=22} @@ -5383,6 +6231,9 @@ packages: web-namespaces@2.0.1: resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} + web-worker@1.5.0: + resolution: {integrity: sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw==} + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -5484,6 +6335,33 @@ packages: snapshots: + '@ai-sdk/gateway@3.0.13(zod@4.4.3)': + dependencies: + '@ai-sdk/provider': 3.0.2 + '@ai-sdk/provider-utils': 4.0.5(zod@4.4.3) + '@vercel/oidc': 3.1.0 + zod: 4.4.3 + + '@ai-sdk/provider-utils@4.0.5(zod@4.4.3)': + dependencies: + '@ai-sdk/provider': 3.0.2 + '@standard-schema/spec': 1.1.0 + eventsource-parser: 3.1.1 + zod: 4.4.3 + + '@ai-sdk/provider@3.0.2': + dependencies: + json-schema: 0.4.0 + + '@ai-sdk/vue@3.0.33(vue@3.5.42(typescript@6.0.3))(zod@4.4.3)': + dependencies: + '@ai-sdk/provider-utils': 4.0.5(zod@4.4.3) + ai: 6.0.33(zod@4.4.3) + swrv: 1.2.0(vue@3.5.42(typescript@6.0.3)) + vue: 3.5.42(typescript@6.0.3) + transitivePeerDependencies: + - zod + '@aws-crypto/crc32@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 @@ -6036,8 +6914,12 @@ snapshots: '@babel/helper-string-parser@7.27.1': {} + '@babel/helper-string-parser@7.29.7': {} + '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-validator-identifier@7.29.7': {} + '@babel/helper-validator-option@7.27.1': {} '@babel/helper-wrap-function@7.28.6(supports-color@7.2.0)': @@ -6057,6 +6939,10 @@ snapshots: dependencies: '@babel/types': 7.29.0 + '@babel/parser@7.29.8': + dependencies: + '@babel/types': 7.29.8 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@7.29.0(supports-color@7.2.0))(supports-color@7.2.0)': dependencies: '@babel/core': 7.29.0(supports-color@7.2.0) @@ -6620,6 +7506,11 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@babel/types@7.29.8': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@bcoe/v8-coverage@1.0.2': {} '@cacheable/memory@2.2.0': @@ -6634,6 +7525,100 @@ snapshots: hashery: 1.5.1 keyv: 5.6.0 + '@codemirror/autocomplete@6.20.3': + dependencies: + '@codemirror/language': 6.12.4 + '@codemirror/state': 6.7.4 + '@codemirror/view': 6.43.11 + '@lezer/common': 1.5.2 + + '@codemirror/commands@6.11.0': + dependencies: + '@codemirror/language': 6.12.4 + '@codemirror/state': 6.7.4 + '@codemirror/view': 6.43.11 + '@lezer/common': 1.5.2 + + '@codemirror/lang-css@6.3.1': + dependencies: + '@codemirror/autocomplete': 6.20.3 + '@codemirror/language': 6.12.4 + '@codemirror/state': 6.7.4 + '@lezer/common': 1.5.2 + '@lezer/css': 1.3.8 + + '@codemirror/lang-html@6.4.12': + dependencies: + '@codemirror/autocomplete': 6.20.3 + '@codemirror/lang-css': 6.3.1 + '@codemirror/lang-javascript': 6.2.5 + '@codemirror/language': 6.12.4 + '@codemirror/state': 6.7.4 + '@codemirror/view': 6.43.11 + '@lezer/common': 1.5.2 + '@lezer/css': 1.3.8 + '@lezer/html': 1.3.13 + + '@codemirror/lang-javascript@6.2.5': + dependencies: + '@codemirror/autocomplete': 6.20.3 + '@codemirror/language': 6.12.4 + '@codemirror/lint': 6.9.7 + '@codemirror/state': 6.7.4 + '@codemirror/view': 6.43.11 + '@lezer/common': 1.5.2 + '@lezer/javascript': 1.5.6 + + '@codemirror/lang-json@6.0.2': + dependencies: + '@codemirror/language': 6.12.4 + '@lezer/json': 1.0.3 + + '@codemirror/lang-xml@6.1.0': + dependencies: + '@codemirror/autocomplete': 6.20.3 + '@codemirror/language': 6.12.4 + '@codemirror/state': 6.7.4 + '@codemirror/view': 6.43.11 + '@lezer/common': 1.5.2 + '@lezer/xml': 1.0.6 + + '@codemirror/lang-yaml@6.1.3': + dependencies: + '@codemirror/autocomplete': 6.20.3 + '@codemirror/language': 6.12.4 + '@codemirror/state': 6.7.4 + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + '@lezer/yaml': 1.0.4 + + '@codemirror/language@6.12.4': + dependencies: + '@codemirror/state': 6.7.4 + '@codemirror/view': 6.43.11 + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + style-mod: 4.1.3 + + '@codemirror/lint@6.9.7': + dependencies: + '@codemirror/state': 6.7.4 + '@codemirror/view': 6.43.11 + crelt: 1.0.7 + + '@codemirror/state@6.7.4': + dependencies: + '@marijn/find-cluster-break': 1.0.4 + + '@codemirror/view@6.43.11': + dependencies: + '@codemirror/state': 6.7.4 + crelt: 1.0.7 + style-mod: 4.1.3 + w3c-keyname: 2.2.8 + '@drizzle-team/brocli@0.11.0': {} '@emnapi/core@1.10.0': @@ -6779,12 +7764,19 @@ snapshots: '@eslint/core': 1.2.1 levn: 0.4.1 + '@fastify/accept-negotiator@2.1.0': {} + '@fastify/ajv-compiler@4.0.6': dependencies: ajv: 8.20.0 ajv-formats: 3.0.1(ajv@8.20.0) fast-uri: 4.1.3 + '@fastify/cookie@11.1.2': + dependencies: + cookie: 2.0.1 + fastify-plugin: 6.0.0 + '@fastify/error@4.2.0': {} '@fastify/fast-json-stringify-compiler@5.1.0': @@ -6793,6 +7785,11 @@ snapshots: '@fastify/forwarded@3.0.2': {} + '@fastify/helmet@13.1.1': + dependencies: + fastify-plugin: 6.0.0 + helmet: 8.3.0 + '@fastify/merge-json-schemas@0.2.1': dependencies: dequal: 2.0.3 @@ -6809,6 +7806,14 @@ snapshots: ip-address: 10.7.2 toad-cache: 3.7.4 + '@fastify/send@4.1.1': + dependencies: + '@lukeed/ms': 2.0.2 + escape-html: 1.0.3 + fast-decode-uri-component: 1.0.1 + http-errors: 2.0.1 + mime: 3.0.0 + '@fastify/sensible@6.0.5': dependencies: '@lukeed/ms': 2.0.2 @@ -6819,6 +7824,24 @@ snapshots: type-is: 2.0.1 vary: 1.1.2 + '@fastify/static@10.1.3': + dependencies: + '@fastify/accept-negotiator': 2.1.0 + '@fastify/error': 4.2.0 + '@fastify/send': 4.1.1 + content-disposition: 2.0.1 + fastify-plugin: 6.0.0 + fastq: 1.20.3 + glob: 13.0.6 + + '@fastify/swagger-ui@6.1.1': + dependencies: + '@fastify/static': 10.1.3 + fastify-plugin: 6.0.0 + openapi-types: 12.1.3 + rfdc: 1.4.1 + yaml: 2.9.0 + '@fastify/swagger@9.7.0(supports-color@10.2.2)': dependencies: fastify-plugin: 5.1.0 @@ -6829,6 +7852,56 @@ snapshots: transitivePeerDependencies: - supports-color + '@fastify/swagger@9.8.1(supports-color@10.2.2)': + dependencies: + fastify-plugin: 6.0.0 + json-schema-resolver: 3.0.0(supports-color@10.2.2) + openapi-types: 12.1.3 + rfdc: 1.4.1 + yaml: 2.9.0 + transitivePeerDependencies: + - supports-color + + '@floating-ui/core@1.8.0': + dependencies: + '@floating-ui/utils': 0.2.12 + + '@floating-ui/dom@1.8.0': + dependencies: + '@floating-ui/core': 1.8.0 + '@floating-ui/utils': 0.2.12 + + '@floating-ui/utils@0.2.10': {} + + '@floating-ui/utils@0.2.12': {} + + '@floating-ui/vue@1.1.11(vue@3.5.42(typescript@6.0.3))': + dependencies: + '@floating-ui/dom': 1.8.0 + '@floating-ui/utils': 0.2.12 + vue-demi: 0.14.10(vue@3.5.42(typescript@6.0.3)) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + + '@floating-ui/vue@1.1.9(vue@3.5.42(typescript@6.0.3))': + dependencies: + '@floating-ui/dom': 1.8.0 + '@floating-ui/utils': 0.2.10 + vue-demi: 0.14.10(vue@3.5.42(typescript@6.0.3)) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + + '@headlessui/tailwindcss@0.2.2(tailwindcss@4.3.3)': + dependencies: + tailwindcss: 4.3.3 + + '@headlessui/vue@1.7.23(vue@3.5.42(typescript@6.0.3))': + dependencies: + '@tanstack/vue-virtual': 3.13.38(vue@3.5.42(typescript@6.0.3)) + vue: 3.5.42(typescript@6.0.3) + '@humanfs/core@0.19.2': dependencies: '@humanfs/types': 0.15.0 @@ -6951,8 +8024,48 @@ snapshots: '@img/sharp-win32-x64@0.35.4': optional: true + '@internationalized/date@3.12.4': + dependencies: + '@swc/helpers': 0.5.23 + + '@internationalized/number@3.6.8': + dependencies: + '@swc/helpers': 0.5.23 + '@ioredis/commands@1.5.1': {} + '@jaredwray/mockhttp@1.9.0(axios@1.18.1(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))(change-case@5.4.4)(supports-color@10.2.2)(tailwindcss@4.3.3)(typescript@6.0.3)(zod@4.4.3)': + dependencies: + '@fastify/cookie': 11.1.2 + '@fastify/helmet': 13.1.1 + '@fastify/rate-limit': 11.2.0 + '@fastify/static': 10.1.3 + '@fastify/swagger': 9.8.1(supports-color@10.2.2) + '@fastify/swagger-ui': 6.1.1 + '@scalar/api-reference': 1.68.0(axios@1.18.1(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))(change-case@5.4.4)(supports-color@10.2.2)(tailwindcss@4.3.3)(typescript@6.0.3)(zod@4.4.3) + detect-port: 2.1.0 + fastify: 5.12.1 + hookified: 3.0.3 + html-escaper: 3.0.3 + pino: 10.3.1 + pino-pretty: 13.1.3 + transitivePeerDependencies: + - '@vue/composition-api' + - async-validator + - axios + - change-case + - drauu + - idb-keyval + - jwt-decode + - nprogress + - qrcode + - sortablejs + - supports-color + - tailwindcss + - typescript + - universal-cookie + - zod + '@jest/diff-sequences@30.0.1': {} '@jridgewell/gen-mapping@0.3.13': @@ -6992,8 +8105,56 @@ snapshots: '@keyv/serialize@1.1.1': {} + '@lezer/common@1.5.2': {} + + '@lezer/css@1.3.8': + dependencies: + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + + '@lezer/highlight@1.2.3': + dependencies: + '@lezer/common': 1.5.2 + + '@lezer/html@1.3.13': + dependencies: + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + + '@lezer/javascript@1.5.6': + dependencies: + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + + '@lezer/json@1.0.3': + dependencies: + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + + '@lezer/lr@1.4.10': + dependencies: + '@lezer/common': 1.5.2 + + '@lezer/xml@1.0.6': + dependencies: + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + + '@lezer/yaml@1.0.4': + dependencies: + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.10 + '@lukeed/ms@2.0.2': {} + '@marijn/find-cluster-break@1.0.4': {} + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': optional: true @@ -7032,6 +8193,8 @@ snapshots: '@tybys/wasm-util': 0.10.3 optional: true + '@noble/hashes@1.4.0': {} + '@nodable/entities@2.1.0': {} '@nx/devkit@22.7.2(nx@22.7.2(debug@4.4.3(supports-color@7.2.0)))': @@ -7152,31 +8315,6 @@ snapshots: '@nx/nx-win32-x64-msvc@23.1.2': optional: true - '@nx/vite@22.7.2(@babel/traverse@7.29.0(supports-color@7.2.0))(debug@4.4.3(supports-color@7.2.0))(nx@23.1.2)(supports-color@7.2.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@4.1.7)': - dependencies: - '@nx/devkit': 22.7.2(nx@23.1.2) - '@nx/js': 22.7.2(@babel/traverse@7.29.0(supports-color@7.2.0))(debug@4.4.3(supports-color@7.2.0))(nx@23.1.2)(supports-color@7.2.0) - '@nx/vitest': 22.7.2(@babel/traverse@7.29.0(supports-color@7.2.0))(debug@4.4.3(supports-color@7.2.0))(nx@23.1.2)(supports-color@7.2.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@4.1.7) - '@phenomnomnominal/tsquery': 6.2.0(typescript@6.0.3) - ajv: 8.20.0 - enquirer: 2.3.6 - picomatch: 4.0.4 - semver: 7.8.0 - tsconfig-paths: 4.2.0 - tslib: 2.8.1 - vite: 8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0) - vitest: 4.1.7(@types/node@26.5.1)(@vitest/coverage-v8@4.1.7)(vite@8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0)) - transitivePeerDependencies: - - '@babel/traverse' - - '@nx/eslint' - - '@swc-node/register' - - '@swc/core' - - debug - - nx - - supports-color - - typescript - - verdaccio - '@nx/vitest@22.7.2(@babel/traverse@7.29.0(supports-color@7.2.0))(debug@4.4.3(supports-color@7.2.0))(nx@23.1.2)(supports-color@7.2.0)(typescript@6.0.3)(vite@8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0))(vitest@4.1.7)': dependencies: '@nx/devkit': 22.7.2(nx@23.1.2) @@ -7186,7 +8324,7 @@ snapshots: tslib: 2.8.1 optionalDependencies: vite: 8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0) - vitest: 4.1.7(@types/node@26.5.1)(@vitest/coverage-v8@4.1.7)(vite@8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.0)(@types/node@26.5.1)(@vitest/coverage-v8@4.1.7)(vite@8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0)) transitivePeerDependencies: - '@babel/traverse' - '@swc-node/register' @@ -7361,6 +8499,8 @@ snapshots: '@octokit/request-error': 7.1.0 '@octokit/webhooks-methods': 6.0.0 + '@opentelemetry/api@1.9.0': {} + '@oxc-parser/binding-android-arm-eabi@0.130.0': optional: true @@ -7494,13 +8634,109 @@ snapshots: '@oxc-resolver/binding-win32-x64-msvc@11.19.1': optional: true - '@phenomnomnominal/tsquery@6.2.0(typescript@6.0.3)': + '@peculiar/asn1-cms@2.9.4': dependencies: - '@types/esquery': 1.5.4 - esquery: 1.7.0 - typescript: 6.0.3 - - '@pinojs/redact@0.4.0': {} + '@peculiar/asn1-schema': 2.9.4 + '@peculiar/asn1-x509': 2.9.4 + '@peculiar/asn1-x509-attr': 2.9.4 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-csr@2.9.4': + dependencies: + '@peculiar/asn1-schema': 2.9.4 + '@peculiar/asn1-x509': 2.9.4 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-ecc@2.9.4': + dependencies: + '@peculiar/asn1-schema': 2.9.4 + '@peculiar/asn1-x509': 2.9.4 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-pfx@2.9.4': + dependencies: + '@peculiar/asn1-cms': 2.9.4 + '@peculiar/asn1-pkcs8': 2.9.4 + '@peculiar/asn1-rsa': 2.9.4 + '@peculiar/asn1-schema': 2.9.4 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-pkcs8@2.9.4': + dependencies: + '@peculiar/asn1-schema': 2.9.4 + '@peculiar/asn1-x509': 2.9.4 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-pkcs9@2.9.4': + dependencies: + '@peculiar/asn1-cms': 2.9.4 + '@peculiar/asn1-pfx': 2.9.4 + '@peculiar/asn1-pkcs8': 2.9.4 + '@peculiar/asn1-schema': 2.9.4 + '@peculiar/asn1-x509': 2.9.4 + '@peculiar/asn1-x509-attr': 2.9.4 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-rsa@2.9.4': + dependencies: + '@peculiar/asn1-schema': 2.9.4 + '@peculiar/asn1-x509': 2.9.4 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-schema@2.9.4': + dependencies: + '@peculiar/utils': 2.0.3 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-x509-attr@2.9.4': + dependencies: + '@peculiar/asn1-schema': 2.9.4 + '@peculiar/asn1-x509': 2.9.4 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-x509@2.9.4': + dependencies: + '@peculiar/asn1-schema': 2.9.4 + '@peculiar/utils': 2.0.3 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/utils@2.0.3': + dependencies: + tslib: 2.8.1 + + '@peculiar/x509@1.14.3': + dependencies: + '@peculiar/asn1-cms': 2.9.4 + '@peculiar/asn1-csr': 2.9.4 + '@peculiar/asn1-ecc': 2.9.4 + '@peculiar/asn1-pkcs9': 2.9.4 + '@peculiar/asn1-rsa': 2.9.4 + '@peculiar/asn1-schema': 2.9.4 + '@peculiar/asn1-x509': 2.9.4 + pvtsutils: 1.3.6 + reflect-metadata: 0.2.2 + tslib: 2.8.1 + tsyringe: 4.10.0 + + '@phenomnomnominal/tsquery@6.2.0(typescript@6.0.3)': + dependencies: + '@types/esquery': 1.5.4 + esquery: 1.7.0 + typescript: 6.0.3 + + '@phosphor-icons/core@2.1.1': {} + + '@pinojs/redact@0.4.0': {} '@publint/pack@0.1.4': {} @@ -7527,6 +8763,12 @@ snapshots: transitivePeerDependencies: - supports-color + '@replit/codemirror-css-color-picker@6.3.0(@codemirror/language@6.12.4)(@codemirror/state@6.7.4)(@codemirror/view@6.43.11)': + dependencies: + '@codemirror/language': 6.12.4 + '@codemirror/state': 6.7.4 + '@codemirror/view': 6.43.11 + '@resvg/resvg-js-android-arm-eabi@2.6.2': optional: true @@ -7625,12 +8867,206 @@ snapshots: '@rolldown/pluginutils@1.0.1': {} + '@scalar/agent-chat@0.12.30(axios@1.18.1(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))(change-case@5.4.4)(supports-color@10.2.2)(tailwindcss@4.3.3)(typescript@6.0.3)(zod@4.4.3)': + dependencies: + '@ai-sdk/vue': 3.0.33(vue@3.5.42(typescript@6.0.3))(zod@4.4.3) + '@scalar/api-client': 3.18.0(axios@1.18.1(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))(change-case@5.4.4)(supports-color@10.2.2)(tailwindcss@4.3.3)(typescript@6.0.3) + '@scalar/components': 0.29.1(supports-color@10.2.2)(tailwindcss@4.3.3)(typescript@6.0.3) + '@scalar/helpers': 0.11.3 + '@scalar/icons': 0.7.6(typescript@6.0.3) + '@scalar/json-magic': 0.13.4 + '@scalar/openapi-types': 0.9.5 + '@scalar/schemas': 0.9.0 + '@scalar/themes': 0.17.4 + '@scalar/types': 0.19.0 + '@scalar/use-toasts': 0.10.5(typescript@6.0.3) + '@scalar/validation': 0.6.3 + '@scalar/workspace-store': 0.60.0(typescript@6.0.3) + '@vueuse/core': 13.9.0(vue@3.5.42(typescript@6.0.3)) + ai: 6.0.33(zod@4.4.3) + js-base64: 3.9.3 + neverpanic: 0.0.8 + truncate-json: 3.0.1 + vue: 3.5.42(typescript@6.0.3) + transitivePeerDependencies: + - '@vue/composition-api' + - async-validator + - axios + - change-case + - drauu + - idb-keyval + - jwt-decode + - nprogress + - qrcode + - sortablejs + - supports-color + - tailwindcss + - typescript + - universal-cookie + - zod + + '@scalar/api-client@3.18.0(axios@1.18.1(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))(change-case@5.4.4)(supports-color@10.2.2)(tailwindcss@4.3.3)(typescript@6.0.3)': + dependencies: + '@headlessui/tailwindcss': 0.2.2(tailwindcss@4.3.3) + '@headlessui/vue': 1.7.23(vue@3.5.42(typescript@6.0.3)) + '@scalar/blocks': 0.1.16(supports-color@10.2.2)(tailwindcss@4.3.3)(typescript@6.0.3) + '@scalar/components': 0.29.1(supports-color@10.2.2)(tailwindcss@4.3.3)(typescript@6.0.3) + '@scalar/helpers': 0.11.3 + '@scalar/icons': 0.7.6(typescript@6.0.3) + '@scalar/oas-utils': 0.19.16(typescript@6.0.3) + '@scalar/openapi-types': 0.9.5 + '@scalar/sidebar': 0.11.1(supports-color@10.2.2)(tailwindcss@4.3.3)(typescript@6.0.3) + '@scalar/snippetz': 0.9.30 + '@scalar/themes': 0.17.4 + '@scalar/typebox': 0.1.3 + '@scalar/types': 0.19.0 + '@scalar/use-codemirror': 0.14.15(typescript@6.0.3) + '@scalar/use-hooks': 0.4.11(typescript@6.0.3) + '@scalar/use-toasts': 0.10.5(typescript@6.0.3) + '@scalar/workspace-store': 0.60.0(typescript@6.0.3) + '@vueuse/core': 13.9.0(vue@3.5.42(typescript@6.0.3)) + '@vueuse/integrations': 13.9.0(axios@1.18.1(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))(change-case@5.4.4)(focus-trap@7.8.0)(fuse.js@7.5.0)(vue@3.5.42(typescript@6.0.3)) + focus-trap: 7.8.0 + fuse.js: 7.5.0 + js-base64: 3.9.3 + jsonc-parser: 3.3.1 + nanoid: 5.1.11 + pretty-ms: 9.3.1 + radix-vue: 1.9.17(vue@3.5.42(typescript@6.0.3)) + set-cookie-parser: 3.1.0 + vue: 3.5.42(typescript@6.0.3) + yaml: 2.9.0 + zod: 4.4.3 + transitivePeerDependencies: + - '@vue/composition-api' + - async-validator + - axios + - change-case + - drauu + - idb-keyval + - jwt-decode + - nprogress + - qrcode + - sortablejs + - supports-color + - tailwindcss + - typescript + - universal-cookie + + '@scalar/api-reference@1.68.0(axios@1.18.1(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))(change-case@5.4.4)(supports-color@10.2.2)(tailwindcss@4.3.3)(typescript@6.0.3)(zod@4.4.3)': + dependencies: + '@headlessui/vue': 1.7.23(vue@3.5.42(typescript@6.0.3)) + '@scalar/agent-chat': 0.12.30(axios@1.18.1(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))(change-case@5.4.4)(supports-color@10.2.2)(tailwindcss@4.3.3)(typescript@6.0.3)(zod@4.4.3) + '@scalar/api-client': 3.18.0(axios@1.18.1(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))(change-case@5.4.4)(supports-color@10.2.2)(tailwindcss@4.3.3)(typescript@6.0.3) + '@scalar/blocks': 0.1.16(supports-color@10.2.2)(tailwindcss@4.3.3)(typescript@6.0.3) + '@scalar/code-highlight': 0.4.5(supports-color@10.2.2) + '@scalar/components': 0.29.1(supports-color@10.2.2)(tailwindcss@4.3.3)(typescript@6.0.3) + '@scalar/helpers': 0.11.3 + '@scalar/icons': 0.7.6(typescript@6.0.3) + '@scalar/oas-utils': 0.19.16(typescript@6.0.3) + '@scalar/schemas': 0.9.0 + '@scalar/sidebar': 0.11.1(supports-color@10.2.2)(tailwindcss@4.3.3)(typescript@6.0.3) + '@scalar/snippetz': 0.9.30 + '@scalar/themes': 0.17.4 + '@scalar/types': 0.19.0 + '@scalar/use-hooks': 0.4.11(typescript@6.0.3) + '@scalar/use-toasts': 0.10.5(typescript@6.0.3) + '@scalar/validation': 0.6.3 + '@scalar/workspace-store': 0.60.0(typescript@6.0.3) + '@unhead/vue': 2.1.17(vue@3.5.42(typescript@6.0.3)) + '@vueuse/core': 13.9.0(vue@3.5.42(typescript@6.0.3)) + fuse.js: 7.5.0 + microdiff: 1.6.0 + nanoid: 5.1.11 + vue: 3.5.42(typescript@6.0.3) + yaml: 2.9.0 + transitivePeerDependencies: + - '@vue/composition-api' + - async-validator + - axios + - change-case + - drauu + - idb-keyval + - jwt-decode + - nprogress + - qrcode + - sortablejs + - supports-color + - tailwindcss + - typescript + - universal-cookie + - zod + + '@scalar/asyncapi-upgrader@0.1.9': + dependencies: + '@scalar/helpers': 0.11.3 + + '@scalar/blocks@0.1.16(supports-color@10.2.2)(tailwindcss@4.3.3)(typescript@6.0.3)': + dependencies: + '@scalar/components': 0.29.1(supports-color@10.2.2)(tailwindcss@4.3.3)(typescript@6.0.3) + '@scalar/helpers': 0.11.3 + '@scalar/icons': 0.7.6(typescript@6.0.3) + '@scalar/snippetz': 0.9.30 + '@scalar/themes': 0.17.4 + '@scalar/types': 0.19.0 + '@scalar/workspace-store': 0.60.0(typescript@6.0.3) + '@types/har-format': 1.2.16 + js-base64: 3.9.3 + vue: 3.5.42(typescript@6.0.3) + transitivePeerDependencies: + - '@vue/composition-api' + - supports-color + - tailwindcss + - typescript + '@scalar/client-side-rendering@0.1.9': dependencies: '@scalar/schemas': 0.2.0 '@scalar/types': 0.11.0 '@scalar/validation': 0.5.0 + '@scalar/code-highlight@0.4.5(supports-color@10.2.2)': + dependencies: + hast-util-to-text: 4.0.2 + highlight.js: 11.12.0 + lowlight: 3.3.0 + rehype-external-links: 3.0.0 + rehype-format: 5.0.1 + rehype-parse: 9.0.1 + rehype-raw: 7.0.0 + rehype-sanitize: 6.0.0 + rehype-stringify: 10.0.1 + remark-gfm: 4.0.1(supports-color@10.2.2) + remark-parse: 11.0.0(supports-color@10.2.2) + remark-rehype: 11.1.2 + remark-stringify: 11.0.0 + unified: 11.0.5 + unist-util-visit: 5.1.0 + transitivePeerDependencies: + - supports-color + + '@scalar/components@0.29.1(supports-color@10.2.2)(tailwindcss@4.3.3)(typescript@6.0.3)': + dependencies: + '@floating-ui/utils': 0.2.10 + '@floating-ui/vue': 1.1.9(vue@3.5.42(typescript@6.0.3)) + '@headlessui/tailwindcss': 0.2.2(tailwindcss@4.3.3) + '@headlessui/vue': 1.7.23(vue@3.5.42(typescript@6.0.3)) + '@scalar/code-highlight': 0.4.5(supports-color@10.2.2) + '@scalar/helpers': 0.11.3 + '@scalar/icons': 0.7.6(typescript@6.0.3) + '@scalar/themes': 0.17.4 + '@scalar/use-hooks': 0.4.11(typescript@6.0.3) + '@vueuse/core': 13.9.0(vue@3.5.42(typescript@6.0.3)) + cva: 1.0.0-beta.4(typescript@6.0.3) + radix-vue: 1.9.17(vue@3.5.42(typescript@6.0.3)) + vue: 3.5.42(typescript@6.0.3) + vue-component-type-helpers: 3.3.11 + transitivePeerDependencies: + - '@vue/composition-api' + - supports-color + - tailwindcss + - typescript + '@scalar/fastify-api-reference@1.57.2': dependencies: '@scalar/client-side-rendering': 0.1.9 @@ -7639,14 +9075,43 @@ snapshots: fastify-plugin: 4.5.1 github-slugger: 2.0.0 + '@scalar/helpers@0.11.3': {} + '@scalar/helpers@0.8.0': {} + '@scalar/icons@0.7.6(typescript@6.0.3)': + dependencies: + '@phosphor-icons/core': 2.1.1 + '@types/node': 24.13.4 + chalk: 5.6.2 + vue: 3.5.42(typescript@6.0.3) + transitivePeerDependencies: + - typescript + '@scalar/json-magic@0.12.14': dependencies: '@scalar/helpers': 0.8.0 pathe: 2.0.3 yaml: 2.9.0 + '@scalar/json-magic@0.13.4': + dependencies: + '@scalar/helpers': 0.11.3 + pathe: 2.0.3 + yaml: 2.9.0 + + '@scalar/oas-utils@0.19.16(typescript@6.0.3)': + dependencies: + '@scalar/helpers': 0.11.3 + '@scalar/themes': 0.17.4 + '@scalar/types': 0.19.0 + '@scalar/workspace-store': 0.60.0(typescript@6.0.3) + flatted: 3.4.2 + vue: 3.5.42(typescript@6.0.3) + yaml: 2.9.0 + transitivePeerDependencies: + - typescript + '@scalar/openapi-parser@0.28.4': dependencies: '@scalar/helpers': 0.8.0 @@ -7662,6 +9127,12 @@ snapshots: '@scalar/openapi-types@0.8.0': {} + '@scalar/openapi-types@0.9.5': {} + + '@scalar/openapi-upgrader@0.2.15': + dependencies: + '@scalar/openapi-types': 0.9.5 + '@scalar/openapi-upgrader@0.2.7': dependencies: '@scalar/openapi-types': 0.8.0 @@ -7670,6 +9141,39 @@ snapshots: dependencies: '@scalar/validation': 0.5.0 + '@scalar/schemas@0.9.0': + dependencies: + '@scalar/helpers': 0.11.3 + '@scalar/validation': 0.6.3 + + '@scalar/sidebar@0.11.1(supports-color@10.2.2)(tailwindcss@4.3.3)(typescript@6.0.3)': + dependencies: + '@scalar/components': 0.29.1(supports-color@10.2.2)(tailwindcss@4.3.3)(typescript@6.0.3) + '@scalar/helpers': 0.11.3 + '@scalar/icons': 0.7.6(typescript@6.0.3) + '@scalar/themes': 0.17.4 + '@scalar/use-hooks': 0.4.11(typescript@6.0.3) + '@scalar/workspace-store': 0.60.0(typescript@6.0.3) + vue: 3.5.42(typescript@6.0.3) + transitivePeerDependencies: + - '@vue/composition-api' + - supports-color + - tailwindcss + - typescript + + '@scalar/snippetz@0.9.30': + dependencies: + '@scalar/helpers': 0.11.3 + '@scalar/types': 0.19.0 + js-base64: 3.9.3 + stringify-object: 6.0.0 + + '@scalar/themes@0.17.4': + dependencies: + nanoid: 5.1.11 + + '@scalar/typebox@0.1.3': {} + '@scalar/types@0.11.0': dependencies: '@scalar/helpers': 0.8.0 @@ -7677,8 +9181,74 @@ snapshots: type-fest: 5.6.0 zod: 4.4.3 + '@scalar/types@0.19.0': + dependencies: + '@scalar/helpers': 0.11.3 + nanoid: 5.1.11 + type-fest: 5.9.0 + zod: 4.4.3 + + '@scalar/use-codemirror@0.14.15(typescript@6.0.3)': + dependencies: + '@codemirror/autocomplete': 6.20.3 + '@codemirror/commands': 6.11.0 + '@codemirror/lang-css': 6.3.1 + '@codemirror/lang-html': 6.4.12 + '@codemirror/lang-json': 6.0.2 + '@codemirror/lang-xml': 6.1.0 + '@codemirror/lang-yaml': 6.1.3 + '@codemirror/language': 6.12.4 + '@codemirror/lint': 6.9.7 + '@codemirror/state': 6.7.4 + '@codemirror/view': 6.43.11 + '@lezer/common': 1.5.2 + '@lezer/highlight': 1.2.3 + '@replit/codemirror-css-color-picker': 6.3.0(@codemirror/language@6.12.4)(@codemirror/state@6.7.4)(@codemirror/view@6.43.11) + vue: 3.5.42(typescript@6.0.3) + transitivePeerDependencies: + - typescript + + '@scalar/use-hooks@0.4.11(typescript@6.0.3)': + dependencies: + '@scalar/helpers': 0.11.3 + '@scalar/use-toasts': 0.10.5(typescript@6.0.3) + '@scalar/validation': 0.6.3 + '@vueuse/core': 13.9.0(vue@3.5.42(typescript@6.0.3)) + cva: 1.0.0-beta.4(typescript@6.0.3) + tailwind-merge: 3.5.0 + vue: 3.5.42(typescript@6.0.3) + transitivePeerDependencies: + - typescript + + '@scalar/use-toasts@0.10.5(typescript@6.0.3)': + dependencies: + vue: 3.5.42(typescript@6.0.3) + vue-sonner: 1.3.2 + transitivePeerDependencies: + - typescript + '@scalar/validation@0.5.0': {} + '@scalar/validation@0.6.3': {} + + '@scalar/workspace-store@0.60.0(typescript@6.0.3)': + dependencies: + '@scalar/asyncapi-upgrader': 0.1.9 + '@scalar/helpers': 0.11.3 + '@scalar/json-magic': 0.13.4 + '@scalar/openapi-upgrader': 0.2.15 + '@scalar/schemas': 0.9.0 + '@scalar/snippetz': 0.9.30 + '@scalar/typebox': 0.1.3 + '@scalar/types': 0.19.0 + '@scalar/validation': 0.6.3 + js-base64: 3.9.3 + type-fest: 5.9.0 + vue: 3.5.42(typescript@6.0.3) + yaml: 2.9.0 + transitivePeerDependencies: + - typescript + '@shuding/opentype.js@1.4.0-beta.0': dependencies: fflate: 0.7.4 @@ -7738,6 +9308,17 @@ snapshots: '@standard-schema/spec@1.1.0': {} + '@swc/helpers@0.5.23': + dependencies: + tslib: 2.8.1 + + '@tanstack/virtual-core@3.17.10': {} + + '@tanstack/vue-virtual@3.13.38(vue@3.5.42(typescript@6.0.3))': + dependencies: + '@tanstack/virtual-core': 3.17.10 + vue: 3.5.42(typescript@6.0.3) + '@tediousjs/connection-string@0.5.0': {} '@tybys/wasm-util@0.10.3': @@ -7770,6 +9351,8 @@ snapshots: '@types/estree@1.0.9': {} + '@types/har-format@1.2.16': {} + '@types/hast@3.0.4': dependencies: '@types/unist': 3.0.3 @@ -7790,6 +9373,10 @@ snapshots: transitivePeerDependencies: - supports-color + '@types/node@24.13.4': + dependencies: + undici-types: 7.18.2 + '@types/node@26.5.1': dependencies: undici-types: 8.9.0 @@ -7808,6 +9395,10 @@ snapshots: '@types/unist@3.0.3': {} + '@types/web-bluetooth@0.0.20': {} + + '@types/web-bluetooth@0.0.21': {} + '@typescript-eslint/eslint-plugin@8.59.4(@typescript-eslint/parser@8.59.4(eslint@10.10.0(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3))(eslint@10.10.0(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3)': dependencies: '@eslint-community/regexpp': 4.12.2 @@ -7909,6 +9500,14 @@ snapshots: '@ungap/structured-clone@1.3.0': {} + '@unhead/vue@2.1.17(vue@3.5.42(typescript@6.0.3))': + dependencies: + hookable: 6.1.1 + unhead: 2.1.17 + vue: 3.5.42(typescript@6.0.3) + + '@vercel/oidc@3.1.0': {} + '@vitest/coverage-v8@4.1.7(vitest@4.1.7)': dependencies: '@bcoe/v8-coverage': 1.0.2 @@ -7921,7 +9520,7 @@ snapshots: obug: 2.1.1 std-env: 4.1.0 tinyrainbow: 3.1.0 - vitest: 4.1.7(@types/node@26.5.1)(@vitest/coverage-v8@4.1.7)(vite@8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0)) + vitest: 4.1.7(@opentelemetry/api@1.9.0)(@types/node@26.5.1)(@vitest/coverage-v8@4.1.7)(vite@8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0)) '@vitest/expect@4.1.7': dependencies: @@ -7964,6 +9563,103 @@ snapshots: convert-source-map: 2.0.0 tinyrainbow: 3.1.0 + '@vue/compiler-core@3.5.42': + dependencies: + '@babel/parser': 7.29.8 + '@vue/shared': 3.5.42 + entities: 7.0.1 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.42': + dependencies: + '@vue/compiler-core': 3.5.42 + '@vue/shared': 3.5.42 + + '@vue/compiler-sfc@3.5.42': + dependencies: + '@babel/parser': 7.29.8 + '@vue/compiler-core': 3.5.42 + '@vue/compiler-dom': 3.5.42 + '@vue/compiler-ssr': 3.5.42 + '@vue/shared': 3.5.42 + estree-walker: 2.0.2 + magic-string: 0.30.21 + postcss: 8.5.26 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.42': + dependencies: + '@vue/compiler-dom': 3.5.42 + '@vue/shared': 3.5.42 + + '@vue/reactivity@3.5.42': + dependencies: + '@vue/shared': 3.5.42 + + '@vue/runtime-core@3.5.42': + dependencies: + '@vue/reactivity': 3.5.42 + '@vue/shared': 3.5.42 + + '@vue/runtime-dom@3.5.42': + dependencies: + '@vue/reactivity': 3.5.42 + '@vue/runtime-core': 3.5.42 + '@vue/shared': 3.5.42 + csstype: 3.2.3 + + '@vue/server-renderer@3.5.42': + dependencies: + '@vue/compiler-ssr': 3.5.42 + '@vue/runtime-dom': 3.5.42 + '@vue/shared': 3.5.42 + + '@vue/shared@3.5.42': {} + + '@vueuse/core@10.11.1(vue@3.5.42(typescript@6.0.3))': + dependencies: + '@types/web-bluetooth': 0.0.20 + '@vueuse/metadata': 10.11.1 + '@vueuse/shared': 10.11.1(vue@3.5.42(typescript@6.0.3)) + vue-demi: 0.14.10(vue@3.5.42(typescript@6.0.3)) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + + '@vueuse/core@13.9.0(vue@3.5.42(typescript@6.0.3))': + dependencies: + '@types/web-bluetooth': 0.0.21 + '@vueuse/metadata': 13.9.0 + '@vueuse/shared': 13.9.0(vue@3.5.42(typescript@6.0.3)) + vue: 3.5.42(typescript@6.0.3) + + '@vueuse/integrations@13.9.0(axios@1.18.1(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))(change-case@5.4.4)(focus-trap@7.8.0)(fuse.js@7.5.0)(vue@3.5.42(typescript@6.0.3))': + dependencies: + '@vueuse/core': 13.9.0(vue@3.5.42(typescript@6.0.3)) + '@vueuse/shared': 13.9.0(vue@3.5.42(typescript@6.0.3)) + vue: 3.5.42(typescript@6.0.3) + optionalDependencies: + axios: 1.18.1(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2) + change-case: 5.4.4 + focus-trap: 7.8.0 + fuse.js: 7.5.0 + + '@vueuse/metadata@10.11.1': {} + + '@vueuse/metadata@13.9.0': {} + + '@vueuse/shared@10.11.1(vue@3.5.42(typescript@6.0.3))': + dependencies: + vue-demi: 0.14.10(vue@3.5.42(typescript@6.0.3)) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + + '@vueuse/shared@13.9.0(vue@3.5.42(typescript@6.0.3))': + dependencies: + vue: 3.5.42(typescript@6.0.3) + '@yarnpkg/lockfile@1.1.0': {} '@zkochan/js-yaml@0.0.7': @@ -7984,6 +9680,13 @@ snapshots: address@2.0.3: {} + agent-base@6.0.2(supports-color@10.2.2): + dependencies: + debug: 4.4.3(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + optional: true + agent-base@6.0.2(supports-color@7.2.0): dependencies: debug: 4.4.3(supports-color@7.2.0) @@ -7992,6 +9695,14 @@ snapshots: agent-base@7.1.4: {} + ai@6.0.33(zod@4.4.3): + dependencies: + '@ai-sdk/gateway': 3.0.13(zod@4.4.3) + '@ai-sdk/provider': 3.0.2 + '@ai-sdk/provider-utils': 4.0.5(zod@4.4.3) + '@opentelemetry/api': 1.9.0 + zod: 4.4.3 + ajv-draft-04@1.0.0(ajv@8.20.0): optionalDependencies: ajv: 8.20.0 @@ -8036,6 +9747,16 @@ snapshots: argparse@2.0.1: {} + aria-hidden@1.2.6: + dependencies: + tslib: 2.8.1 + + asn1js@3.0.10: + dependencies: + pvtsutils: 1.3.6 + pvutils: 1.2.0 + tslib: 2.8.1 + assertion-error@2.0.1: {} ast-v8-to-istanbul@1.0.0: @@ -8061,6 +9782,17 @@ snapshots: transitivePeerDependencies: - debug + axios@1.18.1(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2): + dependencies: + follow-redirects: 1.16.0(debug@4.4.3(supports-color@10.2.2)) + form-data: 4.0.6 + https-proxy-agent: 5.0.1(supports-color@10.2.2) + proxy-from-env: 2.1.0 + transitivePeerDependencies: + - debug + - supports-color + optional: true + axios@1.18.1(debug@4.4.3(supports-color@7.2.0))(supports-color@7.2.0): dependencies: follow-redirects: 1.16.0(debug@4.4.3(supports-color@7.2.0)) @@ -8220,6 +9952,8 @@ snapshots: dependencies: run-applescript: 7.0.0 + bytestreamjs@2.0.1: {} + cacheable@2.5.0: dependencies: '@cacheable/memory': 2.2.0 @@ -8248,8 +9982,14 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + chalk@5.6.2: {} + change-case@5.4.4: {} + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + character-entities@2.0.2: {} cli-cursor@3.1.0: @@ -8275,6 +10015,8 @@ snapshots: clone@1.0.4: {} + clsx@2.1.1: {} + cluster-key-slot@1.1.2: {} color-convert@0.5.3: {} @@ -8289,6 +10031,8 @@ snapshots: colorette@1.4.0: {} + colorette@2.0.20: {} + columnify@1.6.0: dependencies: strip-ansi: 6.0.1 @@ -8302,12 +10046,18 @@ snapshots: commander@11.1.0: {} + content-disposition@2.0.1: {} + content-type@1.0.5: {} + convert-hrtime@5.0.0: {} + convert-source-map@2.0.0: {} cookie@1.1.1: {} + cookie@2.0.1: {} + core-js-compat@3.49.0: dependencies: browserslist: 4.28.2 @@ -8320,6 +10070,8 @@ snapshots: path-type: 4.0.0 yaml: 1.10.3 + crelt@1.0.7: {} + cron-parser@4.9.0: dependencies: luxon: 3.7.2 @@ -8368,6 +10120,16 @@ snapshots: dependencies: css-tree: 2.2.1 + csstype@3.2.3: {} + + cva@1.0.0-beta.4(typescript@6.0.3): + dependencies: + clsx: 2.1.1 + optionalDependencies: + typescript: 6.0.3 + + dateformat@4.6.3: {} + dayjs@1.11.23: {} debug@4.4.3(supports-color@10.2.2): @@ -8410,6 +10172,8 @@ snapshots: define-lazy-prop@3.0.0: {} + defu@6.1.7: {} + delayed-stream@1.0.0: {} denque@2.1.0: {} @@ -8462,11 +10226,12 @@ snapshots: get-tsconfig: 4.13.7 jiti: 2.6.1 - drizzle-orm@1.0.0-beta.20(@types/mssql@9.1.9(supports-color@10.2.2))(@types/pg@8.20.0)(mssql@11.0.1(supports-color@10.2.2))(pg@8.21.0)(typebox@1.1.38)(zod@4.4.3): + drizzle-orm@1.0.0-beta.20(@opentelemetry/api@1.9.0)(@types/mssql@9.1.9(supports-color@10.2.2))(@types/pg@8.20.0)(mssql@11.0.1(supports-color@10.2.2))(pg@8.21.0)(typebox@1.1.38)(zod@4.4.3): dependencies: '@types/mssql': 9.1.9(supports-color@10.2.2) mssql: 11.0.1(supports-color@10.2.2) optionalDependencies: + '@opentelemetry/api': 1.9.0 '@types/pg': 8.20.0 pg: 8.21.0 typebox: 1.1.38 @@ -8504,6 +10269,8 @@ snapshots: entities@6.0.1: {} + entities@7.0.1: {} + environment@1.1.0: {} error-ex@1.3.4: @@ -8632,6 +10399,8 @@ snapshots: estraverse@5.3.0: {} + estree-walker@2.0.2: {} + estree-walker@3.0.3: dependencies: '@types/estree': 1.0.9 @@ -8644,6 +10413,8 @@ snapshots: events@3.3.0: {} + eventsource-parser@3.1.1: {} + expect-type@1.3.0: {} extend-shallow@2.0.1: @@ -8654,6 +10425,8 @@ snapshots: fast-content-type-parse@3.0.0: {} + fast-copy@4.1.1: {} + fast-decode-uri-component@1.0.1: {} fast-deep-equal@3.1.3: {} @@ -8675,6 +10448,8 @@ snapshots: dependencies: fast-decode-uri-component: 1.0.1 + fast-safe-stringify@2.1.1: {} + fast-uri@3.1.0: {} fast-uri@3.1.6: {} @@ -8699,6 +10474,24 @@ snapshots: fastify-plugin@6.0.0: {} + fastify@5.12.1: + dependencies: + '@fastify/ajv-compiler': 4.0.6 + '@fastify/error': 4.2.0 + '@fastify/fast-json-stringify-compiler': 5.1.0 + '@fastify/proxy-addr': 5.1.0 + abstract-logging: 2.0.1 + avvio: 9.3.0 + fast-json-stringify: 7.0.1 + find-my-way: 9.9.0 + light-my-request: 6.6.0 + pino: 10.3.1 + process-warning: 5.1.0 + rfdc: 1.4.1 + secure-json-parse: 4.1.0 + semver: 7.8.5 + toad-cache: 3.7.4 + fastify@5.12.4: dependencies: '@fastify/ajv-compiler': 4.0.6 @@ -8762,6 +10555,15 @@ snapshots: flatted@3.4.2: {} + focus-trap@7.8.0: + dependencies: + tabbable: 6.5.0 + + follow-redirects@1.16.0(debug@4.4.3(supports-color@10.2.2)): + optionalDependencies: + debug: 4.4.3(supports-color@10.2.2) + optional: true + follow-redirects@1.16.0(debug@4.4.3(supports-color@7.2.0)): optionalDependencies: debug: 4.4.3(supports-color@7.2.0) @@ -8795,6 +10597,10 @@ snapshots: function-bind@1.1.2: {} + function-timeout@1.0.2: {} + + fuse.js@7.5.0: {} + gensync@1.0.0-beta.2: {} get-caller-file@2.0.5: {} @@ -8814,6 +10620,8 @@ snapshots: hasown: 2.0.4 math-intrinsics: 1.1.0 + get-own-enumerable-keys@1.0.0: {} + get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 @@ -8833,6 +10641,12 @@ snapshots: dependencies: is-glob: 4.0.3 + glob@13.0.6: + dependencies: + minimatch: 10.2.6 + minipass: 7.1.3 + path-scurry: 2.0.2 + globals@17.6.0: {} gopd@1.2.0: {} @@ -8844,6 +10658,8 @@ snapshots: section-matter: 1.0.0 strip-bom-string: 1.0.0 + guess-json-indent@3.0.1: {} + harfbuzzjs@0.10.0: {} has-flag@4.0.0: {} @@ -8866,6 +10682,21 @@ snapshots: dependencies: function-bind: 1.1.2 + hast-util-embedded@3.0.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-is-element: 3.0.0 + + hast-util-format@1.1.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-embedded: 3.0.0 + hast-util-minify-whitespace: 1.0.1 + hast-util-phrasing: 3.0.1 + hast-util-whitespace: 3.0.0 + html-whitespace-sensitive-tag-names: 3.0.1 + unist-util-visit-parents: 6.0.2 + hast-util-from-html@2.0.3: dependencies: '@types/hast': 3.0.4 @@ -8886,14 +10717,99 @@ snapshots: vfile-location: 5.0.3 web-namespaces: 2.0.1 + hast-util-has-property@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast-util-is-body-ok-link@3.0.1: + dependencies: + '@types/hast': 3.0.4 + + hast-util-is-element@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast-util-minify-whitespace@1.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-embedded: 3.0.0 + hast-util-is-element: 3.0.0 + hast-util-whitespace: 3.0.0 + unist-util-is: 6.0.1 + hast-util-parse-selector@4.0.0: dependencies: '@types/hast': 3.0.4 + hast-util-phrasing@3.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-embedded: 3.0.0 + hast-util-has-property: 3.0.0 + hast-util-is-body-ok-link: 3.0.1 + hast-util-is-element: 3.0.0 + + hast-util-raw@9.1.0: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + '@ungap/structured-clone': 1.3.0 + hast-util-from-parse5: 8.0.3 + hast-util-to-parse5: 8.0.1 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.1 + parse5: 7.3.0 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + + hast-util-sanitize@5.0.2: + dependencies: + '@types/hast': 3.0.4 + '@ungap/structured-clone': 1.3.0 + unist-util-position: 5.0.0 + + hast-util-to-html@9.0.5: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-to-parse5@8.0.1: + dependencies: + '@types/hast': 3.0.4 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + web-namespaces: 2.0.1 + zwitch: 2.0.4 + hast-util-to-string@3.0.1: dependencies: '@types/hast': 3.0.4 + hast-util-to-text@4.0.2: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + hast-util-is-element: 3.0.0 + unist-util-find-after: 5.0.0 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + hastscript@9.0.1: dependencies: '@types/hast': 3.0.4 @@ -8902,14 +10818,32 @@ snapshots: property-information: 7.1.0 space-separated-tokens: 2.0.2 + helmet@8.3.0: {} + + help-me@5.0.0: {} + hex-rgb@4.3.0: {} + highlight.js@11.11.2: {} + + highlight.js@11.12.0: {} + + hookable@6.1.1: {} + hookified@1.15.1: {} hookified@2.2.0: {} + hookified@3.0.3: {} + html-escaper@2.0.2: {} + html-escaper@3.0.3: {} + + html-void-elements@3.0.0: {} + + html-whitespace-sensitive-tag-names@3.0.1: {} + http-errors@2.0.1: dependencies: depd: 2.0.0 @@ -8925,6 +10859,14 @@ snapshots: transitivePeerDependencies: - supports-color + https-proxy-agent@5.0.1(supports-color@10.2.2): + dependencies: + agent-base: 6.0.2(supports-color@10.2.2) + debug: 4.4.3(supports-color@10.2.2) + transitivePeerDependencies: + - supports-color + optional: true + https-proxy-agent@5.0.1(supports-color@7.2.0): dependencies: agent-base: 6.0.2(supports-color@7.2.0) @@ -8949,6 +10891,10 @@ snapshots: dependencies: safer-buffer: 2.1.2 + identifier-regex@1.1.0: + dependencies: + reserved-identifiers: 1.2.0 + ieee754@1.2.1: {} ignore@5.3.2: {} @@ -8986,6 +10932,8 @@ snapshots: ipaddr.js@2.5.0: {} + is-absolute-url@4.0.1: {} + is-arrayish@0.2.1: {} is-core-module@2.16.2: @@ -9010,14 +10958,23 @@ snapshots: dependencies: is-extglob: 2.1.1 + is-identifier@1.1.0: + dependencies: + identifier-regex: 1.1.0 + super-regex: 1.1.0 + is-inside-container@1.0.0: dependencies: is-docker: 3.0.0 is-interactive@1.0.0: {} + is-obj@3.0.0: {} + is-plain-obj@4.1.0: {} + is-regexp@3.1.0: {} + is-unicode-supported@0.1.0: {} is-wsl@2.2.0: @@ -9051,6 +11008,10 @@ snapshots: jiti@2.7.0: {} + joycon@3.1.1: {} + + js-base64@3.9.3: {} + js-levenshtein@1.1.6: {} js-md4@0.3.2: {} @@ -9090,6 +11051,8 @@ snapshots: json-schema-traverse@1.0.0: {} + json-schema@0.4.0: {} + json-stable-stringify-without-jsonify@1.0.1: {} json-with-bigint@3.5.8: {} @@ -9295,6 +11258,12 @@ snapshots: nested-error-stacks: 2.1.1 parse-color: 1.0.0 + lowlight@3.3.0: + dependencies: + '@types/hast': 3.0.4 + devlop: 1.1.0 + highlight.js: 11.11.2 + lru-cache@11.5.2: {} lru-cache@5.1.1: @@ -9313,6 +11282,12 @@ snapshots: '@babel/types': 7.29.0 source-map-js: 1.2.1 + make-asynchronous@1.1.0: + dependencies: + p-event: 6.0.1 + type-fest: 4.41.0 + web-worker: 1.5.0 + make-dir@4.0.0: dependencies: semver: 7.8.5 @@ -9441,6 +11416,8 @@ snapshots: media-typer@1.1.0: {} + microdiff@1.6.0: {} + micromark-core-commonmark@2.0.3: dependencies: decode-named-character-reference: 1.3.0 @@ -9644,6 +11621,8 @@ snapshots: dependencies: mime-db: 1.54.0 + mime@3.0.0: {} + mimic-fn@2.1.0: {} mimic-function@5.0.1: {} @@ -9662,6 +11641,8 @@ snapshots: minimist@1.2.8: {} + minipass@7.1.3: {} + mri@1.2.0: {} ms@2.1.3: {} @@ -9703,6 +11684,8 @@ snapshots: nested-error-stacks@2.1.1: {} + neverpanic@0.0.8: {} + node-abort-controller@3.1.1: {} node-gyp-build-optional-packages@5.2.2: @@ -10131,6 +12114,10 @@ snapshots: - '@emnapi/core' - '@emnapi/runtime' + p-event@6.0.1: + dependencies: + p-timeout: 6.1.4 + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 @@ -10139,6 +12126,8 @@ snapshots: dependencies: p-limit: 3.1.0 + p-timeout@6.1.4: {} + package-manager-detector@1.6.0: {} pako@0.2.9: {} @@ -10169,6 +12158,8 @@ snapshots: index-to-position: 1.2.0 type-fest: 4.41.0 + parse-ms@4.0.0: {} + parse5@7.3.0: dependencies: entities: 6.0.1 @@ -10181,6 +12172,11 @@ snapshots: path-parse@1.0.7: {} + path-scurry@2.0.2: + dependencies: + lru-cache: 11.5.2 + minipass: 7.1.3 + path-type@4.0.0: {} pathe@2.0.3: {} @@ -10230,6 +12226,22 @@ snapshots: dependencies: split2: 4.2.0 + pino-pretty@13.1.3: + dependencies: + colorette: 2.0.20 + dateformat: 4.6.3 + fast-copy: 4.1.1 + fast-safe-stringify: 2.1.1 + help-me: 5.0.0 + joycon: 3.1.1 + minimist: 1.2.8 + on-exit-leak-free: 2.1.2 + pino-abstract-transport: 3.0.0 + pump: 3.0.4 + secure-json-parse: 4.1.0 + sonic-boom: 4.2.1 + strip-json-comments: 5.0.3 + pino-std-serializers@7.1.0: {} pino@10.3.1: @@ -10246,6 +12258,15 @@ snapshots: sonic-boom: 4.2.1 thread-stream: 4.2.0 + pkijs@3.4.0: + dependencies: + '@noble/hashes': 1.4.0 + asn1js: 3.0.10 + bytestreamjs: 2.0.1 + pvtsutils: 1.3.6 + pvutils: 1.2.0 + tslib: 2.8.1 + pluralize@8.0.0: {} postcss-value-parser@4.2.0: {} @@ -10270,6 +12291,10 @@ snapshots: prettier@3.9.6: {} + pretty-ms@9.3.1: + dependencies: + parse-ms: 4.0.0 + process-warning@4.0.1: {} process-warning@5.1.0: {} @@ -10287,14 +12312,42 @@ snapshots: picocolors: 1.1.1 sade: 1.8.1 + pump@3.0.4: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + punycode@2.3.1: {} + pvtsutils@1.3.6: + dependencies: + tslib: 2.8.1 + + pvutils@1.2.0: {} + qified@0.10.1: dependencies: hookified: 2.2.0 quick-format-unescaped@4.0.4: {} + radix-vue@1.9.17(vue@3.5.42(typescript@6.0.3)): + dependencies: + '@floating-ui/dom': 1.8.0 + '@floating-ui/vue': 1.1.11(vue@3.5.42(typescript@6.0.3)) + '@internationalized/date': 3.12.4 + '@internationalized/number': 3.6.8 + '@tanstack/vue-virtual': 3.13.38(vue@3.5.42(typescript@6.0.3)) + '@vueuse/core': 10.11.1(vue@3.5.42(typescript@6.0.3)) + '@vueuse/shared': 10.11.1(vue@3.5.42(typescript@6.0.3)) + aria-hidden: 1.2.6 + defu: 6.1.7 + fast-deep-equal: 3.1.3 + nanoid: 5.1.11 + vue: 3.5.42(typescript@6.0.3) + transitivePeerDependencies: + - '@vue/composition-api' + readable-stream@3.6.2: dependencies: inherits: 2.0.4 @@ -10319,6 +12372,8 @@ snapshots: dependencies: redis-errors: 1.2.0 + reflect-metadata@0.2.2: {} + regenerate-unicode-properties@10.2.2: dependencies: regenerate: 1.4.2 @@ -10340,6 +12395,43 @@ snapshots: dependencies: jsesc: 3.1.0 + rehype-external-links@3.0.0: + dependencies: + '@types/hast': 3.0.4 + '@ungap/structured-clone': 1.3.0 + hast-util-is-element: 3.0.0 + is-absolute-url: 4.0.1 + space-separated-tokens: 2.0.2 + unist-util-visit: 5.1.0 + + rehype-format@5.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-format: 1.1.0 + + rehype-parse@9.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-from-html: 2.0.3 + unified: 11.0.5 + + rehype-raw@7.0.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-raw: 9.1.0 + vfile: 6.0.3 + + rehype-sanitize@6.0.0: + dependencies: + '@types/hast': 3.0.4 + hast-util-sanitize: 5.0.2 + + rehype-stringify@10.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + unified: 11.0.5 + remark-gfm@4.0.1(supports-color@10.2.2): dependencies: '@types/mdast': 4.0.4 @@ -10378,6 +12470,8 @@ snapshots: require-from-string@2.0.2: {} + reserved-identifiers@1.2.0: {} + resolve-from@4.0.0: {} resolve-pkg-maps@1.0.0: {} @@ -10471,6 +12565,11 @@ snapshots: secure-json-parse@4.1.0: {} + selfsigned@5.5.0: + dependencies: + '@peculiar/x509': 1.14.3 + pkijs: 3.4.0 + semver@6.3.1: {} semver@7.7.4: {} @@ -10483,6 +12582,8 @@ snapshots: set-cookie-parser@2.7.2: {} + set-cookie-parser@3.1.0: {} + setprototypeof@1.2.0: {} sharp@0.35.4(@types/node@26.5.1): @@ -10613,6 +12714,10 @@ snapshots: string-argv@0.3.2: {} + string-byte-length@3.0.1: {} + + string-byte-slice@3.0.1: {} + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -10636,6 +12741,18 @@ snapshots: dependencies: safe-buffer: 5.2.1 + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + stringify-object@6.0.0: + dependencies: + get-own-enumerable-keys: 1.0.0 + is-identifier: 1.1.0 + is-obj: 3.0.0 + is-regexp: 3.1.0 + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -10652,6 +12769,14 @@ snapshots: strnum@2.3.0: {} + style-mod@4.1.3: {} + + super-regex@1.1.0: + dependencies: + function-timeout: 1.0.2 + make-asynchronous: 1.1.0 + time-span: 5.1.0 + supports-color@10.2.2: {} supports-color@7.2.0: @@ -10670,8 +12795,18 @@ snapshots: picocolors: 1.1.1 sax: 1.6.1 + swrv@1.2.0(vue@3.5.42(typescript@6.0.3)): + dependencies: + vue: 3.5.42(typescript@6.0.3) + + tabbable@6.5.0: {} + tagged-tag@1.0.0: {} + tailwind-merge@3.5.0: {} + + tailwindcss@4.3.3: {} + tar-stream@2.2.0: dependencies: bl: 4.1.0 @@ -10716,6 +12851,10 @@ snapshots: dependencies: real-require: 1.0.0 + time-span@5.1.0: + dependencies: + convert-hrtime: 5.0.0 + tiny-inflate@1.0.3: {} tinybench@2.9.0: {} @@ -10748,6 +12887,12 @@ snapshots: trough@2.2.0: {} + truncate-json@3.0.1: + dependencies: + guess-json-indent: 3.0.1 + string-byte-length: 3.0.1 + string-byte-slice: 3.0.1 + ts-api-utils@2.5.0(typescript@6.0.3): dependencies: typescript: 6.0.3 @@ -10758,8 +12903,14 @@ snapshots: minimist: 1.2.8 strip-bom: 3.0.0 + tslib@1.14.1: {} + tslib@2.8.1: {} + tsyringe@4.10.0: + dependencies: + tslib: 1.14.1 + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 @@ -10770,6 +12921,10 @@ snapshots: dependencies: tagged-tag: 1.0.0 + type-fest@5.9.0: + dependencies: + tagged-tag: 1.0.0 + type-is@2.0.1: dependencies: content-type: 1.0.5 @@ -10778,6 +12933,8 @@ snapshots: typebox@1.1.38: {} + typebox@1.3.34: {} + typescript-eslint@8.59.4(eslint@10.10.0(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3): dependencies: '@typescript-eslint/eslint-plugin': 8.59.4(@typescript-eslint/parser@8.59.4(eslint@10.10.0(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3))(eslint@10.10.0(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@6.0.3) @@ -10793,10 +12950,16 @@ snapshots: unbash@3.0.0: {} + undici-types@7.18.2: {} + undici-types@8.9.0: {} undici@8.10.2: {} + unhead@2.1.17: + dependencies: + hookable: 6.1.1 + unicode-canonical-property-names-ecmascript@2.0.1: {} unicode-match-property-ecmascript@2.0.0: @@ -10823,6 +12986,11 @@ snapshots: trough: 2.2.0 vfile: 6.0.3 + unist-util-find-after@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-find@3.0.0: dependencies: '@types/unist': 3.0.3 @@ -10898,7 +13066,7 @@ snapshots: jiti: 2.7.0 yaml: 2.9.0 - vitest@4.1.7(@types/node@26.5.1)(@vitest/coverage-v8@4.1.7)(vite@8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0)): + vitest@4.1.7(@opentelemetry/api@1.9.0)(@types/node@26.5.1)(@vitest/coverage-v8@4.1.7)(vite@8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.7 '@vitest/mocker': 4.1.7(vite@8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0)) @@ -10921,11 +13089,32 @@ snapshots: vite: 8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: + '@opentelemetry/api': 1.9.0 '@types/node': 26.5.1 '@vitest/coverage-v8': 4.1.7(vitest@4.1.7) transitivePeerDependencies: - msw + vue-component-type-helpers@3.3.11: {} + + vue-demi@0.14.10(vue@3.5.42(typescript@6.0.3)): + dependencies: + vue: 3.5.42(typescript@6.0.3) + + vue-sonner@1.3.2: {} + + vue@3.5.42(typescript@6.0.3): + dependencies: + '@vue/compiler-dom': 3.5.42 + '@vue/compiler-sfc': 3.5.42 + '@vue/runtime-dom': 3.5.42 + '@vue/server-renderer': 3.5.42 + '@vue/shared': 3.5.42 + optionalDependencies: + typescript: 6.0.3 + + w3c-keyname@2.2.8: {} + walk-up-path@4.0.0: {} wcwidth@1.0.1: @@ -10934,6 +13123,8 @@ snapshots: web-namespaces@2.0.1: {} + web-worker@1.5.0: {} + which@2.0.2: dependencies: isexe: 2.0.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index ed42019c..8e810f1e 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -18,14 +18,6 @@ catalog: vite: ^8.2.2 vitest: ^4.1.7 -onlyBuiltDependencies: - - esbuild - - libpq - - msgpackr-extract - - msw - - nx - - sharp - minimumReleaseAge: 10080 # 7 days overrides: @@ -36,3 +28,4 @@ allowBuilds: msgpackr-extract: true nx: true sharp: true + vue-demi: false diff --git a/tsconfig.json b/tsconfig.json index 5390783d..1ed3d9ed 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,6 +12,11 @@ "erasableSyntaxOnly": true, "allowImportingTsExtensions": true, "esModuleInterop": true, + "noImplicitOverride": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "noPropertyAccessFromIndexSignature": true, + "forceConsistentCasingInFileNames": true, "allowJs": true }, "include": ["eslint.config.mjs"] From 15568c7ec143b8351bf3f8cf975ac249dab1bae6 Mon Sep 17 00:00:00 2001 From: fennifith Date: Mon, 21 Sep 2026 18:11:19 -0400 Subject: [PATCH 02/20] fix ci --- apps/e2e/scripts/generate-cert.ts | 6 ++++-- tsconfig.json | 1 - 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/e2e/scripts/generate-cert.ts b/apps/e2e/scripts/generate-cert.ts index ba9d7de7..1d8101d5 100644 --- a/apps/e2e/scripts/generate-cert.ts +++ b/apps/e2e/scripts/generate-cert.ts @@ -4,9 +4,11 @@ import path from "path"; const outputDir = path.join(import.meta.dirname, "../src/generated"); -const attrs = [{ name: "commonName", value: "example.com" }]; -const pems = await selfsigned.generate(attrs); +const pems = await selfsigned.generate([ + { name: "commonName", value: "example.com" }, +]); +await fs.mkdir(outputDir, { recursive: true }); await fs.writeFile(path.join(outputDir, "key.pem"), pems.private, "utf8"); console.log("Private key saved to key.pem"); diff --git a/tsconfig.json b/tsconfig.json index 1ed3d9ed..2ff41b41 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,7 +15,6 @@ "noImplicitOverride": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, - "noPropertyAccessFromIndexSignature": true, "forceConsistentCasingInFileNames": true, "allowJs": true }, From 0f9cc377ba272ffd560f7352aa9f4bff5780951c Mon Sep 17 00:00:00 2001 From: fennifith Date: Mon, 21 Sep 2026 18:14:08 -0400 Subject: [PATCH 03/20] fix tsconfig.json: remove noImplicitReturns --- tsconfig.json | 1 - 1 file changed, 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index 2ff41b41..83b5b244 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,7 +13,6 @@ "allowImportingTsExtensions": true, "esModuleInterop": true, "noImplicitOverride": true, - "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "forceConsistentCasingInFileNames": true, "allowJs": true From 8c519ee7e25e64c7c02de7a18eb5e8d3d7dbca56 Mon Sep 17 00:00:00 2001 From: fennifith Date: Mon, 21 Sep 2026 18:16:25 -0400 Subject: [PATCH 04/20] fix prettier --- apps/e2e/project.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/e2e/project.json b/apps/e2e/project.json index 44c26987..36a3e96e 100644 --- a/apps/e2e/project.json +++ b/apps/e2e/project.json @@ -40,10 +40,7 @@ "test:docker:clean": { "executor": "nx:run-commands", "options": { - "commands": [ - "docker compose down -v", - "docker compose up -d --wait" - ], + "commands": ["docker compose down -v", "docker compose up -d --wait"], "parallel": false } }, From ed1fcf8a25d097e5701b9b3a0331ef6ca7cbc08e Mon Sep 17 00:00:00 2001 From: fennifith Date: Mon, 21 Sep 2026 18:17:30 -0400 Subject: [PATCH 05/20] revert getAuthorGitHubStats.ts --- packages/github-api/src/getAuthorGitHubStats.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/github-api/src/getAuthorGitHubStats.ts b/packages/github-api/src/getAuthorGitHubStats.ts index bc43e516..a9b01e5d 100644 --- a/packages/github-api/src/getAuthorGitHubStats.ts +++ b/packages/github-api/src/getAuthorGitHubStats.ts @@ -52,12 +52,12 @@ export async function getAuthorGitHubStats( client: Octokit, githubLogin: string, ): Promise { - const userResult = await client.graphql>( + const userResult = (await client.graphql>( `query($login: String!) { user(login: $login) { id } }`, { login: githubLogin }, - ); + )) as Record; - const userId = userResult?.["user"]?.id; + const userId = userResult?.user?.id; if (!userId) return undefined; const response = await client.graphql(statsQuery, { From a72c7458f6f3ff098a2a60b0cff6b6e8ed1eabd1 Mon Sep 17 00:00:00 2001 From: fennifith Date: Mon, 21 Sep 2026 18:20:14 -0400 Subject: [PATCH 06/20] remove docker step from test.yml --- .github/workflows/test.yml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3e8be7a2..d1ad1c03 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -59,12 +59,7 @@ jobs: - name: Install dependencies run: pnpm install --ignore-scripts - - name: Set up environment + - name: Run E2E tests run: | cp .env.example .env - - - name: Start Docker services - run: docker compose up -d --wait - - - name: Run E2E tests - run: pnpm run test:e2e + pnpm run test:e2e From 486e13c4cd0a90370358ded73455ea850d0ba4fa Mon Sep 17 00:00:00 2001 From: fennifith Date: Mon, 21 Sep 2026 19:05:59 -0400 Subject: [PATCH 07/20] improve e2e test setup --- apps/e2e/src/url-metadata.spec.ts | 20 ++++++++++++-------- apps/e2e/test-utils/setup.ts | 19 ++++++++++++++++--- apps/e2e/vitest.config.ts | 1 + 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/apps/e2e/src/url-metadata.spec.ts b/apps/e2e/src/url-metadata.spec.ts index 2201b6d9..8c6dcd20 100644 --- a/apps/e2e/src/url-metadata.spec.ts +++ b/apps/e2e/src/url-metadata.spec.ts @@ -1,5 +1,4 @@ -import { describe, test, expect } from "vitest"; -import { setTimeout } from "node:timers/promises"; +import { describe, test, expect, vi } from "vitest"; describe("url-metadata", () => { test("returns url-metadata", async () => { @@ -32,13 +31,18 @@ describe("url-metadata", () => { expect(enqueueRes.response.status).toBe(201); // Wait for the task to be processed - await setTimeout(2000); + const res = await vi.waitFor( + async () => { + // Fetch the processed result + const res = await client.POST("/tasks/url-metadata", { + body: { url }, + }); + expect(res.response.status).toBe(200); + return res; + }, + { timeout: 3000, interval: 100 }, + ); - // Fetch the processed result - const res = await client.POST("/tasks/url-metadata", { - body: { url }, - }); - expect(res.response.status).toBe(200); expect(res.data).toStrictEqual({ title: "Example Title", error: false, diff --git a/apps/e2e/test-utils/setup.ts b/apps/e2e/test-utils/setup.ts index fd1e4021..9ccb7dd6 100644 --- a/apps/e2e/test-utils/setup.ts +++ b/apps/e2e/test-utils/setup.ts @@ -5,6 +5,7 @@ import net from "net"; import { env } from "./env.ts"; import createClient from "openapi-fetch"; import type { paths } from "../src/generated/api-schema.js"; +import { afterAll, beforeAll } from "vitest"; const apiClient = createClient({ baseUrl: env.API_URL, @@ -21,14 +22,14 @@ const MOCK_HTTP_PORT = 4000; globalThis.mock = new MockHttp({ port: MOCK_HTTP_PORT, + autoDetectPort: false, + apiDocs: false, https: { cert: path.join(import.meta.dirname, "../src/generated/cert.pem"), key: path.join(import.meta.dirname, "../src/generated/key.pem"), }, }); -await mock.start(); - const server = http.createServer((req, res) => { mock.server.server.emit("request", req, res); }); @@ -45,4 +46,16 @@ server.on("connect", (_req, clientSocket, head) => { clientSocket.on("error", () => serverSocket.end()); }); -server.listen(env.MOCK_PROXY_PORT, "0.0.0.0"); +beforeAll(async () => { + await mock.start(); + await new Promise((res) => + server.listen(env.MOCK_PROXY_PORT, "0.0.0.0", undefined, () => + res(undefined), + ), + ); +}); + +afterAll(async () => { + server.close(); + await mock.close(); +}); diff --git a/apps/e2e/vitest.config.ts b/apps/e2e/vitest.config.ts index 50c0ed84..fa747d31 100644 --- a/apps/e2e/vitest.config.ts +++ b/apps/e2e/vitest.config.ts @@ -6,5 +6,6 @@ export default defineConfig({ watch: false, globals: true, fileParallelism: false, + maxWorkers: 1, }, }); From 3535f5c9db8f587b32c41bcf2463673e14de9e40 Mon Sep 17 00:00:00 2001 From: fennifith Date: Mon, 21 Sep 2026 19:12:58 -0400 Subject: [PATCH 08/20] fix typegen using dev server as an nx dependency --- apps/e2e/package.json | 4 ---- apps/e2e/project.json | 7 ++++++- apps/e2e/scripts/typegen.ts | 10 +++++++++- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/apps/e2e/package.json b/apps/e2e/package.json index a704b570..4af922c7 100644 --- a/apps/e2e/package.json +++ b/apps/e2e/package.json @@ -3,10 +3,6 @@ "version": "0.0.1", "private": true, "type": "module", - "scripts": { - "typegen": "node scripts/typegen.ts", - "test": "vitest run" - }, "dependencies": { "@jaredwray/mockhttp": "^1.9.0", "openapi-fetch": "catalog:", diff --git a/apps/e2e/project.json b/apps/e2e/project.json index 36a3e96e..5b5ed7a8 100644 --- a/apps/e2e/project.json +++ b/apps/e2e/project.json @@ -21,7 +21,12 @@ "outputs": ["{projectRoot}/src/generated/api-schema.d.ts"], "cache": true, "inputs": ["{workspaceRoot}/apps/api/**/*"], - "dependsOn": ["test:docker"] + "dependsOn": [ + { + "projects": "api", + "target": "dev" + } + ] }, "build:cert": { "executor": "nx:run-commands", diff --git a/apps/e2e/scripts/typegen.ts b/apps/e2e/scripts/typegen.ts index 3693fe75..2334bb9e 100644 --- a/apps/e2e/scripts/typegen.ts +++ b/apps/e2e/scripts/typegen.ts @@ -8,11 +8,19 @@ const __dirname = path.dirname(__filename); const outputDir = path.join(__dirname, "../src/generated"); const outputFile = path.join(outputDir, "api-schema.d.ts"); +const baseUrl = "http://localhost:3000"; + +console.log("Waiting for the dev server to start..."); +while ( + await fetch(baseUrl) + .then((r) => r.status === 200) + .catch((_) => true) +); export default async function generateTypes() { await fs.mkdir(outputDir, { recursive: true }); - const swaggerUrl = `http://localhost:3000/openapi.json`; + const swaggerUrl = `${baseUrl}/openapi.json`; const response = await fetch(swaggerUrl); if (!response.ok) { From 1c4bc5492e95b771dce87afae4131d9f0aff44e5 Mon Sep 17 00:00:00 2001 From: fennifith Date: Mon, 21 Sep 2026 20:16:38 -0400 Subject: [PATCH 09/20] split proxy/e2e entrypoints, allow running tests outside of docker --- apps/e2e/mocks/index.ts | 25 ++++++++++++ apps/e2e/mocks/mock.ts | 14 +++++++ apps/e2e/mocks/url-metadata.ts | 21 ++++++++++ apps/e2e/package.json | 1 + apps/e2e/src/url-metadata.spec.ts | 22 ----------- apps/e2e/{test-utils => src/util}/env.ts | 5 ++- apps/e2e/test-utils/globalSetup.ts | 21 ++++++++++ apps/e2e/test-utils/setup.ts | 50 +----------------------- apps/e2e/tsconfig.json | 2 +- apps/e2e/vitest.config.ts | 1 + docker-compose.yml | 23 +++++++++-- 11 files changed, 108 insertions(+), 77 deletions(-) create mode 100644 apps/e2e/mocks/index.ts create mode 100644 apps/e2e/mocks/mock.ts create mode 100644 apps/e2e/mocks/url-metadata.ts rename apps/e2e/{test-utils => src/util}/env.ts (61%) create mode 100644 apps/e2e/test-utils/globalSetup.ts diff --git a/apps/e2e/mocks/index.ts b/apps/e2e/mocks/index.ts new file mode 100644 index 00000000..ad5fa065 --- /dev/null +++ b/apps/e2e/mocks/index.ts @@ -0,0 +1,25 @@ +import http from "http"; +import net from "net"; +import { mock, MOCK_HTTP_PORT } from "./mock.ts"; +import "./url-metadata.ts"; +import { env } from "../src/util/env.ts"; + +await mock.start(); + +const server = http.createServer((req, res) => { + mock.server.server.emit("request", req, res); +}); + +server.on("connect", (_req, clientSocket, head) => { + const serverSocket = net.connect(MOCK_HTTP_PORT, "localhost", () => { + clientSocket.write("HTTP/1.1 200 Connection Established\r\n\r\n"); + serverSocket.write(head); + serverSocket.pipe(clientSocket); + clientSocket.pipe(serverSocket); + }); + + serverSocket.on("error", () => clientSocket.end()); + clientSocket.on("error", () => serverSocket.end()); +}); + +server.listen(env.MOCK_PROXY_PORT, "0.0.0.0"); diff --git a/apps/e2e/mocks/mock.ts b/apps/e2e/mocks/mock.ts new file mode 100644 index 00000000..fb281a10 --- /dev/null +++ b/apps/e2e/mocks/mock.ts @@ -0,0 +1,14 @@ +import path from "path"; +import MockHttp from "@jaredwray/mockhttp"; + +export const MOCK_HTTP_PORT = 4000; + +export const mock = new MockHttp({ + port: MOCK_HTTP_PORT, + autoDetectPort: false, + apiDocs: false, + https: { + cert: path.join(import.meta.dirname, "../src/generated/cert.pem"), + key: path.join(import.meta.dirname, "../src/generated/key.pem"), + }, +}); diff --git a/apps/e2e/mocks/url-metadata.ts b/apps/e2e/mocks/url-metadata.ts new file mode 100644 index 00000000..2c4f7b01 --- /dev/null +++ b/apps/e2e/mocks/url-metadata.ts @@ -0,0 +1,21 @@ +import { mock } from "./mock.ts"; + +mock.taps.inject( + { + response: ` + + + + Example Title + + + `, + statusCode: 200, + headers: { "Content-Type": "text/html" }, + }, + { + url: "/", + hostname: "example.com", + method: "GET", + }, +); diff --git a/apps/e2e/package.json b/apps/e2e/package.json index 4af922c7..e841b6a5 100644 --- a/apps/e2e/package.json +++ b/apps/e2e/package.json @@ -3,6 +3,7 @@ "version": "0.0.1", "private": true, "type": "module", + "main": "mocks/index.ts", "dependencies": { "@jaredwray/mockhttp": "^1.9.0", "openapi-fetch": "catalog:", diff --git a/apps/e2e/src/url-metadata.spec.ts b/apps/e2e/src/url-metadata.spec.ts index 8c6dcd20..131f98a5 100644 --- a/apps/e2e/src/url-metadata.spec.ts +++ b/apps/e2e/src/url-metadata.spec.ts @@ -2,26 +2,6 @@ import { describe, test, expect, vi } from "vitest"; describe("url-metadata", () => { test("returns url-metadata", async () => { - const tap = mock.taps.inject( - { - response: ` - - - - Example Title - - - `, - statusCode: 200, - headers: { "Content-Type": "text/html" }, - }, - { - url: "/", - hostname: "example.com", - method: "GET", - }, - ); - const url = `https://example.com/`; // Enqueue the URL metadata task @@ -47,7 +27,5 @@ describe("url-metadata", () => { title: "Example Title", error: false, }); - - mock.taps.removeInjection(tap); }); }); diff --git a/apps/e2e/test-utils/env.ts b/apps/e2e/src/util/env.ts similarity index 61% rename from apps/e2e/test-utils/env.ts rename to apps/e2e/src/util/env.ts index ddbbf2b4..cdc30750 100644 --- a/apps/e2e/test-utils/env.ts +++ b/apps/e2e/src/util/env.ts @@ -3,8 +3,9 @@ import { Settings } from "typebox/system"; import { Value } from "typebox/value"; const EnvSchema = Type.Object({ - MOCK_PROXY_PORT: Type.Integer(), - API_URL: Type.String(), + MOCK_PROXY_PORT: Type.Integer({ default: 3999 }), + API_URL: Type.String({ default: "http://localhost:3333" }), + IN_DOCKER: Type.Boolean({ default: false }), }); Settings.Set({ correctiveParse: true }); diff --git a/apps/e2e/test-utils/globalSetup.ts b/apps/e2e/test-utils/globalSetup.ts new file mode 100644 index 00000000..ca66d13e --- /dev/null +++ b/apps/e2e/test-utils/globalSetup.ts @@ -0,0 +1,21 @@ +import { env } from "../src/util/env.ts"; +import child_process from "child_process"; + +function exec(command: string): Promise { + return new Promise((res, rej) => { + const child = child_process.exec(command, (error) => + error ? rej(error) : res(), + ); + + child.stdout?.on("data", console.log); + child.stderr?.on("data", console.error); + }); +} + +export async function setup() { + if (!env.IN_DOCKER) { + // If a test is started locally, ensure that the e2e containers are running + console.log("Starting app in docker..."); + await exec("docker compose --profile e2e up app --wait"); + } +} diff --git a/apps/e2e/test-utils/setup.ts b/apps/e2e/test-utils/setup.ts index 9ccb7dd6..3c863527 100644 --- a/apps/e2e/test-utils/setup.ts +++ b/apps/e2e/test-utils/setup.ts @@ -1,11 +1,6 @@ -import MockHttp from "@jaredwray/mockhttp"; -import path from "path"; -import http from "http"; -import net from "net"; -import { env } from "./env.ts"; +import { env } from "../src/util/env.ts"; import createClient from "openapi-fetch"; import type { paths } from "../src/generated/api-schema.js"; -import { afterAll, beforeAll } from "vitest"; const apiClient = createClient({ baseUrl: env.API_URL, @@ -13,49 +8,6 @@ const apiClient = createClient({ declare global { var client: typeof apiClient; - var mock: MockHttp; } globalThis.client = apiClient; - -const MOCK_HTTP_PORT = 4000; - -globalThis.mock = new MockHttp({ - port: MOCK_HTTP_PORT, - autoDetectPort: false, - apiDocs: false, - https: { - cert: path.join(import.meta.dirname, "../src/generated/cert.pem"), - key: path.join(import.meta.dirname, "../src/generated/key.pem"), - }, -}); - -const server = http.createServer((req, res) => { - mock.server.server.emit("request", req, res); -}); - -server.on("connect", (_req, clientSocket, head) => { - const serverSocket = net.connect(MOCK_HTTP_PORT, "localhost", () => { - clientSocket.write("HTTP/1.1 200 Connection Established\r\n\r\n"); - serverSocket.write(head); - serverSocket.pipe(clientSocket); - clientSocket.pipe(serverSocket); - }); - - serverSocket.on("error", () => clientSocket.end()); - clientSocket.on("error", () => serverSocket.end()); -}); - -beforeAll(async () => { - await mock.start(); - await new Promise((res) => - server.listen(env.MOCK_PROXY_PORT, "0.0.0.0", undefined, () => - res(undefined), - ), - ); -}); - -afterAll(async () => { - server.close(); - await mock.close(); -}); diff --git a/apps/e2e/tsconfig.json b/apps/e2e/tsconfig.json index 010d9b21..d41be49d 100644 --- a/apps/e2e/tsconfig.json +++ b/apps/e2e/tsconfig.json @@ -1,4 +1,4 @@ { "extends": "../../tsconfig.json", - "include": ["src/**/*", "scripts/**/*", "test-utils/*"] + "include": ["src", "scripts", "test-utils", "mocks"] } diff --git a/apps/e2e/vitest.config.ts b/apps/e2e/vitest.config.ts index fa747d31..0d910536 100644 --- a/apps/e2e/vitest.config.ts +++ b/apps/e2e/vitest.config.ts @@ -2,6 +2,7 @@ import { defineConfig } from "vitest/config"; export default defineConfig({ test: { + globalSetup: "test-utils/globalSetup.ts", setupFiles: ["test-utils/setup.ts"], watch: false, globals: true, diff --git a/docker-compose.yml b/docker-compose.yml index 183ed703..fc1a24d7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -50,6 +50,19 @@ services: timeout: 5s retries: 5 + proxy: + image: playful-programming-hoof:latest + environment: + MOCK_PROXY_PORT: 3999 + IN_DOCKER: true + profiles: [e2e] + command: node apps/e2e/mocks/index.ts + healthcheck: + test: netstat -tln | grep :3999 + interval: 10s + timeout: 5s + retries: 5 + app: build: . image: playful-programming-hoof:latest @@ -58,8 +71,8 @@ services: environment: NODE_USE_ENV_PROXY: 1 NODE_EXTRA_CA_CERTS: /var/app/apps/e2e/src/generated/cert.pem - HTTP_PROXY: http://e2e:3999 - HTTPS_PROXY: http://e2e:3999 + HTTP_PROXY: http://proxy:3999 + HTTPS_PROXY: http://proxy:3999 NO_PROXY: "localhost, 127.0.0.1" S3_ENDPOINT: http://rustfs:9000 POSTGRES_URL: postgresql://postgres:unicorn@pgsql:5432/postgres @@ -71,7 +84,11 @@ services: condition: service_healthy rustfs: condition: service_healthy + proxy: + condition: service_healthy profiles: [e2e] + ports: + - "3333:3000" healthcheck: test: wget --no-verbose --tries=1 --spider http://0.0.0.0:3000 && wget --no-verbose --tries=1 --spider http://0.0.0.0:3001 || exit 1 interval: 10s @@ -83,8 +100,8 @@ services: e2e: image: playful-programming-hoof:latest environment: - MOCK_PROXY_PORT: 3999 API_URL: http://app:3000 + IN_DOCKER: true depends_on: app: condition: service_healthy From 9d4b0fdea1d83a2055cbf290dfbf953fa88140d9 Mon Sep 17 00:00:00 2001 From: fennifith Date: Mon, 21 Sep 2026 20:26:15 -0400 Subject: [PATCH 10/20] fix typegen check --- apps/e2e/scripts/typegen.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/e2e/scripts/typegen.ts b/apps/e2e/scripts/typegen.ts index 2334bb9e..265a1a8e 100644 --- a/apps/e2e/scripts/typegen.ts +++ b/apps/e2e/scripts/typegen.ts @@ -13,7 +13,7 @@ const baseUrl = "http://localhost:3000"; console.log("Waiting for the dev server to start..."); while ( await fetch(baseUrl) - .then((r) => r.status === 200) + .then((r) => r.status !== 200) .catch((_) => true) ); From a281c92b2fa94c4a2b2ba5bf26801835c4289486 Mon Sep 17 00:00:00 2001 From: fennifith Date: Tue, 22 Sep 2026 18:21:16 -0400 Subject: [PATCH 11/20] rework e2e, improve typegen script, enable multiple app instances in docker --- .gitignore | 3 - .prettierignore | 1 + Dockerfile | 14 +- apps/e2e/.gitignore | 1 - apps/e2e/package.json | 17 - apps/e2e/project.json | 57 -- apps/e2e/scripts/typegen.ts | 60 -- apps/e2e/src/generated/api-schema.d.ts | 784 ++++++++++++++++++ apps/e2e/test-utils/globalSetup.ts | 21 - apps/e2e/test-utils/setup.ts | 13 - apps/worker/src/createWorker.ts | 2 + docker-compose.e2e.yml | 35 + docker-compose.yml | 71 +- e2e/mocks/.gitignore | 1 + {apps/e2e => e2e/mocks}/eslint.config.mjs | 0 e2e/mocks/package.json | 17 + e2e/mocks/project.json | 29 + .../mocks}/scripts/generate-cert.ts | 0 {apps/e2e/src/util => e2e/mocks/src}/env.ts | 2 - {apps/e2e/mocks => e2e/mocks/src}/index.ts | 2 +- {apps/e2e/mocks => e2e/mocks/src}/mock.ts | 4 +- .../mocks => e2e/mocks/src}/url-metadata.ts | 0 {apps/e2e => e2e/mocks}/tsconfig.json | 0 e2e/tests/.gitignore | 2 + e2e/tests/eslint.config.mjs | 22 + e2e/tests/package.json | 20 + e2e/tests/project.json | 45 + e2e/tests/scripts/typegen.ts | 41 + {apps/e2e => e2e/tests}/src/health.spec.ts | 2 - .../tests}/src/url-metadata.spec.ts | 2 - e2e/tests/test-utils/environment.ts | 4 + e2e/tests/test-utils/setup.ts | 11 + e2e/tests/test-utils/spawnApp.ts | 94 +++ e2e/tests/tsconfig.json | 7 + {apps/e2e => e2e/tests}/vitest.config.ts | 3 - knip.json | 2 +- package.json | 2 +- packages/bullmq/package.json | 1 + packages/bullmq/src/queues.ts | 2 + packages/common/src/env.ts | 1 + pnpm-lock.yaml | 55 +- pnpm-workspace.yaml | 1 + project.json | 2 +- 43 files changed, 1193 insertions(+), 260 deletions(-) delete mode 100644 apps/e2e/.gitignore delete mode 100644 apps/e2e/package.json delete mode 100644 apps/e2e/project.json delete mode 100644 apps/e2e/scripts/typegen.ts create mode 100644 apps/e2e/src/generated/api-schema.d.ts delete mode 100644 apps/e2e/test-utils/globalSetup.ts delete mode 100644 apps/e2e/test-utils/setup.ts create mode 100644 docker-compose.e2e.yml create mode 100644 e2e/mocks/.gitignore rename {apps/e2e => e2e/mocks}/eslint.config.mjs (100%) create mode 100644 e2e/mocks/package.json create mode 100644 e2e/mocks/project.json rename {apps/e2e => e2e/mocks}/scripts/generate-cert.ts (100%) rename {apps/e2e/src/util => e2e/mocks/src}/env.ts (74%) rename {apps/e2e/mocks => e2e/mocks/src}/index.ts (94%) rename {apps/e2e/mocks => e2e/mocks/src}/mock.ts (62%) rename {apps/e2e/mocks => e2e/mocks/src}/url-metadata.ts (100%) rename {apps/e2e => e2e/mocks}/tsconfig.json (100%) create mode 100644 e2e/tests/.gitignore create mode 100644 e2e/tests/eslint.config.mjs create mode 100644 e2e/tests/package.json create mode 100644 e2e/tests/project.json create mode 100644 e2e/tests/scripts/typegen.ts rename {apps/e2e => e2e/tests}/src/health.spec.ts (86%) rename {apps/e2e => e2e/tests}/src/url-metadata.spec.ts (92%) create mode 100644 e2e/tests/test-utils/environment.ts create mode 100644 e2e/tests/test-utils/setup.ts create mode 100644 e2e/tests/test-utils/spawnApp.ts create mode 100644 e2e/tests/tsconfig.json rename {apps/e2e => e2e/tests}/vitest.config.ts (65%) diff --git a/.gitignore b/.gitignore index 1a0f197c..d0dfb876 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,3 @@ vite.config.*.timestamp* vitest.config.*.timestamp* - -# Generated files -apps/e2e/src/generated/ diff --git a/.prettierignore b/.prettierignore index b32eada1..4e1d0ab9 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,4 @@ pnpm-lock.yaml drizzle packages/post-images/tests/fixtures +e2e/tests/src/generated diff --git a/Dockerfile b/Dockerfile index 73c546b1..801e8831 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,7 +23,19 @@ COPY --parents apps/*/package.json . COPY --parents packages/*/package.json . RUN pnpm install -# Copy the app +FROM base AS e2e-proxy + +# Additionally install e2e dependencies +COPY --parents e2e/mocks/package.json . +RUN pnpm install + COPY . . +CMD [ "/bin/sh", "-c", "node --watch e2e/mocks/src/index.ts" ] + +FROM base AS app + +# Copy the app +COPY --exclude=e2e . . + CMD [ "/bin/sh", "-c", "parallel --jobs 2 --line-buffer --halt now,done=1 node {} ::: apps/api/src/index.ts apps/worker/src/index.ts" ] diff --git a/apps/e2e/.gitignore b/apps/e2e/.gitignore deleted file mode 100644 index 404abb22..00000000 --- a/apps/e2e/.gitignore +++ /dev/null @@ -1 +0,0 @@ -coverage/ diff --git a/apps/e2e/package.json b/apps/e2e/package.json deleted file mode 100644 index e841b6a5..00000000 --- a/apps/e2e/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "e2e", - "version": "0.0.1", - "private": true, - "type": "module", - "main": "mocks/index.ts", - "dependencies": { - "@jaredwray/mockhttp": "^1.9.0", - "openapi-fetch": "catalog:", - "selfsigned": "^5.5.0", - "typebox": "^1.3.30" - }, - "devDependencies": { - "openapi-typescript": "catalog:", - "vitest": "catalog:" - } -} diff --git a/apps/e2e/project.json b/apps/e2e/project.json deleted file mode 100644 index 5b5ed7a8..00000000 --- a/apps/e2e/project.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "name": "e2e", - "$schema": "../../node_modules/nx/schemas/project-schema.json", - "sourceRoot": "apps/e2e/src", - "projectType": "application", - "tags": [], - "targets": { - "lint": { - "executor": "nx:run-commands", - "options": { - "command": "eslint ./src", - "cwd": "apps/e2e" - } - }, - "typegen": { - "executor": "nx:run-commands", - "options": { - "command": "node scripts/typegen.ts", - "cwd": "apps/e2e" - }, - "outputs": ["{projectRoot}/src/generated/api-schema.d.ts"], - "cache": true, - "inputs": ["{workspaceRoot}/apps/api/**/*"], - "dependsOn": [ - { - "projects": "api", - "target": "dev" - } - ] - }, - "build:cert": { - "executor": "nx:run-commands", - "options": { - "command": "node scripts/generate-cert.ts", - "cwd": "apps/e2e" - }, - "cache": true, - "inputs": ["{projectRoot}/scripts/generate-cert.ts"], - "outputs": ["{projectRoot}/src/generated/*.pem"] - }, - "build:docker": { - "dependsOn": ["build:cert"], - "command": "docker compose --profile e2e build" - }, - "test:docker:clean": { - "executor": "nx:run-commands", - "options": { - "commands": ["docker compose down -v", "docker compose up -d --wait"], - "parallel": false - } - }, - "test": { - "dependsOn": ["build:docker", "test:docker:clean"], - "command": "docker compose --profile e2e up e2e --abort-on-container-exit --attach-dependencies" - } - } -} diff --git a/apps/e2e/scripts/typegen.ts b/apps/e2e/scripts/typegen.ts deleted file mode 100644 index 265a1a8e..00000000 --- a/apps/e2e/scripts/typegen.ts +++ /dev/null @@ -1,60 +0,0 @@ -import fs from "fs/promises"; -import path from "path"; -import { fileURLToPath } from "url"; -import { execSync } from "child_process"; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); - -const outputDir = path.join(__dirname, "../src/generated"); -const outputFile = path.join(outputDir, "api-schema.d.ts"); -const baseUrl = "http://localhost:3000"; - -console.log("Waiting for the dev server to start..."); -while ( - await fetch(baseUrl) - .then((r) => r.status !== 200) - .catch((_) => true) -); - -export default async function generateTypes() { - await fs.mkdir(outputDir, { recursive: true }); - - const swaggerUrl = `${baseUrl}/openapi.json`; - - const response = await fetch(swaggerUrl); - if (!response.ok) { - throw new Error( - `Failed to fetch OpenAPI spec: ${response.status} ${response.statusText}`, - ); - } - const spec = await response.json(); - - console.log("Generating types"); - const tempSpecFile = path.join(outputDir, ".api-spec.json"); - try { - await fs.writeFile(tempSpecFile, JSON.stringify(spec, null, 0)); - execSync( - `pnpm exec openapi-typescript "${tempSpecFile}" -o "${outputFile}"`, - { - stdio: "inherit", - }, - ); - } finally { - await fs.unlink(tempSpecFile); - } - - console.log(`Types generated successfully at ${outputFile}`); - console.log( - `You may need to restart your TypeScript language server for changes to reflect`, - ); -} - -if (process.argv[1] === __filename) { - generateTypes() - .then(() => process.exit(0)) - .catch((err) => { - console.error("generateTypes failed:", err); - process.exit(1); - }); -} diff --git a/apps/e2e/src/generated/api-schema.d.ts b/apps/e2e/src/generated/api-schema.d.ts new file mode 100644 index 00000000..0b58a393 --- /dev/null +++ b/apps/e2e/src/generated/api-schema.d.ts @@ -0,0 +1,784 @@ +/** + * This file was auto-generated by openapi-typescript. + * Do not make direct changes to the file. + */ + +export interface paths { + "/": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Healthcheck */ + get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "text/plain": string; + }; + }; + }; + }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/health/postgres": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Healthcheck - Postgres */ + get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "text/plain": string; + }; + }; + }; + }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/health/redis": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Healthcheck - Redis */ + get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "text/plain": string; + }; + }; + }; + }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/tasks/post-images": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** @description Generate static images for a post, to be used for social media metadata / post list banners. */ + post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + /** + * @example { + * "slug": "example", + * "author": "fennifith", + * "path": "content/fennifith/posts/example/index.md", + * "indexMd5": "6cd3556deb0da54bca060b4c39479839" + * } + */ + "application/json": { + slug: string; + author: string; + path: string; + indexMd5: string; + }; + }; + }; + responses: { + /** @description Task complete */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + banner?: string; + linkPreview?: string; + }; + }; + }; + }; + }; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/tasks/url-metadata": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** @description Fetch and cache metadata for a given URL, including the page title, icon, and banner image. */ + post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + /** + * @example { + * "url": "https://playfulprogramming.com" + * } + */ + "application/json": { + /** Format: uri */ + url: string; + }; + }; + }; + responses: { + /** @description Task complete */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + title?: string; + icon?: { + src: string; + width?: number; + height?: number; + altText?: string; + }; + banner?: { + src: string; + width?: number; + height?: number; + altText?: string; + }; + embed?: { + /** @enum {string} */ + type: "gist"; + gist?: { + username: string; + description?: string; + files: { + filename: string; + contentUrl: string; + language: string; + }[]; + }; + } | { + /** @enum {string} */ + type: "post"; + post?: { + author: { + name: string; + handle: string; + avatar?: { + src: string; + width?: number; + height?: number; + altText?: string; + }; + }; + content: string; + url: string; + image?: { + src: string; + width?: number; + height?: number; + altText?: string; + }; + numLikes?: number; + numReposts?: number; + numReplies?: number; + createdAt: string; + }; + } | { + /** @enum {string} */ + type: "video"; + src?: string; + width?: number; + height?: number; + }; + error: boolean; + }; + }; + }; + }; + }; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/webhooks/github": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** @description Receive a GitHub webhook event (https://docs.github.com/en/webhooks/webhook-events-and-payloads). Verifies the X-Hub-Signature-256 header and enqueues the raw payload - interpreting its contents happens in a separate task. */ + post: { + parameters: { + query?: never; + header: { + "x-hub-signature-256": string; + "x-github-delivery": string; + "x-github-event": string; + }; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": unknown; + }; + }; + responses: { + /** @description Webhook received */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + enqueued: boolean; + }; + }; + }; + }; + }; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/content/authors": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** @description Fetch a list of authors */ + get: { + parameters: { + query: { + page: number; + limit: number; + sortBy: "id" | "posts"; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + name: string; + description: string; + profileImageUrl?: string; + posts: number; + }[]; + }; + }; + }; + }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/content/authors/{slug}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** @description Fetch an author profile with their earned achievements */ + get: { + parameters: { + query?: never; + header?: never; + path: { + slug: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + id: string; + name: string; + description: string; + profileImageUrl?: string; + socials: { + [key: string]: string; + }; + roles: string[]; + achievements: { + id: string; + /** Format: date-time */ + grantedAt: string; + }[]; + }; + }; + }; + /** @description Author not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: string; + }; + }; + }; + }; + }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/content/collections": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** @description Fetch a list of collections */ + get: { + parameters: { + query: { + locale: string; + branch: string; + page: number; + limit: number; + author?: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + slug: string; + coverUrl?: string; + title: string; + description: string; + chapterCount: number; + authors: { + id: string; + name: string; + profileImageUrl?: string; + }[]; + }[]; + }; + }; + }; + }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/content/post/{slug}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** @description Fetch a single post, its authors, its collection chapter list, and its other versions */ + get: { + parameters: { + query: { + locale: string; + branch: string; + }; + header?: never; + path: { + slug: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + slug: string; + title: string; + bannerUrl?: string; + wordCount: number; + /** Format: date-time */ + publishedAt?: string; + authors: { + id: string; + name: string; + profileImageUrl?: string; + }[]; + } & { + description: string; + socialImageUrl?: string; + collection?: { + slug: string; + title: string; + chapters: { + slug: string; + title: string; + }[]; + }; + versions: { + slug: string; + versionName: string; + /** Format: date-time */ + publishedAt: string; + }[]; + }; + }; + }; + /** @description Post not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: string; + }; + }; + }; + }; + }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/content/posts": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** @description Fetch a paginated list of posts for post-card display */ + get: { + parameters: { + query: { + locale: string; + page: number; + limit: number; + sort: "newest" | "oldest"; + author?: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": ({ + slug: string; + title: string; + bannerUrl?: string; + wordCount: number; + /** Format: date-time */ + publishedAt?: string; + authors: { + id: string; + name: string; + profileImageUrl?: string; + }[]; + } & { + tags: string[]; + })[]; + }; + }; + }; + }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/content/schema/post": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** @description Fetch the JSON Schema for post frontmatter, as validated by the sync worker. */ + get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + [key: string]: unknown; + }; + }; + }; + }; + }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/content/schema/collection": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** @description Fetch the JSON Schema for collection frontmatter, as validated by the sync worker. */ + get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + [key: string]: unknown; + }; + }; + }; + }; + }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/content/schema/author": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** @description Fetch the JSON Schema for author frontmatter, as validated by the sync worker. */ + get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + [key: string]: unknown; + }; + }; + }; + }; + }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/development/sync-all": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** @description Spawns tasks to sync all posts and authors to the db */ + post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Default Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; +} +export type webhooks = Record; +export interface components { + schemas: never; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; +} +export type $defs = Record; +export type operations = Record; diff --git a/apps/e2e/test-utils/globalSetup.ts b/apps/e2e/test-utils/globalSetup.ts deleted file mode 100644 index ca66d13e..00000000 --- a/apps/e2e/test-utils/globalSetup.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { env } from "../src/util/env.ts"; -import child_process from "child_process"; - -function exec(command: string): Promise { - return new Promise((res, rej) => { - const child = child_process.exec(command, (error) => - error ? rej(error) : res(), - ); - - child.stdout?.on("data", console.log); - child.stderr?.on("data", console.error); - }); -} - -export async function setup() { - if (!env.IN_DOCKER) { - // If a test is started locally, ensure that the e2e containers are running - console.log("Starting app in docker..."); - await exec("docker compose --profile e2e up app --wait"); - } -} diff --git a/apps/e2e/test-utils/setup.ts b/apps/e2e/test-utils/setup.ts deleted file mode 100644 index 3c863527..00000000 --- a/apps/e2e/test-utils/setup.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { env } from "../src/util/env.ts"; -import createClient from "openapi-fetch"; -import type { paths } from "../src/generated/api-schema.js"; - -const apiClient = createClient({ - baseUrl: env.API_URL, -}); - -declare global { - var client: typeof apiClient; -} - -globalThis.client = apiClient; diff --git a/apps/worker/src/createWorker.ts b/apps/worker/src/createWorker.ts index 28a6396b..14dd47b2 100644 --- a/apps/worker/src/createWorker.ts +++ b/apps/worker/src/createWorker.ts @@ -1,6 +1,7 @@ import { Worker } from "bullmq"; import type { TasksValues } from "@playfulprogramming/bullmq"; import { redis } from "@playfulprogramming/redis"; +import { env } from "@playfulprogramming/common"; export function createWorker( task: T, @@ -9,6 +10,7 @@ export function createWorker( const processorFile = new URL(import.meta.resolve(processor)); const worker = new Worker(task, processorFile, { connection: redis as never, + prefix: env.BULLMQ_PREFIX, concurrency: 2, removeOnComplete: { count: 1000 }, removeOnFail: { count: 5000 }, diff --git a/docker-compose.e2e.yml b/docker-compose.e2e.yml new file mode 100644 index 00000000..7db05c64 --- /dev/null +++ b/docker-compose.e2e.yml @@ -0,0 +1,35 @@ +services: + app: + build: . + image: playful-programming-hoof:latest + env_file: + - .env + environment: + NODE_USE_ENV_PROXY: 1 + NODE_EXTRA_CA_CERTS: /var/app/e2e/mocks/src/generated/cert.pem + HTTP_PROXY: http://proxy:3999 + HTTPS_PROXY: http://proxy:3999 + NO_PROXY: "localhost, 127.0.0.1" + S3_ENDPOINT: http://rustfs:9000 + POSTGRES_URL: postgresql://postgres:unicorn@pgsql:5432/${E2E_POSTGRES_DATABASE} + REDIS_URL: redis://redis:6379 + BULLMQ_PREFIX: ${E2E_BULLMQ_PREFIX} + volumes: + - ./e2e:/var/app/e2e:ro + networks: [shared] + ports: + - "${E2E_PORT}:3000" + healthcheck: + test: wget --no-verbose --tries=1 --spider http://0.0.0.0:3000 && wget --no-verbose --tries=1 --spider http://0.0.0.0:3001 || exit 1 + interval: 10s + timeout: 5s + retries: 5 + start_period: 20s + start_interval: 100ms + post_start: + - command: node packages/db/scripts/migrate.ts + +networks: + shared: + name: hoof_shared_network + external: true diff --git a/docker-compose.yml b/docker-compose.yml index fc1a24d7..b353890c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,6 +13,9 @@ services: interval: 10s timeout: 5s retries: 5 + start_period: 10s + start_interval: 500ms + networks: [shared] ports: - "5432:5432" @@ -20,6 +23,7 @@ services: image: redis:8.2.1-alpine3.22 volumes: - redis_storage:/data + networks: [shared] ports: - "6379:6379" healthcheck: @@ -27,9 +31,12 @@ services: interval: 10s timeout: 5s retries: 5 + start_period: 10s + start_interval: 500ms rustfs: image: rustfs/rustfs + networks: [shared] ports: - "9000:9000" # API Port - "9001:9001" # Web UI Port @@ -49,67 +56,33 @@ services: interval: 10s timeout: 5s retries: 5 + start_period: 10s + start_interval: 500ms proxy: - image: playful-programming-hoof:latest + build: + context: . + target: e2e-proxy + image: playful-programming-hoof-e2e-proxy:latest environment: MOCK_PROXY_PORT: 3999 - IN_DOCKER: true - profiles: [e2e] - command: node apps/e2e/mocks/index.ts + volumes: + - ./e2e/mocks/src:/var/app/e2e/mocks/src:ro + networks: [shared] healthcheck: test: netstat -tln | grep :3999 interval: 10s timeout: 5s retries: 5 - - app: - build: . - image: playful-programming-hoof:latest - env_file: - - .env - environment: - NODE_USE_ENV_PROXY: 1 - NODE_EXTRA_CA_CERTS: /var/app/apps/e2e/src/generated/cert.pem - HTTP_PROXY: http://proxy:3999 - HTTPS_PROXY: http://proxy:3999 - NO_PROXY: "localhost, 127.0.0.1" - S3_ENDPOINT: http://rustfs:9000 - POSTGRES_URL: postgresql://postgres:unicorn@pgsql:5432/postgres - REDIS_URL: redis://redis:6379 - depends_on: - pgsql: - condition: service_healthy - redis: - condition: service_healthy - rustfs: - condition: service_healthy - proxy: - condition: service_healthy - profiles: [e2e] - ports: - - "3333:3000" - healthcheck: - test: wget --no-verbose --tries=1 --spider http://0.0.0.0:3000 && wget --no-verbose --tries=1 --spider http://0.0.0.0:3001 || exit 1 - interval: 10s - timeout: 5s - retries: 5 - post_start: - - command: node packages/db/scripts/migrate.ts - - e2e: - image: playful-programming-hoof:latest - environment: - API_URL: http://app:3000 - IN_DOCKER: true - depends_on: - app: - condition: service_healthy - profiles: [e2e] - command: pnpm -C apps/e2e vitest + start_period: 10s + start_interval: 100ms volumes: pgsql_storage: {} pgsql_backups: {} redis_storage: {} rustfs_storage: {} + +networks: + shared: + name: hoof_shared_network diff --git a/e2e/mocks/.gitignore b/e2e/mocks/.gitignore new file mode 100644 index 00000000..c83f90a2 --- /dev/null +++ b/e2e/mocks/.gitignore @@ -0,0 +1 @@ +src/generated diff --git a/apps/e2e/eslint.config.mjs b/e2e/mocks/eslint.config.mjs similarity index 100% rename from apps/e2e/eslint.config.mjs rename to e2e/mocks/eslint.config.mjs diff --git a/e2e/mocks/package.json b/e2e/mocks/package.json new file mode 100644 index 00000000..3e943a90 --- /dev/null +++ b/e2e/mocks/package.json @@ -0,0 +1,17 @@ +{ + "name": "@playfulprogramming/e2e-mocks", + "version": "0.0.1", + "private": true, + "type": "module", + "main": "src/index.ts", + "scripts": { + "test:eslint": "eslint ./src", + "test:build": "publint --strict", + "build": "tsc --noEmit" + }, + "dependencies": { + "@jaredwray/mockhttp": "^1.9.0", + "selfsigned": "^5.5.0", + "typebox": "^1.3.30" + } +} diff --git a/e2e/mocks/project.json b/e2e/mocks/project.json new file mode 100644 index 00000000..60024b52 --- /dev/null +++ b/e2e/mocks/project.json @@ -0,0 +1,29 @@ +{ + "name": "e2e-mocks", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "apps/e2e/src", + "projectType": "application", + "tags": [], + "targets": { + "lint": { + "executor": "nx:run-commands", + "options": { + "command": "eslint ./src", + "cwd": "{projectRoot}" + } + }, + "build:cert": { + "executor": "nx:run-commands", + "options": { + "command": "node scripts/generate-cert.ts", + "cwd": "{projectRoot}" + }, + "cache": true, + "inputs": ["{projectRoot}/scripts/generate-cert.ts"], + "outputs": ["{projectRoot}/src/generated/*.pem"] + }, + "build": { + "dependsOn": ["build:cert"] + } + } +} diff --git a/apps/e2e/scripts/generate-cert.ts b/e2e/mocks/scripts/generate-cert.ts similarity index 100% rename from apps/e2e/scripts/generate-cert.ts rename to e2e/mocks/scripts/generate-cert.ts diff --git a/apps/e2e/src/util/env.ts b/e2e/mocks/src/env.ts similarity index 74% rename from apps/e2e/src/util/env.ts rename to e2e/mocks/src/env.ts index cdc30750..d3f964fb 100644 --- a/apps/e2e/src/util/env.ts +++ b/e2e/mocks/src/env.ts @@ -4,8 +4,6 @@ import { Value } from "typebox/value"; const EnvSchema = Type.Object({ MOCK_PROXY_PORT: Type.Integer({ default: 3999 }), - API_URL: Type.String({ default: "http://localhost:3333" }), - IN_DOCKER: Type.Boolean({ default: false }), }); Settings.Set({ correctiveParse: true }); diff --git a/apps/e2e/mocks/index.ts b/e2e/mocks/src/index.ts similarity index 94% rename from apps/e2e/mocks/index.ts rename to e2e/mocks/src/index.ts index ad5fa065..2bc7056a 100644 --- a/apps/e2e/mocks/index.ts +++ b/e2e/mocks/src/index.ts @@ -2,7 +2,7 @@ import http from "http"; import net from "net"; import { mock, MOCK_HTTP_PORT } from "./mock.ts"; import "./url-metadata.ts"; -import { env } from "../src/util/env.ts"; +import { env } from "./env.ts"; await mock.start(); diff --git a/apps/e2e/mocks/mock.ts b/e2e/mocks/src/mock.ts similarity index 62% rename from apps/e2e/mocks/mock.ts rename to e2e/mocks/src/mock.ts index fb281a10..18560575 100644 --- a/apps/e2e/mocks/mock.ts +++ b/e2e/mocks/src/mock.ts @@ -8,7 +8,7 @@ export const mock = new MockHttp({ autoDetectPort: false, apiDocs: false, https: { - cert: path.join(import.meta.dirname, "../src/generated/cert.pem"), - key: path.join(import.meta.dirname, "../src/generated/key.pem"), + cert: path.join(import.meta.dirname, "./generated/cert.pem"), + key: path.join(import.meta.dirname, "./generated/key.pem"), }, }); diff --git a/apps/e2e/mocks/url-metadata.ts b/e2e/mocks/src/url-metadata.ts similarity index 100% rename from apps/e2e/mocks/url-metadata.ts rename to e2e/mocks/src/url-metadata.ts diff --git a/apps/e2e/tsconfig.json b/e2e/mocks/tsconfig.json similarity index 100% rename from apps/e2e/tsconfig.json rename to e2e/mocks/tsconfig.json diff --git a/e2e/tests/.gitignore b/e2e/tests/.gitignore new file mode 100644 index 00000000..20c5eb62 --- /dev/null +++ b/e2e/tests/.gitignore @@ -0,0 +1,2 @@ +coverage/ +src/generated diff --git a/e2e/tests/eslint.config.mjs b/e2e/tests/eslint.config.mjs new file mode 100644 index 00000000..68809044 --- /dev/null +++ b/e2e/tests/eslint.config.mjs @@ -0,0 +1,22 @@ +import baseConfig from "../../eslint.config.mjs"; + +export default [ + ...baseConfig, + { + files: ["**/*.json"], + rules: { + "@nx/dependency-checks": [ + "error", + { + ignoredFiles: [ + "{projectRoot}/eslint.config.{js,cjs,mjs,ts,cts,mts}", + "{projectRoot}/vite.config.{js,ts,mjs,mts}", + ], + }, + ], + }, + languageOptions: { + parser: await import("jsonc-eslint-parser"), + }, + }, +]; diff --git a/e2e/tests/package.json b/e2e/tests/package.json new file mode 100644 index 00000000..98f09801 --- /dev/null +++ b/e2e/tests/package.json @@ -0,0 +1,20 @@ +{ + "name": "@playfulprogramming/e2e-tests", + "version": "0.0.1", + "private": true, + "type": "module", + "scripts": { + "test:eslint": "eslint ./src", + "test:build": "publint --strict", + "test": "vitest run", + "build": "tsc --noEmit" + }, + "dependencies": { + "@playfulprogramming/db": "workspace:*", + "openapi-fetch": "catalog:" + }, + "devDependencies": { + "openapi-typescript": "catalog:", + "vitest": "catalog:" + } +} diff --git a/e2e/tests/project.json b/e2e/tests/project.json new file mode 100644 index 00000000..71909f1d --- /dev/null +++ b/e2e/tests/project.json @@ -0,0 +1,45 @@ +{ + "name": "e2e-tests", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "apps/e2e/src", + "projectType": "application", + "tags": [], + "targets": { + "lint": { + "executor": "nx:run-commands", + "options": { + "command": "eslint ./src", + "cwd": "e2e/tests" + } + }, + "build:typegen": { + "executor": "nx:run-commands", + "options": { + "command": "node scripts/typegen.ts", + "cwd": "e2e/tests" + }, + "outputs": ["{projectRoot}/src/generated/api-schema.d.ts"], + "cache": true, + "inputs": ["{workspaceRoot}/apps/api/**/*", "{projectRoot}/**/*"], + "dependsOn": ["docker:build", "docker:start"] + }, + "build": { + "dependsOn": ["^build", "build:typegen"] + }, + "docker:build": { + "dependsOn": ["^build:cert"], + "command": "docker compose -f ./docker-compose.yml -f ./docker-compose.e2e.yml build" + }, + "docker:start": { + "executor": "nx:run-commands", + "options": { + "commands": ["docker compose up -d --wait"], + "parallel": false + } + }, + "test": { + "dependsOn": ["build", "docker:build", "docker:start"] + } + }, + "implicitDependencies": ["e2e-mocks"] +} diff --git a/e2e/tests/scripts/typegen.ts b/e2e/tests/scripts/typegen.ts new file mode 100644 index 00000000..83717f78 --- /dev/null +++ b/e2e/tests/scripts/typegen.ts @@ -0,0 +1,41 @@ +import fs from "fs/promises"; +import path from "path"; +import { spawnApp } from "../test-utils/spawnApp.ts"; +import openapiTypescript, { astToString } from "openapi-typescript"; + +const outputDir = path.join(import.meta.dirname, "../src/generated"); +const outputFile = path.join(outputDir, "api-schema.d.ts"); + +export default async function generateTypes() { + await fs.mkdir(outputDir, { recursive: true }); + + const app = await spawnApp(); + const response = await fetch(`${app.baseUrl}/openapi.json`); + if (!response.ok) { + throw new Error( + `Failed to fetch OpenAPI spec: ${response.status} ${response.statusText}`, + ); + } + const spec = await response.json(); + await app.close(); + + console.log("Generating types"); + await fs.writeFile( + outputFile, + astToString(await openapiTypescript(spec, {})), + ); + + console.log(`Types generated successfully at ${outputFile}`); + console.log( + `You may need to restart your TypeScript language server for changes to reflect`, + ); +} + +if (process.argv[1] === import.meta.filename) { + generateTypes() + .then(() => process.exit(0)) + .catch((err) => { + console.error("generateTypes failed:", err); + process.exit(1); + }); +} diff --git a/apps/e2e/src/health.spec.ts b/e2e/tests/src/health.spec.ts similarity index 86% rename from apps/e2e/src/health.spec.ts rename to e2e/tests/src/health.spec.ts index 2411f6ac..db990f81 100644 --- a/apps/e2e/src/health.spec.ts +++ b/e2e/tests/src/health.spec.ts @@ -1,5 +1,3 @@ -import { describe, it, expect } from "vitest"; - describe("E2E: Health Check", () => { it.each(["/", "/health/postgres", "/health/redis"] as const)( "should respond 200 for %s", diff --git a/apps/e2e/src/url-metadata.spec.ts b/e2e/tests/src/url-metadata.spec.ts similarity index 92% rename from apps/e2e/src/url-metadata.spec.ts rename to e2e/tests/src/url-metadata.spec.ts index 131f98a5..abe0c9b7 100644 --- a/apps/e2e/src/url-metadata.spec.ts +++ b/e2e/tests/src/url-metadata.spec.ts @@ -1,5 +1,3 @@ -import { describe, test, expect, vi } from "vitest"; - describe("url-metadata", () => { test("returns url-metadata", async () => { const url = `https://example.com/`; diff --git a/e2e/tests/test-utils/environment.ts b/e2e/tests/test-utils/environment.ts new file mode 100644 index 00000000..1ff1a596 --- /dev/null +++ b/e2e/tests/test-utils/environment.ts @@ -0,0 +1,4 @@ +import path from "path"; +import { loadEnvFile } from "process"; + +loadEnvFile(path.join(import.meta.dirname, "../../../.env.example")); diff --git a/e2e/tests/test-utils/setup.ts b/e2e/tests/test-utils/setup.ts new file mode 100644 index 00000000..0c0a82b5 --- /dev/null +++ b/e2e/tests/test-utils/setup.ts @@ -0,0 +1,11 @@ +import { spawnApp } from "./spawnApp.ts"; + +const app = await spawnApp(); + +declare global { + var client: typeof app.client; +} + +globalThis.client = app.client; + +afterAll(app.close); diff --git a/e2e/tests/test-utils/spawnApp.ts b/e2e/tests/test-utils/spawnApp.ts new file mode 100644 index 00000000..f64e7ecc --- /dev/null +++ b/e2e/tests/test-utils/spawnApp.ts @@ -0,0 +1,94 @@ +import "./environment.ts"; +import createClient from "openapi-fetch"; +import type { paths } from "../src/generated/api-schema.d.ts"; +import process from "process"; +import child_process from "child_process"; +import net from "net"; +import { randomUUID } from "crypto"; +import { db } from "@playfulprogramming/db"; +import { once } from "events"; +import path from "path"; + +function findOpenPort(): Promise { + return new Promise((res) => { + const server = net.createServer(); + server.listen(0, () => { + const { port } = server.address() as net.AddressInfo; + server.close(() => res(port)); + }); + }); +} + +type ExecOptions = { + command: string; + env?: Record; +}; +function exec({ command, env }: ExecOptions) { + const child = child_process.spawn(command, { + env, + shell: true, + detached: true, + stdio: "inherit", + }); + + return { + kill() { + if (child.pid) process.kill(-child.pid); + }, + exit: once(child, "close"), + }; +} + +export async function spawnApp() { + const port = await findOpenPort(); + const uuid = randomUUID(); + + console.log(`Spawning app ${uuid}...`); + + const database = `e2e-${uuid}`; + await db.execute(`CREATE DATABASE "${database}"`); + + const env = { + COMPOSE_PROJECT_NAME: `hoof-e2e-${uuid}`, + COMPOSE_FILE: path.resolve( + import.meta.dirname, + "../../../docker-compose.e2e.yml", + ), + COMPOSE_PROGRESS: "quiet", + E2E_PORT: String(port), + E2E_POSTGRES_DATABASE: database, + E2E_BULLMQ_PREFIX: database, + }; + + await exec({ + command: "docker compose up app --wait", + env, + }).exit; + + const dockerLogs = exec({ + command: "docker compose logs --follow", + env, + }); + + console.log("Ready!"); + + const baseUrl = `http://localhost:${port}`; + const client = createClient({ + baseUrl, + }); + + return { + baseUrl, + client, + async close() { + console.log(`Closing app ${uuid}`); + dockerLogs.kill(); + await dockerLogs.exit; + await exec({ + command: "docker compose down", + env, + }).exit; + await db.execute(`DROP DATABASE "${database}"`); + }, + }; +} diff --git a/e2e/tests/tsconfig.json b/e2e/tests/tsconfig.json new file mode 100644 index 00000000..776ca57a --- /dev/null +++ b/e2e/tests/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "types": ["node", "vitest/globals"] + }, + "include": ["src", "scripts", "test-utils"] +} diff --git a/apps/e2e/vitest.config.ts b/e2e/tests/vitest.config.ts similarity index 65% rename from apps/e2e/vitest.config.ts rename to e2e/tests/vitest.config.ts index 0d910536..c78b4170 100644 --- a/apps/e2e/vitest.config.ts +++ b/e2e/tests/vitest.config.ts @@ -2,11 +2,8 @@ import { defineConfig } from "vitest/config"; export default defineConfig({ test: { - globalSetup: "test-utils/globalSetup.ts", setupFiles: ["test-utils/setup.ts"], watch: false, globals: true, - fileParallelism: false, - maxWorkers: 1, }, }); diff --git a/knip.json b/knip.json index 0edde293..29285f5d 100644 --- a/knip.json +++ b/knip.json @@ -1,6 +1,6 @@ { "$schema": "./node_modules/knip/schema.json", - "ignoreDependencies": ["@aws-sdk/types", "openapi-typescript"], + "ignoreDependencies": ["@aws-sdk/types"], "workspaces": { "apps/worker": { "entry": ["src/tasks/*/*.ts"] diff --git a/package.json b/package.json index c57ccb88..5b089fa2 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "prepare": "husky", "test": "nx run test", "test:unit": "nx run test:unit", - "test:e2e": "nx run e2e:test", + "test:e2e": "nx run e2e-tests:test", "build:all": "nx run-many --target=build", "dev": "nx run dev", "prettier": "prettier . --check --ignore-unknown", diff --git a/packages/bullmq/package.json b/packages/bullmq/package.json index fc5a5a92..9466e72f 100644 --- a/packages/bullmq/package.json +++ b/packages/bullmq/package.json @@ -11,6 +11,7 @@ "build": "tsc --noEmit" }, "dependencies": { + "@playfulprogramming/common": "workspace:*", "@playfulprogramming/redis": "workspace:*", "bullmq": "catalog:", "typebox": "catalog:" diff --git a/packages/bullmq/src/queues.ts b/packages/bullmq/src/queues.ts index 8f89f793..eafa240e 100644 --- a/packages/bullmq/src/queues.ts +++ b/packages/bullmq/src/queues.ts @@ -1,5 +1,6 @@ import { Queue } from "bullmq"; import { redis } from "@playfulprogramming/redis"; +import { env } from "@playfulprogramming/common"; import { type TasksValues, type TaskInputs, @@ -18,6 +19,7 @@ export function createQueue( const newQueue: Queue = new Queue(task, { connection: redis, + prefix: env.BULLMQ_PREFIX, defaultJobOptions: { attempts: 3, backoff: { diff --git a/packages/common/src/env.ts b/packages/common/src/env.ts index cbc77fa5..1effc094 100644 --- a/packages/common/src/env.ts +++ b/packages/common/src/env.ts @@ -22,6 +22,7 @@ const EnvSchema = Type.Object({ REDIS_URL: Type.String({ pattern: "^redis://.+$" }), REDIS_PASSWORD: Type.Optional(Type.String()), + BULLMQ_PREFIX: Type.Optional(Type.String()), // Rate limiting configuration RATE_LIMIT_MAX: Type.Integer({ default: 10_000 }), diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3400aa60..7167bac8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -315,28 +315,6 @@ importers: specifier: 'catalog:' version: 4.1.7(@opentelemetry/api@1.9.0)(@types/node@26.5.1)(@vitest/coverage-v8@4.1.7)(vite@8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0)) - apps/e2e: - dependencies: - '@jaredwray/mockhttp': - specifier: ^1.9.0 - version: 1.9.0(axios@1.18.1(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))(change-case@5.4.4)(supports-color@10.2.2)(tailwindcss@4.3.3)(typescript@6.0.3)(zod@4.4.3) - openapi-fetch: - specifier: 'catalog:' - version: 0.17.0 - selfsigned: - specifier: ^5.5.0 - version: 5.5.0 - typebox: - specifier: ^1.3.30 - version: 1.3.34 - devDependencies: - openapi-typescript: - specifier: 'catalog:' - version: 7.13.0(typescript@6.0.3) - vitest: - specifier: 'catalog:' - version: 4.1.7(@opentelemetry/api@1.9.0)(@types/node@26.5.1)(@vitest/coverage-v8@4.1.7)(vite@8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0)) - apps/worker: dependencies: '@playfulprogramming/bullmq': @@ -425,8 +403,39 @@ importers: specifier: 'catalog:' version: 4.1.7(@opentelemetry/api@1.9.0)(@types/node@26.5.1)(@vitest/coverage-v8@4.1.7)(vite@8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0)) + e2e/mocks: + dependencies: + '@jaredwray/mockhttp': + specifier: ^1.9.0 + version: 1.9.0(axios@1.18.1(debug@4.4.3(supports-color@10.2.2))(supports-color@10.2.2))(change-case@5.4.4)(supports-color@10.2.2)(tailwindcss@4.3.3)(typescript@6.0.3)(zod@4.4.3) + selfsigned: + specifier: ^5.5.0 + version: 5.5.0 + typebox: + specifier: ^1.3.30 + version: 1.3.34 + + e2e/tests: + dependencies: + '@playfulprogramming/db': + specifier: workspace:* + version: link:../../packages/db + openapi-fetch: + specifier: 'catalog:' + version: 0.17.0 + devDependencies: + openapi-typescript: + specifier: 'catalog:' + version: 7.13.0(typescript@6.0.3) + vitest: + specifier: 'catalog:' + version: 4.1.7(@opentelemetry/api@1.9.0)(@types/node@26.5.1)(@vitest/coverage-v8@4.1.7)(vite@8.2.2(@types/node@26.5.1)(jiti@2.7.0)(yaml@2.9.0)) + packages/bullmq: dependencies: + '@playfulprogramming/common': + specifier: workspace:* + version: link:../common '@playfulprogramming/redis': specifier: workspace:* version: link:../redis @@ -7887,7 +7896,7 @@ snapshots: '@floating-ui/vue@1.1.9(vue@3.5.42(typescript@6.0.3))': dependencies: '@floating-ui/dom': 1.8.0 - '@floating-ui/utils': 0.2.10 + '@floating-ui/utils': 0.2.12 vue-demi: 0.14.10(vue@3.5.42(typescript@6.0.3)) transitivePeerDependencies: - '@vue/composition-api' diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 8e810f1e..ee578e5c 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,5 +1,6 @@ packages: - apps/** + - e2e/** - packages/** catalog: diff --git a/project.json b/project.json index cda7fd3b..7fe5361a 100644 --- a/project.json +++ b/project.json @@ -29,7 +29,7 @@ "dependsOn": [ "test:unit", { - "projects": "apps/e2e", + "projects": "e2e-tests", "target": "test" } ] From 8126db04c5854ab93e91bacce3ef6e0e0cf40062 Mon Sep 17 00:00:00 2001 From: fennifith Date: Tue, 22 Sep 2026 18:28:26 -0400 Subject: [PATCH 12/20] exclude e2e typegen from unit test tasks --- package.json | 2 +- project.json | 30 ++++++++++++++++++++---------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 5b089fa2..32ab37c0 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "prepare": "husky", "test": "nx run test", "test:unit": "nx run test:unit", - "test:e2e": "nx run e2e-tests:test", + "test:e2e": "nx run test:e2e", "build:all": "nx run-many --target=build", "dev": "nx run dev", "prettier": "prettier . --check --ignore-unknown", diff --git a/project.json b/project.json index 7fe5361a..3e9995e9 100644 --- a/project.json +++ b/project.json @@ -26,29 +26,23 @@ "inputs": ["{workspaceRoot}/**/*"] }, "test": { - "dependsOn": [ - "test:unit", - { - "projects": "e2e-tests", - "target": "test" - } - ] + "dependsOn": ["test:unit", "test:e2e"] }, "test:unit": { "dependsOn": [ "prettier", { - "projects": "*", + "projects": ["apps/*", "packages/*"], "target": "test:build" }, "test:sherif", "test:knip", { - "projects": "*", + "projects": ["apps/*", "packages/*"], "target": "test:eslint" }, { - "projects": ["apps/api", "apps/worker"], + "projects": "apps/*", "target": "test" }, { @@ -57,6 +51,22 @@ } ] }, + "test:e2e": { + "dependsOn": [ + { + "projects": "e2e/*", + "target": "test:build" + }, + { + "projects": "e2e/*", + "target": "test:eslint" + }, + { + "projects": "e2e/*", + "target": "test" + } + ] + }, "test:build": { "cache": true, "inputs": ["{workspaceRoot}/**/*"], From 056cb000a62206b414b93f3d2060a890397bcd70 Mon Sep 17 00:00:00 2001 From: fennifith Date: Tue, 22 Sep 2026 18:37:12 -0400 Subject: [PATCH 13/20] make e2e eslint consistent with other packages, remove unused checks --- e2e/mocks/eslint.config.mjs | 25 ++++--------------------- e2e/mocks/tsconfig.json | 2 +- e2e/tests/eslint.config.mjs | 25 ++++--------------------- e2e/tests/tsconfig.json | 2 +- package.json | 1 - pnpm-lock.yaml | 13 ------------- 6 files changed, 10 insertions(+), 58 deletions(-) diff --git a/e2e/mocks/eslint.config.mjs b/e2e/mocks/eslint.config.mjs index 68809044..1bdecdcd 100644 --- a/e2e/mocks/eslint.config.mjs +++ b/e2e/mocks/eslint.config.mjs @@ -1,22 +1,5 @@ -import baseConfig from "../../eslint.config.mjs"; +// @ts-check -export default [ - ...baseConfig, - { - files: ["**/*.json"], - rules: { - "@nx/dependency-checks": [ - "error", - { - ignoredFiles: [ - "{projectRoot}/eslint.config.{js,cjs,mjs,ts,cts,mts}", - "{projectRoot}/vite.config.{js,ts,mjs,mts}", - ], - }, - ], - }, - languageOptions: { - parser: await import("jsonc-eslint-parser"), - }, - }, -]; +import rootConfig from "../../eslint.config.mjs"; + +export default [...rootConfig]; diff --git a/e2e/mocks/tsconfig.json b/e2e/mocks/tsconfig.json index d41be49d..ddd9b8c7 100644 --- a/e2e/mocks/tsconfig.json +++ b/e2e/mocks/tsconfig.json @@ -1,4 +1,4 @@ { "extends": "../../tsconfig.json", - "include": ["src", "scripts", "test-utils", "mocks"] + "include": ["src", "scripts", "test-utils", "mocks", "eslint.config.mjs"] } diff --git a/e2e/tests/eslint.config.mjs b/e2e/tests/eslint.config.mjs index 68809044..1bdecdcd 100644 --- a/e2e/tests/eslint.config.mjs +++ b/e2e/tests/eslint.config.mjs @@ -1,22 +1,5 @@ -import baseConfig from "../../eslint.config.mjs"; +// @ts-check -export default [ - ...baseConfig, - { - files: ["**/*.json"], - rules: { - "@nx/dependency-checks": [ - "error", - { - ignoredFiles: [ - "{projectRoot}/eslint.config.{js,cjs,mjs,ts,cts,mts}", - "{projectRoot}/vite.config.{js,ts,mjs,mts}", - ], - }, - ], - }, - languageOptions: { - parser: await import("jsonc-eslint-parser"), - }, - }, -]; +import rootConfig from "../../eslint.config.mjs"; + +export default [...rootConfig]; diff --git a/e2e/tests/tsconfig.json b/e2e/tests/tsconfig.json index 776ca57a..aa7f5c86 100644 --- a/e2e/tests/tsconfig.json +++ b/e2e/tests/tsconfig.json @@ -3,5 +3,5 @@ "compilerOptions": { "types": ["node", "vitest/globals"] }, - "include": ["src", "scripts", "test-utils"] + "include": ["src", "scripts", "test-utils", "eslint.config.mjs"] } diff --git a/package.json b/package.json index 32ab37c0..397199bb 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,6 @@ "eslint": "^10.10.0", "globals": "^17.6.0", "husky": "^9.1.7", - "jsonc-eslint-parser": "^3.1.0", "knip": "^6.14.1", "lint-staged": "^17.0.5", "nx": "23.1.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7167bac8..fc49fe78 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -232,9 +232,6 @@ importers: husky: specifier: ^9.1.7 version: 9.1.7 - jsonc-eslint-parser: - specifier: ^3.1.0 - version: 3.1.0 knip: specifier: ^6.14.1 version: 6.14.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3) @@ -4679,10 +4676,6 @@ packages: engines: {node: '>=6'} hasBin: true - jsonc-eslint-parser@3.1.0: - resolution: {integrity: sha512-75EA7EWZExL/j+MDKQrRbdzcRI2HOkRlmUw8fZJc1ioqFEOvBsq7Rt+A6yCxOt9w/TYNpkt52gC6nm/g5tFIng==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - jsonc-parser@3.2.0: resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} @@ -11068,12 +11061,6 @@ snapshots: json5@2.2.3: {} - jsonc-eslint-parser@3.1.0: - dependencies: - acorn: 8.16.0 - eslint-visitor-keys: 5.0.1 - semver: 7.8.0 - jsonc-parser@3.2.0: {} jsonc-parser@3.3.1: {} From c9c870f00d1af5ca8d728956b8220a50e730e3b4 Mon Sep 17 00:00:00 2001 From: fennifith Date: Tue, 22 Sep 2026 21:59:46 -0400 Subject: [PATCH 14/20] refactor spawnApp to await using --- e2e/tests/scripts/typegen.ts | 3 +-- e2e/tests/src/health.spec.ts | 4 +++- e2e/tests/src/url-metadata.spec.ts | 6 ++++-- e2e/tests/test-utils/setup.ts | 10 +++------- e2e/tests/test-utils/spawnApp.ts | 2 +- 5 files changed, 12 insertions(+), 13 deletions(-) diff --git a/e2e/tests/scripts/typegen.ts b/e2e/tests/scripts/typegen.ts index 83717f78..9d04a803 100644 --- a/e2e/tests/scripts/typegen.ts +++ b/e2e/tests/scripts/typegen.ts @@ -9,7 +9,7 @@ const outputFile = path.join(outputDir, "api-schema.d.ts"); export default async function generateTypes() { await fs.mkdir(outputDir, { recursive: true }); - const app = await spawnApp(); + await using app = await spawnApp(); const response = await fetch(`${app.baseUrl}/openapi.json`); if (!response.ok) { throw new Error( @@ -17,7 +17,6 @@ export default async function generateTypes() { ); } const spec = await response.json(); - await app.close(); console.log("Generating types"); await fs.writeFile( diff --git a/e2e/tests/src/health.spec.ts b/e2e/tests/src/health.spec.ts index db990f81..c15a7f62 100644 --- a/e2e/tests/src/health.spec.ts +++ b/e2e/tests/src/health.spec.ts @@ -1,8 +1,10 @@ +await using app = await spawnApp(); + describe("E2E: Health Check", () => { it.each(["/", "/health/postgres", "/health/redis"] as const)( "should respond 200 for %s", async (path) => { - const res = await client.GET(path, { + const res = await app.client.GET(path, { parseAs: "text", }); expect(res.response.status).toBe(200); diff --git a/e2e/tests/src/url-metadata.spec.ts b/e2e/tests/src/url-metadata.spec.ts index abe0c9b7..7763bdc8 100644 --- a/e2e/tests/src/url-metadata.spec.ts +++ b/e2e/tests/src/url-metadata.spec.ts @@ -1,9 +1,11 @@ +await using app = await spawnApp(); + describe("url-metadata", () => { test("returns url-metadata", async () => { const url = `https://example.com/`; // Enqueue the URL metadata task - const enqueueRes = await client.POST("/tasks/url-metadata", { + const enqueueRes = await app.client.POST("/tasks/url-metadata", { body: { url }, }); expect(enqueueRes.response.status).toBe(201); @@ -12,7 +14,7 @@ describe("url-metadata", () => { const res = await vi.waitFor( async () => { // Fetch the processed result - const res = await client.POST("/tasks/url-metadata", { + const res = await app.client.POST("/tasks/url-metadata", { body: { url }, }); expect(res.response.status).toBe(200); diff --git a/e2e/tests/test-utils/setup.ts b/e2e/tests/test-utils/setup.ts index 0c0a82b5..ac8fd9ba 100644 --- a/e2e/tests/test-utils/setup.ts +++ b/e2e/tests/test-utils/setup.ts @@ -1,11 +1,7 @@ -import { spawnApp } from "./spawnApp.ts"; - -const app = await spawnApp(); +import { spawnApp as spawnAppImpl } from "./spawnApp.ts"; declare global { - var client: typeof app.client; + var spawnApp: typeof spawnAppImpl; } -globalThis.client = app.client; - -afterAll(app.close); +globalThis.spawnApp = spawnAppImpl; diff --git a/e2e/tests/test-utils/spawnApp.ts b/e2e/tests/test-utils/spawnApp.ts index f64e7ecc..ff6e439f 100644 --- a/e2e/tests/test-utils/spawnApp.ts +++ b/e2e/tests/test-utils/spawnApp.ts @@ -80,7 +80,7 @@ export async function spawnApp() { return { baseUrl, client, - async close() { + async [Symbol.asyncDispose]() { console.log(`Closing app ${uuid}`); dockerLogs.kill(); await dockerLogs.exit; From 4449a34dbcf66a41495b630e3cb8ed07195f69c9 Mon Sep 17 00:00:00 2001 From: fennifith Date: Thu, 24 Sep 2026 08:51:22 -0400 Subject: [PATCH 15/20] use await using for docker cleanup --- e2e/tests/src/health.spec.ts | 2 +- e2e/tests/src/url-metadata.spec.ts | 2 +- e2e/tests/test-utils/setup.ts | 6 +++++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/e2e/tests/src/health.spec.ts b/e2e/tests/src/health.spec.ts index c15a7f62..139d29a3 100644 --- a/e2e/tests/src/health.spec.ts +++ b/e2e/tests/src/health.spec.ts @@ -1,4 +1,4 @@ -await using app = await spawnApp(); +const app = await spawnApp(); describe("E2E: Health Check", () => { it.each(["/", "/health/postgres", "/health/redis"] as const)( diff --git a/e2e/tests/src/url-metadata.spec.ts b/e2e/tests/src/url-metadata.spec.ts index 7763bdc8..a1068b96 100644 --- a/e2e/tests/src/url-metadata.spec.ts +++ b/e2e/tests/src/url-metadata.spec.ts @@ -1,4 +1,4 @@ -await using app = await spawnApp(); +const app = await spawnApp(); describe("url-metadata", () => { test("returns url-metadata", async () => { diff --git a/e2e/tests/test-utils/setup.ts b/e2e/tests/test-utils/setup.ts index ac8fd9ba..1fe451f4 100644 --- a/e2e/tests/test-utils/setup.ts +++ b/e2e/tests/test-utils/setup.ts @@ -4,4 +4,8 @@ declare global { var spawnApp: typeof spawnAppImpl; } -globalThis.spawnApp = spawnAppImpl; +globalThis.spawnApp = async () => { + const app = await spawnAppImpl(); + afterAll(app[Symbol.asyncDispose]); + return app; +}; From eed4703809692c6d509a755b808b3736cae6545b Mon Sep 17 00:00:00 2001 From: fennifith Date: Thu, 24 Sep 2026 08:55:20 -0400 Subject: [PATCH 16/20] check exit code when awaiting process --- e2e/tests/test-utils/spawnApp.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/e2e/tests/test-utils/spawnApp.ts b/e2e/tests/test-utils/spawnApp.ts index ff6e439f..3c5503a7 100644 --- a/e2e/tests/test-utils/spawnApp.ts +++ b/e2e/tests/test-utils/spawnApp.ts @@ -35,7 +35,11 @@ function exec({ command, env }: ExecOptions) { kill() { if (child.pid) process.kill(-child.pid); }, - exit: once(child, "close"), + exit: once(child, "close").then(() => { + if (child.exitCode !== null && child.exitCode !== 0) { + throw new Error(`'${command} exited with code ${child.exitCode}`); + } + }), }; } From 788eef6da011943d4ae5d528ec38877d1f0cd48c Mon Sep 17 00:00:00 2001 From: fennifith Date: Thu, 24 Sep 2026 09:00:33 -0400 Subject: [PATCH 17/20] add docker:build dep to docker:start --- e2e/tests/project.json | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/e2e/tests/project.json b/e2e/tests/project.json index 71909f1d..689b1020 100644 --- a/e2e/tests/project.json +++ b/e2e/tests/project.json @@ -31,11 +31,8 @@ "command": "docker compose -f ./docker-compose.yml -f ./docker-compose.e2e.yml build" }, "docker:start": { - "executor": "nx:run-commands", - "options": { - "commands": ["docker compose up -d --wait"], - "parallel": false - } + "dependsOn": ["docker:build"], + "command": "docker compose up -d --wait" }, "test": { "dependsOn": ["build", "docker:build", "docker:start"] From 4e150adad512f1b273df56c067dc69f6810ddc25 Mon Sep 17 00:00:00 2001 From: fennifith Date: Fri, 9 Oct 2026 20:16:40 -0400 Subject: [PATCH 18/20] remove apps/e2e --- apps/e2e/src/generated/api-schema.d.ts | 784 ------------------------- 1 file changed, 784 deletions(-) delete mode 100644 apps/e2e/src/generated/api-schema.d.ts diff --git a/apps/e2e/src/generated/api-schema.d.ts b/apps/e2e/src/generated/api-schema.d.ts deleted file mode 100644 index 0b58a393..00000000 --- a/apps/e2e/src/generated/api-schema.d.ts +++ /dev/null @@ -1,784 +0,0 @@ -/** - * This file was auto-generated by openapi-typescript. - * Do not make direct changes to the file. - */ - -export interface paths { - "/": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** Healthcheck */ - get: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Successful response */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "text/plain": string; - }; - }; - }; - }; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/health/postgres": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** Healthcheck - Postgres */ - get: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Successful response */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "text/plain": string; - }; - }; - }; - }; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/health/redis": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** Healthcheck - Redis */ - get: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Successful response */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "text/plain": string; - }; - }; - }; - }; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/tasks/post-images": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** @description Generate static images for a post, to be used for social media metadata / post list banners. */ - post: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - /** - * @example { - * "slug": "example", - * "author": "fennifith", - * "path": "content/fennifith/posts/example/index.md", - * "indexMd5": "6cd3556deb0da54bca060b4c39479839" - * } - */ - "application/json": { - slug: string; - author: string; - path: string; - indexMd5: string; - }; - }; - }; - responses: { - /** @description Task complete */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": { - banner?: string; - linkPreview?: string; - }; - }; - }; - }; - }; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/tasks/url-metadata": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** @description Fetch and cache metadata for a given URL, including the page title, icon, and banner image. */ - post: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - /** - * @example { - * "url": "https://playfulprogramming.com" - * } - */ - "application/json": { - /** Format: uri */ - url: string; - }; - }; - }; - responses: { - /** @description Task complete */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": { - title?: string; - icon?: { - src: string; - width?: number; - height?: number; - altText?: string; - }; - banner?: { - src: string; - width?: number; - height?: number; - altText?: string; - }; - embed?: { - /** @enum {string} */ - type: "gist"; - gist?: { - username: string; - description?: string; - files: { - filename: string; - contentUrl: string; - language: string; - }[]; - }; - } | { - /** @enum {string} */ - type: "post"; - post?: { - author: { - name: string; - handle: string; - avatar?: { - src: string; - width?: number; - height?: number; - altText?: string; - }; - }; - content: string; - url: string; - image?: { - src: string; - width?: number; - height?: number; - altText?: string; - }; - numLikes?: number; - numReposts?: number; - numReplies?: number; - createdAt: string; - }; - } | { - /** @enum {string} */ - type: "video"; - src?: string; - width?: number; - height?: number; - }; - error: boolean; - }; - }; - }; - }; - }; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/webhooks/github": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** @description Receive a GitHub webhook event (https://docs.github.com/en/webhooks/webhook-events-and-payloads). Verifies the X-Hub-Signature-256 header and enqueues the raw payload - interpreting its contents happens in a separate task. */ - post: { - parameters: { - query?: never; - header: { - "x-hub-signature-256": string; - "x-github-delivery": string; - "x-github-event": string; - }; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": unknown; - }; - }; - responses: { - /** @description Webhook received */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": { - enqueued: boolean; - }; - }; - }; - }; - }; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/content/authors": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** @description Fetch a list of authors */ - get: { - parameters: { - query: { - page: number; - limit: number; - sortBy: "id" | "posts"; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Successful */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": { - id: string; - name: string; - description: string; - profileImageUrl?: string; - posts: number; - }[]; - }; - }; - }; - }; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/content/authors/{slug}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** @description Fetch an author profile with their earned achievements */ - get: { - parameters: { - query?: never; - header?: never; - path: { - slug: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Successful */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": { - id: string; - name: string; - description: string; - profileImageUrl?: string; - socials: { - [key: string]: string; - }; - roles: string[]; - achievements: { - id: string; - /** Format: date-time */ - grantedAt: string; - }[]; - }; - }; - }; - /** @description Author not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": { - error: string; - }; - }; - }; - }; - }; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/content/collections": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** @description Fetch a list of collections */ - get: { - parameters: { - query: { - locale: string; - branch: string; - page: number; - limit: number; - author?: string; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Successful */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": { - slug: string; - coverUrl?: string; - title: string; - description: string; - chapterCount: number; - authors: { - id: string; - name: string; - profileImageUrl?: string; - }[]; - }[]; - }; - }; - }; - }; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/content/post/{slug}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** @description Fetch a single post, its authors, its collection chapter list, and its other versions */ - get: { - parameters: { - query: { - locale: string; - branch: string; - }; - header?: never; - path: { - slug: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Successful */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": { - slug: string; - title: string; - bannerUrl?: string; - wordCount: number; - /** Format: date-time */ - publishedAt?: string; - authors: { - id: string; - name: string; - profileImageUrl?: string; - }[]; - } & { - description: string; - socialImageUrl?: string; - collection?: { - slug: string; - title: string; - chapters: { - slug: string; - title: string; - }[]; - }; - versions: { - slug: string; - versionName: string; - /** Format: date-time */ - publishedAt: string; - }[]; - }; - }; - }; - /** @description Post not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": { - error: string; - }; - }; - }; - }; - }; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/content/posts": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** @description Fetch a paginated list of posts for post-card display */ - get: { - parameters: { - query: { - locale: string; - page: number; - limit: number; - sort: "newest" | "oldest"; - author?: string; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Successful */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": ({ - slug: string; - title: string; - bannerUrl?: string; - wordCount: number; - /** Format: date-time */ - publishedAt?: string; - authors: { - id: string; - name: string; - profileImageUrl?: string; - }[]; - } & { - tags: string[]; - })[]; - }; - }; - }; - }; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/content/schema/post": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** @description Fetch the JSON Schema for post frontmatter, as validated by the sync worker. */ - get: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Successful */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": { - [key: string]: unknown; - }; - }; - }; - }; - }; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/content/schema/collection": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** @description Fetch the JSON Schema for collection frontmatter, as validated by the sync worker. */ - get: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Successful */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": { - [key: string]: unknown; - }; - }; - }; - }; - }; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/content/schema/author": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** @description Fetch the JSON Schema for author frontmatter, as validated by the sync worker. */ - get: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Successful */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": { - [key: string]: unknown; - }; - }; - }; - }; - }; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/development/sync-all": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** @description Spawns tasks to sync all posts and authors to the db */ - post: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Default Response */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; -} -export type webhooks = Record; -export interface components { - schemas: never; - responses: never; - parameters: never; - requestBodies: never; - headers: never; - pathItems: never; -} -export type $defs = Record; -export type operations = Record; From 5d268331bc6e12454fa2a2eb287b409be93ae3fd Mon Sep 17 00:00:00 2001 From: fennifith Date: Tue, 13 Oct 2026 08:39:31 -0400 Subject: [PATCH 19/20] pass configured prefix to bullmq FlowProducer --- packages/bullmq/src/flow-producer.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/bullmq/src/flow-producer.ts b/packages/bullmq/src/flow-producer.ts index 67be5c87..4584929f 100644 --- a/packages/bullmq/src/flow-producer.ts +++ b/packages/bullmq/src/flow-producer.ts @@ -1,4 +1,8 @@ +import { env } from "@playfulprogramming/common"; import { redis } from "@playfulprogramming/redis"; import { FlowProducer } from "bullmq"; -export const flowProducer = new FlowProducer({ connection: redis }); +export const flowProducer = new FlowProducer({ + connection: redis, + prefix: env.BULLMQ_PREFIX, +}); From 026575c5a0c8ee9f1a4aa054b6a2fec98726c130 Mon Sep 17 00:00:00 2001 From: fennifith Date: Tue, 13 Oct 2026 08:56:58 -0400 Subject: [PATCH 20/20] configuration fixes --- .dockerignore | 1 - e2e/mocks/package.json | 2 +- e2e/mocks/project.json | 2 +- e2e/tests/project.json | 2 +- pnpm-lock.yaml | 2 +- 5 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.dockerignore b/.dockerignore index c95239b7..afefbe2a 100644 --- a/.dockerignore +++ b/.dockerignore @@ -10,7 +10,6 @@ .nx .pnpm-store .prettier* -.husky **/node_modules **/dist npm-debug.log diff --git a/e2e/mocks/package.json b/e2e/mocks/package.json index 3e943a90..f53f8715 100644 --- a/e2e/mocks/package.json +++ b/e2e/mocks/package.json @@ -12,6 +12,6 @@ "dependencies": { "@jaredwray/mockhttp": "^1.9.0", "selfsigned": "^5.5.0", - "typebox": "^1.3.30" + "typebox": "catalog:" } } diff --git a/e2e/mocks/project.json b/e2e/mocks/project.json index 60024b52..90274249 100644 --- a/e2e/mocks/project.json +++ b/e2e/mocks/project.json @@ -1,7 +1,7 @@ { "name": "e2e-mocks", "$schema": "../../node_modules/nx/schemas/project-schema.json", - "sourceRoot": "apps/e2e/src", + "sourceRoot": "e2e/mocks/src", "projectType": "application", "tags": [], "targets": { diff --git a/e2e/tests/project.json b/e2e/tests/project.json index 689b1020..750b8608 100644 --- a/e2e/tests/project.json +++ b/e2e/tests/project.json @@ -1,7 +1,7 @@ { "name": "e2e-tests", "$schema": "../../node_modules/nx/schemas/project-schema.json", - "sourceRoot": "apps/e2e/src", + "sourceRoot": "e2e/tests/src", "projectType": "application", "tags": [], "targets": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fc49fe78..3ae4696d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -409,7 +409,7 @@ importers: specifier: ^5.5.0 version: 5.5.0 typebox: - specifier: ^1.3.30 + specifier: 'catalog:' version: 1.3.34 e2e/tests: