diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile deleted file mode 100644 index d3d0b4c8..00000000 --- a/.devcontainer/Dockerfile +++ /dev/null @@ -1,9 +0,0 @@ -# Bun doesn't run well on Musl but this seems to work -FROM oven/bun:1.2.15-alpine as base - -# Switch to Bash by editing /etc/passwd -RUN apk add --no-cache libstdc++ git bash curl openssh cloc && \ - sed -i -e 's|/bin/ash|/bin/bash|g' /etc/passwd - -# Extract Node from its docker image (node:22-alpine) -COPY --from=node:22-alpine /usr/local/bin/node /usr/local/bin/node diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json deleted file mode 100644 index c4a2af66..00000000 --- a/.devcontainer/devcontainer.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "versia Dev Container", - "dockerFile": "Dockerfile", - "runArgs": [ - "-v", - "${localWorkspaceFolder}/config:/workspace/config", - "-v", - "${localWorkspaceFolder}/logs:/workspace/logs", - "-v", - "${localWorkspaceFolder}/uploads:/workspace/uploads", - "--network=host" - ], - "mounts": [ - "source=node_modules,target=/workspace/node_modules,type=bind,consistency=cached", - "type=bind,source=/home/${localEnv:USER}/.ssh,target=/root/.ssh,readonly" - ], - "customizations": { - "vscode": { - "settings": { - "terminal.integrated.shell.linux": "/bin/bash" - }, - "extensions": [ - "biomejs.biome", - "ms-vscode-remote.remote-containers", - "oven.bun-vscode", - "vivaxy.vscode-conventional-commits", - "EditorConfig.EditorConfig", - "tamasfe.even-better-toml", - "YoavBls.pretty-ts-errors", - "eamodio.gitlens" - ] - } - } -} diff --git a/.github/config.workflow.toml b/.github/config.workflow.toml index a88391b8..2a933b98 100644 --- a/.github/config.workflow.toml +++ b/.github/config.workflow.toml @@ -429,31 +429,28 @@ text = "No spam" [logging] -# Available levels: debug, info, warning, error, fatal -log_level = "debug" - -log_file_path = "logs/versia.log" - -[logging.types] -# Either pass a boolean -# requests = true -# Or a table with the following keys: -# requests_content = { level = "debug", log_file_path = "logs/requests.log" } -# Available types are: requests, responses, requests_content, filters +# Available levels: trace, debug, info, warning, error, fatal +log_level = "info" # For console output +# [logging.file] +# path = "logs/versia.log" +# log_level = "info" +# +# [logging.file.rotation] +# max_size = 10_000_000 # 10 MB +# max_files = 10 # Keep 10 rotated files +# # https://sentry.io support -# Uncomment to enable # [logging.sentry] -# Sentry DSN for error logging # dsn = "https://example.com" # debug = false - # sample_rate = 1.0 # traces_sample_rate = 1.0 # Can also be regex # trace_propagation_targets = [] # max_breadcrumbs = 100 # environment = "production" +# log_level = "info" [plugins] # Whether to automatically load all plugins in the plugins directory diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index d312a696..5638c288 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -13,4 +13,10 @@ const add = (a: number, b: number): number => a + b; We always write TypeScript with double quotes and four spaces for indentation, so when your responses include TypeScript code, please follow those conventions. -Our codebase uses Drizzle as an ORM, with custom abstractions in `classes/database/` for interacting with the database. The `@versia/kit/db` and `@versia/kit/tables` packages are aliases for these abstractions. +Our codebase uses Drizzle as an ORM, which is exposed in the `@versia-server/kit/db` and `@versia-server/kit/tables` packages. This project uses a monorepo structure with Bun as the package manager. + +The app has two modes: worker and API. The worker mode is used for background tasks, while the API mode serves HTTP requests. The entry point for the worker is `worker.ts`, and for the API, it is `api.ts`. + +Run the typechecker with `bun run typecheck` to ensure that all TypeScript code is type-checked correctly. Run tests with `bun test` to ensure that all tests pass. Run the linter and formatter with `bun lint` to ensure that the code adheres to our style guidelines, and `bun lint --write` to automatically fix minor/formatting issues. + +Cover all new functionality with tests, and ensure that all tests pass before submitting your code. diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 51e3b3d5..f26c0106 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -24,4 +24,4 @@ jobs: - name: Run typechecks run: | - bun run check + bun run typecheck diff --git a/.github/workflows/circular-imports.yml b/.github/workflows/circular-imports.yml new file mode 100644 index 00000000..f85882d6 --- /dev/null +++ b/.github/workflows/circular-imports.yml @@ -0,0 +1,27 @@ +name: Check Circular Imports + +on: + workflow_call: + +jobs: + tests: + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + + - name: Install NPM packages + run: | + bun install + + - name: Run typechecks + run: | + bun run detect-circular diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 8b1155b0..edbd8bc5 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -18,6 +18,9 @@ jobs: tests: uses: ./.github/workflows/tests.yml + detect-circular: + uses: ./.github/workflows/circular-imports.yml + build: if: ${{ success() }} needs: [lint, check, tests] diff --git a/.madgerc b/.madgerc new file mode 100644 index 00000000..b407c6b4 --- /dev/null +++ b/.madgerc @@ -0,0 +1,7 @@ +{ + "detectiveOptions": { + "ts": { + "skipTypeImports": true + } + } +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e6daf4a..20043794 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ ### Backend -- [x] 🚀 Upgraded Bun to `1.2.15` +- [x] 🚀 Upgraded Bun to `1.2.18` # `0.8.0` • Federation 2: Electric Boogaloo diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f87dc67e..9880297b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -112,7 +112,7 @@ TypeScript errors should be ignored with `// @ts-expect-error` comments, as well To scan for all TypeScript errors, run: ```sh -bun check +bun typecheck ``` ### Commit messages @@ -153,4 +153,4 @@ If you find a bug, please open an issue on GitHub. Please make sure to include t # License -Versia Server is licensed under the [AGPLv3 or later](https://www.gnu.org/licenses/agpl-3.0.en.html) license. By contributing to Versia, you agree to license your contributions under the same license. \ No newline at end of file +Versia Server is licensed under the [AGPLv3 or later](https://www.gnu.org/licenses/agpl-3.0.en.html) license. By contributing to Versia, you agree to license your contributions under the same license. diff --git a/Dockerfile b/Dockerfile index 41f5dadf..bf08eea0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,5 @@ # Node is required for building the project -FROM imbios/bun-node:1-20-alpine AS base - -RUN apk add --no-cache libstdc++ +FROM imbios/bun-node:latest-23-alpine AS base # Install dependencies into temp directory # This will cache them and speed up future builds @@ -22,20 +20,19 @@ COPY --from=install /temp/node_modules /temp/node_modules # Build the project WORKDIR /temp -RUN bun run build +RUN bun run build api WORKDIR /temp/dist # Copy production dependencies and source code into final image -FROM oven/bun:1.2.15-alpine +FROM oven/bun:1.2.18-alpine # Install libstdc++ for Bun and create app directory -RUN apk add --no-cache libstdc++ && \ - mkdir -p /app +RUN mkdir -p /app COPY --from=build /temp/dist /app/dist COPY entrypoint.sh /app -LABEL org.opencontainers.image.authors="Gaspard Wierzbinski (https://cpluspatch.dev)" +LABEL org.opencontainers.image.authors="Gaspard Wierzbinski (https://cpluspatch.com)" LABEL org.opencontainers.image.source="https://github.com/versia-pub/server" LABEL org.opencontainers.image.vendor="Versia Pub" LABEL org.opencontainers.image.licenses="AGPL-3.0-or-later" @@ -51,4 +48,4 @@ WORKDIR /app ENV NODE_ENV=production ENTRYPOINT [ "/bin/sh", "/app/entrypoint.sh" ] # Run migrations and start the server -CMD [ "bun", "run", "index.js" ] +CMD [ "bun", "run", "api.js" ] diff --git a/Worker.Dockerfile b/Worker.Dockerfile index 5c9a146b..1c7dae39 100644 --- a/Worker.Dockerfile +++ b/Worker.Dockerfile @@ -1,7 +1,5 @@ # Node is required for building the project -FROM imbios/bun-node:1-20-alpine AS base - -RUN apk add --no-cache libstdc++ +FROM imbios/bun-node:latest-23-alpine AS base # Install dependencies into temp directory # This will cache them and speed up future builds @@ -22,20 +20,19 @@ COPY --from=install /temp/node_modules /temp/node_modules # Build the project WORKDIR /temp -RUN bun run build:worker +RUN bun run build worker WORKDIR /temp/dist # Copy production dependencies and source code into final image -FROM oven/bun:1.2.15-alpine +FROM oven/bun:1.2.18-alpine # Install libstdc++ for Bun and create app directory -RUN apk add --no-cache libstdc++ && \ - mkdir -p /app +RUN mkdir -p /app COPY --from=build /temp/dist /app/dist COPY entrypoint.sh /app -LABEL org.opencontainers.image.authors="Gaspard Wierzbinski (https://cpluspatch.dev)" +LABEL org.opencontainers.image.authors="Gaspard Wierzbinski (https://cpluspatch.com)" LABEL org.opencontainers.image.source="https://github.com/versia-pub/server" LABEL org.opencontainers.image.vendor="Versia Pub" LABEL org.opencontainers.image.licenses="AGPL-3.0-or-later" @@ -47,7 +44,8 @@ ARG GIT_COMMIT ENV GIT_COMMIT=$GIT_COMMIT # CD to app -WORKDIR /app/dist +WORKDIR /app ENV NODE_ENV=production +ENTRYPOINT [ "/bin/sh", "/app/entrypoint.sh" ] # Run migrations and start the server CMD [ "bun", "run", "worker.js" ] diff --git a/entrypoints/api/index.ts b/api.ts similarity index 62% rename from entrypoints/api/index.ts rename to api.ts index 0ebcafdc..3a700fbc 100644 --- a/entrypoints/api/index.ts +++ b/api.ts @@ -1,9 +1,8 @@ import process from "node:process"; +import { appFactory } from "@versia-server/api"; +import { config } from "@versia-server/config"; import { Youch } from "youch"; -import { sentry } from "@/sentry"; import { createServer } from "@/server"; -import { appFactory } from "~/app"; -import { config } from "~/config.ts"; process.on("SIGINT", () => { process.exit(); @@ -15,7 +14,6 @@ process.on("uncaughtException", async (error) => { console.error(await youch.toANSI(error)); }); -await import("~/entrypoints/api/setup.ts"); -sentry?.captureMessage("Server started", "info"); +await import("@versia-server/api/setup"); createServer(config, await appFactory()); diff --git a/benchmarks/timeline.ts b/benchmarks/timeline.ts index 45dcc8c4..1cbf9b81 100644 --- a/benchmarks/timeline.ts +++ b/benchmarks/timeline.ts @@ -1,10 +1,11 @@ import type { Status } from "@versia/client/schemas"; +import { + fakeRequest, + getTestStatuses, + getTestUsers, +} from "@versia-server/tests"; import { bench, run } from "mitata"; -import type { z } from "zod"; -import { configureLoggers } from "@/loggers"; -import { fakeRequest, getTestStatuses, getTestUsers } from "~/tests/utils"; - -await configureLoggers(true); +import type { z } from "zod/v4"; const { users, tokens, deleteUsers } = await getTestUsers(5); await getTestStatuses(40, users[0]); diff --git a/biome.json b/biome.json index 088e0efb..2852132f 100644 --- a/biome.json +++ b/biome.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/2.0.0-beta.5/schema.json", + "$schema": "https://biomejs.dev/schemas/2.0.6/schema.json", "assist": { "actions": { "source": { @@ -49,7 +49,6 @@ } }, "useLiteralEnumMembers": "error", - "noCommaOperator": "error", "noNegationElse": "error", "noYodaExpression": "error", "useBlockStatements": "error", @@ -89,7 +88,6 @@ "accessibility": "explicit" } }, - "noArguments": "error", "useImportType": "error", "useExportType": "error", "noUselessElse": "error", @@ -99,7 +97,15 @@ "noCommonJs": "warn", "noExportedImports": "warn", "noSubstr": "warn", - "useTrimStartEnd": "warn" + "useTrimStartEnd": "warn", + "noRestrictedImports": { + "options": { + "paths": { + "~/packages/": "Use the appropriate package instead of importing from the packages directory directly." + } + }, + "level": "error" + } }, "performance": { "noDynamicNamespaceImportAccess": "warn" @@ -119,6 +125,7 @@ "noGlobalDirnameFilename": "error", "noProcessGlobal": "warn", "noTsIgnore": "warn", + "useReadonlyClassProperties": "error", "useConsistentObjectDefinition": { "level": "warn", "options": { @@ -135,7 +142,9 @@ "noUselessEscapeInRegex": "warn", "useSimplifiedLogicExpression": "error", "useWhile": "error", - "useNumericLiterals": "error" + "useNumericLiterals": "error", + "noArguments": "error", + "noCommaOperator": "error" }, "suspicious": { "noDuplicateTestHooks": "error", diff --git a/build-worker.ts b/build-worker.ts deleted file mode 100644 index 748349a2..00000000 --- a/build-worker.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { $, build } from "bun"; - -console.log("Building..."); - -await $`rm -rf dist && mkdir dist`; - -await build({ - entrypoints: [ - "worker.ts", - // HACK: Include to avoid cyclical import errors - "config.ts", - ], - outdir: "dist", - target: "bun", - splitting: true, - minify: false, -}); - -console.log("Copying files..."); - -// Copy Drizzle migrations to dist -await $`cp -rL drizzle dist/drizzle`; - -// Copy Sharp to dist -await $`mkdir -p dist/node_modules/@img`; -await $`cp -rL node_modules/@img/sharp-libvips-linux* dist/node_modules/@img`; -await $`cp -rL node_modules/@img/sharp-linux* dist/node_modules/@img`; - -await $`cp -rL node_modules/detect-libc dist/node_modules/`; - -console.log("Build complete!"); diff --git a/build.ts b/build.ts index f02a3d15..de2ceb77 100644 --- a/build.ts +++ b/build.ts @@ -1,63 +1,55 @@ -import { readdir } from "node:fs/promises"; -import { $, build } from "bun"; -import { routes } from "~/routes"; +import process from "node:process"; +import { $, build, file, write } from "bun"; +import manifest from "./package.json" with { type: "json" }; console.log("Building..."); await $`rm -rf dist && mkdir dist`; -// Get all directories under the plugins/ directory -const pluginDirs = await readdir("plugins", { withFileTypes: true }); +const type = process.argv[2] as "api" | "worker"; + +if (type !== "api" && type !== "worker") { + throw new Error("Invalid build type. Use 'api' or 'worker'."); +} + +const packages = Object.keys(manifest.dependencies) + .filter((dep) => dep.startsWith("@versia")) + .filter((dep) => dep !== "@versia-server/tests"); await build({ - entrypoints: [ - "index.ts", - // HACK: Include to avoid cyclical import errors - "config.ts", - "cli/index.ts", - // Force Bun to include endpoints - ...Object.values(routes), - // Include all plugins - ...pluginDirs - .filter((dir) => dir.isDirectory()) - .map((dir) => `plugins/${dir.name}/index.ts`), - ], + entrypoints: [`./${type}.ts`], outdir: "dist", target: "bun", splitting: true, - minify: false, - external: ["acorn", "@bull-board/ui"], + minify: true, + external: [...packages], }); console.log("Copying files..."); -// Copy Drizzle migrations to dist -await $`cp -r drizzle dist/drizzle`; +// Copy each package into dist/node_modules +for (const pkg of packages) { + const directory = pkg.split("/")[1] || pkg; + await $`mkdir -p dist/node_modules/${pkg}`; + // Copy the built package files + await $`cp -rL packages/${directory}/{dist,package.json} dist/node_modules/${pkg}`; -// Copy plugin manifests -await $`cp plugins/openid/manifest.json dist/plugins/openid/manifest.json`; - -await $`mkdir -p dist/node_modules`; - -// Copy Sharp to dist -await $`mkdir -p dist/node_modules/@img`; -await $`cp -rL node_modules/@img/sharp-libvips-linux* dist/node_modules/@img`; -await $`cp -rL node_modules/@img/sharp-linux* dist/node_modules/@img`; - -// Copy acorn to dist -await $`cp -rL node_modules/acorn dist/node_modules/acorn`; - -// Copy bull-board to dist -await $`mkdir -p dist/node_modules/@bull-board`; -await $`cp -rL node_modules/@bull-board/ui dist/node_modules/@bull-board/ui`; - -// Copy the Bee Movie script from pages -await $`cp beemovie.txt dist/beemovie.txt`; - -// Copy package.json -await $`cp package.json dist/package.json`; - -// Fixes issues with sharp -await $`cp -rL node_modules/detect-libc dist/node_modules/`; + // Rewrite package.json "exports" field to point to the dist directory and use .js extension + const packageJsonPath = `dist/node_modules/${pkg}/package.json`; + const packageJson = await file(packageJsonPath).json(); + for (const [key, value] of Object.entries(packageJson.exports) as [ + string, + { import?: string }, + ][]) { + if (value.import) { + packageJson.exports[key] = { + import: value.import + .replace("./", "./dist/") + .replace(/\.ts$/, ".js"), + }; + } + } + await write(packageJsonPath, JSON.stringify(packageJson, null, 4)); +} console.log("Build complete!"); diff --git a/bun.lock b/bun.lock index 271fa4bc..f4c396b7 100644 --- a/bun.lock +++ b/bun.lock @@ -4,84 +4,120 @@ "": { "name": "versia-server", "dependencies": { - "@bull-board/api": "^6.10.1", - "@bull-board/hono": "^6.10.1", - "@clerc/plugin-completions": "^0.44.0", - "@clerc/plugin-friendly-error": "^0.44.0", - "@clerc/plugin-help": "^0.44.0", - "@clerc/plugin-not-found": "^0.44.0", - "@clerc/plugin-version": "^0.44.0", - "@hackmd/markdown-it-task-lists": "^2.1.4", - "@hono/zod-validator": "^0.7.0", - "@inquirer/confirm": "^5.1.12", - "@logtape/file": "^0.12.0", - "@logtape/logtape": "^0.12.0", - "@scalar/hono-api-reference": "^0.9.4", - "@sentry/bun": "^9.29.0", + "@bull-board/api": "catalog:", + "@bull-board/hono": "catalog:", + "@clerc/plugin-completions": "catalog:", + "@clerc/plugin-friendly-error": "catalog:", + "@clerc/plugin-help": "catalog:", + "@clerc/plugin-not-found": "catalog:", + "@clerc/plugin-version": "catalog:", + "@hackmd/markdown-it-task-lists": "catalog:", + "@hono/standard-validator": "catalog:", + "@inquirer/confirm": "catalog:", + "@scalar/hono-api-reference": "catalog:", + "@sentry/bun": "catalog:", + "@versia-server/api": "workspace:*", + "@versia-server/config": "workspace:*", + "@versia-server/kit": "workspace:*", + "@versia-server/logging": "workspace:*", + "@versia-server/tests": "workspace:*", + "@versia-server/worker": "workspace:*", "@versia/client": "workspace:*", - "@versia/kit": "workspace:*", "@versia/sdk": "workspace:*", - "altcha-lib": "^1.3.0", - "blurhash": "^2.0.5", - "bullmq": "^5.53.3", - "chalk": "^5.4.1", - "clerc": "^0.44.0", - "confbox": "^0.2.2", - "drizzle-orm": "^0.44.2", - "feed": "^5.1.0", - "hono": "^4.7.11", - "hono-openapi": "^0.4.8", - "hono-rate-limiter": "^0.4.2", - "html-to-text": "^9.0.5", - "ioredis": "^5.6.1", - "ip-matching": "^2.1.2", - "iso-639-1": "^3.1.5", - "jose": "^6.0.11", - "linkify-html": "^4.3.1", - "linkify-string": "^4.3.1", - "linkifyjs": "^4.3.1", - "magic-regexp": "^0.10.0", - "markdown-it": "^14.1.0", - "markdown-it-anchor": "^9.2.0", - "markdown-it-container": "^4.0.0", - "markdown-it-mathjax3": "^4.3.2", - "markdown-it-toc-done-right": "^4.2.0", - "mime-types": "^3.0.1", - "mitata": "^1.0.34", - "oauth4webapi": "^3.5.2", - "ora": "^8.2.0", - "qs": "^6.14.0", - "sharp": "^0.34.2", - "sonic-channel": "^1.3.1", - "string-comparison": "^1.3.0", - "stringify-entities": "^4.0.4", - "unicode-emoji-json": "^0.8.0", - "uqr": "^0.1.2", - "web-push": "^3.6.7", - "xss": "^1.0.15", - "youch": "^4.1.0-beta.7", - "zod": "^3.25.64", - "zod-openapi": "^4.2.4", - "zod-validation-error": "^3.5.0", + "altcha-lib": "catalog:", + "blurhash": "catalog:", + "bullmq": "catalog:", + "chalk": "catalog:", + "clerc": "catalog:", + "confbox": "catalog:", + "drizzle-orm": "catalog:", + "feed": "catalog:", + "hono": "catalog:", + "hono-openapi": "catalog:", + "hono-rate-limiter": "catalog:", + "html-to-text": "catalog:", + "ioredis": "catalog:", + "ip-matching": "catalog:", + "iso-639-1": "catalog:", + "jose": "catalog:", + "linkify-html": "catalog:", + "linkify-string": "catalog:", + "linkifyjs": "catalog:", + "magic-regexp": "catalog:", + "markdown-it": "catalog:", + "markdown-it-anchor": "catalog:", + "markdown-it-container": "catalog:", + "markdown-it-mathjax3": "catalog:", + "markdown-it-toc-done-right": "catalog:", + "mime-types": "catalog:", + "mitata": "catalog:", + "oauth4webapi": "catalog:", + "ora": "catalog:", + "qs": "catalog:", + "sharp": "catalog:", + "sonic-channel": "catalog:", + "string-comparison": "catalog:", + "stringify-entities": "catalog:", + "unicode-emoji-json": "catalog:", + "uqr": "catalog:", + "web-push": "catalog:", + "xss": "catalog:", + "youch": "catalog:", + "zod": "catalog:", + "zod-openapi": "catalog:", + "zod-validation-error": "catalog:", }, "devDependencies": { - "@biomejs/biome": "2.0.0-beta.5", - "@types/bun": "^1.2.16", - "@types/html-to-text": "^9.0.4", - "@types/markdown-it-container": "^2.0.10", - "@types/mime-types": "^3.0.1", - "@types/qs": "^6.14.0", - "@types/web-push": "^3.6.4", - "bun-bagel": "^1.2.0", - "drizzle-kit": "^0.31.1", - "markdown-it-image-figures": "^2.1.1", - "ts-prune": "^0.10.3", - "typescript": "^5.8.3", - "vitepress": "^1.6.3", - "vitepress-plugin-tabs": "^0.7.1", - "vitepress-sidebar": "^1.31.1", - "vue": "^3.5.16", - "zod-to-json-schema": "^3.24.5", + "@biomejs/biome": "catalog:", + "@types/bun": "catalog:", + "@types/html-to-text": "catalog:", + "@types/markdown-it-container": "catalog:", + "@types/mime-types": "catalog:", + "@types/qs": "catalog:", + "@types/web-push": "catalog:", + "bun-bagel": "catalog:", + "drizzle-kit": "catalog:", + "markdown-it-image-figures": "catalog:", + "ts-prune": "catalog:", + "typescript": "catalog:", + "vitepress": "catalog:", + "vitepress-plugin-tabs": "catalog:", + "vitepress-sidebar": "catalog:", + "vue": "catalog:", + }, + }, + "packages/api": { + "name": "@versia-server/api", + "version": "0.9.0-alpha.0", + "dependencies": { + "@hono/standard-validator": "catalog:", + "@scalar/hono-api-reference": "catalog:", + "@versia-server/config": "workspace:*", + "@versia-server/kit": "workspace:*", + "@versia-server/logging": "workspace:*", + "@versia-server/tests": "workspace:*", + "@versia/client": "workspace:*", + "@versia/sdk": "workspace:*", + "altcha-lib": "catalog:", + "bun-bagel": "catalog:", + "chalk": "catalog:", + "confbox": "catalog:", + "drizzle-orm": "catalog:", + "hono": "catalog:", + "hono-openapi": "catalog:", + "hono-rate-limiter": "catalog:", + "ip-matching": "catalog:", + "iso-639-1": "catalog:", + "jose": "catalog:", + "oauth4webapi": "catalog:", + "qs": "catalog:", + "sharp": "catalog:", + "string-comparison": "catalog:", + "unicode-emoji-json": "catalog:", + "youch": "catalog:", + "zod": "catalog:", + "zod-openapi": "catalog:", + "zod-validation-error": "catalog:", }, }, "packages/client": { @@ -89,31 +125,100 @@ "version": "0.2.0-alpha.4", "dependencies": { "@badgateway/oauth2-client": "^3.0.0", - "iso-639-1": "^3.1.5", - "magic-regexp": "^0.10.0", - "zod": "^3.24.2", - "zod-openapi": "^4.2.4", + "iso-639-1": "catalog:", + "magic-regexp": "catalog:", + "zod": "catalog:", + "zod-openapi": "catalog:", }, }, - "packages/plugin-kit": { - "name": "@versia/kit", + "packages/config": { + "name": "@versia-server/config", + "version": "0.0.1", + "dependencies": { + "@versia/client": "workspace:*", + "chalk": "catalog:", + "confbox": "catalog:", + "iso-639-1": "catalog:", + "mime-types": "catalog:", + "web-push": "catalog:", + "zod": "catalog:", + "zod-validation-error": "catalog:", + }, + }, + "packages/kit": { + "name": "@versia-server/kit", "version": "0.0.0", "dependencies": { - "drizzle-orm": "^0.44.2", - "hono": "^4.7.7", - "mitt": "^3.0.1", - "zod": "^3.24.2", - "zod-to-json-schema": "^3.24.5", - "zod-validation-error": "^3.4.0", + "@hackmd/markdown-it-task-lists": "catalog:", + "@hono/standard-validator": "catalog:", + "@versia-server/config": "workspace:*", + "@versia-server/logging": "workspace:*", + "@versia/client": "workspace:*", + "@versia/sdk": "workspace:*", + "altcha-lib": "catalog:", + "bullmq": "catalog:", + "chalk": "catalog:", + "drizzle-orm": "catalog:", + "hono": "catalog:", + "hono-openapi": "catalog:", + "html-to-text": "catalog:", + "ioredis": "catalog:", + "ip-matching": "catalog:", + "linkify-html": "catalog:", + "magic-regexp": "catalog:", + "markdown-it": "catalog:", + "markdown-it-container": "catalog:", + "markdown-it-toc-done-right": "catalog:", + "mitt": "catalog:", + "qs": "catalog:", + "sharp": "catalog:", + "sonic-channel": "catalog:", + "web-push": "catalog:", + "zod": "catalog:", + "zod-validation-error": "catalog:", + }, + }, + "packages/logging": { + "name": "@versia-server/logging", + "version": "0.0.1", + "dependencies": { + "@logtape/file": "catalog:", + "@logtape/logtape": "catalog:", + "@logtape/otel": "catalog:", + "@logtape/sentry": "catalog:", + "@sentry/bun": "catalog:", + "@versia-server/config": "workspace:*", + "chalk": "catalog:", }, }, "packages/sdk": { "name": "@versia/sdk", "version": "0.0.1", "dependencies": { - "magic-regexp": "^0.10.0", - "mime-types": "^3.0.1", - "zod": "^3.24.2", + "magic-regexp": "catalog:", + "mime-types": "catalog:", + "zod": "catalog:", + }, + }, + "packages/tests": { + "name": "@versia-server/tests", + "version": "0.0.1", + "dependencies": { + "@versia-server/config": "workspace:*", + "@versia-server/kit": "workspace:*", + "@versia/client": "workspace:*", + "altcha-lib": "catalog:", + "drizzle-orm": "catalog:", + }, + }, + "packages/worker": { + "name": "@versia-server/worker", + "version": "0.9.0-alpha.0", + "dependencies": { + "@versia-server/config": "workspace:*", + "@versia-server/kit": "workspace:*", + "@versia-server/logging": "workspace:*", + "chalk": "catalog:", }, }, }, @@ -122,7 +227,85 @@ "esbuild", "@biomejs/biome", "msgpackr-extract", + "protobufjs", ], + "catalog": { + "@biomejs/biome": "^2.0.6", + "@bull-board/api": "^6.11.0", + "@bull-board/hono": "^6.11.0", + "@clerc/plugin-completions": "^0.44.0", + "@clerc/plugin-friendly-error": "^0.44.0", + "@clerc/plugin-help": "^0.44.0", + "@clerc/plugin-not-found": "^0.44.0", + "@clerc/plugin-version": "^0.44.0", + "@hackmd/markdown-it-task-lists": "^2.1.4", + "@hono/standard-validator": "^0.1.2", + "@inquirer/confirm": "^5.1.13", + "@logtape/file": "^1.0.0", + "@logtape/logtape": "^1.0.0", + "@logtape/otel": "^1.0.0", + "@logtape/sentry": "^1.0.0", + "@scalar/hono-api-reference": "^0.9.7", + "@sentry/bun": "^9.35.0", + "@types/bun": "^1.2.18", + "@types/html-to-text": "^9.0.4", + "@types/markdown-it-container": "^2.0.10", + "@types/mime-types": "^3.0.1", + "@types/qs": "^6.14.0", + "@types/web-push": "^3.6.4", + "altcha-lib": "^1.3.0", + "blurhash": "^2.0.5", + "bullmq": "^5.56.1", + "bun-bagel": "^1.2.0", + "chalk": "^5.4.1", + "clerc": "^0.44.0", + "confbox": "^0.2.2", + "drizzle-kit": "^0.31.4", + "drizzle-orm": "^0.44.2", + "feed": "^5.1.0", + "hono": "^4.8.4", + "hono-openapi": "npm:@cpluspatch/hono-openapi@0.5.1", + "hono-rate-limiter": "^0.4.2", + "html-to-text": "^9.0.5", + "ioredis": "^5.6.1", + "ip-matching": "^2.1.2", + "iso-639-1": "^3.1.5", + "jose": "^6.0.11", + "linkify-html": "^4.3.1", + "linkify-string": "^4.3.1", + "linkifyjs": "^4.3.1", + "magic-regexp": "^0.10.0", + "markdown-it": "^14.1.0", + "markdown-it-anchor": "^9.2.0", + "markdown-it-container": "^4.0.0", + "markdown-it-image-figures": "^2.1.1", + "markdown-it-mathjax3": "^4.3.2", + "markdown-it-toc-done-right": "^4.2.0", + "mime-types": "^3.0.1", + "mitata": "^1.0.34", + "mitt": "^3.0.1", + "oauth4webapi": "^3.5.5", + "ora": "^8.2.0", + "qs": "^6.14.0", + "sharp": "^0.34.2", + "sonic-channel": "^1.3.1", + "string-comparison": "^1.3.0", + "stringify-entities": "^4.0.4", + "ts-prune": "^0.10.3", + "typescript": "^5.8.3", + "unicode-emoji-json": "^0.8.0", + "uqr": "^0.1.2", + "vitepress": "^1.6.3", + "vitepress-plugin-tabs": "^0.7.1", + "vitepress-sidebar": "^1.32.1", + "vue": "^3.5.17", + "web-push": "^3.6.7", + "xss": "^1.0.15", + "youch": "^4.1.0-beta.7", + "zod": "^3.25.74", + "zod-openapi": "^5.0.0", + "zod-validation-error": "^4.0.0-beta.1", + }, "packages": { "@algolia/autocomplete-core": ["@algolia/autocomplete-core@1.17.7", "", { "dependencies": { "@algolia/autocomplete-plugin-algolia-insights": "1.17.7", "@algolia/autocomplete-shared": "1.17.7" } }, "sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q=="], @@ -132,33 +315,31 @@ "@algolia/autocomplete-shared": ["@algolia/autocomplete-shared@1.17.7", "", { "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", "algoliasearch": ">= 4.9.1 < 6" } }, "sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg=="], - "@algolia/client-abtesting": ["@algolia/client-abtesting@5.24.0", "", { "dependencies": { "@algolia/client-common": "5.24.0", "@algolia/requester-browser-xhr": "5.24.0", "@algolia/requester-fetch": "5.24.0", "@algolia/requester-node-http": "5.24.0" } }, "sha512-pNTIB5YqVVwu6UogvdX8TqsRZENaflqMMjdY7/XIPMNGrBoNH9tewINLI7+qc9tIaOLcAp3ZldqoEwAihZZ3ig=="], + "@algolia/client-abtesting": ["@algolia/client-abtesting@5.30.0", "", { "dependencies": { "@algolia/client-common": "5.30.0", "@algolia/requester-browser-xhr": "5.30.0", "@algolia/requester-fetch": "5.30.0", "@algolia/requester-node-http": "5.30.0" } }, "sha512-Q3OQXYlTNqVUN/V1qXX8VIzQbLjP3yrRBO9m6NRe1CBALmoGHh9JrYosEGvfior28+DjqqU3Q+nzCSuf/bX0Gw=="], - "@algolia/client-analytics": ["@algolia/client-analytics@5.24.0", "", { "dependencies": { "@algolia/client-common": "5.24.0", "@algolia/requester-browser-xhr": "5.24.0", "@algolia/requester-fetch": "5.24.0", "@algolia/requester-node-http": "5.24.0" } }, "sha512-IF+r9RRQsIf0ylIBNFxo7c6hDxxuhIfIbffhBXEF1HD13rjhP5AVfiaea9RzbsAZoySkm318plDpH/nlGIjbRA=="], + "@algolia/client-analytics": ["@algolia/client-analytics@5.30.0", "", { "dependencies": { "@algolia/client-common": "5.30.0", "@algolia/requester-browser-xhr": "5.30.0", "@algolia/requester-fetch": "5.30.0", "@algolia/requester-node-http": "5.30.0" } }, "sha512-/b+SAfHjYjx/ZVeVReCKTTnFAiZWOyvYLrkYpeNMraMT6akYRR8eC1AvFcvR60GLG/jytxcJAp42G8nN5SdcLg=="], - "@algolia/client-common": ["@algolia/client-common@5.24.0", "", {}, "sha512-p8K6tiXQTebRBxbrzWIfGCvfkT+Umml+2lzI92acZjHsvl6KYH6igOfVstKqXJRei9pvRzEEvVDNDLXDVleGTA=="], + "@algolia/client-common": ["@algolia/client-common@5.30.0", "", {}, "sha512-tbUgvkp2d20mHPbM0+NPbLg6SzkUh0lADUUjzNCF+HiPkjFRaIW3NGMlESKw5ia4Oz6ZvFzyREquUX6rdkdJcQ=="], - "@algolia/client-insights": ["@algolia/client-insights@5.24.0", "", { "dependencies": { "@algolia/client-common": "5.24.0", "@algolia/requester-browser-xhr": "5.24.0", "@algolia/requester-fetch": "5.24.0", "@algolia/requester-node-http": "5.24.0" } }, "sha512-jOHF0+tixR3IZJMhZPquFNdCVPzwzzXoiqVsbTvfKojeaY6ZXybgUiTSB8JNX+YpsUT8Ebhu3UvRy4mw2PbEzw=="], + "@algolia/client-insights": ["@algolia/client-insights@5.30.0", "", { "dependencies": { "@algolia/client-common": "5.30.0", "@algolia/requester-browser-xhr": "5.30.0", "@algolia/requester-fetch": "5.30.0", "@algolia/requester-node-http": "5.30.0" } }, "sha512-caXuZqJK761m32KoEAEkjkE2WF/zYg1McuGesWXiLSgfxwZZIAf+DljpiSToBUXhoPesvjcLtINyYUzbkwE0iw=="], - "@algolia/client-personalization": ["@algolia/client-personalization@5.24.0", "", { "dependencies": { "@algolia/client-common": "5.24.0", "@algolia/requester-browser-xhr": "5.24.0", "@algolia/requester-fetch": "5.24.0", "@algolia/requester-node-http": "5.24.0" } }, "sha512-Fx/Fp6d8UmDBHecTt0XYF8C9TAaA3qeCQortfGSZzWp4gVmtrUCFNZ1SUwb8ULREnO9DanVrM5hGE8R8C4zZTQ=="], + "@algolia/client-personalization": ["@algolia/client-personalization@5.30.0", "", { "dependencies": { "@algolia/client-common": "5.30.0", "@algolia/requester-browser-xhr": "5.30.0", "@algolia/requester-fetch": "5.30.0", "@algolia/requester-node-http": "5.30.0" } }, "sha512-7K6P7TRBHLX1zTmwKDrIeBSgUidmbj6u3UW/AfroLRDGf9oZFytPKU49wg28lz/yulPuHY0nZqiwbyAxq9V17w=="], - "@algolia/client-query-suggestions": ["@algolia/client-query-suggestions@5.24.0", "", { "dependencies": { "@algolia/client-common": "5.24.0", "@algolia/requester-browser-xhr": "5.24.0", "@algolia/requester-fetch": "5.24.0", "@algolia/requester-node-http": "5.24.0" } }, "sha512-F8ypOedSMhz6W7zuT5O1SXXsdXSOVhY2U6GkRbYk/mzrhs3jWFR3uQIfeQVWmsJjUwIGZmPoAr9E+T/Zm2M4wA=="], + "@algolia/client-query-suggestions": ["@algolia/client-query-suggestions@5.30.0", "", { "dependencies": { "@algolia/client-common": "5.30.0", "@algolia/requester-browser-xhr": "5.30.0", "@algolia/requester-fetch": "5.30.0", "@algolia/requester-node-http": "5.30.0" } }, "sha512-WMjWuBjYxJheRt7Ec5BFr33k3cV0mq2WzmH9aBf5W4TT8kUp34x91VRsYVaWOBRlxIXI8o/WbhleqSngiuqjLA=="], - "@algolia/client-search": ["@algolia/client-search@5.24.0", "", { "dependencies": { "@algolia/client-common": "5.24.0", "@algolia/requester-browser-xhr": "5.24.0", "@algolia/requester-fetch": "5.24.0", "@algolia/requester-node-http": "5.24.0" } }, "sha512-k+nuciQuq7WERNNE+hsx3DX636zIy+9R4xdtvW3PANT2a2BDGOv3fv2mta8+QUMcVTVcGe/Mo3QCb4pc1HNoxA=="], + "@algolia/client-search": ["@algolia/client-search@5.30.0", "", { "dependencies": { "@algolia/client-common": "5.30.0", "@algolia/requester-browser-xhr": "5.30.0", "@algolia/requester-fetch": "5.30.0", "@algolia/requester-node-http": "5.30.0" } }, "sha512-puc1/LREfSqzgmrOFMY5L/aWmhYOlJ0TTpa245C0ZNMKEkdOkcimFbXTXQ8lZhzh+rlyFgR7cQGNtXJ5H0XgZg=="], - "@algolia/ingestion": ["@algolia/ingestion@1.24.0", "", { "dependencies": { "@algolia/client-common": "5.24.0", "@algolia/requester-browser-xhr": "5.24.0", "@algolia/requester-fetch": "5.24.0", "@algolia/requester-node-http": "5.24.0" } }, "sha512-/lqVxmrvwoA+OyVK4XLMdz/PJaCTW4qYchX1AZ+98fdnH3K6XM/kMydQLfP0bUNGBQbmVrF88MqhqZRnZEn/MA=="], + "@algolia/ingestion": ["@algolia/ingestion@1.30.0", "", { "dependencies": { "@algolia/client-common": "5.30.0", "@algolia/requester-browser-xhr": "5.30.0", "@algolia/requester-fetch": "5.30.0", "@algolia/requester-node-http": "5.30.0" } }, "sha512-NfqiIKVgGKTLr6T9F81oqB39pPiEtILTy0z8ujxPKg2rCvI/qQeDqDWFBmQPElCfUTU6kk67QAgMkQ7T6fE+gg=="], - "@algolia/monitoring": ["@algolia/monitoring@1.24.0", "", { "dependencies": { "@algolia/client-common": "5.24.0", "@algolia/requester-browser-xhr": "5.24.0", "@algolia/requester-fetch": "5.24.0", "@algolia/requester-node-http": "5.24.0" } }, "sha512-cRisDXQJhvfZCXL4hD22qca2CmW52TniOx6L7pvkaBDx0oQk1k9o+3w11fgfcCG+47OndMeNx5CMpu+K+COMzg=="], + "@algolia/monitoring": ["@algolia/monitoring@1.30.0", "", { "dependencies": { "@algolia/client-common": "5.30.0", "@algolia/requester-browser-xhr": "5.30.0", "@algolia/requester-fetch": "5.30.0", "@algolia/requester-node-http": "5.30.0" } }, "sha512-/eeM3aqLKro5KBZw0W30iIA6afkGa+bcpvEM0NDa92m5t3vil4LOmJI9FkgzfmSkF4368z/SZMOTPShYcaVXjA=="], - "@algolia/recommend": ["@algolia/recommend@5.24.0", "", { "dependencies": { "@algolia/client-common": "5.24.0", "@algolia/requester-browser-xhr": "5.24.0", "@algolia/requester-fetch": "5.24.0", "@algolia/requester-node-http": "5.24.0" } }, "sha512-JTMz0JqN2gidvKa2QCF/rMe8LNtdHaght03px2cluZaZfBRYy8TgHgkCeBspKKvV/abWJwl7J0FzWThCshqT3w=="], + "@algolia/recommend": ["@algolia/recommend@5.30.0", "", { "dependencies": { "@algolia/client-common": "5.30.0", "@algolia/requester-browser-xhr": "5.30.0", "@algolia/requester-fetch": "5.30.0", "@algolia/requester-node-http": "5.30.0" } }, "sha512-iWeAUWqw+xT+2IyUyTqnHCK+cyCKYV5+B6PXKdagc9GJJn6IaPs8vovwoC0Za5vKCje/aXQ24a2Z1pKpc/tdHg=="], - "@algolia/requester-browser-xhr": ["@algolia/requester-browser-xhr@5.24.0", "", { "dependencies": { "@algolia/client-common": "5.24.0" } }, "sha512-B2Gc+iSxct1WSza5CF6AgfNgmLvVb61d5bqmIWUZixtJIhyAC6lSQZuF+nvt+lmKhQwuY2gYjGGClil8onQvKQ=="], + "@algolia/requester-browser-xhr": ["@algolia/requester-browser-xhr@5.30.0", "", { "dependencies": { "@algolia/client-common": "5.30.0" } }, "sha512-alo3ly0tdNLjfMSPz9dmNwYUFHx7guaz5dTGlIzVGnOiwLgIoM6NgA+MJLMcH6e1S7OpmE2AxOy78svlhst2tQ=="], - "@algolia/requester-fetch": ["@algolia/requester-fetch@5.24.0", "", { "dependencies": { "@algolia/client-common": "5.24.0" } }, "sha512-6E5+hliqGc5w8ZbyTAQ+C3IGLZ/GiX623Jl2bgHA974RPyFWzVSj4rKqkboUAxQmrFY7Z02ybJWVZS5OhPQocA=="], + "@algolia/requester-fetch": ["@algolia/requester-fetch@5.30.0", "", { "dependencies": { "@algolia/client-common": "5.30.0" } }, "sha512-WOnTYUIY2InllHBy6HHMpGIOo7Or4xhYUx/jkoSK/kPIa1BRoFEHqa8v4pbKHtoG7oLvM2UAsylSnjVpIhGZXg=="], - "@algolia/requester-node-http": ["@algolia/requester-node-http@5.24.0", "", { "dependencies": { "@algolia/client-common": "5.24.0" } }, "sha512-zM+nnqZpiQj20PyAh6uvgdSz+hD7Rj7UfAZwizqNP+bLvcbGXZwABERobuilkCQqyDBBH4uv0yqIcPRl8dSBEg=="], - - "@apidevtools/json-schema-ref-parser": ["@apidevtools/json-schema-ref-parser@11.9.3", "", { "dependencies": { "@jsdevtools/ono": "^7.1.3", "@types/json-schema": "^7.0.15", "js-yaml": "^4.1.0" } }, "sha512-60vepv88RwcJtSHrD6MjIL6Ta3SOYbgfnkHb+ppAVK+o9mXprRtulx7VlRl3lN3bbvysAfCS7WMVfhUYemB0IQ=="], + "@algolia/requester-node-http": ["@algolia/requester-node-http@5.30.0", "", { "dependencies": { "@algolia/client-common": "5.30.0" } }, "sha512-uSTUh9fxeHde1c7KhvZKUrivk90sdiDftC+rSKNFKKEU9TiIKAGA7B2oKC+AoMCqMymot1vW9SGbeESQPTZd0w=="], "@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="], @@ -166,37 +347,37 @@ "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.27.1", "", {}, "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="], - "@babel/parser": ["@babel/parser@7.27.2", "", { "dependencies": { "@babel/types": "^7.27.1" }, "bin": "./bin/babel-parser.js" }, "sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw=="], + "@babel/parser": ["@babel/parser@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.0" }, "bin": "./bin/babel-parser.js" }, "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g=="], - "@babel/runtime": ["@babel/runtime@7.27.1", "", {}, "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog=="], + "@babel/runtime": ["@babel/runtime@7.27.6", "", {}, "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q=="], - "@babel/types": ["@babel/types@7.27.1", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q=="], + "@babel/types": ["@babel/types@7.28.0", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg=="], "@badgateway/oauth2-client": ["@badgateway/oauth2-client@3.2.0", "", {}, "sha512-EHsoV6oLHot7HeYkIoSxCZApNgBjwNo1OTV9kXIDnmijGAshlVkJreVAAtexFn+sfDKPE0JW5SCPYJV1y4IoMg=="], - "@biomejs/biome": ["@biomejs/biome@2.0.0-beta.5", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.0.0-beta.5", "@biomejs/cli-darwin-x64": "2.0.0-beta.5", "@biomejs/cli-linux-arm64": "2.0.0-beta.5", "@biomejs/cli-linux-arm64-musl": "2.0.0-beta.5", "@biomejs/cli-linux-x64": "2.0.0-beta.5", "@biomejs/cli-linux-x64-musl": "2.0.0-beta.5", "@biomejs/cli-win32-arm64": "2.0.0-beta.5", "@biomejs/cli-win32-x64": "2.0.0-beta.5" }, "bin": { "biome": "bin/biome" } }, "sha512-1ldO4AepieVvg4aLi1ubZkA7NsefQT2UTNssbJbDiQTGem8kCHx/PZCwLxIR6UzFpGIjh0xsDzivyVvhnmqmuA=="], + "@biomejs/biome": ["@biomejs/biome@2.0.6", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.0.6", "@biomejs/cli-darwin-x64": "2.0.6", "@biomejs/cli-linux-arm64": "2.0.6", "@biomejs/cli-linux-arm64-musl": "2.0.6", "@biomejs/cli-linux-x64": "2.0.6", "@biomejs/cli-linux-x64-musl": "2.0.6", "@biomejs/cli-win32-arm64": "2.0.6", "@biomejs/cli-win32-x64": "2.0.6" }, "bin": { "biome": "bin/biome" } }, "sha512-RRP+9cdh5qwe2t0gORwXaa27oTOiQRQvrFf49x2PA1tnpsyU7FIHX4ZOFMtBC4QNtyWsN7Dqkf5EDbg4X+9iqA=="], - "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.0.0-beta.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-pnJiaoDpwGo+ctGkMu4POcO8jgOgCErBdYbhutr+K9rxxJS+TlHLr0LR91GCEWbGV2O1oyZRFQcW21rYFoak4w=="], + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.0.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-AzdiNNjNzsE6LfqWyBvcL29uWoIuZUkndu+wwlXW13EKcBHbbKjNQEZIJKYDc6IL+p7bmWGx3v9ZtcRyIoIz5A=="], - "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.0.0-beta.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-WwEZpqcmsNoFpZkUFNQcbZo52WK4hLGQ0vZk3PQ8JlZ55gJsHiyhtv6aem6fVlyVCvZgpsC0sYPLE3VvFVKNAQ=="], + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.0.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-wJjjP4E7bO4WJmiQaLnsdXMa516dbtC6542qeRkyJg0MqMXP0fvs4gdsHhZ7p9XWTAmGIjZHFKXdsjBvKGIJJQ=="], - "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.0.0-beta.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-lAF1de+Ki0vnq14NwDXouKkAR/iviyMNrUngSHjTGFC4z8XGVEfIw0ZMSm7fAdJZ5fAWodt9HiYmEAVs5EtHQg=="], + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.0.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-ZSVf6TYo5rNMUHIW1tww+rs/krol7U5A1Is/yzWyHVZguuB0lBnIodqyFuwCNqG9aJGyk7xIMS8HG0qGUPz0SA=="], - "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.0.0-beta.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-4vxNkYx1uEt211W8hLdXddc7icRHQgYENb72g6uTd/tLVPSBvIwqUAxAOkU+9Ai1E/8R4sWy7HIxREgpuFgbNA=="], + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.0.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-CVPEMlin3bW49sBqLBg2x016Pws7eUXA27XYDFlEtponD0luYjg2zQaMJ2nOqlkKG9fqzzkamdYxHdMDc2gZFw=="], - "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.0.0-beta.5", "", { "os": "linux", "cpu": "x64" }, "sha512-I0Pt1VHeL1mN8G7ZwV2u9AfzBd5ZKfbvHUI4x2wETUZbwcQlAu/nEzEa2LUe5HqSmnctTR36ig7RkkM9qbmIrA=="], + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.0.6", "", { "os": "linux", "cpu": "x64" }, "sha512-geM1MkHTV1Kh2Cs/Xzot9BOF3WBacihw6bkEmxkz4nSga8B9/hWy5BDiOG3gHDGIBa8WxT0nzsJs2f/hPqQIQw=="], - "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.0.0-beta.5", "", { "os": "linux", "cpu": "x64" }, "sha512-nUeKGO517GtRCxziVD9les1HiCs2s2/WIVITMN9+9RRuLOko8r+T77E8ZXEmlfLOfOIOeE6z62WITqei3oNccA=="], + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.0.6", "", { "os": "linux", "cpu": "x64" }, "sha512-mKHE/e954hR/hSnAcJSjkf4xGqZc/53Kh39HVW1EgO5iFi0JutTN07TSjEMg616julRtfSNJi0KNyxvc30Y4rQ=="], - "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.0.0-beta.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-YXW6hgbrgBcWQ1SLO69ypWlluPchgQV5C1lTG4xOcBUWdCsfYuQirM64S6Dov7SFPqsMIoFC6LlQRW+n8qAyiA=="], + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.0.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-290V4oSFoKaprKE1zkYVsDfAdn0An5DowZ+GIABgjoq1ndhvNxkJcpxPsiYtT7slbVe3xmlT0ncdfOsN7KruzA=="], - "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.0.0-beta.5", "", { "os": "win32", "cpu": "x64" }, "sha512-N7Yby52BJmvEdst1iMbclE5hxxefboaXKRJLm1tLfBYr4FeuoCe6j8HdiQSwhCRdIUGFFqBLaDXh//LLF6EReA=="], + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.0.6", "", { "os": "win32", "cpu": "x64" }, "sha512-bfM1Bce0d69Ao7pjTjUS+AWSZ02+5UHdiAP85Th8e9yV5xzw6JrHXbL5YWlcEKQ84FIZMdDc7ncuti1wd2sdbw=="], - "@bull-board/api": ["@bull-board/api@6.10.1", "", { "dependencies": { "redis-info": "^3.1.0" }, "peerDependencies": { "@bull-board/ui": "6.10.1" } }, "sha512-VPkZa2XZI2Wk2MqK1XyiiS+tOhNan54mnm2fpv2KA0fdZ92mQqNjhKkOpsykhQv9XUEc8cCRlZqGxf67YCMJbQ=="], + "@bull-board/api": ["@bull-board/api@6.11.0", "", { "dependencies": { "redis-info": "^3.1.0" }, "peerDependencies": { "@bull-board/ui": "6.11.0" } }, "sha512-HLbIuXIthrgeVRmN7Vec9/7ZKWx8i1xTC6Nzi//l7ua+Xu5wn6f/aZllUNVzty5ilLTHqWFkfVOwpuN91o7yxA=="], - "@bull-board/hono": ["@bull-board/hono@6.10.1", "", { "dependencies": { "@bull-board/api": "6.10.1", "@bull-board/ui": "6.10.1", "ejs": "^3.1.10" }, "peerDependencies": { "hono": "^4" } }, "sha512-2I9BUS7jbtQ4tCKOJdVKQn6uW8MXJKUGIhHLK2r4X8kiXZvk2I7jjt0KZ4VNyF9nfoBblgX5WemxE4sU61kGGg=="], + "@bull-board/hono": ["@bull-board/hono@6.11.0", "", { "dependencies": { "@bull-board/api": "6.11.0", "@bull-board/ui": "6.11.0", "ejs": "^3.1.10" }, "peerDependencies": { "hono": "^4" } }, "sha512-M1eEHpDIiwsDKssoqeiuimeDhPPWRjN8teW6lydN95uFzWArAG8aT0J/r//+m4kZZkzwPsPeiPzwZXFAJ/wQ2g=="], - "@bull-board/ui": ["@bull-board/ui@6.10.1", "", { "dependencies": { "@bull-board/api": "6.10.1" } }, "sha512-b6z6MBid/0DEShAMFPjPVZoPSoWqRBHCvTknyaxr/m8gL2/C+QP7jlCXut+L7uTFbCj9qs+CreAP0x/VdLI/Ig=="], + "@bull-board/ui": ["@bull-board/ui@6.11.0", "", { "dependencies": { "@bull-board/api": "6.11.0" } }, "sha512-NB2mYr8l850BOLzytUyeYl8T3M9ZgPDDfT9WTOCVCDPr77kFF7iEM5jSE9AZg86bmZyWAgO/ogOUJaPSCNHY7g=="], "@clerc/core": ["@clerc/core@0.44.0", "", { "dependencies": { "@clerc/utils": "0.44.0", "defu": "^6.1.2", "is-platform": "^1.0.0", "lite-emit": "^2.3.0", "type-fest": "^4.3.1", "type-flag": "^3.0.0" } }, "sha512-o8RgXNcMRoHRujSw9OPDMxqrmoNk7HG0XAZkjZgOrSyIfRXCf85VLyHGBT3XmaOrPEGY964h02ZxMVFdp8RnNQ=="], @@ -228,61 +409,61 @@ "@esbuild-kit/esm-loader": ["@esbuild-kit/esm-loader@2.6.5", "", { "dependencies": { "@esbuild-kit/core-utils": "^3.3.2", "get-tsconfig": "^4.7.0" } }, "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA=="], - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.3", "", { "os": "aix", "cpu": "ppc64" }, "sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ=="], + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA=="], - "@esbuild/android-arm": ["@esbuild/android-arm@0.25.3", "", { "os": "android", "cpu": "arm" }, "sha512-PuwVXbnP87Tcff5I9ngV0lmiSu40xw1At6i3GsU77U7cjDDB4s0X2cyFuBiDa1SBk9DnvWwnGvVaGBqoFWPb7A=="], + "@esbuild/android-arm": ["@esbuild/android-arm@0.25.5", "", { "os": "android", "cpu": "arm" }, "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA=="], - "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.3", "", { "os": "android", "cpu": "arm64" }, "sha512-XelR6MzjlZuBM4f5z2IQHK6LkK34Cvv6Rj2EntER3lwCBFdg6h2lKbtRjpTTsdEjD/WSe1q8UyPBXP1x3i/wYQ=="], + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.5", "", { "os": "android", "cpu": "arm64" }, "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg=="], - "@esbuild/android-x64": ["@esbuild/android-x64@0.25.3", "", { "os": "android", "cpu": "x64" }, "sha512-ogtTpYHT/g1GWS/zKM0cc/tIebFjm1F9Aw1boQ2Y0eUQ+J89d0jFY//s9ei9jVIlkYi8AfOjiixcLJSGNSOAdQ=="], + "@esbuild/android-x64": ["@esbuild/android-x64@0.25.5", "", { "os": "android", "cpu": "x64" }, "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw=="], - "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-eESK5yfPNTqpAmDfFWNsOhmIOaQA59tAcF/EfYvo5/QWQCzXn5iUSOnqt3ra3UdzBv073ykTtmeLJZGt3HhA+w=="], + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ=="], - "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-Kd8glo7sIZtwOLcPbW0yLpKmBNWMANZhrC1r6K++uDR2zyzb6AeOYtI6udbtabmQpFaxJ8uduXMAo1gs5ozz8A=="], + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ=="], - "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-EJiyS70BYybOBpJth3M0KLOus0n+RRMKTYzhYhFeMwp7e/RaajXvP+BWlmEXNk6uk+KAu46j/kaQzr6au+JcIw=="], + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw=="], - "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Q+wSjaLpGxYf7zC0kL0nDlhsfuFkoN+EXrx2KSB33RhinWzejOd6AvgmP5JbkgXKmjhmpfgKZq24pneodYqE8Q=="], + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw=="], - "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.3", "", { "os": "linux", "cpu": "arm" }, "sha512-dUOVmAUzuHy2ZOKIHIKHCm58HKzFqd+puLaS424h6I85GlSDRZIA5ycBixb3mFgM0Jdh+ZOSB6KptX30DD8YOQ=="], + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.5", "", { "os": "linux", "cpu": "arm" }, "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw=="], - "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-xCUgnNYhRD5bb1C1nqrDV1PfkwgbswTTBRbAd8aH5PhYzikdf/ddtsYyMXFfGSsb/6t6QaPSzxtbfAZr9uox4A=="], + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg=="], - "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.3", "", { "os": "linux", "cpu": "ia32" }, "sha512-yplPOpczHOO4jTYKmuYuANI3WhvIPSVANGcNUeMlxH4twz/TeXuzEP41tGKNGWJjuMhotpGabeFYGAOU2ummBw=="], + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.5", "", { "os": "linux", "cpu": "ia32" }, "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA=="], - "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.3", "", { "os": "linux", "cpu": "none" }, "sha512-P4BLP5/fjyihmXCELRGrLd793q/lBtKMQl8ARGpDxgzgIKJDRJ/u4r1A/HgpBpKpKZelGct2PGI4T+axcedf6g=="], + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.5", "", { "os": "linux", "cpu": "none" }, "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg=="], - "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.3", "", { "os": "linux", "cpu": "none" }, "sha512-eRAOV2ODpu6P5divMEMa26RRqb2yUoYsuQQOuFUexUoQndm4MdpXXDBbUoKIc0iPa4aCO7gIhtnYomkn2x+bag=="], + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.5", "", { "os": "linux", "cpu": "none" }, "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg=="], - "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-ZC4jV2p7VbzTlnl8nZKLcBkfzIf4Yad1SJM4ZMKYnJqZFD4rTI+pBG65u8ev4jk3/MPwY9DvGn50wi3uhdaghg=="], + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ=="], - "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.3", "", { "os": "linux", "cpu": "none" }, "sha512-LDDODcFzNtECTrUUbVCs6j9/bDVqy7DDRsuIXJg6so+mFksgwG7ZVnTruYi5V+z3eE5y+BJZw7VvUadkbfg7QA=="], + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.5", "", { "os": "linux", "cpu": "none" }, "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA=="], - "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-s+w/NOY2k0yC2p9SLen+ymflgcpRkvwwa02fqmAwhBRI3SC12uiS10edHHXlVWwfAagYSY5UpmT/zISXPMW3tQ=="], + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ=="], - "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.3", "", { "os": "linux", "cpu": "x64" }, "sha512-nQHDz4pXjSDC6UfOE1Fw9Q8d6GCAd9KdvMZpfVGWSJztYCarRgSDfOVBY5xwhQXseiyxapkiSJi/5/ja8mRFFA=="], + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.5", "", { "os": "linux", "cpu": "x64" }, "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw=="], - "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.3", "", { "os": "none", "cpu": "arm64" }, "sha512-1QaLtOWq0mzK6tzzp0jRN3eccmN3hezey7mhLnzC6oNlJoUJz4nym5ZD7mDnS/LZQgkrhEbEiTn515lPeLpgWA=="], + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.5", "", { "os": "none", "cpu": "arm64" }, "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw=="], - "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.3", "", { "os": "none", "cpu": "x64" }, "sha512-i5Hm68HXHdgv8wkrt+10Bc50zM0/eonPb/a/OFVfB6Qvpiirco5gBA5bz7S2SHuU+Y4LWn/zehzNX14Sp4r27g=="], + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.5", "", { "os": "none", "cpu": "x64" }, "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ=="], - "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.3", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-zGAVApJEYTbOC6H/3QBr2mq3upG/LBEXr85/pTtKiv2IXcgKV0RT0QA/hSXZqSvLEpXeIxah7LczB4lkiYhTAQ=="], + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.5", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw=="], - "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.3", "", { "os": "openbsd", "cpu": "x64" }, "sha512-fpqctI45NnCIDKBH5AXQBsD0NDPbEFczK98hk/aa6HJxbl+UtLkJV2+Bvy5hLSLk3LHmqt0NTkKNso1A9y1a4w=="], + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg=="], - "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.3", "", { "os": "sunos", "cpu": "x64" }, "sha512-ROJhm7d8bk9dMCUZjkS8fgzsPAZEjtRJqCAmVgB0gMrvG7hfmPmz9k1rwO4jSiblFjYmNvbECL9uhaPzONMfgA=="], + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.5", "", { "os": "sunos", "cpu": "x64" }, "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA=="], - "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-YWcow8peiHpNBiIXHwaswPnAXLsLVygFwCB3A7Bh5jRkIBFWHGmNQ48AlX4xDvQNoMZlPYzjVOQDYEzWCqufMQ=="], + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw=="], - "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-qspTZOIGoXVS4DpNqUYUs9UxVb04khS1Degaw/MnfMe7goQ3lTfQ13Vw4qY/Nj0979BGvMRpAYbs/BAxEvU8ew=="], + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ=="], - "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.3", "", { "os": "win32", "cpu": "x64" }, "sha512-ICgUR+kPimx0vvRzf+N/7L7tVSQeE3BYY+NhHRHXS1kBuPO7z2+7ea2HbhDyZdTephgvNvKrlDDKUexuCVBVvg=="], + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.5", "", { "os": "win32", "cpu": "x64" }, "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g=="], "@hackmd/markdown-it-task-lists": ["@hackmd/markdown-it-task-lists@2.1.4", "", {}, "sha512-njMloWVihC7a7N4zxczv547bgNxPVG3GBzh6Z6f2xnO8/92JaxTmQuMV7YvaKKkOyhh2RW4RT84uSgax8u4qfQ=="], - "@hono/zod-validator": ["@hono/zod-validator@0.7.0", "", { "peerDependencies": { "hono": ">=3.9.0", "zod": "^3.25.0" } }, "sha512-qe2ZE6sHFE98dcUrbYMtS3bAV8hqcCOflykvZga2S7XhmNSZzT+dIz4OuMILsjLHkJw9JMn912/dB7dQOmuPvg=="], + "@hono/standard-validator": ["@hono/standard-validator@0.1.2", "", { "peerDependencies": { "@standard-schema/spec": "1.0.0", "hono": ">=3.9.0" } }, "sha512-mVyv2fpx/o0MNAEhjXhvuVbW3BWTGnf8F4w8ZifztE+TWXjUAKr7KAOZfcDhVrurgVhKw7RbTnEog2beZM6QtQ=="], - "@iconify-json/simple-icons": ["@iconify-json/simple-icons@1.2.33", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-nL5/UmI9x5PQ/AHv6bOaL2pH6twEdEz4pI89efB/K7HFn5etQnxMtGx9DFlOg/sRA2/yFpX8KXvc95CSDv5bJA=="], + "@iconify-json/simple-icons": ["@iconify-json/simple-icons@1.2.41", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-4tt29cKzNsxvt6rjAOVhEgpZV0L8jleTDTMdtvIJjF14Afp9aH8peuwGYyX35l6idfFwuzbvjSVfVyVjJtfmYA=="], "@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="], @@ -328,9 +509,9 @@ "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.2", "", { "os": "win32", "cpu": "x64" }, "sha512-aUdT6zEYtDKCaxkofmmJDJYGCf0+pJg3eU9/oBuqvEeoB9dKI6ZLc/1iLJCTuJQDO4ptntAlkUmHgGjyuobZbw=="], - "@inquirer/confirm": ["@inquirer/confirm@5.1.12", "", { "dependencies": { "@inquirer/core": "^10.1.13", "@inquirer/type": "^3.0.7" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-dpq+ielV9/bqgXRUbNH//KsY6WEw9DrGPmipkpmgC1Y46cwuBTNx7PXFWTjc3MQ+urcc0QxoVHcMI0FW4Ok0hg=="], + "@inquirer/confirm": ["@inquirer/confirm@5.1.13", "", { "dependencies": { "@inquirer/core": "^10.1.14", "@inquirer/type": "^3.0.7" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-EkCtvp67ICIVVzjsquUiVSd+V5HRGOGQfsqA4E4vMWhYnB7InUL0pa0TIWt1i+OfP16Gkds8CdIu6yGZwOM1Yw=="], - "@inquirer/core": ["@inquirer/core@10.1.13", "", { "dependencies": { "@inquirer/figures": "^1.0.12", "@inquirer/type": "^3.0.7", "ansi-escapes": "^4.3.2", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-1viSxebkYN2nJULlzCxES6G9/stgHSepZ9LqqfdIGPHj5OHhiBUXVS0a6R0bEC2A+VL4D9w6QB66ebCr6HGllA=="], + "@inquirer/core": ["@inquirer/core@10.1.14", "", { "dependencies": { "@inquirer/figures": "^1.0.12", "@inquirer/type": "^3.0.7", "ansi-escapes": "^4.3.2", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-Ma+ZpOJPewtIYl6HZHZckeX1STvDnHTCB2GVINNUlSEn2Am6LddWwfPkIGY0IUFVjUUrr/93XlBwTK6mfLjf0A=="], "@inquirer/figures": ["@inquirer/figures@1.0.12", "", {}, "sha512-MJttijd8rMFcKJC8NYmprWr6hD3r9Gd9qUC0XwPNwoEPWSMVJwA2MlXxF+nhZZNMY+HXsWa+o7KY2emWYIn0jQ=="], @@ -340,13 +521,15 @@ "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="], - "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="], + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.4", "", {}, "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw=="], - "@jsdevtools/ono": ["@jsdevtools/ono@7.1.3", "", {}, "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg=="], + "@logtape/file": ["@logtape/file@1.0.2", "", { "peerDependencies": { "@logtape/logtape": "1.0.2" } }, "sha512-V5fiudPkjz0+R5+eVNceYwn65oZ/XrUXlRqbn0xFaHZ/XBPgVRTPf2fReFeyzcl3d3hcPBGk2K6smsJQBSJavw=="], - "@logtape/file": ["@logtape/file@0.12.0", "", { "peerDependencies": { "@logtape/logtape": "0.12.0" } }, "sha512-C3VLzKF8RQJUjozj8xstmfw3LRVMHScR9275MaIb/krLooNdy8z14IaQ2XGESfQr2NM946wRPBdAcNKm0DV6Ww=="], + "@logtape/logtape": ["@logtape/logtape@1.0.2", "", {}, "sha512-6EWfs4KyTAVsiAnXXSFpzEmUYI2k7qLJogqPv3JqwFd8S8Zr2iUBPv3pbIC+70cW4P6Zpq1l1hnX/jDVZwvc+Q=="], - "@logtape/logtape": ["@logtape/logtape@0.12.0", "", {}, "sha512-FPAI0VHDLaU/+sFC53k50RhJyMvj8tsuENF7Bzf+jKdMac2HavV2/eB4YyFltjt4PwSbvKimXc0cE9iwfHbPpw=="], + "@logtape/otel": ["@logtape/otel@1.0.2", "", { "dependencies": { "@opentelemetry/api": "^1.9.0", "@opentelemetry/api-logs": "^0.202.0", "@opentelemetry/exporter-logs-otlp-http": "^0.202.0", "@opentelemetry/otlp-exporter-base": "^0.202.0", "@opentelemetry/resources": "^2.0.1", "@opentelemetry/sdk-logs": "^0.202.0", "@opentelemetry/semantic-conventions": "^1.34.0" }, "peerDependencies": { "@logtape/logtape": "1.0.2" } }, "sha512-t/nHGLMtibqB/ywYOsUMQDHlSlbZ/ge2EpP/5MuwJYukNfckh72Xryu7PIUGcR09/y7Qkn0Fi7g5jDHrUWjPQw=="], + + "@logtape/sentry": ["@logtape/sentry@1.0.2", "", { "dependencies": { "@sentry/core": "^9.28.1" }, "peerDependencies": { "@logtape/logtape": "1.0.2" } }, "sha512-S14Vqb4+IuBVoD429x30/9zv8OWfxITSjwA6LlANAANAZ/t1kM4AOvsl3jJQWyecaicxTdtgJnj6d5iAbbWB7A=="], "@msgpackr-extract/msgpackr-extract-darwin-arm64": ["@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw=="], @@ -368,12 +551,14 @@ "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], - "@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.57.2", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-uIX52NnTM0iBh84MShlpouI7UKqkZ7MrUszTmaypHBu4r7NofznSnQRfJ+uUeDtQDj6w8eFGg5KBLDAwAPz1+A=="], + "@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.202.0", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-fTBjMqKCfotFWfLzaKyhjLvyEyq5vDKTTFfBmx21btv3gvy8Lq6N5Dh2OzqeuN4DjtpSvNT1uNVfg08eD2Rfxw=="], "@opentelemetry/context-async-hooks": ["@opentelemetry/context-async-hooks@1.30.1", "", { "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA=="], "@opentelemetry/core": ["@opentelemetry/core@1.30.1", "", { "dependencies": { "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ=="], + "@opentelemetry/exporter-logs-otlp-http": ["@opentelemetry/exporter-logs-otlp-http@0.202.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.202.0", "@opentelemetry/core": "2.0.1", "@opentelemetry/otlp-exporter-base": "0.202.0", "@opentelemetry/otlp-transformer": "0.202.0", "@opentelemetry/sdk-logs": "0.202.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-mJWLkmoG+3r+SsYQC+sbWoy1rjowJhMhFvFULeIPTxSI+EZzKPya0+NZ3+vhhgx2UTybGQlye3FBtCH3o6Rejg=="], + "@opentelemetry/instrumentation": ["@opentelemetry/instrumentation@0.57.2", "", { "dependencies": { "@opentelemetry/api-logs": "0.57.2", "@types/shimmer": "^1.2.0", "import-in-the-middle": "^1.8.1", "require-in-the-middle": "^7.1.1", "semver": "^7.5.2", "shimmer": "^1.2.1" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-BdBGhQBh8IjZ2oIIX6F2/Q3LKm/FDDKi6ccYKcBTeilh6SNdNKveDOLk73BkSJjQLJk6qe4Yh+hHw1UPhCDdrg=="], "@opentelemetry/instrumentation-amqplib": ["@opentelemetry/instrumentation-amqplib@0.46.1", "", { "dependencies": { "@opentelemetry/core": "^1.8.0", "@opentelemetry/instrumentation": "^0.57.1", "@opentelemetry/semantic-conventions": "^1.27.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-AyXVnlCf/xV3K/rNumzKxZqsULyITJH6OVLiW6730JPRqWA7Zc9bvYoVNpN6iOpTU8CasH34SU/ksVJmObFibQ=="], @@ -420,9 +605,17 @@ "@opentelemetry/instrumentation-undici": ["@opentelemetry/instrumentation-undici@0.10.1", "", { "dependencies": { "@opentelemetry/core": "^1.8.0", "@opentelemetry/instrumentation": "^0.57.1" }, "peerDependencies": { "@opentelemetry/api": "^1.7.0" } }, "sha512-rkOGikPEyRpMCmNu9AQuV5dtRlDmJp2dK5sw8roVshAGoB6hH/3QjDtRhdwd75SsJwgynWUNRUYe0wAkTo16tQ=="], + "@opentelemetry/otlp-exporter-base": ["@opentelemetry/otlp-exporter-base@0.202.0", "", { "dependencies": { "@opentelemetry/core": "2.0.1", "@opentelemetry/otlp-transformer": "0.202.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-nMEOzel+pUFYuBJg2znGmHJWbmvMbdX5/RhoKNKowguMbURhz0fwik5tUKplLcUtl8wKPL1y9zPnPxeBn65N0Q=="], + + "@opentelemetry/otlp-transformer": ["@opentelemetry/otlp-transformer@0.202.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.202.0", "@opentelemetry/core": "2.0.1", "@opentelemetry/resources": "2.0.1", "@opentelemetry/sdk-logs": "0.202.0", "@opentelemetry/sdk-metrics": "2.0.1", "@opentelemetry/sdk-trace-base": "2.0.1", "protobufjs": "^7.3.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-5XO77QFzs9WkexvJQL9ksxL8oVFb/dfi9NWQSq7Sv0Efr9x3N+nb1iklP1TeVgxqJ7m1xWiC/Uv3wupiQGevMw=="], + "@opentelemetry/redis-common": ["@opentelemetry/redis-common@0.36.2", "", {}, "sha512-faYX1N0gpLhej/6nyp6bgRjzAKXn5GOEMYY7YhciSfCoITAktLUtQ36d24QEWNA1/WA1y6qQunCe0OhHRkVl9g=="], - "@opentelemetry/resources": ["@opentelemetry/resources@1.30.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA=="], + "@opentelemetry/resources": ["@opentelemetry/resources@2.0.1", "", { "dependencies": { "@opentelemetry/core": "2.0.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-dZOB3R6zvBwDKnHDTB4X1xtMArB/d324VsbiPkX/Yu0Q8T2xceRthoIVFhJdvgVM2QhGVUyX9tzwiNxGtoBJUw=="], + + "@opentelemetry/sdk-logs": ["@opentelemetry/sdk-logs@0.202.0", "", { "dependencies": { "@opentelemetry/api-logs": "0.202.0", "@opentelemetry/core": "2.0.1", "@opentelemetry/resources": "2.0.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, "sha512-pv8QiQLQzk4X909YKm0lnW4hpuQg4zHwJ4XBd5bZiXcd9urvrJNoNVKnxGHPiDVX/GiLFvr5DMYsDBQbZCypRQ=="], + + "@opentelemetry/sdk-metrics": ["@opentelemetry/sdk-metrics@2.0.1", "", { "dependencies": { "@opentelemetry/core": "2.0.1", "@opentelemetry/resources": "2.0.1" }, "peerDependencies": { "@opentelemetry/api": ">=1.9.0 <1.10.0" } }, "sha512-wf8OaJoSnujMAHWR3g+/hGvNcsC16rf9s1So4JlMiFaFHiE4HpIA3oUh+uWZQ7CNuK8gVW/pQSkgoa5HkkOl0g=="], "@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@1.30.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/resources": "1.30.1", "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg=="], @@ -432,71 +625,91 @@ "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], - "@poppinss/colors": ["@poppinss/colors@4.1.4", "", { "dependencies": { "kleur": "^4.1.5" } }, "sha512-FA+nTU8p6OcSH4tLDY5JilGYr1bVWHpNmcLr7xmMEdbWmKHa+3QZ+DqefrXKmdjO/brHTnQZo20lLSjaO7ydog=="], + "@poppinss/colors": ["@poppinss/colors@4.1.5", "", { "dependencies": { "kleur": "^4.1.5" } }, "sha512-FvdDqtcRCtz6hThExcFOgW0cWX+xwSMWcRuQe5ZEb2m7cVQOAVZOIMt+/v9RxGiD9/OY16qJBXK4CVKWAPalBw=="], - "@poppinss/dumper": ["@poppinss/dumper@0.6.3", "", { "dependencies": { "@poppinss/colors": "^4.1.4", "@sindresorhus/is": "^7.0.1", "supports-color": "^10.0.0" } }, "sha512-iombbn8ckOixMtuV1p3f8jN6vqhXefNjJttoPaJDMeIk/yIGhkkL3OrHkEjE9SRsgoAx1vBUU2GtgggjvA5hCA=="], + "@poppinss/dumper": ["@poppinss/dumper@0.6.4", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@sindresorhus/is": "^7.0.2", "supports-color": "^10.0.0" } }, "sha512-iG0TIdqv8xJ3Lt9O8DrPRxw1MRLjNpoqiSGU03P/wNLP/s0ra0udPJ1J2Tx5M0J3H/cVyEgpbn8xUKRY9j59kQ=="], - "@poppinss/exception": ["@poppinss/exception@1.2.1", "", {}, "sha512-aQypoot0HPSJa6gDPEPTntc1GT6QINrSbgRlRhadGW2WaYqUK3tK4Bw9SBMZXhmxd3GeAlZjVcODHgiu+THY7A=="], + "@poppinss/exception": ["@poppinss/exception@1.2.2", "", {}, "sha512-m7bpKCD4QMlFCjA/nKTs23fuvoVFoA83brRKmObCUNmi/9tVu8Ve3w4YQAnJu4q3Tjf5fr685HYIC/IA2zHRSg=="], - "@prisma/instrumentation": ["@prisma/instrumentation@6.8.2", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.52.0 || ^0.53.0 || ^0.54.0 || ^0.55.0 || ^0.56.0 || ^0.57.0" }, "peerDependencies": { "@opentelemetry/api": "^1.8" } }, "sha512-5NCTbZjw7a+WIZ/ey6G8SY+YKcyM2zBF0hOT1muvqC9TbVtTCr5Qv3RL/2iNDOzLUHEvo4I1uEfioyfuNOGK8Q=="], + "@prisma/instrumentation": ["@prisma/instrumentation@6.10.1", "", { "dependencies": { "@opentelemetry/instrumentation": "^0.52.0 || ^0.53.0 || ^0.54.0 || ^0.55.0 || ^0.56.0 || ^0.57.0" }, "peerDependencies": { "@opentelemetry/api": "^1.8" } }, "sha512-JC8qzgEDuFKjuBsqrZvXHINUb12psnE6Qy3q5p2MBhalC1KW1MBBUwuonx6iS5TCfCdtNslHft8uc2r+EdLWWg=="], - "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.40.1", "", { "os": "android", "cpu": "arm" }, "sha512-kxz0YeeCrRUHz3zyqvd7n+TVRlNyTifBsmnmNPtk3hQURUyG9eAB+usz6DAwagMusjx/zb3AjvDUvhFGDAexGw=="], + "@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="], - "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.40.1", "", { "os": "android", "cpu": "arm64" }, "sha512-PPkxTOisoNC6TpnDKatjKkjRMsdaWIhyuMkA4UsBXT9WEZY4uHezBTjs6Vl4PbqQQeu6oION1w2voYZv9yquCw=="], + "@protobufjs/base64": ["@protobufjs/base64@1.1.2", "", {}, "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="], - "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.40.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-VWXGISWFY18v/0JyNUy4A46KCFCb9NVsH+1100XP31lud+TzlezBbz24CYzbnA4x6w4hx+NYCXDfnvDVO6lcAA=="], + "@protobufjs/codegen": ["@protobufjs/codegen@2.0.4", "", {}, "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="], - "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.40.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-nIwkXafAI1/QCS7pxSpv/ZtFW6TXcNUEHAIA9EIyw5OzxJZQ1YDrX+CL6JAIQgZ33CInl1R6mHet9Y/UZTg2Bw=="], + "@protobufjs/eventemitter": ["@protobufjs/eventemitter@1.1.0", "", {}, "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="], - "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.40.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-BdrLJ2mHTrIYdaS2I99mriyJfGGenSaP+UwGi1kB9BLOCu9SR8ZpbkmmalKIALnRw24kM7qCN0IOm6L0S44iWw=="], + "@protobufjs/fetch": ["@protobufjs/fetch@1.1.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" } }, "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ=="], - "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.40.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-VXeo/puqvCG8JBPNZXZf5Dqq7BzElNJzHRRw3vjBE27WujdzuOPecDPc/+1DcdcTptNBep3861jNq0mYkT8Z6Q=="], + "@protobufjs/float": ["@protobufjs/float@1.0.2", "", {}, "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="], - "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.40.1", "", { "os": "linux", "cpu": "arm" }, "sha512-ehSKrewwsESPt1TgSE/na9nIhWCosfGSFqv7vwEtjyAqZcvbGIg4JAcV7ZEh2tfj/IlfBeZjgOXm35iOOjadcg=="], + "@protobufjs/inquire": ["@protobufjs/inquire@1.1.0", "", {}, "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="], - "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.40.1", "", { "os": "linux", "cpu": "arm" }, "sha512-m39iO/aaurh5FVIu/F4/Zsl8xppd76S4qoID8E+dSRQvTyZTOI2gVk3T4oqzfq1PtcvOfAVlwLMK3KRQMaR8lg=="], + "@protobufjs/path": ["@protobufjs/path@1.1.2", "", {}, "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="], - "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.40.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-Y+GHnGaku4aVLSgrT0uWe2o2Rq8te9hi+MwqGF9r9ORgXhmHK5Q71N757u0F8yU1OIwUIFy6YiJtKjtyktk5hg=="], + "@protobufjs/pool": ["@protobufjs/pool@1.1.0", "", {}, "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="], - "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.40.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-jEwjn3jCA+tQGswK3aEWcD09/7M5wGwc6+flhva7dsQNRZZTe30vkalgIzV4tjkopsTS9Jd7Y1Bsj6a4lzz8gQ=="], + "@protobufjs/utf8": ["@protobufjs/utf8@1.1.0", "", {}, "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="], - "@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.40.1", "", { "os": "linux", "cpu": "none" }, "sha512-ySyWikVhNzv+BV/IDCsrraOAZ3UaC8SZB67FZlqVwXwnFhPihOso9rPOxzZbjp81suB1O2Topw+6Ug3JNegejQ=="], + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.44.2", "", { "os": "android", "cpu": "arm" }, "sha512-g0dF8P1e2QYPOj1gu7s/3LVP6kze9A7m6x0BZ9iTdXK8N5c2V7cpBKHV3/9A4Zd8xxavdhK0t4PnqjkqVmUc9Q=="], - "@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.40.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-BvvA64QxZlh7WZWqDPPdt0GH4bznuL6uOO1pmgPnnv86rpUpc8ZxgZwcEgXvo02GRIZX1hQ0j0pAnhwkhwPqWg=="], + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.44.2", "", { "os": "android", "cpu": "arm64" }, "sha512-Yt5MKrOosSbSaAK5Y4J+vSiID57sOvpBNBR6K7xAaQvk3MkcNVV0f9fE20T+41WYN8hDn6SGFlFrKudtx4EoxA=="], - "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.40.1", "", { "os": "linux", "cpu": "none" }, "sha512-EQSP+8+1VuSulm9RKSMKitTav89fKbHymTf25n5+Yr6gAPZxYWpj3DzAsQqoaHAk9YX2lwEyAf9S4W8F4l3VBQ=="], + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.44.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-EsnFot9ZieM35YNA26nhbLTJBHD0jTwWpPwmRVDzjylQT6gkar+zenfb8mHxWpRrbn+WytRRjE0WKsfaxBkVUA=="], - "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.40.1", "", { "os": "linux", "cpu": "none" }, "sha512-n/vQ4xRZXKuIpqukkMXZt9RWdl+2zgGNx7Uda8NtmLJ06NL8jiHxUawbwC+hdSq1rrw/9CghCpEONor+l1e2gA=="], + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.44.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-dv/t1t1RkCvJdWWxQ2lWOO+b7cMsVw5YFaS04oHpZRWehI1h0fV1gF4wgGCTyQHHjJDfbNpwOi6PXEafRBBezw=="], - "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.40.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-h8d28xzYb98fMQKUz0w2fMc1XuGzLLjdyxVIbhbil4ELfk5/orZlSTpF/xdI9C8K0I8lCkq+1En2RJsawZekkg=="], + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.44.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-W4tt4BLorKND4qeHElxDoim0+BsprFTwb+vriVQnFFtT/P6v/xO5I99xvYnVzKWrK6j7Hb0yp3x7V5LUbaeOMg=="], - "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.40.1", "", { "os": "linux", "cpu": "x64" }, "sha512-XiK5z70PEFEFqcNj3/zRSz/qX4bp4QIraTy9QjwJAb/Z8GM7kVUsD0Uk8maIPeTyPCP03ChdI+VVmJriKYbRHQ=="], + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.44.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tdT1PHopokkuBVyHjvYehnIe20fxibxFCEhQP/96MDSOcyjM/shlTkZZLOufV3qO6/FQOSiJTBebhVc12JyPTA=="], - "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.40.1", "", { "os": "linux", "cpu": "x64" }, "sha512-2BRORitq5rQ4Da9blVovzNCMaUlyKrzMSvkVR0D4qPuOy/+pMCrh1d7o01RATwVy+6Fa1WBw+da7QPeLWU/1mQ=="], + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.44.2", "", { "os": "linux", "cpu": "arm" }, "sha512-+xmiDGGaSfIIOXMzkhJ++Oa0Gwvl9oXUeIiwarsdRXSe27HUIvjbSIpPxvnNsRebsNdUo7uAiQVgBD1hVriwSQ=="], - "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.40.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-b2bcNm9Kbde03H+q+Jjw9tSfhYkzrDUf2d5MAd1bOJuVplXvFhWz7tRtWvD8/ORZi7qSCy0idW6tf2HgxSXQSg=="], + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.44.2", "", { "os": "linux", "cpu": "arm" }, "sha512-bDHvhzOfORk3wt8yxIra8N4k/N0MnKInCW5OGZaeDYa/hMrdPaJzo7CSkjKZqX4JFUWjUGm88lI6QJLCM7lDrA=="], - "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.40.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-DfcogW8N7Zg7llVEfpqWMZcaErKfsj9VvmfSyRjCyo4BI3wPEfrzTtJkZG6gKP/Z92wFm6rz2aDO7/JfiR/whA=="], + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.44.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-NMsDEsDiYghTbeZWEGnNi4F0hSbGnsuOG+VnNvxkKg0IGDvFh7UVpM/14mnMwxRxUf9AdAVJgHPvKXf6FpMB7A=="], - "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.40.1", "", { "os": "win32", "cpu": "x64" }, "sha512-ECyOuDeH3C1I8jH2MK1RtBJW+YPMvSfT0a5NN0nHfQYnDSJ6tUiZH3gzwVP5/Kfh/+Tt7tpWVF9LXNTnhTJ3kA=="], + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.44.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-lb5bxXnxXglVq+7imxykIp5xMq+idehfl+wOgiiix0191av84OqbjUED+PRC5OA8eFJYj5xAGcpAZ0pF2MnW+A=="], - "@scalar/core": ["@scalar/core@0.3.3", "", { "dependencies": { "@scalar/types": "0.2.3" } }, "sha512-OedH8YQriBsE7JnCN2ly4p3u7iobjzAoDkxD/o1hiF55m6WRicvtklEdByHlceu9UnJ/znikZ50ymo98uG3aqA=="], + "@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.44.2", "", { "os": "linux", "cpu": "none" }, "sha512-Yl5Rdpf9pIc4GW1PmkUGHdMtbx0fBLE1//SxDmuf3X0dUC57+zMepow2LK0V21661cjXdTn8hO2tXDdAWAqE5g=="], - "@scalar/hono-api-reference": ["@scalar/hono-api-reference@0.9.4", "", { "dependencies": { "@scalar/core": "0.3.3" }, "peerDependencies": { "hono": "^4.0.0" } }, "sha512-pAUWS8MRGcS5VjMjAVd6DrhST63NpVXGel2BbYsKPQAaONwwA3hU30fAFxSs3PUGA8M7q+e76F6w4ALBtHxLRA=="], + "@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.44.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-03vUDH+w55s680YYryyr78jsO1RWU9ocRMaeV2vMniJJW/6HhoTBwyyiiTPVHNWLnhsnwcQ0oH3S9JSBEKuyqw=="], - "@scalar/openapi-types": ["@scalar/openapi-types@0.3.3", "", { "dependencies": { "zod": "3.24.1" } }, "sha512-mr3OvbCyvuzSDgv9gR9D3pJAsl82BRNBp1P8ldgRgKF4Wiswfa7nQeKibpX0kHSGXLgiyvxWJULUFwgdWMXNNA=="], + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.44.2", "", { "os": "linux", "cpu": "none" }, "sha512-iYtAqBg5eEMG4dEfVlkqo05xMOk6y/JXIToRca2bAWuqjrJYJlx/I7+Z+4hSrsWU8GdJDFPL4ktV3dy4yBSrzg=="], - "@scalar/types": ["@scalar/types@0.2.3", "", { "dependencies": { "@scalar/openapi-types": "0.3.3", "nanoid": "^5.1.5", "zod": "3.24.1" } }, "sha512-K1/Vp5xaQ8TEGDYwHg88OBuQWJRWrE3ToZ0Z2LJwskcY1Eg+uDiRQXKF4QPngZzu5rrWgRErAq4yte4Y7omqgA=="], + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.44.2", "", { "os": "linux", "cpu": "none" }, "sha512-e6vEbgaaqz2yEHqtkPXa28fFuBGmUJ0N2dOJK8YUfijejInt9gfCSA7YDdJ4nYlv67JfP3+PSWFX4IVw/xRIPg=="], + + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.44.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-evFOtkmVdY3udE+0QKrV5wBx7bKI0iHz5yEVx5WqDJkxp9YQefy4Mpx3RajIVcM6o7jxTvVd/qpC1IXUhGc1Mw=="], + + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.44.2", "", { "os": "linux", "cpu": "x64" }, "sha512-/bXb0bEsWMyEkIsUL2Yt5nFB5naLAwyOWMEviQfQY1x3l5WsLKgvZf66TM7UTfED6erckUVUJQ/jJ1FSpm3pRQ=="], + + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.44.2", "", { "os": "linux", "cpu": "x64" }, "sha512-3D3OB1vSSBXmkGEZR27uiMRNiwN08/RVAcBKwhUYPaiZ8bcvdeEwWPvbnXvvXHY+A/7xluzcN+kaiOFNiOZwWg=="], + + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.44.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-VfU0fsMK+rwdK8mwODqYeM2hDrF2WiHaSmCBrS7gColkQft95/8tphyzv2EupVxn3iE0FI78wzffoULH1G+dkw=="], + + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.44.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-+qMUrkbUurpE6DVRjiJCNGZBGo9xM4Y0FXU5cjgudWqIBWbcLkjE3XprJUsOFgC6xjBClwVa9k6O3A7K3vxb5Q=="], + + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.44.2", "", { "os": "win32", "cpu": "x64" }, "sha512-3+QZROYfJ25PDcxFF66UEk8jGWigHJeecZILvkPkyQN7oc5BvFo4YEXFkOs154j3FTMp9mn9Ky8RCOwastduEA=="], + + "@scalar/core": ["@scalar/core@0.3.5", "", { "dependencies": { "@scalar/types": "0.2.5" } }, "sha512-tcLLaGT/otQuEZzTtWUlnBMtcs95gcqwJSQCHqDui2v/+2ctV3FKjGJFoZLutICmQaSGLk0zNt14w39/VAsEqw=="], + + "@scalar/hono-api-reference": ["@scalar/hono-api-reference@0.9.7", "", { "dependencies": { "@scalar/core": "0.3.5" }, "peerDependencies": { "hono": "^4.0.0" } }, "sha512-IJngqUlvIEZ7e4VrUUdviNbGQIx6NoHb7XrkTAUMLYvcTw1J3TcdFY4i7hWiD4bp2z+pVbi3UFStlt7y6KzdUA=="], + + "@scalar/openapi-types": ["@scalar/openapi-types@0.3.4", "", { "dependencies": { "zod": "3.24.1" } }, "sha512-MfHRX1jqLcf6kwDDeXSYENywZXJLZx9Jn8r7DCQApw2QJW/yxb4GnCU0vygCh5KBk1GVJ4x6pyXiWNozb+fGAw=="], + + "@scalar/types": ["@scalar/types@0.2.5", "", { "dependencies": { "@scalar/openapi-types": "0.3.4", "nanoid": "5.1.5", "zod": "3.24.1" } }, "sha512-kXulR6peh+zzP9DFqhncUKtvnV59e6Cf/y3N5Q8RQIa8huRB+901MbXewUEXV4MXYzMMBKJXRgc1LwwbAZUtRA=="], "@selderee/plugin-htmlparser2": ["@selderee/plugin-htmlparser2@0.11.0", "", { "dependencies": { "domhandler": "^5.0.3", "selderee": "^0.11.0" } }, "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ=="], - "@sentry/bun": ["@sentry/bun@9.29.0", "", { "dependencies": { "@sentry/core": "9.29.0", "@sentry/node": "9.29.0", "@sentry/opentelemetry": "9.29.0" } }, "sha512-85wFqI0oEOZ/73KcTTmd4wMkKCyaxMLMWCiDJVrXxGbuC37pevQqZmIEgNABfs6NZLc2bHSxOzKlWzgJs30Pig=="], + "@sentry/bun": ["@sentry/bun@9.35.0", "", { "dependencies": { "@sentry/core": "9.35.0", "@sentry/node": "9.35.0" } }, "sha512-zVaefPcYMOBONKJCu1XsL8zIw7G3CtcYsPaU9+03+3+t5XwTAFbpv+kXRobIj7H7AzgDDsj2XD0skxk6xg698A=="], - "@sentry/core": ["@sentry/core@9.29.0", "", {}, "sha512-wDyNe45PM+RCGtUn1tK7LzJ08ksv8i8KRUHrst7lsinEfRm83YH+wbWrPmwkVNEngUZvYkHwGLbNXM7xgFUuDQ=="], + "@sentry/core": ["@sentry/core@9.35.0", "", {}, "sha512-bdAtzVQZ/wn4L/m8r2OUCCG/NWr0Q8dyZDwdwvINJaMbyhDRUdQh/MWjrz+id/3JoOL1LigAyTV1h4FJDGuwUQ=="], - "@sentry/node": ["@sentry/node@9.29.0", "", { "dependencies": { "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^1.30.1", "@opentelemetry/core": "^1.30.1", "@opentelemetry/instrumentation": "^0.57.2", "@opentelemetry/instrumentation-amqplib": "^0.46.1", "@opentelemetry/instrumentation-connect": "0.43.1", "@opentelemetry/instrumentation-dataloader": "0.16.1", "@opentelemetry/instrumentation-express": "0.47.1", "@opentelemetry/instrumentation-fs": "0.19.1", "@opentelemetry/instrumentation-generic-pool": "0.43.1", "@opentelemetry/instrumentation-graphql": "0.47.1", "@opentelemetry/instrumentation-hapi": "0.45.2", "@opentelemetry/instrumentation-http": "0.57.2", "@opentelemetry/instrumentation-ioredis": "0.47.1", "@opentelemetry/instrumentation-kafkajs": "0.7.1", "@opentelemetry/instrumentation-knex": "0.44.1", "@opentelemetry/instrumentation-koa": "0.47.1", "@opentelemetry/instrumentation-lru-memoizer": "0.44.1", "@opentelemetry/instrumentation-mongodb": "0.52.0", "@opentelemetry/instrumentation-mongoose": "0.46.1", "@opentelemetry/instrumentation-mysql": "0.45.1", "@opentelemetry/instrumentation-mysql2": "0.45.2", "@opentelemetry/instrumentation-pg": "0.51.1", "@opentelemetry/instrumentation-redis-4": "0.46.1", "@opentelemetry/instrumentation-tedious": "0.18.1", "@opentelemetry/instrumentation-undici": "0.10.1", "@opentelemetry/resources": "^1.30.1", "@opentelemetry/sdk-trace-base": "^1.30.1", "@opentelemetry/semantic-conventions": "^1.34.0", "@prisma/instrumentation": "6.8.2", "@sentry/core": "9.29.0", "@sentry/opentelemetry": "9.29.0", "import-in-the-middle": "^1.13.1", "minimatch": "^9.0.0" } }, "sha512-oABipgC/fClRuvyMeK43rigv9F+OAaoR84UaMKB7aPXN6iz634wBRVsaoZAwiR3xLL+R7MafEPPA/s9XqlG7ag=="], + "@sentry/node": ["@sentry/node@9.35.0", "", { "dependencies": { "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^1.30.1", "@opentelemetry/core": "^1.30.1", "@opentelemetry/instrumentation": "^0.57.2", "@opentelemetry/instrumentation-amqplib": "^0.46.1", "@opentelemetry/instrumentation-connect": "0.43.1", "@opentelemetry/instrumentation-dataloader": "0.16.1", "@opentelemetry/instrumentation-express": "0.47.1", "@opentelemetry/instrumentation-fs": "0.19.1", "@opentelemetry/instrumentation-generic-pool": "0.43.1", "@opentelemetry/instrumentation-graphql": "0.47.1", "@opentelemetry/instrumentation-hapi": "0.45.2", "@opentelemetry/instrumentation-http": "0.57.2", "@opentelemetry/instrumentation-ioredis": "0.47.1", "@opentelemetry/instrumentation-kafkajs": "0.7.1", "@opentelemetry/instrumentation-knex": "0.44.1", "@opentelemetry/instrumentation-koa": "0.47.1", "@opentelemetry/instrumentation-lru-memoizer": "0.44.1", "@opentelemetry/instrumentation-mongodb": "0.52.0", "@opentelemetry/instrumentation-mongoose": "0.46.1", "@opentelemetry/instrumentation-mysql": "0.45.1", "@opentelemetry/instrumentation-mysql2": "0.45.2", "@opentelemetry/instrumentation-pg": "0.51.1", "@opentelemetry/instrumentation-redis-4": "0.46.1", "@opentelemetry/instrumentation-tedious": "0.18.1", "@opentelemetry/instrumentation-undici": "0.10.1", "@opentelemetry/resources": "^1.30.1", "@opentelemetry/sdk-trace-base": "^1.30.1", "@opentelemetry/semantic-conventions": "^1.34.0", "@prisma/instrumentation": "6.10.1", "@sentry/core": "9.35.0", "@sentry/opentelemetry": "9.35.0", "import-in-the-middle": "^1.14.2", "minimatch": "^9.0.0" } }, "sha512-7ifFqTsa3BtZGRAgqoWqYf7OJizKSyEzQlSixgBc253wyYWiLaVJ15By9Y4ozd+PbgpOPqfDN5B45Y+OxtQnQw=="], - "@sentry/opentelemetry": ["@sentry/opentelemetry@9.29.0", "", { "dependencies": { "@sentry/core": "9.29.0" }, "peerDependencies": { "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^1.30.1 || ^2.0.0", "@opentelemetry/core": "^1.30.1 || ^2.0.0", "@opentelemetry/instrumentation": "^0.57.1 || ^0.200.0", "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.0.0", "@opentelemetry/semantic-conventions": "^1.34.0" } }, "sha512-QTUmre8i5+832RjzQW+g8IQ3UmBe5fbQXGbCF5hQ0UNuHle9r3Z8UZcIff5W8tm5AXMxPqvptTnDEZUUXHgBiA=="], + "@sentry/opentelemetry": ["@sentry/opentelemetry@9.35.0", "", { "dependencies": { "@sentry/core": "9.35.0" }, "peerDependencies": { "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^1.30.1 || ^2.0.0", "@opentelemetry/core": "^1.30.1 || ^2.0.0", "@opentelemetry/instrumentation": "^0.57.1 || ^0.200.0", "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.0.0", "@opentelemetry/semantic-conventions": "^1.34.0" } }, "sha512-XJmSC71KaN+qwYf5EEobLDyWum4FijpIjnpTVTYOrq037uUCpxJEGtgQHq0X+DE/ycVUX/Og2PiAgTeCQEYfDg=="], "@shikijs/core": ["@shikijs/core@2.5.0", "", { "dependencies": { "@shikijs/engine-javascript": "2.5.0", "@shikijs/engine-oniguruma": "2.5.0", "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.4" } }, "sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg=="], @@ -514,17 +727,23 @@ "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="], - "@sindresorhus/is": ["@sindresorhus/is@7.0.1", "", {}, "sha512-QWLl2P+rsCJeofkDNIT3WFmb6NrRud1SUYW8dIhXK/46XFV8Q/g7Bsvib0Askb0reRLe+WYPeeE+l5cH7SlkuQ=="], + "@sindresorhus/is": ["@sindresorhus/is@7.0.2", "", {}, "sha512-d9xRovfKNz1SKieM0qJdO+PQonjnnIfSNWfHYnBSJ9hkjm0ZPw6HlxscDXYstp3z+7V2GOFHc+J0CYrYTjqCJw=="], "@speed-highlight/core": ["@speed-highlight/core@1.2.7", "", {}, "sha512-0dxmVj4gxg3Jg879kvFS/msl4s9F3T9UXC1InxgOf7t5NvcPD97u/WTA5vL/IxWHMn7qSxBozqrnnE2wvl1m8g=="], + "@standard-community/standard-json": ["@standard-community/standard-json@0.3.0-rc.1", "", { "peerDependencies": { "@standard-schema/spec": "^1.0.0", "@types/json-schema": "^7.0.15", "@valibot/to-json-schema": "^1.3.0", "arktype": "^2.1.20", "effect": "^3.16.8", "valibot": "^1.1.0", "zod": "^3.25.67", "zod-to-json-schema": "^3.24.5" }, "optionalPeers": ["@valibot/to-json-schema", "arktype", "effect", "valibot", "zod", "zod-to-json-schema"] }, "sha512-WF0OkR3cbKwtUxis8HFDRzkwPVbmk4WFhrZa35gFslIOKKLKlkh/ejjIeW6nGVoCxVQOQg5AayuggJo8bhn0Cg=="], + + "@standard-community/standard-openapi": ["@standard-community/standard-openapi@0.2.0-rc.0", "", { "dependencies": { "zod-openapi": "^4.2.4" }, "peerDependencies": { "@standard-community/standard-json": "^0.3.0-rc.1", "@standard-schema/spec": "^1.0.0", "arktype": "^2.1.20", "openapi-types": "^12.1.3", "valibot": "^1.1.0", "zod": "^3.25.67" }, "optionalPeers": ["arktype", "valibot", "zod"] }, "sha512-UFN2H9aB7rCbvY4z072IikQMQ6PYrCAushiBrjgpGmhHww2Q8NDa8wwY1vPc5tJTTufjGAtMUZnVe2rskdD8/w=="], + + "@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], + "@ts-morph/common": ["@ts-morph/common@0.12.3", "", { "dependencies": { "fast-glob": "^3.2.7", "minimatch": "^3.0.4", "mkdirp": "^1.0.4", "path-browserify": "^1.0.1" } }, "sha512-4tUmeLyXJnJWvTFOKtcNJ1yh0a3SsTLi2MUoyj8iUNznFRN1ZquaNe7Oukqrnki2FzZkm0J9adCNLDZxUzvj+w=="], - "@types/bun": ["@types/bun@1.2.16", "", { "dependencies": { "bun-types": "1.2.16" } }, "sha512-1aCZJ/6nSiViw339RsaNhkNoEloLaPzZhxMOYEa7OzRzO41IGg5n/7I43/ZIAW/c+Q6cT12Vf7fOZOoVIzb5BQ=="], + "@types/bun": ["@types/bun@1.2.18", "", { "dependencies": { "bun-types": "1.2.18" } }, "sha512-Xf6RaWVheyemaThV0kUfaAUvCNokFr+bH8Jxp+tTZfx7dAPA8z9ePnP9S9+Vspzuxxx9JRAXhnyccRj3GyCMdQ=="], "@types/connect": ["@types/connect@3.4.38", "", { "dependencies": { "@types/node": "*" } }, "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug=="], - "@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="], + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="], @@ -546,7 +765,7 @@ "@types/mysql": ["@types/mysql@2.15.26", "", { "dependencies": { "@types/node": "*" } }, "sha512-DSLCOXhkvfS5WNNPbfn2KdICAmk8lLc+/PNvnPnF7gOdMZCxopXduqv0OQ13y/yA/zXTSikZZqVgybUxOEg6YQ=="], - "@types/node": ["@types/node@22.15.3", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-lX7HFZeHf4QG/J7tBZqrCAXwz9J5RD56Y6MpP0eJkka8p+K0RY/yBTW7CYFJ4VGCclxqOLKmiGP5juQc6MKgcw=="], + "@types/node": ["@types/node@24.0.10", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ENHwaH+JIRTDIEEbDK6QSQntAYGtbvdDXnMXnZaZ6k13Du1dPMmprkEHIL7ok2Wl2aZevetwTAb5S+7yIF+enA=="], "@types/parse-json": ["@types/parse-json@4.0.2", "", {}, "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="], @@ -556,6 +775,8 @@ "@types/qs": ["@types/qs@6.14.0", "", {}, "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ=="], + "@types/react": ["@types/react@19.1.8", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g=="], + "@types/shimmer": ["@types/shimmer@1.2.0", "", {}, "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg=="], "@types/tedious": ["@types/tedious@4.0.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw=="], @@ -570,37 +791,47 @@ "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], - "@versia/client": ["@versia/client@workspace:packages/client"], + "@versia-server/api": ["@versia-server/api@workspace:packages/api"], - "@versia/kit": ["@versia/kit@workspace:packages/plugin-kit"], + "@versia-server/config": ["@versia-server/config@workspace:packages/config"], + + "@versia-server/kit": ["@versia-server/kit@workspace:packages/kit"], + + "@versia-server/logging": ["@versia-server/logging@workspace:packages/logging"], + + "@versia-server/tests": ["@versia-server/tests@workspace:packages/tests"], + + "@versia-server/worker": ["@versia-server/worker@workspace:packages/worker"], + + "@versia/client": ["@versia/client@workspace:packages/client"], "@versia/sdk": ["@versia/sdk@workspace:packages/sdk"], - "@vitejs/plugin-vue": ["@vitejs/plugin-vue@5.2.3", "", { "peerDependencies": { "vite": "^5.0.0 || ^6.0.0", "vue": "^3.2.25" } }, "sha512-IYSLEQj4LgZZuoVpdSUCw3dIynTWQgPlaRP6iAvMle4My0HdYwr5g5wQAfwOeHQBmYwEkqF70nRpSilr6PoUDg=="], + "@vitejs/plugin-vue": ["@vitejs/plugin-vue@5.2.4", "", { "peerDependencies": { "vite": "^5.0.0 || ^6.0.0", "vue": "^3.2.25" } }, "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA=="], - "@vue/compiler-core": ["@vue/compiler-core@3.5.16", "", { "dependencies": { "@babel/parser": "^7.27.2", "@vue/shared": "3.5.16", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-AOQS2eaQOaaZQoL1u+2rCJIKDruNXVBZSiUD3chnUrsoX5ZTQMaCvXlWNIfxBJuU15r1o7+mpo5223KVtIhAgQ=="], + "@vue/compiler-core": ["@vue/compiler-core@3.5.17", "", { "dependencies": { "@babel/parser": "^7.27.5", "@vue/shared": "3.5.17", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-Xe+AittLbAyV0pabcN7cP7/BenRBNcteM4aSDCtRvGw0d9OL+HG1u/XHLY/kt1q4fyMeZYXyIYrsHuPSiDPosA=="], - "@vue/compiler-dom": ["@vue/compiler-dom@3.5.16", "", { "dependencies": { "@vue/compiler-core": "3.5.16", "@vue/shared": "3.5.16" } }, "sha512-SSJIhBr/teipXiXjmWOVWLnxjNGo65Oj/8wTEQz0nqwQeP75jWZ0n4sF24Zxoht1cuJoWopwj0J0exYwCJ0dCQ=="], + "@vue/compiler-dom": ["@vue/compiler-dom@3.5.17", "", { "dependencies": { "@vue/compiler-core": "3.5.17", "@vue/shared": "3.5.17" } }, "sha512-+2UgfLKoaNLhgfhV5Ihnk6wB4ljyW1/7wUIog2puUqajiC29Lp5R/IKDdkebh9jTbTogTbsgB+OY9cEWzG95JQ=="], - "@vue/compiler-sfc": ["@vue/compiler-sfc@3.5.16", "", { "dependencies": { "@babel/parser": "^7.27.2", "@vue/compiler-core": "3.5.16", "@vue/compiler-dom": "3.5.16", "@vue/compiler-ssr": "3.5.16", "@vue/shared": "3.5.16", "estree-walker": "^2.0.2", "magic-string": "^0.30.17", "postcss": "^8.5.3", "source-map-js": "^1.2.1" } }, "sha512-rQR6VSFNpiinDy/DVUE0vHoIDUF++6p910cgcZoaAUm3POxgNOOdS/xgoll3rNdKYTYPnnbARDCZOyZ+QSe6Pw=="], + "@vue/compiler-sfc": ["@vue/compiler-sfc@3.5.17", "", { "dependencies": { "@babel/parser": "^7.27.5", "@vue/compiler-core": "3.5.17", "@vue/compiler-dom": "3.5.17", "@vue/compiler-ssr": "3.5.17", "@vue/shared": "3.5.17", "estree-walker": "^2.0.2", "magic-string": "^0.30.17", "postcss": "^8.5.6", "source-map-js": "^1.2.1" } }, "sha512-rQQxbRJMgTqwRugtjw0cnyQv9cP4/4BxWfTdRBkqsTfLOHWykLzbOc3C4GGzAmdMDxhzU/1Ija5bTjMVrddqww=="], - "@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.16", "", { "dependencies": { "@vue/compiler-dom": "3.5.16", "@vue/shared": "3.5.16" } }, "sha512-d2V7kfxbdsjrDSGlJE7my1ZzCXViEcqN6w14DOsDrUCHEA6vbnVCpRFfrc4ryCP/lCKzX2eS1YtnLE/BuC9f/A=="], + "@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.17", "", { "dependencies": { "@vue/compiler-dom": "3.5.17", "@vue/shared": "3.5.17" } }, "sha512-hkDbA0Q20ZzGgpj5uZjb9rBzQtIHLS78mMilwrlpWk2Ep37DYntUz0PonQ6kr113vfOEdM+zTBuJDaceNIW0tQ=="], - "@vue/devtools-api": ["@vue/devtools-api@7.7.6", "", { "dependencies": { "@vue/devtools-kit": "^7.7.6" } }, "sha512-b2Xx0KvXZObePpXPYHvBRRJLDQn5nhKjXh7vUhMEtWxz1AYNFOVIsh5+HLP8xDGL7sy+Q7hXeUxPHB/KgbtsPw=="], + "@vue/devtools-api": ["@vue/devtools-api@7.7.7", "", { "dependencies": { "@vue/devtools-kit": "^7.7.7" } }, "sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg=="], - "@vue/devtools-kit": ["@vue/devtools-kit@7.7.6", "", { "dependencies": { "@vue/devtools-shared": "^7.7.6", "birpc": "^2.3.0", "hookable": "^5.5.3", "mitt": "^3.0.1", "perfect-debounce": "^1.0.0", "speakingurl": "^14.0.1", "superjson": "^2.2.2" } }, "sha512-geu7ds7tem2Y7Wz+WgbnbZ6T5eadOvozHZ23Atk/8tksHMFOFylKi1xgGlQlVn0wlkEf4hu+vd5ctj1G4kFtwA=="], + "@vue/devtools-kit": ["@vue/devtools-kit@7.7.7", "", { "dependencies": { "@vue/devtools-shared": "^7.7.7", "birpc": "^2.3.0", "hookable": "^5.5.3", "mitt": "^3.0.1", "perfect-debounce": "^1.0.0", "speakingurl": "^14.0.1", "superjson": "^2.2.2" } }, "sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA=="], - "@vue/devtools-shared": ["@vue/devtools-shared@7.7.6", "", { "dependencies": { "rfdc": "^1.4.1" } }, "sha512-yFEgJZ/WblEsojQQceuyK6FzpFDx4kqrz2ohInxNj5/DnhoX023upTv4OD6lNPLAA5LLkbwPVb10o/7b+Y4FVA=="], + "@vue/devtools-shared": ["@vue/devtools-shared@7.7.7", "", { "dependencies": { "rfdc": "^1.4.1" } }, "sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw=="], - "@vue/reactivity": ["@vue/reactivity@3.5.16", "", { "dependencies": { "@vue/shared": "3.5.16" } }, "sha512-FG5Q5ee/kxhIm1p2bykPpPwqiUBV3kFySsHEQha5BJvjXdZTUfmya7wP7zC39dFuZAcf/PD5S4Lni55vGLMhvA=="], + "@vue/reactivity": ["@vue/reactivity@3.5.17", "", { "dependencies": { "@vue/shared": "3.5.17" } }, "sha512-l/rmw2STIscWi7SNJp708FK4Kofs97zc/5aEPQh4bOsReD/8ICuBcEmS7KGwDj5ODQLYWVN2lNibKJL1z5b+Lw=="], - "@vue/runtime-core": ["@vue/runtime-core@3.5.16", "", { "dependencies": { "@vue/reactivity": "3.5.16", "@vue/shared": "3.5.16" } }, "sha512-bw5Ykq6+JFHYxrQa7Tjr+VSzw7Dj4ldR/udyBZbq73fCdJmyy5MPIFR9IX/M5Qs+TtTjuyUTCnmK3lWWwpAcFQ=="], + "@vue/runtime-core": ["@vue/runtime-core@3.5.17", "", { "dependencies": { "@vue/reactivity": "3.5.17", "@vue/shared": "3.5.17" } }, "sha512-QQLXa20dHg1R0ri4bjKeGFKEkJA7MMBxrKo2G+gJikmumRS7PTD4BOU9FKrDQWMKowz7frJJGqBffYMgQYS96Q=="], - "@vue/runtime-dom": ["@vue/runtime-dom@3.5.16", "", { "dependencies": { "@vue/reactivity": "3.5.16", "@vue/runtime-core": "3.5.16", "@vue/shared": "3.5.16", "csstype": "^3.1.3" } }, "sha512-T1qqYJsG2xMGhImRUV9y/RseB9d0eCYZQ4CWca9ztCuiPj/XWNNN+lkNBuzVbia5z4/cgxdL28NoQCvC0Xcfww=="], + "@vue/runtime-dom": ["@vue/runtime-dom@3.5.17", "", { "dependencies": { "@vue/reactivity": "3.5.17", "@vue/runtime-core": "3.5.17", "@vue/shared": "3.5.17", "csstype": "^3.1.3" } }, "sha512-8El0M60TcwZ1QMz4/os2MdlQECgGoVHPuLnQBU3m9h3gdNRW9xRmI8iLS4t/22OQlOE6aJvNNlBiCzPHur4H9g=="], - "@vue/server-renderer": ["@vue/server-renderer@3.5.16", "", { "dependencies": { "@vue/compiler-ssr": "3.5.16", "@vue/shared": "3.5.16" }, "peerDependencies": { "vue": "3.5.16" } }, "sha512-BrX0qLiv/WugguGsnQUJiYOE0Fe5mZTwi6b7X/ybGB0vfrPH9z0gD/Y6WOR1sGCgX4gc25L1RYS5eYQKDMoNIg=="], + "@vue/server-renderer": ["@vue/server-renderer@3.5.17", "", { "dependencies": { "@vue/compiler-ssr": "3.5.17", "@vue/shared": "3.5.17" }, "peerDependencies": { "vue": "3.5.17" } }, "sha512-BOHhm8HalujY6lmC3DbqF6uXN/K00uWiEeF22LfEsm9Q93XeJ/plHTepGwf6tqFcF7GA5oGSSAAUock3VvzaCA=="], - "@vue/shared": ["@vue/shared@3.5.13", "", {}, "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ=="], + "@vue/shared": ["@vue/shared@3.5.17", "", {}, "sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg=="], "@vueuse/core": ["@vueuse/core@12.8.2", "", { "dependencies": { "@types/web-bluetooth": "^0.0.21", "@vueuse/metadata": "12.8.2", "@vueuse/shared": "12.8.2", "vue": "^3.5.13" } }, "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ=="], @@ -612,13 +843,13 @@ "@xmldom/xmldom": ["@xmldom/xmldom@0.9.8", "", {}, "sha512-p96FSY54r+WJ50FIOsCOjyj/wavs8921hG5+kVMmZgKcvIKxMXHTrjNJvRgWa/zuX3B6t2lijLNFaOyuxUH+2A=="], - "acorn": ["acorn@8.14.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg=="], + "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], "acorn-import-attributes": ["acorn-import-attributes@1.9.5", "", { "peerDependencies": { "acorn": "^8" } }, "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ=="], "agent-base": ["agent-base@7.1.3", "", {}, "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw=="], - "algoliasearch": ["algoliasearch@5.24.0", "", { "dependencies": { "@algolia/client-abtesting": "5.24.0", "@algolia/client-analytics": "5.24.0", "@algolia/client-common": "5.24.0", "@algolia/client-insights": "5.24.0", "@algolia/client-personalization": "5.24.0", "@algolia/client-query-suggestions": "5.24.0", "@algolia/client-search": "5.24.0", "@algolia/ingestion": "1.24.0", "@algolia/monitoring": "1.24.0", "@algolia/recommend": "5.24.0", "@algolia/requester-browser-xhr": "5.24.0", "@algolia/requester-fetch": "5.24.0", "@algolia/requester-node-http": "5.24.0" } }, "sha512-CkaUygzZ91Xbw11s0CsHMawrK3tl+Ue57725HGRgRzKgt2Z4wvXVXRCtQfvzh8K7Tp4Zp7f1pyHAtMROtTJHxg=="], + "algoliasearch": ["algoliasearch@5.30.0", "", { "dependencies": { "@algolia/client-abtesting": "5.30.0", "@algolia/client-analytics": "5.30.0", "@algolia/client-common": "5.30.0", "@algolia/client-insights": "5.30.0", "@algolia/client-personalization": "5.30.0", "@algolia/client-query-suggestions": "5.30.0", "@algolia/client-search": "5.30.0", "@algolia/ingestion": "1.30.0", "@algolia/monitoring": "1.30.0", "@algolia/recommend": "5.30.0", "@algolia/requester-browser-xhr": "5.30.0", "@algolia/requester-fetch": "5.30.0", "@algolia/requester-node-http": "5.30.0" } }, "sha512-ILSdPX4je0n5WUKD34TMe57/eqiXUzCIjAsdtLQYhomqOjTtFUg1s6dE7kUegc4Mc43Xr7IXYlMutU9HPiYfdw=="], "altcha-lib": ["altcha-lib@1.3.0", "", {}, "sha512-PpFg/JPuR+Jiud7Vs54XSDqDxvylcp+0oDa/i1ARxBA/iKDqLeNlO8PorQbfuDTMVLYRypAa/2VDK3nbBTAu5A=="], @@ -638,7 +869,7 @@ "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], - "birpc": ["birpc@2.3.0", "", {}, "sha512-ijbtkn/F3Pvzb6jHypHRyve2QApOCZDR25D/VnkY2G/lBNcXCTsnsCxgY4k4PkVB7zfwzYbY3O9Lcqe3xufS5g=="], + "birpc": ["birpc@2.4.0", "", {}, "sha512-5IdNxTyhXHv2UlgnPHQ0h+5ypVmkrYHzL8QT+DwFZ//2N/oNV8Ch+BCRmTJ3x6/z9Axo/cXYBc9eprsUVK/Jsg=="], "blurhash": ["blurhash@2.0.5", "", {}, "sha512-cRygWd7kGBQO3VEhPiTgq4Wc43ctsM+o46urrmPOiuAe+07fzlSB9OJVdpgDL0jPqXUVQ9ht7aq7kxOeJHRK+w=="], @@ -646,7 +877,7 @@ "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="], - "brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], + "brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], @@ -654,11 +885,11 @@ "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], - "bullmq": ["bullmq@5.53.3", "", { "dependencies": { "cron-parser": "^4.9.0", "ioredis": "^5.4.1", "msgpackr": "^1.11.2", "node-abort-controller": "^3.1.1", "semver": "^7.5.4", "tslib": "^2.0.0", "uuid": "^9.0.0" } }, "sha512-8E/5fbVmJ3W/5FoDnRf3TW7vXgpg+A2RqOoQqpHxlvs3ws6SALw6f2NjHA1q5ePF0T4zsDZ/LAZwdMknk3b0MQ=="], + "bullmq": ["bullmq@5.56.1", "", { "dependencies": { "cron-parser": "^4.9.0", "ioredis": "^5.4.1", "msgpackr": "^1.11.2", "node-abort-controller": "^3.1.1", "semver": "^7.5.4", "tslib": "^2.0.0", "uuid": "^9.0.0" } }, "sha512-HmX53sf24bbDj5NVrDLDEjy8OSDmbGMESfvFhVK8NpHsA4WlAdKxPDf7c5b9RZf5wZGHyAd2XhWSkE9dmNPYww=="], "bun-bagel": ["bun-bagel@1.2.0", "", { "peerDependencies": { "typescript": "^5.0.0" } }, "sha512-c4S68dNddpnog9nxXp9PAhcep0alOy49jpRlC1yACoxplUvgX22NZxeQUIIov5TCJJDH/snT5R9bMyix7AG0KQ=="], - "bun-types": ["bun-types@1.2.16", "", { "dependencies": { "@types/node": "*" } }, "sha512-ciXLrHV4PXax9vHvUrkvun9VPVGOVwbbbBF/Ev1cXz12lyEZMoJpIJABOfPcN9gDJRaiKF9MVbSygLg4NXu3/A=="], + "bun-types": ["bun-types@1.2.18", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-04+Eha5NP7Z0A9YgDAzMk5PHR16ZuLVa83b26kH5+cp1qZW4F6FmAURngE7INf4tKOvCE69vYvDEwoNl1tGiWw=="], "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], @@ -688,8 +919,6 @@ "cli-width": ["cli-width@4.1.0", "", {}, "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ=="], - "clone": ["clone@2.1.2", "", {}, "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w=="], - "cluster-key-slot": ["cluster-key-slot@1.1.2", "", {}, "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA=="], "code-block-writer": ["code-block-writer@11.0.3", "", {}, "sha512-NiujjUFB4SwScJq2bwbYUtXbZhBSlY6vYzm++3Q6oC+U+injTqfPYFK8wS9COOmb2lueqp0ZRB4nK1VYeHgNyw=="], @@ -722,13 +951,13 @@ "css-select": ["css-select@4.3.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.0.1", "domhandler": "^4.3.1", "domutils": "^2.8.0", "nth-check": "^2.0.1" } }, "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ=="], - "css-what": ["css-what@6.1.0", "", {}, "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw=="], + "css-what": ["css-what@6.2.2", "", {}, "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA=="], "cssfilter": ["cssfilter@0.0.10", "", {}, "sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw=="], "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], - "debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + "debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="], @@ -752,7 +981,7 @@ "domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="], - "drizzle-kit": ["drizzle-kit@0.31.1", "", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.25.2", "esbuild-register": "^3.5.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-PUjYKWtzOzPtdtQlTHQG3qfv4Y0XT8+Eas6UbxCmxTj7qgMf+39dDujf1BP1I+qqZtw9uzwTh8jYtkMuCq+B0Q=="], + "drizzle-kit": ["drizzle-kit@0.31.4", "", { "dependencies": { "@drizzle-team/brocli": "^0.10.2", "@esbuild-kit/esm-loader": "^2.5.5", "esbuild": "^0.25.4", "esbuild-register": "^3.5.0" }, "bin": { "drizzle-kit": "bin.cjs" } }, "sha512-tCPWVZWZqWVx2XUsVpJRnH9Mx0ClVOf5YUHerZ5so1OKSlqww4zy1R5ksEdGRcO3tM3zj0PYN6V48TbQCL1RfA=="], "drizzle-orm": ["drizzle-orm@0.44.2", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1.13", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@upstash/redis": ">=1.34.7", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@upstash/redis", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-zGAqBzWWkVSFjZpwPOrmCrgO++1kZ5H/rZ4qTGeGOe18iXGVJWf3WPfHOVwFIbmi8kHjfJstC6rJomzGx8g/dQ=="], @@ -780,7 +1009,7 @@ "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], - "esbuild": ["esbuild@0.25.3", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.3", "@esbuild/android-arm": "0.25.3", "@esbuild/android-arm64": "0.25.3", "@esbuild/android-x64": "0.25.3", "@esbuild/darwin-arm64": "0.25.3", "@esbuild/darwin-x64": "0.25.3", "@esbuild/freebsd-arm64": "0.25.3", "@esbuild/freebsd-x64": "0.25.3", "@esbuild/linux-arm": "0.25.3", "@esbuild/linux-arm64": "0.25.3", "@esbuild/linux-ia32": "0.25.3", "@esbuild/linux-loong64": "0.25.3", "@esbuild/linux-mips64el": "0.25.3", "@esbuild/linux-ppc64": "0.25.3", "@esbuild/linux-riscv64": "0.25.3", "@esbuild/linux-s390x": "0.25.3", "@esbuild/linux-x64": "0.25.3", "@esbuild/netbsd-arm64": "0.25.3", "@esbuild/netbsd-x64": "0.25.3", "@esbuild/openbsd-arm64": "0.25.3", "@esbuild/openbsd-x64": "0.25.3", "@esbuild/sunos-x64": "0.25.3", "@esbuild/win32-arm64": "0.25.3", "@esbuild/win32-ia32": "0.25.3", "@esbuild/win32-x64": "0.25.3" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q=="], + "esbuild": ["esbuild@0.25.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.5", "@esbuild/android-arm": "0.25.5", "@esbuild/android-arm64": "0.25.5", "@esbuild/android-x64": "0.25.5", "@esbuild/darwin-arm64": "0.25.5", "@esbuild/darwin-x64": "0.25.5", "@esbuild/freebsd-arm64": "0.25.5", "@esbuild/freebsd-x64": "0.25.5", "@esbuild/linux-arm": "0.25.5", "@esbuild/linux-arm64": "0.25.5", "@esbuild/linux-ia32": "0.25.5", "@esbuild/linux-loong64": "0.25.5", "@esbuild/linux-mips64el": "0.25.5", "@esbuild/linux-ppc64": "0.25.5", "@esbuild/linux-riscv64": "0.25.5", "@esbuild/linux-s390x": "0.25.5", "@esbuild/linux-x64": "0.25.5", "@esbuild/netbsd-arm64": "0.25.5", "@esbuild/netbsd-x64": "0.25.5", "@esbuild/openbsd-arm64": "0.25.5", "@esbuild/openbsd-x64": "0.25.5", "@esbuild/sunos-x64": "0.25.5", "@esbuild/win32-arm64": "0.25.5", "@esbuild/win32-ia32": "0.25.5", "@esbuild/win32-x64": "0.25.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ=="], "esbuild-register": ["esbuild-register@3.6.0", "", { "dependencies": { "debug": "^4.3.4" }, "peerDependencies": { "esbuild": ">=0.12 <1" } }, "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg=="], @@ -810,7 +1039,7 @@ "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], - "focus-trap": ["focus-trap@7.6.4", "", { "dependencies": { "tabbable": "^6.2.0" } }, "sha512-xx560wGBk7seZ6y933idtjJQc1l+ck+pI3sKvhKozdBV1dRZoKhkW5xoCaFv9tQiX5RH1xfSxjuNu6g+lmN/gw=="], + "focus-trap": ["focus-trap@7.6.5", "", { "dependencies": { "tabbable": "^6.2.0" } }, "sha512-7Ke1jyybbbPZyZXFxEftUtxFGLMpE2n6A+z//m4CRDlj0hW+o3iYSmh8nFlYMurOiJVDmJRilUQtJr08KfIxlg=="], "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], @@ -826,7 +1055,7 @@ "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], - "get-tsconfig": ["get-tsconfig@4.10.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A=="], + "get-tsconfig": ["get-tsconfig@4.10.1", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ=="], "glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="], @@ -846,9 +1075,9 @@ "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="], - "hono": ["hono@4.7.11", "", {}, "sha512-rv0JMwC0KALbbmwJDEnxvQCeJh+xbS3KEWW5PC9cMJ08Ur9xgatI0HmtgYZfOdOSOeYsp5LO2cOhdI8cLEbDEQ=="], + "hono": ["hono@4.8.4", "", {}, "sha512-KOIBp1+iUs0HrKztM4EHiB2UtzZDTBihDtOF5K6+WaJjCPeaW4Q92R8j63jOhvJI5+tZSMuKD9REVEXXY9illg=="], - "hono-openapi": ["hono-openapi@0.4.8", "", { "dependencies": { "json-schema-walker": "^2.0.0" }, "peerDependencies": { "@hono/arktype-validator": "^2.0.0", "@hono/effect-validator": "^1.2.0", "@hono/typebox-validator": "^0.2.0 || ^0.3.0", "@hono/valibot-validator": "^0.5.1", "@hono/zod-validator": "^0.4.1", "@sinclair/typebox": "^0.34.9", "@valibot/to-json-schema": "^1.0.0-beta.3", "arktype": "^2.0.0", "effect": "^3.11.3", "hono": "^4.6.13", "openapi-types": "^12.1.3", "valibot": "^1.0.0-beta.9", "zod": "^3.23.8", "zod-openapi": "^4.0.0" }, "optionalPeers": ["@hono/arktype-validator", "@hono/effect-validator", "@hono/typebox-validator", "@hono/valibot-validator", "@hono/zod-validator", "@sinclair/typebox", "@valibot/to-json-schema", "arktype", "effect", "hono", "valibot", "zod", "zod-openapi"] }, "sha512-LYr5xdtD49M7hEAduV1PftOMzuT8ZNvkyWfh1DThkLsIr4RkvDb12UxgIiFbwrJB6FLtFXLoOZL9x4IeDk2+VA=="], + "hono-openapi": ["@cpluspatch/hono-openapi@0.5.1", "", { "peerDependencies": { "@hono/standard-validator": "^0.1.2", "@sinclair/typebox": "^0.34.9", "@standard-community/standard-json": "^0.3.0-rc.1", "@standard-community/standard-openapi": "^0.2.0-rc.0", "arktype": "^2.0.0", "effect": "^3.16.12", "hono": "^4.8.3", "openapi-types": "^12.1.3", "valibot": "^1.0.0-beta.9", "zod": "^3.23.8" }, "optionalPeers": ["@hono/standard-validator", "@sinclair/typebox", "arktype", "effect", "hono", "valibot", "zod"] }, "sha512-lecsN4jEzIwDb1HfArk5BuaR1O1AG2i6Dmtkc+K9BCs0LWRMZ0iWqPvo5LOwTTPttR4oG+mg50vepYQ5imV5Pg=="], "hono-rate-limiter": ["hono-rate-limiter@0.4.2", "", { "peerDependencies": { "hono": "^4.1.1" } }, "sha512-AAtFqgADyrmbDijcRTT/HJfwqfvhalya2Zo+MgfdrMPas3zSMD8SU03cv+ZsYwRU1swv7zgVt0shwN059yzhjw=="], @@ -866,7 +1095,7 @@ "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="], - "import-in-the-middle": ["import-in-the-middle@1.13.1", "", { "dependencies": { "acorn": "^8.14.0", "acorn-import-attributes": "^1.9.5", "cjs-module-lexer": "^1.2.2", "module-details-from-path": "^1.0.3" } }, "sha512-k2V9wNm9B+ysuelDTHjI9d5KPc4l8zAZTGqj+pcynvWkypZd857ryzN8jNC7Pg2YZXNMJcHRPpaDyCBbNyVRpA=="], + "import-in-the-middle": ["import-in-the-middle@1.14.2", "", { "dependencies": { "acorn": "^8.14.0", "acorn-import-attributes": "^1.9.5", "cjs-module-lexer": "^1.2.2", "module-details-from-path": "^1.0.3" } }, "sha512-5tCuY9BV8ujfOpwtAGgsTx9CGUapcFMEEyByLv1B+v2+6DhAcw+Zr0nhQT7uwaZ7DiourxFEscghOR8e1aPLQw=="], "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], @@ -912,13 +1141,11 @@ "json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="], - "json-schema-walker": ["json-schema-walker@2.0.0", "", { "dependencies": { "@apidevtools/json-schema-ref-parser": "^11.1.0", "clone": "^2.1.2" } }, "sha512-nXN2cMky0Iw7Af28w061hmxaPDaML5/bQD9nwm1lOoIKEGjHcRGxqWe4MfrkYThYAPjSUhmsp4bJNoLAyVn9Xw=="], - "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], "juice": ["juice@8.1.0", "", { "dependencies": { "cheerio": "1.0.0-rc.10", "commander": "^6.1.0", "mensch": "^0.3.4", "slick": "^1.12.2", "web-resource-inliner": "^6.0.1" }, "bin": { "juice": "bin/juice" } }, "sha512-FLzurJrx5Iv1e7CfBSZH68dC04EEvXvvVvPYB7Vx1WAuhCp1ZPIMtqxc+WTWxVkpTIC2Ach/GAv0rQbtGf6YMA=="], - "jwa": ["jwa@2.0.0", "", { "dependencies": { "buffer-equal-constant-time": "1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA=="], + "jwa": ["jwa@2.0.1", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg=="], "jws": ["jws@4.0.0", "", { "dependencies": { "jwa": "^2.0.0", "safe-buffer": "^5.0.1" } }, "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg=="], @@ -952,6 +1179,8 @@ "log-symbols": ["log-symbols@6.0.0", "", { "dependencies": { "chalk": "^5.3.0", "is-unicode-supported": "^1.3.0" } }, "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw=="], + "long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="], + "lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], "luxon": ["luxon@3.6.1", "", {}, "sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ=="], @@ -1032,7 +1261,7 @@ "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], - "msgpackr": ["msgpackr@1.11.2", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.2" } }, "sha512-F9UngXRlPyWCDEASDpTf6c9uNhGPTqnTeLVt7bN+bU1eajoR/8V9ys2BRaV5C/e5ihE6sJ9uPIKaYt6bFuO32g=="], + "msgpackr": ["msgpackr@1.11.4", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.2" } }, "sha512-uaff7RG9VIC4jacFW9xzL3jc0iM32DNHe4jYVycBcjUePT/Klnfj7pqtWJt9khvDFizmjN2TlYniYmSS2LIaZg=="], "msgpackr-extract": ["msgpackr-extract@3.0.3", "", { "dependencies": { "node-gyp-build-optional-packages": "5.2.2" }, "optionalDependencies": { "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" }, "bin": { "download-msgpackr-prebuilds": "bin/download-prebuilds.js" } }, "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA=="], @@ -1048,7 +1277,7 @@ "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="], - "oauth4webapi": ["oauth4webapi@3.5.2", "", {}, "sha512-VYz5BaP3izIrUc1GAVzIoz4JnljiW0YAUFObMBwsqDnfHxz2sjLu3W7/8vE8Ms9IbMewN9+1kcvhY3tMscAeGQ=="], + "oauth4webapi": ["oauth4webapi@3.5.5", "", {}, "sha512-1K88D2GiAydGblHo39NBro5TebGXa+7tYoyIbxvqv3+haDDry7CBE1eSYuNbOSsYCCU6y0gdynVZAkm4YPw4hg=="], "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], @@ -1090,7 +1319,7 @@ "pg-int8": ["pg-int8@1.0.1", "", {}, "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="], - "pg-protocol": ["pg-protocol@1.9.5", "", {}, "sha512-DYTWtWpfd5FOro3UnAfwvhD8jh59r2ig8bPtc9H8Ds7MscE/9NYruUQWFAOuraRl29jwcT2kyMFQ3MxeaVjUhg=="], + "pg-protocol": ["pg-protocol@1.10.3", "", {}, "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ=="], "pg-types": ["pg-types@2.2.0", "", { "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", "postgres-bytea": "~1.0.0", "postgres-date": "~1.0.4", "postgres-interval": "^1.1.0" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="], @@ -1100,7 +1329,7 @@ "pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="], - "postcss": ["postcss@8.5.3", "", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A=="], + "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], "postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="], @@ -1110,15 +1339,17 @@ "postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="], - "preact": ["preact@10.26.5", "", {}, "sha512-fmpDkgfGU6JYux9teDWLhj9mKN55tyepwYbxHgQuIxbWQzgFg5vk7Mrrtfx7xRxq798ynkY4DDDxZr235Kk+4w=="], + "preact": ["preact@10.26.9", "", {}, "sha512-SSjF9vcnF27mJK1XyFMNJzFd5u3pQiATFqoaDy03XuN00u4ziveVVEGt5RKJrDR8MHE/wJo9Nnad56RLzS2RMA=="], - "property-information": ["property-information@7.0.0", "", {}, "sha512-7D/qOz/+Y4X/rzSB6jKxKUsQnphO046ei8qxG59mtM3RG3DHgTK81HrxrmoDVINJb8NKT5ZsRbwHvQ6B68Iyhg=="], + "property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="], + + "protobufjs": ["protobufjs@7.5.3", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-sildjKwVqOI2kmFDiXQ6aEB0fjYTafpEvIBs8tOR8qI4spuL9OPROLVu2qZqi/xgCfsHIwVqlaF8JBjWFHnKbw=="], "punycode.js": ["punycode.js@2.3.1", "", {}, "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA=="], "qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="], - "qsu": ["qsu@1.10.0", "", {}, "sha512-60UGE7IEYXX/xy/n1w7vDm+is43pmePajAdXAnFOczvVDJKbVqZ5/RvRy+yobjA4iQitr4H/4zojVFtAkNWW9g=="], + "qsu": ["qsu@1.10.1", "", {}, "sha512-LQBaOApxuFZKZfRIZKzsuCRxSM1XWize9/SIWc5cBVlH/w8Pub4YVxx3mHMPQsmg6jM2cS6Uhkg7DPfwyTr0ew=="], "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], @@ -1150,7 +1381,7 @@ "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], - "rollup": ["rollup@4.40.1", "", { "dependencies": { "@types/estree": "1.0.7" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.40.1", "@rollup/rollup-android-arm64": "4.40.1", "@rollup/rollup-darwin-arm64": "4.40.1", "@rollup/rollup-darwin-x64": "4.40.1", "@rollup/rollup-freebsd-arm64": "4.40.1", "@rollup/rollup-freebsd-x64": "4.40.1", "@rollup/rollup-linux-arm-gnueabihf": "4.40.1", "@rollup/rollup-linux-arm-musleabihf": "4.40.1", "@rollup/rollup-linux-arm64-gnu": "4.40.1", "@rollup/rollup-linux-arm64-musl": "4.40.1", "@rollup/rollup-linux-loongarch64-gnu": "4.40.1", "@rollup/rollup-linux-powerpc64le-gnu": "4.40.1", "@rollup/rollup-linux-riscv64-gnu": "4.40.1", "@rollup/rollup-linux-riscv64-musl": "4.40.1", "@rollup/rollup-linux-s390x-gnu": "4.40.1", "@rollup/rollup-linux-x64-gnu": "4.40.1", "@rollup/rollup-linux-x64-musl": "4.40.1", "@rollup/rollup-win32-arm64-msvc": "4.40.1", "@rollup/rollup-win32-ia32-msvc": "4.40.1", "@rollup/rollup-win32-x64-msvc": "4.40.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-C5VvvgCCyfyotVITIAv+4efVytl5F7wt+/I2i9q9GZcEXW9BP52YYOXC58igUi+LFZVHukErIIqQSWwv/M3WRw=="], + "rollup": ["rollup@4.44.2", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.44.2", "@rollup/rollup-android-arm64": "4.44.2", "@rollup/rollup-darwin-arm64": "4.44.2", "@rollup/rollup-darwin-x64": "4.44.2", "@rollup/rollup-freebsd-arm64": "4.44.2", "@rollup/rollup-freebsd-x64": "4.44.2", "@rollup/rollup-linux-arm-gnueabihf": "4.44.2", "@rollup/rollup-linux-arm-musleabihf": "4.44.2", "@rollup/rollup-linux-arm64-gnu": "4.44.2", "@rollup/rollup-linux-arm64-musl": "4.44.2", "@rollup/rollup-linux-loongarch64-gnu": "4.44.2", "@rollup/rollup-linux-powerpc64le-gnu": "4.44.2", "@rollup/rollup-linux-riscv64-gnu": "4.44.2", "@rollup/rollup-linux-riscv64-musl": "4.44.2", "@rollup/rollup-linux-s390x-gnu": "4.44.2", "@rollup/rollup-linux-x64-gnu": "4.44.2", "@rollup/rollup-linux-x64-musl": "4.44.2", "@rollup/rollup-win32-arm64-msvc": "4.44.2", "@rollup/rollup-win32-ia32-msvc": "4.44.2", "@rollup/rollup-win32-x64-msvc": "4.44.2", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-PVoapzTwSEcelaWGth3uR66u7ZRo6qhPHc0f2uRO9fX6XDVNrIiGYS0Pj9+R8yIIYSD/mCx2b16Ws9itljKSPg=="], "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], @@ -1250,7 +1481,7 @@ "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - "type-fest": ["type-fest@4.40.1", "", {}, "sha512-9YvLNnORDpI+vghLU/Nf+zSv0kL47KbVJ1o3sKgoTefl6i+zebxbiDQWoe/oWWqPhIgQdRZRT1KA9sCPL810SA=="], + "type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], "type-flag": ["type-flag@3.0.0", "", {}, "sha512-3YaYwMseXCAhBB14RXW5cRQfJQlEknS6i4C8fCfeUdS3ihG9EdccdR9kt3vP73ZdeTGmPb4bZtkDn5XMIn1DLA=="], @@ -1262,7 +1493,7 @@ "ufo": ["ufo@1.6.1", "", {}, "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA=="], - "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="], "unicode-emoji-json": ["unicode-emoji-json@0.8.0", "", {}, "sha512-3wDXXvp6YGoKGhS2O2H7+V+bYduOBydN1lnI0uVfr1cIdY02uFFiEH1i3kE5CCE4l6UqbLKVmEFW9USxTAMD1g=="], @@ -1276,7 +1507,7 @@ "unist-util-visit-parents": ["unist-util-visit-parents@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw=="], - "unplugin": ["unplugin@2.3.2", "", { "dependencies": { "acorn": "^8.14.1", "picomatch": "^4.0.2", "webpack-virtual-modules": "^0.6.2" } }, "sha512-3n7YA46rROb3zSj8fFxtxC/PqoyvYQ0llwz9wtUPUutr9ig09C8gGo5CWCwHrUzlqC1LLR43kxp5vEIyH1ac1w=="], + "unplugin": ["unplugin@2.3.5", "", { "dependencies": { "acorn": "^8.14.1", "picomatch": "^4.0.2", "webpack-virtual-modules": "^0.6.2" } }, "sha512-RyWSb5AHmGtjjNQ6gIlA67sHOsWpsbWpwDokLwTcejVdOjEkJZh7QKu14J00gDDVSh8kGH4KYC/TNBceXFZhtw=="], "uqr": ["uqr@0.1.2", "", {}, "sha512-MJu7ypHq6QasgF5YRTjqscSzQp/W11zoUk6kvmlH+fmWEs63Y0Eib13hYFwAzagRJcVY8WVnlV+eBDUGMJ5IbA=="], @@ -1294,9 +1525,9 @@ "vitepress-plugin-tabs": ["vitepress-plugin-tabs@0.7.1", "", { "peerDependencies": { "vitepress": "^1.0.0", "vue": "^3.5.0" } }, "sha512-jxJvsicxnMSIYX9b8mAFLD2nwyKUcMO10dEt4nDSbinZhM8cGvAmMFOHPdf6TBX6gYZRl+/++/iYHtoM14fERQ=="], - "vitepress-sidebar": ["vitepress-sidebar@1.31.1", "", { "dependencies": { "glob": "10.4.5", "gray-matter": "4.0.3", "qsu": "^1.10.0" } }, "sha512-Hx10z5le87jIIXVfKq4AtRrVqVJJ/1cQsZhmwT+ghVR/j4Yor9FjNMszyigJ54ktrEtoxSLO6C9tvuLauT4lZA=="], + "vitepress-sidebar": ["vitepress-sidebar@1.32.1", "", { "dependencies": { "glob": "10.4.5", "gray-matter": "4.0.3", "qsu": "^1.10.1" } }, "sha512-OMLeQ7jfgZqR6jbqEtMcHkcy1Pr434Grxc0fQlnJRSLc8XtWAJvEMxCWhDQcZ3m3O9UODbf9aa92lLOZ8CLsBA=="], - "vue": ["vue@3.5.16", "", { "dependencies": { "@vue/compiler-dom": "3.5.16", "@vue/compiler-sfc": "3.5.16", "@vue/runtime-dom": "3.5.16", "@vue/server-renderer": "3.5.16", "@vue/shared": "3.5.16" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-rjOV2ecxMd5SiAmof2xzh2WxntRcigkX/He4YFJ6WdRvVUrbt6DxC1Iujh10XLl8xCDRDtGKMeO3D+pRQ1PP9w=="], + "vue": ["vue@3.5.17", "", { "dependencies": { "@vue/compiler-dom": "3.5.17", "@vue/compiler-sfc": "3.5.17", "@vue/runtime-dom": "3.5.17", "@vue/server-renderer": "3.5.17", "@vue/shared": "3.5.17" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-LbHV3xPN9BeljML+Xctq4lbz2lVHCR6DtbpTf5XIO6gugpXUN49j2QQPcMj086r9+AkJ0FfUT8xjulKKBkkr9g=="], "web-push": ["web-push@3.6.7", "", { "dependencies": { "asn1.js": "^5.3.0", "http_ece": "1.2.0", "https-proxy-agent": "^7.0.0", "jws": "^4.0.0", "minimist": "^1.2.5" }, "bin": { "web-push": "src/cli.js" } }, "sha512-OpiIUe8cuGjrj3mMBFWY+e4MMIkW3SVT+7vEIjvD9kejGUypv8GPDf84JdPWskK8zMRIJ6xYGm+Kxr8YkPyA0A=="], @@ -1328,22 +1559,20 @@ "yoctocolors-cjs": ["yoctocolors-cjs@2.1.2", "", {}, "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA=="], - "youch": ["youch@4.1.0-beta.7", "", { "dependencies": { "@poppinss/dumper": "^0.6.3", "@speed-highlight/core": "^1.2.7", "cookie": "^1.0.2", "youch-core": "^0.3.1" } }, "sha512-HUn0M24AUTMvjdkoMtH8fJz2FEd+k1xvtR9EoTrDUoVUi6o7xl5X+pST/vjk4T3GEQo2mJ9FlAvhWBm8dIdD4g=="], + "youch": ["youch@4.1.0-beta.10", "", { "dependencies": { "@poppinss/colors": "^4.1.5", "@poppinss/dumper": "^0.6.4", "@speed-highlight/core": "^1.2.7", "cookie": "^1.0.2", "youch-core": "^0.3.3" } }, "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ=="], - "youch-core": ["youch-core@0.3.2", "", { "dependencies": { "@poppinss/exception": "^1.2.0", "error-stack-parser-es": "^1.0.5" } }, "sha512-fusrlIMLeRvTFYLUjJ9KzlGC3N+6MOPJ68HNj/yJv2nz7zq8t4HEviLms2gkdRPUS7F5rZ5n+pYx9r88m6IE1g=="], + "youch-core": ["youch-core@0.3.3", "", { "dependencies": { "@poppinss/exception": "^1.2.2", "error-stack-parser-es": "^1.0.5" } }, "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA=="], - "zod": ["zod@3.25.64", "", {}, "sha512-hbP9FpSZf7pkS7hRVUrOjhwKJNyampPgtXKc3AN6DsWtoHsg2Sb4SQaS4Tcay380zSwd2VPo9G9180emBACp5g=="], + "zod": ["zod@3.25.74", "", {}, "sha512-J8poo92VuhKjNknViHRAIuuN6li/EwFbAC8OedzI8uxpEPGiXHGQu9wemIAioIpqgfB4SySaJhdk0mH5Y4ICBg=="], - "zod-openapi": ["zod-openapi@4.2.4", "", { "peerDependencies": { "zod": "^3.21.4" } }, "sha512-tsrQpbpqFCXqVXUzi3TPwFhuMtLN3oNZobOtYnK6/5VkXsNdnIgyNr4r8no4wmYluaxzN3F7iS+8xCW8BmMQ8g=="], + "zod-openapi": ["zod-openapi@5.0.0", "", { "peerDependencies": { "zod": "^3.25.74" } }, "sha512-fNwuOsflpILVVsx+3e8ODA0AnI60xGtMVWcvzv733ggEj7fVvE4NQMoOlQGbqIleyOdFQuc5N6cpO1BevApQig=="], - "zod-to-json-schema": ["zod-to-json-schema@3.24.5", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g=="], + "zod-to-json-schema": ["zod-to-json-schema@3.24.6", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg=="], - "zod-validation-error": ["zod-validation-error@3.5.0", "", { "peerDependencies": { "zod": "^3.25.0" } }, "sha512-IWK6O51sRkq0YsnYD2oLDuK2BNsIjYUlR0+1YSd4JyBzm6/892IWroUnLc7oW4FU+b0f6948BHi6H8MDcqpOGw=="], + "zod-validation-error": ["zod-validation-error@4.0.0-beta.1", "", { "peerDependencies": { "zod": "^3.25.0" } }, "sha512-42DSXwZyDKeLHrug+luXt6RMaoYsgMXc68bCz9kOyk66k7XBG35cAxsu2Lg42uVrJ1kEem2RyHxMBAv25SeZzQ=="], "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], - "@apidevtools/json-schema-ref-parser/js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], - "@esbuild-kit/core-utils/esbuild": ["esbuild@0.18.20", "", { "optionalDependencies": { "@esbuild/android-arm": "0.18.20", "@esbuild/android-arm64": "0.18.20", "@esbuild/android-x64": "0.18.20", "@esbuild/darwin-arm64": "0.18.20", "@esbuild/darwin-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.18.20", "@esbuild/freebsd-x64": "0.18.20", "@esbuild/linux-arm": "0.18.20", "@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-x64": "0.18.20", "@esbuild/netbsd-x64": "0.18.20", "@esbuild/openbsd-x64": "0.18.20", "@esbuild/sunos-x64": "0.18.20", "@esbuild/win32-arm64": "0.18.20", "@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-x64": "0.18.20" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA=="], "@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], @@ -1352,43 +1581,25 @@ "@opentelemetry/core/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="], - "@opentelemetry/instrumentation/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="], + "@opentelemetry/exporter-logs-otlp-http/@opentelemetry/core": ["@opentelemetry/core@2.0.1", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw=="], - "@opentelemetry/instrumentation-amqplib/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.32.0", "", {}, "sha512-s0OpmpQFSfMrmedAn9Lhg4KWJELHCU6uU9dtIJ28N8UGhf9Y55im5X8fEzwhwDwiSqN+ZPSNrDJF7ivf/AuRPQ=="], - - "@opentelemetry/instrumentation-connect/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.32.0", "", {}, "sha512-s0OpmpQFSfMrmedAn9Lhg4KWJELHCU6uU9dtIJ28N8UGhf9Y55im5X8fEzwhwDwiSqN+ZPSNrDJF7ivf/AuRPQ=="], - - "@opentelemetry/instrumentation-express/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.32.0", "", {}, "sha512-s0OpmpQFSfMrmedAn9Lhg4KWJELHCU6uU9dtIJ28N8UGhf9Y55im5X8fEzwhwDwiSqN+ZPSNrDJF7ivf/AuRPQ=="], - - "@opentelemetry/instrumentation-hapi/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.32.0", "", {}, "sha512-s0OpmpQFSfMrmedAn9Lhg4KWJELHCU6uU9dtIJ28N8UGhf9Y55im5X8fEzwhwDwiSqN+ZPSNrDJF7ivf/AuRPQ=="], + "@opentelemetry/instrumentation/@opentelemetry/api-logs": ["@opentelemetry/api-logs@0.57.2", "", { "dependencies": { "@opentelemetry/api": "^1.3.0" } }, "sha512-uIX52NnTM0iBh84MShlpouI7UKqkZ7MrUszTmaypHBu4r7NofznSnQRfJ+uUeDtQDj6w8eFGg5KBLDAwAPz1+A=="], "@opentelemetry/instrumentation-http/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="], - "@opentelemetry/instrumentation-http/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="], + "@opentelemetry/otlp-exporter-base/@opentelemetry/core": ["@opentelemetry/core@2.0.1", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw=="], - "@opentelemetry/instrumentation-ioredis/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.32.0", "", {}, "sha512-s0OpmpQFSfMrmedAn9Lhg4KWJELHCU6uU9dtIJ28N8UGhf9Y55im5X8fEzwhwDwiSqN+ZPSNrDJF7ivf/AuRPQ=="], + "@opentelemetry/otlp-transformer/@opentelemetry/core": ["@opentelemetry/core@2.0.1", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw=="], - "@opentelemetry/instrumentation-kafkajs/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.32.0", "", {}, "sha512-s0OpmpQFSfMrmedAn9Lhg4KWJELHCU6uU9dtIJ28N8UGhf9Y55im5X8fEzwhwDwiSqN+ZPSNrDJF7ivf/AuRPQ=="], + "@opentelemetry/otlp-transformer/@opentelemetry/sdk-trace-base": ["@opentelemetry/sdk-trace-base@2.0.1", "", { "dependencies": { "@opentelemetry/core": "2.0.1", "@opentelemetry/resources": "2.0.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.3.0 <1.10.0" } }, "sha512-xYLlvk/xdScGx1aEqvxLwf6sXQLXCjk3/1SQT9X9AoN5rXRhkdvIFShuNNmtTEPRBqcsMbS4p/gJLNI2wXaDuQ=="], - "@opentelemetry/instrumentation-knex/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.32.0", "", {}, "sha512-s0OpmpQFSfMrmedAn9Lhg4KWJELHCU6uU9dtIJ28N8UGhf9Y55im5X8fEzwhwDwiSqN+ZPSNrDJF7ivf/AuRPQ=="], + "@opentelemetry/resources/@opentelemetry/core": ["@opentelemetry/core@2.0.1", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw=="], - "@opentelemetry/instrumentation-koa/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.32.0", "", {}, "sha512-s0OpmpQFSfMrmedAn9Lhg4KWJELHCU6uU9dtIJ28N8UGhf9Y55im5X8fEzwhwDwiSqN+ZPSNrDJF7ivf/AuRPQ=="], + "@opentelemetry/sdk-logs/@opentelemetry/core": ["@opentelemetry/core@2.0.1", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw=="], - "@opentelemetry/instrumentation-mongodb/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.32.0", "", {}, "sha512-s0OpmpQFSfMrmedAn9Lhg4KWJELHCU6uU9dtIJ28N8UGhf9Y55im5X8fEzwhwDwiSqN+ZPSNrDJF7ivf/AuRPQ=="], + "@opentelemetry/sdk-metrics/@opentelemetry/core": ["@opentelemetry/core@2.0.1", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-MaZk9SJIDgo1peKevlbhP6+IwIiNPNmswNL4AF0WaQJLbHXjr9SrZMgS12+iqr9ToV4ZVosCcc0f8Rg67LXjxw=="], - "@opentelemetry/instrumentation-mongoose/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.32.0", "", {}, "sha512-s0OpmpQFSfMrmedAn9Lhg4KWJELHCU6uU9dtIJ28N8UGhf9Y55im5X8fEzwhwDwiSqN+ZPSNrDJF7ivf/AuRPQ=="], - - "@opentelemetry/instrumentation-mysql/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.32.0", "", {}, "sha512-s0OpmpQFSfMrmedAn9Lhg4KWJELHCU6uU9dtIJ28N8UGhf9Y55im5X8fEzwhwDwiSqN+ZPSNrDJF7ivf/AuRPQ=="], - - "@opentelemetry/instrumentation-mysql2/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.32.0", "", {}, "sha512-s0OpmpQFSfMrmedAn9Lhg4KWJELHCU6uU9dtIJ28N8UGhf9Y55im5X8fEzwhwDwiSqN+ZPSNrDJF7ivf/AuRPQ=="], - - "@opentelemetry/instrumentation-pg/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.32.0", "", {}, "sha512-s0OpmpQFSfMrmedAn9Lhg4KWJELHCU6uU9dtIJ28N8UGhf9Y55im5X8fEzwhwDwiSqN+ZPSNrDJF7ivf/AuRPQ=="], - - "@opentelemetry/instrumentation-redis-4/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.32.0", "", {}, "sha512-s0OpmpQFSfMrmedAn9Lhg4KWJELHCU6uU9dtIJ28N8UGhf9Y55im5X8fEzwhwDwiSqN+ZPSNrDJF7ivf/AuRPQ=="], - - "@opentelemetry/instrumentation-tedious/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.32.0", "", {}, "sha512-s0OpmpQFSfMrmedAn9Lhg4KWJELHCU6uU9dtIJ28N8UGhf9Y55im5X8fEzwhwDwiSqN+ZPSNrDJF7ivf/AuRPQ=="], - - "@opentelemetry/resources/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="], + "@opentelemetry/sdk-trace-base/@opentelemetry/resources": ["@opentelemetry/resources@1.30.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA=="], "@opentelemetry/sdk-trace-base/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="], @@ -1398,34 +1609,16 @@ "@scalar/types/zod": ["zod@3.24.1", "", {}, "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A=="], - "@ts-morph/common/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + "@sentry/node/@opentelemetry/resources": ["@opentelemetry/resources@1.30.1", "", { "dependencies": { "@opentelemetry/core": "1.30.1", "@opentelemetry/semantic-conventions": "1.28.0" }, "peerDependencies": { "@opentelemetry/api": ">=1.0.0 <1.10.0" } }, "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA=="], - "@vue/compiler-core/@vue/shared": ["@vue/shared@3.5.16", "", {}, "sha512-c/0fWy3Jw6Z8L9FmTyYfkpM5zklnqqa9+a6dz3DvONRKW2NEbh46BP0FHuLFSWi2TnQEtp91Z6zOWNrU6QiyPg=="], + "@standard-community/standard-openapi/zod-openapi": ["zod-openapi@4.2.4", "", { "peerDependencies": { "zod": "^3.21.4" } }, "sha512-tsrQpbpqFCXqVXUzi3TPwFhuMtLN3oNZobOtYnK6/5VkXsNdnIgyNr4r8no4wmYluaxzN3F7iS+8xCW8BmMQ8g=="], + + "@ts-morph/common/minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], "@vue/compiler-core/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], - "@vue/compiler-dom/@vue/shared": ["@vue/shared@3.5.16", "", {}, "sha512-c/0fWy3Jw6Z8L9FmTyYfkpM5zklnqqa9+a6dz3DvONRKW2NEbh46BP0FHuLFSWi2TnQEtp91Z6zOWNrU6QiyPg=="], - - "@vue/compiler-sfc/@vue/shared": ["@vue/shared@3.5.16", "", {}, "sha512-c/0fWy3Jw6Z8L9FmTyYfkpM5zklnqqa9+a6dz3DvONRKW2NEbh46BP0FHuLFSWi2TnQEtp91Z6zOWNrU6QiyPg=="], - "@vue/compiler-sfc/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], - "@vue/compiler-ssr/@vue/shared": ["@vue/shared@3.5.16", "", {}, "sha512-c/0fWy3Jw6Z8L9FmTyYfkpM5zklnqqa9+a6dz3DvONRKW2NEbh46BP0FHuLFSWi2TnQEtp91Z6zOWNrU6QiyPg=="], - - "@vue/reactivity/@vue/shared": ["@vue/shared@3.5.16", "", {}, "sha512-c/0fWy3Jw6Z8L9FmTyYfkpM5zklnqqa9+a6dz3DvONRKW2NEbh46BP0FHuLFSWi2TnQEtp91Z6zOWNrU6QiyPg=="], - - "@vue/runtime-core/@vue/shared": ["@vue/shared@3.5.16", "", {}, "sha512-c/0fWy3Jw6Z8L9FmTyYfkpM5zklnqqa9+a6dz3DvONRKW2NEbh46BP0FHuLFSWi2TnQEtp91Z6zOWNrU6QiyPg=="], - - "@vue/runtime-dom/@vue/shared": ["@vue/shared@3.5.16", "", {}, "sha512-c/0fWy3Jw6Z8L9FmTyYfkpM5zklnqqa9+a6dz3DvONRKW2NEbh46BP0FHuLFSWi2TnQEtp91Z6zOWNrU6QiyPg=="], - - "@vue/server-renderer/@vue/shared": ["@vue/shared@3.5.16", "", {}, "sha512-c/0fWy3Jw6Z8L9FmTyYfkpM5zklnqqa9+a6dz3DvONRKW2NEbh46BP0FHuLFSWi2TnQEtp91Z6zOWNrU6QiyPg=="], - - "@vueuse/core/vue": ["vue@3.5.13", "", { "dependencies": { "@vue/compiler-dom": "3.5.13", "@vue/compiler-sfc": "3.5.13", "@vue/runtime-dom": "3.5.13", "@vue/server-renderer": "3.5.13", "@vue/shared": "3.5.13" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ=="], - - "@vueuse/integrations/vue": ["vue@3.5.13", "", { "dependencies": { "@vue/compiler-dom": "3.5.13", "@vue/compiler-sfc": "3.5.13", "@vue/runtime-dom": "3.5.13", "@vue/server-renderer": "3.5.13", "@vue/shared": "3.5.13" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ=="], - - "@vueuse/shared/vue": ["vue@3.5.13", "", { "dependencies": { "@vue/compiler-dom": "3.5.13", "@vue/compiler-sfc": "3.5.13", "@vue/runtime-dom": "3.5.13", "@vue/server-renderer": "3.5.13", "@vue/shared": "3.5.13" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ=="], - "ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="], "cheerio/dom-serializer": ["dom-serializer@1.4.1", "", { "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.2.0", "entities": "^2.0.0" } }, "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag=="], @@ -1472,10 +1665,6 @@ "vite/esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="], - "vitepress/vue": ["vue@3.5.13", "", { "dependencies": { "@vue/compiler-dom": "3.5.13", "@vue/compiler-sfc": "3.5.13", "@vue/runtime-dom": "3.5.13", "@vue/server-renderer": "3.5.13", "@vue/shared": "3.5.13" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ=="], - - "vue/@vue/shared": ["@vue/shared@3.5.16", "", {}, "sha512-c/0fWy3Jw6Z8L9FmTyYfkpM5zklnqqa9+a6dz3DvONRKW2NEbh46BP0FHuLFSWi2TnQEtp91Z6zOWNrU6QiyPg=="], - "web-resource-inliner/htmlparser2": ["htmlparser2@5.0.1", "", { "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^3.3.0", "domutils": "^2.4.2", "entities": "^2.0.0" } }, "sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ=="], "wrap-ansi/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -1536,31 +1725,9 @@ "@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], - "@ts-morph/common/minimatch/brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="], + "@sentry/node/@opentelemetry/resources/@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.28.0", "", {}, "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA=="], - "@vueuse/core/vue/@vue/compiler-dom": ["@vue/compiler-dom@3.5.13", "", { "dependencies": { "@vue/compiler-core": "3.5.13", "@vue/shared": "3.5.13" } }, "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA=="], - - "@vueuse/core/vue/@vue/compiler-sfc": ["@vue/compiler-sfc@3.5.13", "", { "dependencies": { "@babel/parser": "^7.25.3", "@vue/compiler-core": "3.5.13", "@vue/compiler-dom": "3.5.13", "@vue/compiler-ssr": "3.5.13", "@vue/shared": "3.5.13", "estree-walker": "^2.0.2", "magic-string": "^0.30.11", "postcss": "^8.4.48", "source-map-js": "^1.2.0" } }, "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ=="], - - "@vueuse/core/vue/@vue/runtime-dom": ["@vue/runtime-dom@3.5.13", "", { "dependencies": { "@vue/reactivity": "3.5.13", "@vue/runtime-core": "3.5.13", "@vue/shared": "3.5.13", "csstype": "^3.1.3" } }, "sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog=="], - - "@vueuse/core/vue/@vue/server-renderer": ["@vue/server-renderer@3.5.13", "", { "dependencies": { "@vue/compiler-ssr": "3.5.13", "@vue/shared": "3.5.13" }, "peerDependencies": { "vue": "3.5.13" } }, "sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA=="], - - "@vueuse/integrations/vue/@vue/compiler-dom": ["@vue/compiler-dom@3.5.13", "", { "dependencies": { "@vue/compiler-core": "3.5.13", "@vue/shared": "3.5.13" } }, "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA=="], - - "@vueuse/integrations/vue/@vue/compiler-sfc": ["@vue/compiler-sfc@3.5.13", "", { "dependencies": { "@babel/parser": "^7.25.3", "@vue/compiler-core": "3.5.13", "@vue/compiler-dom": "3.5.13", "@vue/compiler-ssr": "3.5.13", "@vue/shared": "3.5.13", "estree-walker": "^2.0.2", "magic-string": "^0.30.11", "postcss": "^8.4.48", "source-map-js": "^1.2.0" } }, "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ=="], - - "@vueuse/integrations/vue/@vue/runtime-dom": ["@vue/runtime-dom@3.5.13", "", { "dependencies": { "@vue/reactivity": "3.5.13", "@vue/runtime-core": "3.5.13", "@vue/shared": "3.5.13", "csstype": "^3.1.3" } }, "sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog=="], - - "@vueuse/integrations/vue/@vue/server-renderer": ["@vue/server-renderer@3.5.13", "", { "dependencies": { "@vue/compiler-ssr": "3.5.13", "@vue/shared": "3.5.13" }, "peerDependencies": { "vue": "3.5.13" } }, "sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA=="], - - "@vueuse/shared/vue/@vue/compiler-dom": ["@vue/compiler-dom@3.5.13", "", { "dependencies": { "@vue/compiler-core": "3.5.13", "@vue/shared": "3.5.13" } }, "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA=="], - - "@vueuse/shared/vue/@vue/compiler-sfc": ["@vue/compiler-sfc@3.5.13", "", { "dependencies": { "@babel/parser": "^7.25.3", "@vue/compiler-core": "3.5.13", "@vue/compiler-dom": "3.5.13", "@vue/compiler-ssr": "3.5.13", "@vue/shared": "3.5.13", "estree-walker": "^2.0.2", "magic-string": "^0.30.11", "postcss": "^8.4.48", "source-map-js": "^1.2.0" } }, "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ=="], - - "@vueuse/shared/vue/@vue/runtime-dom": ["@vue/runtime-dom@3.5.13", "", { "dependencies": { "@vue/reactivity": "3.5.13", "@vue/runtime-core": "3.5.13", "@vue/shared": "3.5.13", "csstype": "^3.1.3" } }, "sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog=="], - - "@vueuse/shared/vue/@vue/server-renderer": ["@vue/server-renderer@3.5.13", "", { "dependencies": { "@vue/compiler-ssr": "3.5.13", "@vue/shared": "3.5.13" }, "peerDependencies": { "vue": "3.5.13" } }, "sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA=="], + "@ts-morph/common/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], "cheerio-select/domutils/dom-serializer": ["dom-serializer@1.4.1", "", { "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.2.0", "entities": "^2.0.0" } }, "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag=="], @@ -1574,7 +1741,7 @@ "jake/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - "jake/minimatch/brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="], + "jake/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], "string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], @@ -1624,14 +1791,6 @@ "vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="], - "vitepress/vue/@vue/compiler-dom": ["@vue/compiler-dom@3.5.13", "", { "dependencies": { "@vue/compiler-core": "3.5.13", "@vue/shared": "3.5.13" } }, "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA=="], - - "vitepress/vue/@vue/compiler-sfc": ["@vue/compiler-sfc@3.5.13", "", { "dependencies": { "@babel/parser": "^7.25.3", "@vue/compiler-core": "3.5.13", "@vue/compiler-dom": "3.5.13", "@vue/compiler-ssr": "3.5.13", "@vue/shared": "3.5.13", "estree-walker": "^2.0.2", "magic-string": "^0.30.11", "postcss": "^8.4.48", "source-map-js": "^1.2.0" } }, "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ=="], - - "vitepress/vue/@vue/runtime-dom": ["@vue/runtime-dom@3.5.13", "", { "dependencies": { "@vue/reactivity": "3.5.13", "@vue/runtime-core": "3.5.13", "@vue/shared": "3.5.13", "csstype": "^3.1.3" } }, "sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog=="], - - "vitepress/vue/@vue/server-renderer": ["@vue/server-renderer@3.5.13", "", { "dependencies": { "@vue/compiler-ssr": "3.5.13", "@vue/shared": "3.5.13" }, "peerDependencies": { "vue": "3.5.13" } }, "sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA=="], - "web-resource-inliner/htmlparser2/domhandler": ["domhandler@3.3.0", "", { "dependencies": { "domelementtype": "^2.0.1" } }, "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA=="], "web-resource-inliner/htmlparser2/domutils": ["domutils@2.8.0", "", { "dependencies": { "dom-serializer": "^1.0.1", "domelementtype": "^2.2.0", "domhandler": "^4.2.0" } }, "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A=="], @@ -1646,92 +1805,12 @@ "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "@vueuse/core/vue/@vue/compiler-dom/@vue/compiler-core": ["@vue/compiler-core@3.5.13", "", { "dependencies": { "@babel/parser": "^7.25.3", "@vue/shared": "3.5.13", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.0" } }, "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q=="], - - "@vueuse/core/vue/@vue/compiler-sfc/@babel/parser": ["@babel/parser@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" }, "bin": "./bin/babel-parser.js" }, "sha512-I0dZ3ZpCrJ1c04OqlNsQcKiZlsrXf/kkE4FXzID9rIOYICsAbA8mMDzhW/luRNAHdCNt7os/u8wenklZDlUVUQ=="], - - "@vueuse/core/vue/@vue/compiler-sfc/@vue/compiler-core": ["@vue/compiler-core@3.5.13", "", { "dependencies": { "@babel/parser": "^7.25.3", "@vue/shared": "3.5.13", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.0" } }, "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q=="], - - "@vueuse/core/vue/@vue/compiler-sfc/@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.13", "", { "dependencies": { "@vue/compiler-dom": "3.5.13", "@vue/shared": "3.5.13" } }, "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA=="], - - "@vueuse/core/vue/@vue/compiler-sfc/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], - - "@vueuse/core/vue/@vue/runtime-dom/@vue/reactivity": ["@vue/reactivity@3.5.13", "", { "dependencies": { "@vue/shared": "3.5.13" } }, "sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg=="], - - "@vueuse/core/vue/@vue/runtime-dom/@vue/runtime-core": ["@vue/runtime-core@3.5.13", "", { "dependencies": { "@vue/reactivity": "3.5.13", "@vue/shared": "3.5.13" } }, "sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw=="], - - "@vueuse/core/vue/@vue/server-renderer/@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.13", "", { "dependencies": { "@vue/compiler-dom": "3.5.13", "@vue/shared": "3.5.13" } }, "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA=="], - - "@vueuse/integrations/vue/@vue/compiler-dom/@vue/compiler-core": ["@vue/compiler-core@3.5.13", "", { "dependencies": { "@babel/parser": "^7.25.3", "@vue/shared": "3.5.13", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.0" } }, "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q=="], - - "@vueuse/integrations/vue/@vue/compiler-sfc/@babel/parser": ["@babel/parser@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" }, "bin": "./bin/babel-parser.js" }, "sha512-I0dZ3ZpCrJ1c04OqlNsQcKiZlsrXf/kkE4FXzID9rIOYICsAbA8mMDzhW/luRNAHdCNt7os/u8wenklZDlUVUQ=="], - - "@vueuse/integrations/vue/@vue/compiler-sfc/@vue/compiler-core": ["@vue/compiler-core@3.5.13", "", { "dependencies": { "@babel/parser": "^7.25.3", "@vue/shared": "3.5.13", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.0" } }, "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q=="], - - "@vueuse/integrations/vue/@vue/compiler-sfc/@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.13", "", { "dependencies": { "@vue/compiler-dom": "3.5.13", "@vue/shared": "3.5.13" } }, "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA=="], - - "@vueuse/integrations/vue/@vue/compiler-sfc/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], - - "@vueuse/integrations/vue/@vue/runtime-dom/@vue/reactivity": ["@vue/reactivity@3.5.13", "", { "dependencies": { "@vue/shared": "3.5.13" } }, "sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg=="], - - "@vueuse/integrations/vue/@vue/runtime-dom/@vue/runtime-core": ["@vue/runtime-core@3.5.13", "", { "dependencies": { "@vue/reactivity": "3.5.13", "@vue/shared": "3.5.13" } }, "sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw=="], - - "@vueuse/integrations/vue/@vue/server-renderer/@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.13", "", { "dependencies": { "@vue/compiler-dom": "3.5.13", "@vue/shared": "3.5.13" } }, "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA=="], - - "@vueuse/shared/vue/@vue/compiler-dom/@vue/compiler-core": ["@vue/compiler-core@3.5.13", "", { "dependencies": { "@babel/parser": "^7.25.3", "@vue/shared": "3.5.13", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.0" } }, "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q=="], - - "@vueuse/shared/vue/@vue/compiler-sfc/@babel/parser": ["@babel/parser@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" }, "bin": "./bin/babel-parser.js" }, "sha512-I0dZ3ZpCrJ1c04OqlNsQcKiZlsrXf/kkE4FXzID9rIOYICsAbA8mMDzhW/luRNAHdCNt7os/u8wenklZDlUVUQ=="], - - "@vueuse/shared/vue/@vue/compiler-sfc/@vue/compiler-core": ["@vue/compiler-core@3.5.13", "", { "dependencies": { "@babel/parser": "^7.25.3", "@vue/shared": "3.5.13", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.0" } }, "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q=="], - - "@vueuse/shared/vue/@vue/compiler-sfc/@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.13", "", { "dependencies": { "@vue/compiler-dom": "3.5.13", "@vue/shared": "3.5.13" } }, "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA=="], - - "@vueuse/shared/vue/@vue/compiler-sfc/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], - - "@vueuse/shared/vue/@vue/runtime-dom/@vue/reactivity": ["@vue/reactivity@3.5.13", "", { "dependencies": { "@vue/shared": "3.5.13" } }, "sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg=="], - - "@vueuse/shared/vue/@vue/runtime-dom/@vue/runtime-core": ["@vue/runtime-core@3.5.13", "", { "dependencies": { "@vue/reactivity": "3.5.13", "@vue/shared": "3.5.13" } }, "sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw=="], - - "@vueuse/shared/vue/@vue/server-renderer/@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.13", "", { "dependencies": { "@vue/compiler-dom": "3.5.13", "@vue/shared": "3.5.13" } }, "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA=="], - "cheerio-select/domutils/dom-serializer/entities": ["entities@2.2.0", "", {}, "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="], "css-select/domutils/dom-serializer/entities": ["entities@2.2.0", "", {}, "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="], - "vitepress/vue/@vue/compiler-dom/@vue/compiler-core": ["@vue/compiler-core@3.5.13", "", { "dependencies": { "@babel/parser": "^7.25.3", "@vue/shared": "3.5.13", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.0" } }, "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q=="], - - "vitepress/vue/@vue/compiler-sfc/@babel/parser": ["@babel/parser@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" }, "bin": "./bin/babel-parser.js" }, "sha512-I0dZ3ZpCrJ1c04OqlNsQcKiZlsrXf/kkE4FXzID9rIOYICsAbA8mMDzhW/luRNAHdCNt7os/u8wenklZDlUVUQ=="], - - "vitepress/vue/@vue/compiler-sfc/@vue/compiler-core": ["@vue/compiler-core@3.5.13", "", { "dependencies": { "@babel/parser": "^7.25.3", "@vue/shared": "3.5.13", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.0" } }, "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q=="], - - "vitepress/vue/@vue/compiler-sfc/@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.13", "", { "dependencies": { "@vue/compiler-dom": "3.5.13", "@vue/shared": "3.5.13" } }, "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA=="], - - "vitepress/vue/@vue/compiler-sfc/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], - - "vitepress/vue/@vue/runtime-dom/@vue/reactivity": ["@vue/reactivity@3.5.13", "", { "dependencies": { "@vue/shared": "3.5.13" } }, "sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg=="], - - "vitepress/vue/@vue/runtime-dom/@vue/runtime-core": ["@vue/runtime-core@3.5.13", "", { "dependencies": { "@vue/reactivity": "3.5.13", "@vue/shared": "3.5.13" } }, "sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw=="], - - "vitepress/vue/@vue/server-renderer/@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.13", "", { "dependencies": { "@vue/compiler-dom": "3.5.13", "@vue/shared": "3.5.13" } }, "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA=="], - "web-resource-inliner/htmlparser2/domutils/dom-serializer": ["dom-serializer@1.4.1", "", { "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.2.0", "entities": "^2.0.0" } }, "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag=="], "web-resource-inliner/htmlparser2/domutils/domhandler": ["domhandler@4.3.1", "", { "dependencies": { "domelementtype": "^2.2.0" } }, "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ=="], - - "@vueuse/core/vue/@vue/compiler-dom/@vue/compiler-core/@babel/parser": ["@babel/parser@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" }, "bin": "./bin/babel-parser.js" }, "sha512-I0dZ3ZpCrJ1c04OqlNsQcKiZlsrXf/kkE4FXzID9rIOYICsAbA8mMDzhW/luRNAHdCNt7os/u8wenklZDlUVUQ=="], - - "@vueuse/core/vue/@vue/compiler-dom/@vue/compiler-core/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], - - "@vueuse/integrations/vue/@vue/compiler-dom/@vue/compiler-core/@babel/parser": ["@babel/parser@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" }, "bin": "./bin/babel-parser.js" }, "sha512-I0dZ3ZpCrJ1c04OqlNsQcKiZlsrXf/kkE4FXzID9rIOYICsAbA8mMDzhW/luRNAHdCNt7os/u8wenklZDlUVUQ=="], - - "@vueuse/integrations/vue/@vue/compiler-dom/@vue/compiler-core/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], - - "@vueuse/shared/vue/@vue/compiler-dom/@vue/compiler-core/@babel/parser": ["@babel/parser@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" }, "bin": "./bin/babel-parser.js" }, "sha512-I0dZ3ZpCrJ1c04OqlNsQcKiZlsrXf/kkE4FXzID9rIOYICsAbA8mMDzhW/luRNAHdCNt7os/u8wenklZDlUVUQ=="], - - "@vueuse/shared/vue/@vue/compiler-dom/@vue/compiler-core/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], - - "vitepress/vue/@vue/compiler-dom/@vue/compiler-core/@babel/parser": ["@babel/parser@7.27.1", "", { "dependencies": { "@babel/types": "^7.27.1" }, "bin": "./bin/babel-parser.js" }, "sha512-I0dZ3ZpCrJ1c04OqlNsQcKiZlsrXf/kkE4FXzID9rIOYICsAbA8mMDzhW/luRNAHdCNt7os/u8wenklZDlUVUQ=="], - - "vitepress/vue/@vue/compiler-dom/@vue/compiler-core/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], } } diff --git a/bunfig.toml b/bunfig.toml index 43df4271..aa1fb8b4 100644 --- a/bunfig.toml +++ b/bunfig.toml @@ -2,4 +2,4 @@ "@jsr" = "https://npm.jsr.io" [test] -preload = ["./tests/setup.ts"] +preload = ["./packages/tests/setup.ts"] diff --git a/classes/config/to-json-schema.ts b/classes/config/to-json-schema.ts deleted file mode 100644 index 3afd989b..00000000 --- a/classes/config/to-json-schema.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { zodToJsonSchema } from "zod-to-json-schema"; - -await import("~/config.ts"); - -// This is an awkward way to avoid import cycles for some reason -await (async () => { - const { ConfigSchema } = await import("./schema.ts"); - - const jsonSchema = zodToJsonSchema(ConfigSchema, {}); - - console.write(`${JSON.stringify(jsonSchema, null, 4)}\n`); -})(); diff --git a/classes/functions/status.ts b/classes/functions/status.ts deleted file mode 100644 index 55988fd3..00000000 --- a/classes/functions/status.ts +++ /dev/null @@ -1,359 +0,0 @@ -import markdownItTaskLists from "@hackmd/markdown-it-task-lists"; -import { db, type Note, User } from "@versia/kit/db"; -import { Instances, Users } from "@versia/kit/tables"; -import { FederationRequester } from "@versia/sdk/http"; -import { and, eq, inArray, isNull, or, sql } from "drizzle-orm"; -import linkifyHtml from "linkify-html"; -import { - anyOf, - charIn, - createRegExp, - digit, - exactly, - global, - letter, -} from "magic-regexp"; -import MarkdownIt from "markdown-it"; -import markdownItContainer from "markdown-it-container"; -import markdownItTocDoneRight from "markdown-it-toc-done-right"; -import { mentionValidator } from "@/api"; -import { sanitizeHtml, sanitizeHtmlInline } from "@/sanitization"; -import { config } from "~/config.ts"; -import type * as VersiaEntities from "~/packages/sdk/entities/index.ts"; -import { transformOutputToUserWithRelations, userRelations } from "./user.ts"; - -/** - * Wrapper against the Status object to make it easier to work with - * @param query - * @returns - */ -export const findManyNotes = async ( - query: Parameters[0], - userId?: string, -): Promise<(typeof Note.$type)[]> => { - const output = await db.query.Notes.findMany({ - ...query, - with: { - ...query?.with, - attachments: { - with: { - media: true, - }, - }, - reactions: { - with: { - emoji: { - with: { - instance: true, - media: true, - }, - }, - }, - }, - emojis: { - with: { - emoji: { - with: { - instance: true, - media: true, - }, - }, - }, - }, - author: { - with: { - ...userRelations, - }, - }, - mentions: { - with: { - user: { - with: { - instance: true, - }, - }, - }, - }, - reblog: { - with: { - attachments: { - with: { - media: true, - }, - }, - reactions: { - with: { - emoji: { - with: { - instance: true, - media: true, - }, - }, - }, - }, - emojis: { - with: { - emoji: { - with: { - instance: true, - media: true, - }, - }, - }, - }, - likes: true, - application: true, - mentions: { - with: { - user: { - with: userRelations, - }, - }, - }, - author: { - with: { - ...userRelations, - }, - }, - }, - extras: { - pinned: userId - ? sql`EXISTS (SELECT 1 FROM "UserToPinnedNotes" WHERE "UserToPinnedNotes"."noteId" = "Notes_reblog".id AND "UserToPinnedNotes"."userId" = ${userId})`.as( - "pinned", - ) - : sql`false`.as("pinned"), - reblogged: userId - ? sql`EXISTS (SELECT 1 FROM "Notes" WHERE "Notes"."authorId" = ${userId} AND "Notes"."reblogId" = "Notes_reblog".id)`.as( - "reblogged", - ) - : sql`false`.as("reblogged"), - muted: userId - ? sql`EXISTS (SELECT 1 FROM "Relationships" WHERE "Relationships"."ownerId" = ${userId} AND "Relationships"."subjectId" = "Notes_reblog"."authorId" AND "Relationships"."muting" = true)`.as( - "muted", - ) - : sql`false`.as("muted"), - liked: userId - ? sql`EXISTS (SELECT 1 FROM "Likes" WHERE "Likes"."likedId" = "Notes_reblog".id AND "Likes"."likerId" = ${userId})`.as( - "liked", - ) - : sql`false`.as("liked"), - }, - }, - reply: true, - quote: true, - }, - extras: { - pinned: userId - ? sql`EXISTS (SELECT 1 FROM "UserToPinnedNotes" WHERE "UserToPinnedNotes"."noteId" = "Notes".id AND "UserToPinnedNotes"."userId" = ${userId})`.as( - "pinned", - ) - : sql`false`.as("pinned"), - reblogged: userId - ? sql`EXISTS (SELECT 1 FROM "Notes" WHERE "Notes"."authorId" = ${userId} AND "Notes"."reblogId" = "Notes".id)`.as( - "reblogged", - ) - : sql`false`.as("reblogged"), - muted: userId - ? sql`EXISTS (SELECT 1 FROM "Relationships" WHERE "Relationships"."ownerId" = ${userId} AND "Relationships"."subjectId" = "Notes"."authorId" AND "Relationships"."muting" = true)`.as( - "muted", - ) - : sql`false`.as("muted"), - liked: userId - ? sql`EXISTS (SELECT 1 FROM "Likes" WHERE "Likes"."likedId" = "Notes".id AND "Likes"."likerId" = ${userId})`.as( - "liked", - ) - : sql`false`.as("liked"), - ...query?.extras, - }, - }); - - return output.map((post) => ({ - ...post, - author: transformOutputToUserWithRelations(post.author), - mentions: post.mentions.map((mention) => ({ - ...mention.user, - endpoints: mention.user.endpoints, - })), - attachments: post.attachments.map((attachment) => attachment.media), - emojis: (post.emojis ?? []).map((emoji) => emoji.emoji), - reblog: post.reblog && { - ...post.reblog, - author: transformOutputToUserWithRelations(post.reblog.author), - mentions: post.reblog.mentions.map((mention) => ({ - ...mention.user, - endpoints: mention.user.endpoints, - })), - attachments: post.reblog.attachments.map( - (attachment) => attachment.media, - ), - emojis: (post.reblog.emojis ?? []).map((emoji) => emoji.emoji), - pinned: Boolean(post.reblog.pinned), - reblogged: Boolean(post.reblog.reblogged), - muted: Boolean(post.reblog.muted), - liked: Boolean(post.reblog.liked), - }, - pinned: Boolean(post.pinned), - reblogged: Boolean(post.reblogged), - muted: Boolean(post.muted), - liked: Boolean(post.liked), - })); -}; - -/** - * Get people mentioned in the content (match @username or @username@domain.com mentions) - * @param text The text to parse mentions from. - * @returns An array of users mentioned in the text. - */ -export const parseTextMentions = async (text: string): Promise => { - const mentionedPeople = [...text.matchAll(mentionValidator)]; - if (mentionedPeople.length === 0) { - return []; - } - - const baseUrlHost = config.http.base_url.host; - const isLocal = (host?: string): boolean => host === baseUrlHost || !host; - - // Find local and matching users - const foundUsers = await db - .select({ - id: Users.id, - username: Users.username, - baseUrl: Instances.baseUrl, - }) - .from(Users) - .leftJoin(Instances, eq(Users.instanceId, Instances.id)) - .where( - or( - ...mentionedPeople.map((person) => - and( - eq(Users.username, person[1] ?? ""), - isLocal(person[2]) - ? isNull(Users.instanceId) - : eq(Instances.baseUrl, person[2] ?? ""), - ), - ), - ), - ); - - // Separate found and unresolved users - const finalList = await User.manyFromSql( - inArray( - Users.id, - foundUsers.map((u) => u.id), - ), - ); - - // Every remote user that isn't in database - const notFoundRemoteUsers = mentionedPeople.filter( - (p) => - !( - foundUsers.some( - (user) => user.username === p[1] && user.baseUrl === p[2], - ) || isLocal(p[2]) - ), - ); - - // Resolve remote mentions not in database - for (const person of notFoundRemoteUsers) { - const url = await FederationRequester.resolveWebFinger( - person[1] ?? "", - person[2] ?? "", - ); - - if (url) { - const user = await User.resolve(url); - - if (user) { - finalList.push(user); - } - } - } - - return finalList; -}; - -export const replaceTextMentions = (text: string, mentions: User[]): string => { - return mentions.reduce((finalText, mention) => { - const { username, instance } = mention.data; - const { uri } = mention; - const baseHost = config.http.base_url.host; - const linkTemplate = (displayText: string): string => - `${displayText}`; - - if (mention.remote) { - return finalText.replaceAll( - `@${username}@${instance?.baseUrl}`, - linkTemplate(`@${username}@${instance?.baseUrl}`), - ); - } - - return finalText.replace( - createRegExp( - exactly( - exactly(`@${username}`) - .notBefore(anyOf(letter, digit, charIn("@"))) - .notAfter(anyOf(letter, digit, charIn("@"))), - ).or(exactly(`@${username}@${baseHost}`)), - [global], - ), - linkTemplate(`@${username}@${baseHost}`), - ); - }, text); -}; - -export const contentToHtml = async ( - content: VersiaEntities.TextContentFormat, - mentions: User[] = [], - inline = false, -): Promise => { - const sanitizer = inline ? sanitizeHtmlInline : sanitizeHtml; - let htmlContent = ""; - - if (content.data["text/html"]) { - htmlContent = await sanitizer(content.data["text/html"].content); - } else if (content.data["text/markdown"]) { - htmlContent = await sanitizer( - await markdownParse(content.data["text/markdown"].content), - ); - } else if (content.data["text/plain"]?.content) { - htmlContent = (await sanitizer(content.data["text/plain"].content)) - .split("\n") - .map((line) => `

${line}

`) - .join("\n"); - } - - htmlContent = replaceTextMentions(htmlContent, mentions); - - return linkifyHtml(htmlContent, { - defaultProtocol: "https", - validate: { email: (): false => false }, - target: "_blank", - rel: "nofollow noopener noreferrer", - }); -}; - -export const markdownParse = async (content: string): Promise => { - return (await getMarkdownRenderer()).render(content); -}; - -export const getMarkdownRenderer = (): MarkdownIt => { - const renderer = MarkdownIt({ - html: true, - linkify: true, - }); - - renderer.use(markdownItTocDoneRight, { - containerClass: "toc", - level: [1, 2, 3, 4], - listType: "ul", - listClass: "toc-list", - itemClass: "toc-item", - linkClass: "toc-link", - }); - - renderer.use(markdownItTaskLists); - - renderer.use(markdownItContainer); - - return renderer; -}; diff --git a/classes/functions/user.ts b/classes/functions/user.ts deleted file mode 100644 index a9cb5726..00000000 --- a/classes/functions/user.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { - type Application, - db, - type Emoji, - type Instance, - type Media, - type Role, - type Token, - type User, -} from "@versia/kit/db"; -import type { Users } from "@versia/kit/tables"; -import type { InferSelectModel } from "drizzle-orm"; - -export const userRelations = { - instance: true, - emojis: { - with: { - emoji: { - with: { - instance: true, - media: true, - }, - }, - }, - }, - avatar: true, - header: true, - roles: { - with: { - role: true, - }, - }, -} as const; - -export interface AuthData { - user: User | null; - token: Token | null; - application: Application | null; -} - -export const transformOutputToUserWithRelations = ( - user: Omit, "endpoints"> & { - followerCount: unknown; - followingCount: unknown; - statusCount: unknown; - avatar: typeof Media.$type | null; - header: typeof Media.$type | null; - emojis: { - userId: string; - emojiId: string; - emoji?: typeof Emoji.$type; - }[]; - instance: typeof Instance.$type | null; - roles: { - userId: string; - roleId: string; - role?: typeof Role.$type; - }[]; - endpoints: unknown; - }, -): typeof User.$type => { - return { - ...user, - followerCount: Number(user.followerCount), - followingCount: Number(user.followingCount), - statusCount: Number(user.statusCount), - endpoints: - user.endpoints ?? - ({} as Partial<{ - dislikes: string; - featured: string; - likes: string; - followers: string; - following: string; - inbox: string; - outbox: string; - }>), - emojis: user.emojis.map( - (emoji) => - (emoji as unknown as Record) - .emoji as typeof Emoji.$type, - ), - roles: user.roles - .map((role) => role.role) - .filter(Boolean) as (typeof Role.$type)[], - }; -}; - -export const findManyUsers = async ( - query: Parameters[0], -): Promise<(typeof User.$type)[]> => { - const output = await db.query.Users.findMany({ - ...query, - with: { - ...userRelations, - ...query?.with, - }, - }); - - return output.map((user) => transformOutputToUserWithRelations(user)); -}; diff --git a/classes/media/preprocessors/blurhash.test.ts b/classes/media/preprocessors/blurhash.test.ts index 9c959e77..6800df2b 100644 --- a/classes/media/preprocessors/blurhash.test.ts +++ b/classes/media/preprocessors/blurhash.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "bun:test"; +import { mockModule } from "@versia-server/tests"; import sharp from "sharp"; -import { mockModule } from "~/tests/utils.ts"; import { calculateBlurhash } from "./blurhash.ts"; describe("BlurhashPreprocessor", () => { diff --git a/classes/media/url.ts b/classes/media/url.ts deleted file mode 100644 index bd0e3302..00000000 --- a/classes/media/url.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { config } from "~/config.ts"; - -export class ProxiableUrl extends URL { - private isAllowedOrigin(): boolean { - const allowedOrigins: URL[] = [config.http.base_url].concat( - config.s3?.public_url ?? [], - ); - - return allowedOrigins.some((origin) => - this.hostname.endsWith(origin.hostname), - ); - } - - public get proxied(): string { - // Don't proxy from CDN and self, since those sources are trusted - if (this.isAllowedOrigin()) { - return this.href; - } - - const urlAsBase64Url = Buffer.from(this.href).toString("base64url"); - - return new URL(`/media/proxy/${urlAsBase64Url}`, config.http.base_url) - .href; - } -} diff --git a/classes/plugin/loader.test.ts b/classes/plugin/loader.test.ts deleted file mode 100644 index 816b7516..00000000 --- a/classes/plugin/loader.test.ts +++ /dev/null @@ -1,221 +0,0 @@ -/* import { - afterEach, - beforeEach, - describe, - expect, - jest, - mock, - test, -} from "bun:test"; -import { ZodError, type ZodTypeAny, z } from "zod"; -import { Plugin } from "~/packages/plugin-kit"; -import { type Manifest, manifestSchema } from "~/packages/plugin-kit/schema"; -import { PluginLoader } from "./loader.ts"; - -const mockReaddir = jest.fn(); -const mockGetLogger = jest.fn(() => ({ - fatal: jest.fn(), -})); -const mockParseJSON5 = jest.fn(); -const mockParseJSONC = jest.fn(); -const mockFromZodError = jest.fn(); - -mock.module("node:fs/promises", () => ({ - readdir: mockReaddir, -})); - -mock.module("@logtape/logtape", () => ({ - getLogger: mockGetLogger, -})); - -mock.module("confbox", () => ({ - parseJSON5: mockParseJSON5, - parseJSONC: mockParseJSONC, -})); - -mock.module("zod-validation-error", () => ({ - fromZodError: mockFromZodError, -})); - -describe("PluginLoader", () => { - let pluginLoader: PluginLoader; - - beforeEach(() => { - pluginLoader = new PluginLoader(); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - test("getDirectories should return directories", async () => { - mockReaddir.mockResolvedValue([ - { name: "dir1", isDirectory: (): true => true }, - { name: "file1", isDirectory: (): false => false }, - { name: "dir2", isDirectory: (): true => true }, - ]); - - // biome-ignore lint/complexity/useLiteralKeys: Private method - const directories = await PluginLoader["getDirectories"]("/some/path"); - expect(directories).toEqual(["dir1", "dir2"]); - }); - - test("findManifestFile should return manifest file if found", async () => { - mockReaddir.mockResolvedValue(["manifest.json", "otherfile.txt"]); - - const manifestFile = - // biome-ignore lint/complexity/useLiteralKeys: Private method - await PluginLoader["findManifestFile"]("/some/path"); - expect(manifestFile).toBe("manifest.json"); - }); - - test("hasEntrypoint should return true if entrypoint file is found", async () => { - mockReaddir.mockResolvedValue(["index.ts", "otherfile.txt"]); - - // biome-ignore lint/complexity/useLiteralKeys: Private method - const hasEntrypoint = await PluginLoader["hasEntrypoint"]("/some/path"); - expect(hasEntrypoint).toBe(true); - }); - - test("parseManifestFile should parse JSON manifest", async () => { - const manifestContent = { name: "test-plugin" }; - Bun.file = jest.fn().mockReturnValue({ - text: (): Promise => - Promise.resolve(JSON.stringify(manifestContent)), - }); - - // biome-ignore lint/complexity/useLiteralKeys: Private method - const manifest = await pluginLoader["parseManifestFile"]( - "/some/path/manifest.json", - "manifest.json", - ); - expect(manifest).toEqual(manifestContent); - }); - - test("findPlugins should return plugin directories with valid manifest and entrypoint", async () => { - mockReaddir - .mockResolvedValueOnce([ - { name: "plugin1", isDirectory: (): true => true }, - { name: "plugin2", isDirectory: (): true => true }, - ]) - .mockResolvedValue(["manifest.json", "index.ts"]); - - const plugins = await PluginLoader.findPlugins("/some/path"); - expect(plugins).toEqual(["plugin1", "plugin2"]); - }); - - test("parseManifest should parse and validate manifest", async () => { - const manifestContent: Manifest = { - name: "test-plugin", - version: "1.1.0", - description: "Doobaee", - }; - mockReaddir.mockResolvedValue(["manifest.json"]); - Bun.file = jest.fn().mockReturnValue({ - text: (): Promise => - Promise.resolve(JSON.stringify(manifestContent)), - }); - manifestSchema.safeParseAsync = jest.fn().mockResolvedValue({ - success: true, - data: manifestContent, - }); - - const manifest = await pluginLoader.parseManifest( - "/some/path", - "plugin1", - ); - expect(manifest).toEqual(manifestContent); - }); - - test("parseManifest should throw error if manifest is missing", async () => { - mockReaddir.mockResolvedValue([]); - - await expect( - pluginLoader.parseManifest("/some/path", "plugin1"), - ).rejects.toThrow("Plugin plugin1 is missing a manifest file"); - }); - - test("parseManifest should throw error if manifest is invalid", async () => { - // @ts-expect-error trying to cause a type error here - const manifestContent: Manifest = { - name: "test-plugin", - version: "1.1.0", - }; - mockReaddir.mockResolvedValue(["manifest.json"]); - Bun.file = jest.fn().mockReturnValue({ - text: (): Promise => - Promise.resolve(JSON.stringify(manifestContent)), - }); - manifestSchema.safeParseAsync = jest.fn().mockResolvedValue({ - success: false, - error: new ZodError([]), - }); - - await expect( - pluginLoader.parseManifest("/some/path", "plugin1"), - ).rejects.toThrow(); - }); - - test("loadPlugin should load and return a Plugin instance", async () => { - const mockPlugin = new Plugin(z.object({})); - mock.module("/some/path/index.ts", () => ({ - default: mockPlugin, - })); - - const plugin = await pluginLoader.loadPlugin("/some/path", "index.ts"); - expect(plugin).toBeInstanceOf(Plugin); - }); - - test("loadPlugin should throw error if default export is not a Plugin", async () => { - mock.module("/some/path/index.ts", () => ({ - default: "cheese", - })); - - await expect( - pluginLoader.loadPlugin("/some/path", "index.ts"), - ).rejects.toThrow("Entrypoint is not a Plugin"); - }); - - test("loadPlugins should load all plugins in a directory", async () => { - const manifestContent: Manifest = { - name: "test-plugin", - version: "1.1.0", - description: "Doobaee", - }; - const mockPlugin = new Plugin(z.object({})); - - mockReaddir - .mockResolvedValueOnce([ - { name: "plugin1", isDirectory: (): true => true }, - { name: "plugin2", isDirectory: (): true => true }, - ]) - .mockResolvedValue(["manifest.json", "index.ts"]); - Bun.file = jest.fn().mockReturnValue({ - text: (): Promise => - Promise.resolve(JSON.stringify(manifestContent)), - }); - manifestSchema.safeParseAsync = jest.fn().mockResolvedValue({ - success: true, - data: manifestContent, - }); - mock.module("/some/path/plugin1/index", () => ({ - default: mockPlugin, - })); - mock.module("/some/path/plugin2/index", () => ({ - default: mockPlugin, - })); - - const plugins = await pluginLoader.loadPlugins("/some/path", true); - expect(plugins).toEqual([ - { - manifest: manifestContent, - plugin: mockPlugin as unknown as Plugin, - }, - { - manifest: manifestContent, - plugin: mockPlugin as unknown as Plugin, - }, - ]); - }); -}); - */ diff --git a/classes/search/search-manager.ts b/classes/search/search-manager.ts index 21762bae..e69de29b 100644 --- a/classes/search/search-manager.ts +++ b/classes/search/search-manager.ts @@ -1,317 +0,0 @@ -/** - * @file search-manager.ts - * @description Sonic search integration for indexing and searching accounts and statuses - */ - -import { getLogger } from "@logtape/logtape"; -import { db, Note, User } from "@versia/kit/db"; -import type { SQL, ValueOrArray } from "drizzle-orm"; -import { - Ingest as SonicChannelIngest, - Search as SonicChannelSearch, -} from "sonic-channel"; -import { config } from "~/config.ts"; - -/** - * Enum for Sonic index types - */ -export enum SonicIndexType { - Accounts = "accounts", - Statuses = "statuses", -} - -/** - * Class for managing Sonic search operations - */ -export class SonicSearchManager { - private searchChannel: SonicChannelSearch; - private ingestChannel: SonicChannelIngest; - private connected = false; - private logger = getLogger("sonic"); - - /** - * @param config Configuration for Sonic - */ - public constructor() { - if (!config.search.sonic) { - throw new Error("Sonic configuration is missing"); - } - - this.searchChannel = new SonicChannelSearch({ - host: config.search.sonic.host, - port: config.search.sonic.port, - auth: config.search.sonic.password, - }); - - this.ingestChannel = new SonicChannelIngest({ - host: config.search.sonic.host, - port: config.search.sonic.port, - auth: config.search.sonic.password, - }); - } - - /** - * Connect to Sonic - */ - public async connect(silent = false): Promise { - if (!config.search.enabled) { - !silent && this.logger.info`Sonic search is disabled`; - return; - } - - if (this.connected) { - return; - } - - !silent && this.logger.info`Connecting to Sonic...`; - - // Connect to Sonic - await new Promise((resolve, reject) => { - this.searchChannel.connect({ - connected: (): void => { - !silent && - this.logger.info`Connected to Sonic Search Channel`; - resolve(true); - }, - disconnected: (): void => - this.logger - .error`Disconnected from Sonic Search Channel. You might be using an incorrect password.`, - timeout: (): void => - this.logger - .error`Sonic Search Channel connection timed out`, - retrying: (): void => - this.logger - .warn`Retrying connection to Sonic Search Channel`, - error: (error): void => { - this.logger - .error`Failed to connect to Sonic Search Channel: ${error}`; - reject(error); - }, - }); - }); - - await new Promise((resolve, reject) => { - this.ingestChannel.connect({ - connected: (): void => { - !silent && - this.logger.info`Connected to Sonic Ingest Channel`; - resolve(true); - }, - disconnected: (): void => - this.logger.error`Disconnected from Sonic Ingest Channel`, - timeout: (): void => - this.logger - .error`Sonic Ingest Channel connection timed out`, - retrying: (): void => - this.logger - .warn`Retrying connection to Sonic Ingest Channel`, - error: (error): void => { - this.logger - .error`Failed to connect to Sonic Ingest Channel: ${error}`; - reject(error); - }, - }); - }); - - try { - await Promise.all([ - this.searchChannel.ping(), - this.ingestChannel.ping(), - ]); - this.connected = true; - !silent && this.logger.info`Connected to Sonic`; - } catch (error) { - this.logger.fatal`Error while connecting to Sonic: ${error}`; - throw error; - } - } - - /** - * Add a user to Sonic - * @param user User to add - */ - public async addUser(user: User): Promise { - if (!config.search.enabled) { - return; - } - - try { - await this.ingestChannel.push( - SonicIndexType.Accounts, - "users", - user.id, - `${user.data.username} ${user.data.displayName} ${user.data.note}`, - ); - } catch (error) { - this.logger.error`Failed to add user to Sonic: ${error}`; - } - } - - /** - * Get a batch of accounts from the database - * @param n Batch number - * @param batchSize Size of the batch - */ - private static getNthDatabaseAccountBatch( - n: number, - batchSize = 1000, - ): Promise[]> { - return db.query.Users.findMany({ - offset: n * batchSize, - limit: batchSize, - columns: { - id: true, - username: true, - displayName: true, - note: true, - createdAt: true, - }, - orderBy: (user, { asc }): ValueOrArray => asc(user.createdAt), - }); - } - - /** - * Get a batch of statuses from the database - * @param n Batch number - * @param batchSize Size of the batch - */ - private static getNthDatabaseStatusBatch( - n: number, - batchSize = 1000, - ): Promise[]> { - return db.query.Notes.findMany({ - offset: n * batchSize, - limit: batchSize, - columns: { - id: true, - content: true, - createdAt: true, - }, - orderBy: (status, { asc }): ValueOrArray => - asc(status.createdAt), - }); - } - - /** - * Rebuild search indexes - * @param indexes Indexes to rebuild - * @param batchSize Size of each batch - * @param progressCallback Callback for progress updates - */ - public async rebuildSearchIndexes( - indexes: SonicIndexType[], - batchSize = 100, - progressCallback?: (progress: number) => void, - ): Promise { - for (const index of indexes) { - if (index === SonicIndexType.Accounts) { - await this.rebuildAccountsIndex(batchSize, progressCallback); - } else if (index === SonicIndexType.Statuses) { - await this.rebuildStatusesIndex(batchSize, progressCallback); - } - } - } - - /** - * Rebuild accounts index - * @param batchSize Size of each batch - * @param progressCallback Callback for progress updates - */ - private async rebuildAccountsIndex( - batchSize: number, - progressCallback?: (progress: number) => void, - ): Promise { - const accountCount = await User.getCount(); - const batchCount = Math.ceil(accountCount / batchSize); - - for (let i = 0; i < batchCount; i++) { - const accounts = - await SonicSearchManager.getNthDatabaseAccountBatch( - i, - batchSize, - ); - await Promise.all( - accounts.map((account) => - this.ingestChannel.push( - SonicIndexType.Accounts, - "users", - account.id as string, - `${account.username} ${account.displayName} ${account.note}`, - ), - ), - ); - progressCallback?.((i + 1) / batchCount); - } - } - - /** - * Rebuild statuses index - * @param batchSize Size of each batch - * @param progressCallback Callback for progress updates - */ - private async rebuildStatusesIndex( - batchSize: number, - progressCallback?: (progress: number) => void, - ): Promise { - const statusCount = await Note.getCount(); - const batchCount = Math.ceil(statusCount / batchSize); - - for (let i = 0; i < batchCount; i++) { - const statuses = await SonicSearchManager.getNthDatabaseStatusBatch( - i, - batchSize, - ); - await Promise.all( - statuses.map((status) => - this.ingestChannel.push( - SonicIndexType.Statuses, - "notes", - status.id as string, - status.content as string, - ), - ), - ); - progressCallback?.((i + 1) / batchCount); - } - } - - /** - * Search for accounts - * @param query Search query - * @param limit Maximum number of results - * @param offset Offset for pagination - */ - public searchAccounts( - query: string, - limit = 10, - offset = 0, - ): Promise { - return this.searchChannel.query( - SonicIndexType.Accounts, - "users", - query, - { limit, offset }, - ); - } - - /** - * Search for statuses - * @param query Search query - * @param limit Maximum number of results - * @param offset Offset for pagination - */ - public searchStatuses( - query: string, - limit = 10, - offset = 0, - ): Promise { - return this.searchChannel.query( - SonicIndexType.Statuses, - "notes", - query, - { limit, offset }, - ); - } -} - -export const searchManager = new SonicSearchManager(); diff --git a/cli/index.ts b/cli/index.ts index 2d315504..4db09ca8 100644 --- a/cli/index.ts +++ b/cli/index.ts @@ -3,10 +3,10 @@ import { friendlyErrorPlugin } from "@clerc/plugin-friendly-error"; import { helpPlugin } from "@clerc/plugin-help"; import { notFoundPlugin } from "@clerc/plugin-not-found"; import { versionPlugin } from "@clerc/plugin-version"; +import { setupDatabase } from "@versia-server/kit/db"; +import { searchManager } from "@versia-server/kit/search"; import { Clerc } from "clerc"; -import { searchManager } from "~/classes/search/search-manager.ts"; -import { setupDatabase } from "~/drizzle/db.ts"; -import pkg from "~/package.json" with { type: "json" }; +import pkg from "../package.json" with { type: "json" }; import { rebuildIndexCommand } from "./index/rebuild.ts"; import { refetchInstanceCommand } from "./instance/refetch.ts"; import { createUserCommand } from "./user/create.ts"; diff --git a/cli/index/rebuild.ts b/cli/index/rebuild.ts index a99ec2f5..b3b6d424 100644 --- a/cli/index/rebuild.ts +++ b/cli/index/rebuild.ts @@ -1,12 +1,9 @@ +import { config } from "@versia-server/config"; +import { SonicIndexType, searchManager } from "@versia-server/kit/search"; // @ts-expect-error - Root import is required or the Clec type definitions won't work // biome-ignore lint/correctness/noUnusedImports: Root import is required or the Clec type definitions won't work import { defineCommand, type Root } from "clerc"; import ora from "ora"; -import { - SonicIndexType, - searchManager, -} from "~/classes/search/search-manager.ts"; -import { config } from "~/config.ts"; export const rebuildIndexCommand = defineCommand( { diff --git a/cli/instance/refetch.ts b/cli/instance/refetch.ts index 6f63b40a..a8ae898f 100644 --- a/cli/instance/refetch.ts +++ b/cli/instance/refetch.ts @@ -1,11 +1,11 @@ +import { Instance } from "@versia-server/kit/db"; +import { FetchJobType, fetchQueue } from "@versia-server/kit/queues/fetch"; +import { Instances } from "@versia-server/kit/tables"; import chalk from "chalk"; // @ts-expect-error - Root import is required or the Clec type definitions won't work // biome-ignore lint/correctness/noUnusedImports: Root import is required or the Clec type definitions won't work import { defineCommand, type Root } from "clerc"; import { eq } from "drizzle-orm"; -import { Instance } from "~/classes/database/instance.ts"; -import { FetchJobType, fetchQueue } from "~/classes/queues/fetch.ts"; -import { Instances } from "~/drizzle/schema.ts"; export const refetchInstanceCommand = defineCommand( { diff --git a/cli/user/create.ts b/cli/user/create.ts index 8340c521..1a116cb1 100644 --- a/cli/user/create.ts +++ b/cli/user/create.ts @@ -1,12 +1,13 @@ +import { config } from "@versia-server/config"; +import { User } from "@versia-server/kit/db"; +import { searchManager } from "@versia-server/kit/search"; +import { Users } from "@versia-server/kit/tables"; import chalk from "chalk"; // @ts-expect-error - Root import is required or the Clec type definitions won't work // biome-ignore lint/correctness/noUnusedImports: Root import is required or the Clec type definitions won't work import { defineCommand, type Root } from "clerc"; import { and, eq, isNull } from "drizzle-orm"; import { renderUnicodeCompact } from "uqr"; -import { User } from "~/classes/database/user"; -import { config } from "~/config"; -import { Users } from "~/drizzle/schema"; export const createUserCommand = defineCommand( { @@ -54,6 +55,9 @@ export const createUserCommand = defineCommand( isAdmin: admin, }); + // Add to search index + await searchManager.addUser(user); + if (!user) { throw new Error("Failed to create user."); } diff --git a/cli/user/refetch.ts b/cli/user/refetch.ts index 39bc1279..6535f4e2 100644 --- a/cli/user/refetch.ts +++ b/cli/user/refetch.ts @@ -1,9 +1,9 @@ +import { User } from "@versia-server/kit/db"; import chalk from "chalk"; // @ts-expect-error - Root import is required or the Clec type definitions won't work // biome-ignore lint/correctness/noUnusedImports: Root import is required or the Clec type definitions won't work import { defineCommand, type Root } from "clerc"; import ora from "ora"; -import { User } from "~/classes/database/user.ts"; import { retrieveUser } from "../utils.ts"; export const refetchUserCommand = defineCommand( diff --git a/cli/user/token.ts b/cli/user/token.ts index 083a1b9f..6c88327c 100644 --- a/cli/user/token.ts +++ b/cli/user/token.ts @@ -1,10 +1,10 @@ +import { Token } from "@versia-server/kit/db"; import { randomUUIDv7 } from "bun"; import chalk from "chalk"; // @ts-expect-error - Root import is required or the Clec type definitions won't work // biome-ignore lint/correctness/noUnusedImports: Root import is required or the Clec type definitions won't work import { defineCommand, type Root } from "clerc"; import { randomString } from "@/math.ts"; -import { Token } from "~/classes/database/token.ts"; import { retrieveUser } from "../utils.ts"; export const generateTokenCommand = defineCommand( diff --git a/cli/utils.ts b/cli/utils.ts index 1c7c3688..b75ffc06 100644 --- a/cli/utils.ts +++ b/cli/utils.ts @@ -1,8 +1,7 @@ +import { Instance, User } from "@versia-server/kit/db"; +import { parseUserAddress } from "@versia-server/kit/parsers"; +import { Users } from "@versia-server/kit/tables"; import { and, eq, isNull } from "drizzle-orm"; -import { parseUserAddress } from "@/api"; -import { Instance } from "~/classes/database/instance"; -import { User } from "~/classes/database/user"; -import { Users } from "~/drizzle/schema"; export const retrieveUser = async ( usernameOrHandle: string, diff --git a/config.ts b/config.ts deleted file mode 100644 index 429019f2..00000000 --- a/config.ts +++ /dev/null @@ -1,48 +0,0 @@ -/** - * @file config.ts - * @summary Config system to retrieve and modify system configuration - * @description Can read from a hand-written file, config.toml, or from a machine-saved file, config.internal.toml - * Fuses both and provides a way to retrieve individual values - */ - -import { env, file } from "bun"; -import chalk from "chalk"; -import { parseTOML } from "confbox"; -import type { z } from "zod"; -import { fromZodError } from "zod-validation-error"; -import { ConfigSchema } from "./classes/config/schema.ts"; - -const CONFIG_LOCATION = env.CONFIG_LOCATION ?? "./config/config.toml"; -const configFile = file(CONFIG_LOCATION); - -if (!(await configFile.exists())) { - throw new Error( - `config file at "${CONFIG_LOCATION}" does not exist or is not accessible.`, - ); -} - -const configText = await configFile.text(); -const config = await parseTOML>(configText); - -const parsed = await ConfigSchema.safeParseAsync(config); - -if (!parsed.success) { - console.error( - `⚠ Error encountered while loading ${chalk.gray(CONFIG_LOCATION)}.`, - ); - console.error( - "⚠ This is due to invalid, missing or incorrect values in the configuration file.", - ); - console.error( - "⚠ Here is the error message, please fix the configuration file accordingly:", - ); - const errorMessage = fromZodError(parsed.error).message; - - console.info(errorMessage); - - throw new Error("Configuration file is invalid."); -} - -const exportedConfig = parsed.data; - -export { exportedConfig as config }; diff --git a/config/config b/config/config new file mode 120000 index 00000000..3ca249e0 --- /dev/null +++ b/config/config @@ -0,0 +1 @@ +../config \ No newline at end of file diff --git a/config/config.example.toml b/config/config.example.toml index ce0ebb76..ac825c0b 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -435,31 +435,29 @@ text = "No spam" [logging] -# Available levels: debug, info, warning, error, fatal -log_level = "debug" - -log_file_path = "logs/versia.log" - -[logging.types] -# Either pass a boolean -# requests = true -# Or a table with the following keys: -# requests_content = { level = "debug", log_file_path = "logs/requests.log" } -# Available types are: requests, responses, requests_content, filters +# Available levels: trace, debug, info, warning, error, fatal +log_level = "info" # For console output +# [logging.file] +# path = "logs/versia.log" +# log_level = "info" +# +# [logging.file.rotation] +# max_size = 10_000_000 # 10 MB +# max_files = 10 # Keep 10 rotated files +# # https://sentry.io support -# Uncomment to enable # [logging.sentry] -# Sentry DSN for error logging # dsn = "https://example.com" # debug = false - # sample_rate = 1.0 # traces_sample_rate = 1.0 # Can also be regex # trace_propagation_targets = [] # max_breadcrumbs = 100 # environment = "production" +# log_level = "info" + [plugins] # Whether to automatically load all plugins in the plugins directory diff --git a/drizzle.config.ts b/drizzle.config.ts index f1b48f49..49d2ec86 100644 --- a/drizzle.config.ts +++ b/drizzle.config.ts @@ -1,5 +1,5 @@ +import { config } from "@versia-server/config"; import type { Config } from "drizzle-kit"; -import { config } from "~/config.ts"; /** * Drizzle can't properly resolve imports with top-level await, so uncomment diff --git a/entrypoints/worker/index.ts b/entrypoints/worker/index.ts deleted file mode 100644 index 95e2656d..00000000 --- a/entrypoints/worker/index.ts +++ /dev/null @@ -1,45 +0,0 @@ -import process from "node:process"; -import { getLogger } from "@logtape/logtape"; -import chalk from "chalk"; -import { sentry } from "@/sentry"; -import { getDeliveryWorker } from "~/classes/queues/delivery"; -import { getFetchWorker } from "~/classes/queues/fetch"; -import { getInboxWorker } from "~/classes/queues/inbox"; -import { getMediaWorker } from "~/classes/queues/media"; -import { getPushWorker } from "~/classes/queues/push"; -import { getRelationshipWorker } from "~/classes/queues/relationships"; - -process.on("SIGINT", () => { - process.exit(); -}); - -await import("~/entrypoints/worker/setup.ts"); -sentry?.captureMessage("Server started", "info"); - -const serverLogger = getLogger("server"); - -serverLogger.info`Starting Fetch Worker...`; -getFetchWorker(); -serverLogger.info`${chalk.green("✔")} Fetch Worker started`; - -serverLogger.info`Starting Delivery Worker...`; -getDeliveryWorker(); -serverLogger.info`${chalk.green("✔")} Delivery Worker started`; - -serverLogger.info`Starting Inbox Worker...`; -getInboxWorker(); -serverLogger.info`${chalk.green("✔")} Inbox Worker started`; - -serverLogger.info`Starting Push Worker...`; -getPushWorker(); -serverLogger.info`${chalk.green("✔")} Push Worker started`; - -serverLogger.info`Starting Media Worker...`; -getMediaWorker(); -serverLogger.info`${chalk.green("✔")} Media Worker started`; - -serverLogger.info`Starting Relationship Worker...`; -getRelationshipWorker(); -serverLogger.info`${chalk.green("✔")} Relationship Worker started`; - -serverLogger.info`${chalk.green("✔✔✔✔✔✔")} All workers started`; diff --git a/flake.lock b/flake.lock index e59f2580..eeab4a1a 100644 --- a/flake.lock +++ b/flake.lock @@ -20,16 +20,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1744536153, - "narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=", - "owner": "nixos", + "lastModified": 1751637120, + "narHash": "sha256-xVNy/XopSfIG9c46nRmPaKfH1Gn/56vQ8++xWA8itO4=", + "owner": "NixOS", "repo": "nixpkgs", - "rev": "18dd725c29603f582cf1900e0d25f9f1063dbf11", + "rev": "5c724ed1388e53cc231ed98330a60eb2f7be4be3", "type": "github" }, "original": { - "owner": "nixos", - "ref": "nixpkgs-unstable", + "owner": "NixOS", + "ref": "nixos-unstable", "repo": "nixpkgs", "type": "github" } diff --git a/flake.nix b/flake.nix index 09f67774..8fb917da 100644 --- a/flake.nix +++ b/flake.nix @@ -2,7 +2,7 @@ description = "Versia Server"; inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; }; @@ -15,7 +15,9 @@ }: { overlays.default = final: prev: rec { - versia-server = final.callPackage ./nix/package.nix {}; + versia-server = + final.callPackage ./nix/package.nix { + }; versia-server-worker = final.callPackage ./nix/package-worker.nix { inherit versia-server; }; @@ -54,7 +56,6 @@ buildInputs = with pkgs; [ bun vips - pnpm nodePackages.typescript nodePackages.typescript-language-server nix-ld diff --git a/index.ts b/index.ts deleted file mode 100644 index 6ba16223..00000000 --- a/index.ts +++ /dev/null @@ -1 +0,0 @@ -await import("~/entrypoints/api/index.ts"); diff --git a/middlewares/logger.ts b/middlewares/logger.ts deleted file mode 100644 index 21e604cf..00000000 --- a/middlewares/logger.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { getLogger } from "@logtape/logtape"; -import { SHA256 } from "bun"; -import chalk from "chalk"; -import { createMiddleware } from "hono/factory"; -import { config } from "~/config.ts"; - -export const logger = createMiddleware(async (context, next) => { - if (config.logging.types.requests) { - const serverLogger = getLogger("server"); - const body = await context.req.raw.clone().text(); - - const urlAndMethod = `${chalk.green(context.req.method)} ${chalk.blue(context.req.url)}`; - - const hash = `${chalk.bold("Hash")}: ${chalk.yellow( - new SHA256().update(body).digest("hex"), - )}`; - - const headers = `${chalk.bold("Headers")}:\n${Array.from( - context.req.raw.headers.entries(), - ) - .map( - ([key, value]) => - ` - ${chalk.cyan(key)}: ${chalk.white(value)}`, - ) - .join("\n")}`; - - const bodyLog = `${chalk.bold("Body")}: ${chalk.gray(body)}`; - - if (config.logging.types.requests_content) { - serverLogger.debug`${urlAndMethod}\n${hash}\n${headers}\n${bodyLog}`; - } else { - serverLogger.debug`${urlAndMethod}`; - } - } - - await next(); -}); diff --git a/nix/package-worker.nix b/nix/package-worker.nix index 293ac0e1..982af761 100644 --- a/nix/package-worker.nix +++ b/nix/package-worker.nix @@ -2,14 +2,8 @@ {versia-server, ...}: versia-server.overrideAttrs (oldAttrs: { pname = "${oldAttrs.pname}-worker"; - buildPhase = '' - runHook preBuild - bun run build:worker - - runHook postBuild - ''; - entrypointPath = "worker.js"; + buildType = "worker"; meta = oldAttrs.meta diff --git a/nix/package.nix b/nix/package.nix index 49c7b86c..9999c2de 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -1,11 +1,12 @@ { lib, stdenv, - pnpm, bun, nodejs, vips, makeWrapper, + stdenvNoCC, + writableTmpDirAsHomeHook, ... }: let packageJson = builtins.fromJSON (builtins.readFile ../package.json); @@ -16,35 +17,70 @@ in src = ../.; - # Fixes the build script mv usage - pnpmInstallFlags = ["--shamefully-hoist"]; + node_modules = stdenvNoCC.mkDerivation { + pname = "${finalAttrs.pname}-node_modules"; + inherit (finalAttrs) version src; - pnpmDeps = pnpm.fetchDeps { - inherit (finalAttrs) pname version src pnpmInstallFlags; - hash = "sha256-/VCzDp8EfvQkaz/5W3rcoEyOlSB4zeW97qqOTJf6WvA="; + nativeBuildInputs = [ + bun + nodejs + writableTmpDirAsHomeHook + ]; + + dontConfigure = true; + + buildPhase = '' + runHook preBuild + + export BUN_INSTALL_CACHE_DIR=$(mktemp -d) + + bun install \ + --force \ + --frozen-lockfile \ + --no-progress + + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + + mkdir -p $out/node_modules + cp -R ./node_modules $out + + runHook postInstall + ''; + + # Required else we get errors that our fixed-output derivation references store paths + dontFixup = true; + + outputHash = "sha256-/RQv87hjLdH6+41yR7+bGp3j200DVhIrKWoI1MKIqJs="; + outputHashAlgo = "sha256"; + outputHashMode = "recursive"; }; nativeBuildInputs = [ - pnpm - pnpm.configHook bun - nodejs makeWrapper ]; - buildInputs = [ - vips - ]; + configurePhase = '' + runHook preConfigure + + cp -R ${finalAttrs.node_modules}/node_modules . + + runHook postConfigure + ''; buildPhase = '' runHook preBuild - bun run build + bun run build ${finalAttrs.buildType} runHook postBuild ''; - entrypointPath = "index.js"; + buildType = "api"; installPhase = let libPath = lib.makeLibraryPath [ @@ -62,7 +98,7 @@ in cp -r dist $out/${finalAttrs.pname} makeWrapper ${lib.getExe bun} $out/bin/${finalAttrs.pname} \ - --add-flags "run $out/${finalAttrs.pname}/${finalAttrs.entrypointPath}" \ + --add-flags "run $out/${finalAttrs.pname}/${finalAttrs.buildType}.js" \ --set NODE_PATH $out/${finalAttrs.pname}/node_modules \ --set MSGPACKR_NATIVE_ACCELERATION_DISABLED true \ --prefix PATH : ${binPath} \ diff --git a/package.json b/package.json index 4ff7b7f7..3177a42b 100644 --- a/package.json +++ b/package.json @@ -20,9 +20,88 @@ "activitypub", "bun" ], - "workspaces": [ - "packages/*" - ], + "workspaces": { + "packages": [ + "packages/*" + ], + "catalog": { + "@biomejs/biome": "^2.0.6", + "@types/bun": "^1.2.18", + "@types/html-to-text": "^9.0.4", + "@types/markdown-it-container": "^2.0.10", + "@types/mime-types": "^3.0.1", + "@types/qs": "^6.14.0", + "@types/web-push": "^3.6.4", + "bun-bagel": "^1.2.0", + "drizzle-kit": "^0.31.4", + "mitt": "^3.0.1", + "markdown-it-image-figures": "^2.1.1", + "ts-prune": "^0.10.3", + "typescript": "^5.8.3", + "vitepress": "^1.6.3", + "vitepress-plugin-tabs": "^0.7.1", + "vitepress-sidebar": "^1.32.1", + "vue": "^3.5.17", + "@bull-board/api": "^6.11.0", + "@bull-board/hono": "^6.11.0", + "@clerc/plugin-completions": "^0.44.0", + "@clerc/plugin-friendly-error": "^0.44.0", + "@clerc/plugin-help": "^0.44.0", + "@clerc/plugin-not-found": "^0.44.0", + "@clerc/plugin-version": "^0.44.0", + "@hackmd/markdown-it-task-lists": "^2.1.4", + "@hono/standard-validator": "^0.1.2", + "@inquirer/confirm": "^5.1.13", + "@logtape/file": "^1.0.0", + "@logtape/logtape": "^1.0.0", + "@logtape/sentry": "^1.0.0", + "@logtape/otel": "^1.0.0", + "@scalar/hono-api-reference": "^0.9.7", + "@sentry/bun": "^9.35.0", + "altcha-lib": "^1.3.0", + "blurhash": "^2.0.5", + "bullmq": "^5.56.1", + "chalk": "^5.4.1", + "clerc": "^0.44.0", + "confbox": "^0.2.2", + "drizzle-orm": "^0.44.2", + "feed": "^5.1.0", + "hono": "^4.8.4", + "hono-openapi": "npm:@cpluspatch/hono-openapi@0.5.1", + "hono-rate-limiter": "^0.4.2", + "html-to-text": "^9.0.5", + "ioredis": "^5.6.1", + "ip-matching": "^2.1.2", + "iso-639-1": "^3.1.5", + "jose": "^6.0.11", + "linkify-html": "^4.3.1", + "linkify-string": "^4.3.1", + "linkifyjs": "^4.3.1", + "magic-regexp": "^0.10.0", + "markdown-it": "^14.1.0", + "markdown-it-anchor": "^9.2.0", + "markdown-it-container": "^4.0.0", + "markdown-it-mathjax3": "^4.3.2", + "markdown-it-toc-done-right": "^4.2.0", + "mime-types": "^3.0.1", + "mitata": "^1.0.34", + "oauth4webapi": "^3.5.5", + "ora": "^8.2.0", + "qs": "^6.14.0", + "sharp": "^0.34.2", + "sonic-channel": "^1.3.1", + "string-comparison": "^1.3.0", + "stringify-entities": "^4.0.4", + "unicode-emoji-json": "^0.8.0", + "uqr": "^0.1.2", + "web-push": "^3.6.7", + "xss": "^1.0.15", + "youch": "^4.1.0-beta.7", + "zod": "^3.25.74", + "zod-openapi": "^5.0.0", + "zod-validation-error": "^4.0.0-beta.1" + } + }, "maintainers": [ { "email": "contact@cpluspatch.com", @@ -36,107 +115,107 @@ }, "private": true, "scripts": { - "dev": "bun run --hot index.ts", - "start": "NODE_ENV=production bun run dist/index.js --prod", "lint": "biome check .", - "build": "bun run build.ts", - "build:worker": "bun run build-worker.ts", "cloc": "cloc . --exclude-dir node_modules,dist,.output,.nuxt,meta,logs --exclude-ext sql,log,pem", - "wc": "find server database *.ts docs packages types utils drizzle tests -type f -print0 | wc -m --files0-from=-", "cli": "bun run cli/index.ts", - "prune": "ts-prune | grep -v server/ | grep -v dist/ | grep -v '(used in module)'", - "schema:generate": "bun run classes/config/to-json-schema.ts > config/config.schema.json && bun run packages/plugin-kit/json-schema.ts > packages/plugin-kit/manifest.schema.json", - "check": "bunx tsc -p .", + "typecheck": "bunx tsc -p .", "test": "bun test", - "docs:dev": "vitepress dev docs", - "docs:build": "vitepress build docs", - "docs:preview": "vitepress preview docs" + "build": "bun run --filter \"*\" build && bun run build.ts", + "detect-circular": "bunx madge --circular --extensions ts ./", + "update-nix-hashes": "bash scripts/update-nix.sh", + "run-api": "bun run build api && cd dist && ln -s ../config config && bun run api.js", + "run-worker": "bun run build worker && cd dist && ln -s ../config config && bun run worker.js", + "dev": "bun run --hot api.ts", + "worker:dev": "bun run --hot worker.ts" }, "trustedDependencies": [ "@biomejs/biome", "es5-ext", "esbuild", "msgpackr-extract", + "protobufjs", "sharp" ], "devDependencies": { - "@biomejs/biome": "2.0.0-beta.5", - "@types/bun": "^1.2.16", - "@types/html-to-text": "^9.0.4", - "@types/markdown-it-container": "^2.0.10", - "@types/mime-types": "^3.0.1", - "@types/qs": "^6.14.0", - "@types/web-push": "^3.6.4", - "bun-bagel": "^1.2.0", - "drizzle-kit": "^0.31.1", - "markdown-it-image-figures": "^2.1.1", - "ts-prune": "^0.10.3", - "typescript": "^5.8.3", - "vitepress": "^1.6.3", - "vitepress-plugin-tabs": "^0.7.1", - "vitepress-sidebar": "^1.31.1", - "vue": "^3.5.16", - "zod-to-json-schema": "^3.24.5" + "@biomejs/biome": "catalog:", + "@types/bun": "catalog:", + "@types/html-to-text": "catalog:", + "@types/markdown-it-container": "catalog:", + "@types/mime-types": "catalog:", + "@types/qs": "catalog:", + "@types/web-push": "catalog:", + "bun-bagel": "catalog:", + "drizzle-kit": "catalog:", + "markdown-it-image-figures": "catalog:", + "ts-prune": "catalog:", + "typescript": "catalog:", + "vitepress": "catalog:", + "vitepress-plugin-tabs": "catalog:", + "vitepress-sidebar": "catalog:", + "vue": "catalog:" }, "dependencies": { - "@bull-board/api": "^6.10.1", - "@bull-board/hono": "^6.10.1", - "@clerc/plugin-completions": "^0.44.0", - "@clerc/plugin-friendly-error": "^0.44.0", - "@clerc/plugin-help": "^0.44.0", - "@clerc/plugin-not-found": "^0.44.0", - "@clerc/plugin-version": "^0.44.0", - "@hackmd/markdown-it-task-lists": "^2.1.4", - "@hono/zod-validator": "^0.7.0", - "@inquirer/confirm": "^5.1.12", - "@logtape/file": "^0.12.0", - "@logtape/logtape": "^0.12.0", - "@scalar/hono-api-reference": "^0.9.4", - "@sentry/bun": "^9.29.0", + "@bull-board/api": "catalog:", + "@bull-board/hono": "catalog:", + "@clerc/plugin-completions": "catalog:", + "@clerc/plugin-friendly-error": "catalog:", + "@clerc/plugin-help": "catalog:", + "@clerc/plugin-not-found": "catalog:", + "@clerc/plugin-version": "catalog:", + "@hackmd/markdown-it-task-lists": "catalog:", + "@hono/standard-validator": "catalog:", + "@inquirer/confirm": "catalog:", + "@scalar/hono-api-reference": "catalog:", + "@sentry/bun": "catalog:", + "@versia-server/api": "workspace:*", + "@versia-server/config": "workspace:*", + "@versia-server/kit": "workspace:*", + "@versia-server/logging": "workspace:*", + "@versia-server/tests": "workspace:*", + "@versia-server/worker": "workspace:*", "@versia/client": "workspace:*", - "@versia/kit": "workspace:*", "@versia/sdk": "workspace:*", - "altcha-lib": "^1.3.0", - "blurhash": "^2.0.5", - "bullmq": "^5.53.3", - "chalk": "^5.4.1", - "clerc": "^0.44.0", - "confbox": "^0.2.2", - "drizzle-orm": "^0.44.2", - "feed": "^5.1.0", - "hono": "^4.7.11", - "hono-openapi": "^0.4.8", - "hono-rate-limiter": "^0.4.2", - "html-to-text": "^9.0.5", - "ioredis": "^5.6.1", - "ip-matching": "^2.1.2", - "iso-639-1": "^3.1.5", - "jose": "^6.0.11", - "linkify-html": "^4.3.1", - "linkify-string": "^4.3.1", - "linkifyjs": "^4.3.1", - "magic-regexp": "^0.10.0", - "markdown-it": "^14.1.0", - "markdown-it-anchor": "^9.2.0", - "markdown-it-container": "^4.0.0", - "markdown-it-mathjax3": "^4.3.2", - "markdown-it-toc-done-right": "^4.2.0", - "mime-types": "^3.0.1", - "mitata": "^1.0.34", - "oauth4webapi": "^3.5.2", - "ora": "^8.2.0", - "qs": "^6.14.0", - "sharp": "^0.34.2", - "sonic-channel": "^1.3.1", - "string-comparison": "^1.3.0", - "stringify-entities": "^4.0.4", - "unicode-emoji-json": "^0.8.0", - "uqr": "^0.1.2", - "web-push": "^3.6.7", - "xss": "^1.0.15", - "youch": "^4.1.0-beta.7", - "zod": "^3.25.64", - "zod-openapi": "^4.2.4", - "zod-validation-error": "^3.5.0" + "altcha-lib": "catalog:", + "blurhash": "catalog:", + "bullmq": "catalog:", + "chalk": "catalog:", + "clerc": "catalog:", + "confbox": "catalog:", + "drizzle-orm": "catalog:", + "feed": "catalog:", + "hono": "catalog:", + "hono-openapi": "catalog:", + "hono-rate-limiter": "catalog:", + "html-to-text": "catalog:", + "ioredis": "catalog:", + "ip-matching": "catalog:", + "iso-639-1": "catalog:", + "jose": "catalog:", + "linkify-html": "catalog:", + "linkify-string": "catalog:", + "linkifyjs": "catalog:", + "magic-regexp": "catalog:", + "markdown-it": "catalog:", + "markdown-it-anchor": "catalog:", + "markdown-it-container": "catalog:", + "markdown-it-mathjax3": "catalog:", + "markdown-it-toc-done-right": "catalog:", + "mime-types": "catalog:", + "mitata": "catalog:", + "oauth4webapi": "catalog:", + "ora": "catalog:", + "qs": "catalog:", + "sharp": "catalog:", + "sonic-channel": "catalog:", + "string-comparison": "catalog:", + "stringify-entities": "catalog:", + "unicode-emoji-json": "catalog:", + "uqr": "catalog:", + "web-push": "catalog:", + "xss": "catalog:", + "youch": "catalog:", + "zod": "catalog:", + "zod-openapi": "catalog:", + "zod-validation-error": "catalog:" } } diff --git a/app.ts b/packages/api/app.ts similarity index 80% rename from app.ts rename to packages/api/app.ts index 7afdca5a..7d422a43 100644 --- a/app.ts +++ b/packages/api/app.ts @@ -1,6 +1,8 @@ -import { resolve } from "node:path"; -import { getLogger } from "@logtape/logtape"; +import { join } from "node:path"; import { Scalar } from "@scalar/hono-api-reference"; +import { config } from "@versia-server/config"; +import { ApiError } from "@versia-server/kit"; +import { serverLogger } from "@versia-server/logging"; import chalk from "chalk"; import { Hono } from "hono"; import { serveStatic } from "hono/bun"; @@ -8,29 +10,20 @@ import { cors } from "hono/cors"; import { createMiddleware } from "hono/factory"; import { prettyJSON } from "hono/pretty-json"; import { secureHeaders } from "hono/secure-headers"; -import { openAPISpecs } from "hono-openapi"; +import { generateSpecs } from "hono-openapi"; import { Youch } from "youch"; import { applyToHono } from "@/bull-board.ts"; -import { configureLoggers } from "@/loggers"; -import { sentry } from "@/sentry"; -import { config } from "~/config.ts"; -import pkg from "~/package.json" with { type: "application/json" }; -import { ApiError } from "./classes/errors/api-error.ts"; -import { PluginLoader } from "./classes/plugin/loader.ts"; +import pkg from "../../package.json" with { type: "application/json" }; +import type { ApiRouteExports, HonoEnv } from "../../types/api.ts"; import { agentBans } from "./middlewares/agent-bans.ts"; import { boundaryCheck } from "./middlewares/boundary-check.ts"; import { ipBans } from "./middlewares/ip-bans.ts"; import { logger } from "./middlewares/logger.ts"; import { rateLimit } from "./middlewares/rate-limit.ts"; +import { PluginLoader } from "./plugin-loader.ts"; import { routes } from "./routes.ts"; -import type { ApiRouteExports, HonoEnv } from "./types/api.ts"; -// Extends Zod with OpenAPI schema generation -import "zod-openapi/extend"; export const appFactory = async (): Promise> => { - await configureLoggers(); - const serverLogger = getLogger("server"); - const app = new Hono({ strict: false, }); @@ -118,13 +111,13 @@ export const appFactory = async (): Promise> => { const loader = new PluginLoader(); const plugins = await loader.loadPlugins( - resolve("./plugins"), + join(import.meta.dir, "plugins"), config.plugins?.autoload ?? true, config.plugins?.overrides.enabled, config.plugins?.overrides.disabled, ); - await PluginLoader.addToApp(plugins, app, serverLogger); + await PluginLoader.addToApp(plugins, app); const time2 = performance.now(); @@ -132,22 +125,23 @@ export const appFactory = async (): Promise> => { (time2 - time1).toFixed(2), )}ms`}`; - app.get( - "/openapi.json", - openAPISpecs(app, { - documentation: { - info: { - title: "Versia Server API", - version: pkg.version, - license: { - name: "AGPL-3.0", - url: "https://www.gnu.org/licenses/agpl-3.0.html", - }, - contact: pkg.author, + const openApiSpecs = await generateSpecs(app, { + documentation: { + info: { + title: "Versia Server API", + version: pkg.version, + license: { + name: "AGPL-3.0", + url: "https://www.gnu.org/licenses/agpl-3.0.html", }, + contact: pkg.author, }, - }), - ); + }, + }); + + app.get("/openapi.json", (context) => { + return context.json(openApiSpecs, 200); + }); app.get( "/docs", @@ -193,7 +187,6 @@ export const appFactory = async (): Promise> => { const youch = new Youch(); console.error(await youch.toANSI(error)); - sentry?.captureException(error); return c.json( { error: "A server error occured", diff --git a/packages/api/build.ts b/packages/api/build.ts new file mode 100644 index 00000000..57ef52cd --- /dev/null +++ b/packages/api/build.ts @@ -0,0 +1,49 @@ +import { readdir } from "node:fs/promises"; +import { $, build } from "bun"; +import manifest from "./package.json" with { type: "json" }; +import { routes } from "./routes.ts"; + +console.log("Building..."); + +await $`rm -rf dist && mkdir dist`; + +// Get all directories under the plugins/ directory +const pluginDirs = await readdir("plugins", { withFileTypes: true }); + +await build({ + entrypoints: [ + ...Object.values(manifest.exports).map((entry) => entry.import), + // Force Bun to include endpoints + ...Object.values(routes), + // Include all plugins + ...pluginDirs + .filter((dir) => dir.isDirectory()) + .map((dir) => `plugins/${dir.name}/index.ts`), + ], + outdir: "dist", + target: "bun", + splitting: true, + minify: true, + external: [ + ...Object.keys(manifest.dependencies).filter((dep) => + dep.startsWith("@versia"), + ), + "@bull-board/ui", + // Excluded because Standard Schema imports those, but the code is never executed + "@valibot/to-json-schema", + "effect", + ], +}); + +console.log("Copying files..."); + +// Copy plugin manifests +await $`cp plugins/openid/manifest.json dist/plugins/openid/manifest.json`; + +await $`mkdir -p dist/node_modules`; + +// Copy bull-board to dist +await $`mkdir -p dist/node_modules/@bull-board`; +await $`cp -rL ../../node_modules/@bull-board/ui dist/node_modules/@bull-board/ui`; + +console.log("Build complete!"); diff --git a/middlewares/agent-bans.ts b/packages/api/middlewares/agent-bans.ts similarity index 81% rename from middlewares/agent-bans.ts rename to packages/api/middlewares/agent-bans.ts index 7de10db0..ee592ab5 100644 --- a/middlewares/agent-bans.ts +++ b/packages/api/middlewares/agent-bans.ts @@ -1,6 +1,6 @@ +import { config } from "@versia-server/config"; +import { ApiError } from "@versia-server/kit"; import { createMiddleware } from "hono/factory"; -import { ApiError } from "~/classes/errors/api-error"; -import { config } from "~/config.ts"; export const agentBans = createMiddleware(async (context, next) => { // Check for banned user agents (regex) diff --git a/middlewares/boundary-check.ts b/packages/api/middlewares/boundary-check.ts similarity index 93% rename from middlewares/boundary-check.ts rename to packages/api/middlewares/boundary-check.ts index 66fcd223..dd2a13f3 100644 --- a/middlewares/boundary-check.ts +++ b/packages/api/middlewares/boundary-check.ts @@ -1,5 +1,5 @@ +import { ApiError } from "@versia-server/kit"; import { createMiddleware } from "hono/factory"; -import { ApiError } from "~/classes/errors/api-error"; export const boundaryCheck = createMiddleware(async (context, next) => { // Checks that FormData boundary is present diff --git a/middlewares/ip-bans.ts b/packages/api/middlewares/ip-bans.ts similarity index 68% rename from middlewares/ip-bans.ts rename to packages/api/middlewares/ip-bans.ts index ba170fcf..6c0c3d32 100644 --- a/middlewares/ip-bans.ts +++ b/packages/api/middlewares/ip-bans.ts @@ -1,10 +1,9 @@ -import { getLogger } from "@logtape/logtape"; +import { config } from "@versia-server/config"; +import { ApiError } from "@versia-server/kit"; +import { serverLogger } from "@versia-server/logging"; import type { SocketAddress } from "bun"; import { createMiddleware } from "hono/factory"; import { matches } from "ip-matching"; -import { sentry } from "@/sentry"; -import { ApiError } from "~/classes/errors/api-error"; -import { config } from "~/config.ts"; export const ipBans = createMiddleware(async (context, next) => { // Check for banned IPs @@ -22,11 +21,8 @@ export const ipBans = createMiddleware(async (context, next) => { throw new ApiError(403, "Forbidden"); } } catch (e) { - const logger = getLogger("server"); - - logger.error`Error while parsing banned IP "${ip}" `; - logger.error`${e}`; - sentry?.captureException(e); + serverLogger.error`Error while parsing banned IP "${ip}" `; + serverLogger.error`${e}`; return context.json( { error: `A server error occured: ${(e as Error).message}` }, diff --git a/packages/api/middlewares/logger.ts b/packages/api/middlewares/logger.ts new file mode 100644 index 00000000..4a310eea --- /dev/null +++ b/packages/api/middlewares/logger.ts @@ -0,0 +1,26 @@ +import { serverLogger } from "@versia-server/logging"; +import { SHA256 } from "bun"; +import chalk from "chalk"; +import { createMiddleware } from "hono/factory"; + +export const logger = createMiddleware(async (context, next) => { + const body = await context.req.raw.clone().text(); + + const urlAndMethod = `${chalk.green(context.req.method)} ${chalk.blue(context.req.url)}`; + + const hash = `${chalk.bold("Hash")}: ${chalk.yellow( + new SHA256().update(body).digest("hex"), + )}`; + + const headers = `${chalk.bold("Headers")}:\n${Array.from( + context.req.raw.headers.entries(), + ) + .map(([key, value]) => ` - ${chalk.cyan(key)}: ${chalk.white(value)}`) + .join("\n")}`; + + const bodyLog = `${chalk.bold("Body")}: ${chalk.gray(body)}`; + + serverLogger.debug`${urlAndMethod}\n${hash}\n${headers}\n${bodyLog}`; + + await next(); +}); diff --git a/middlewares/rate-limit.ts b/packages/api/middlewares/rate-limit.ts similarity index 93% rename from middlewares/rate-limit.ts rename to packages/api/middlewares/rate-limit.ts index bddb1d72..b8cf1208 100644 --- a/middlewares/rate-limit.ts +++ b/packages/api/middlewares/rate-limit.ts @@ -1,8 +1,8 @@ +import type { ApiError } from "@versia-server/kit"; import { env } from "bun"; import type { MiddlewareHandler } from "hono"; import { rateLimiter } from "hono-rate-limiter"; -import type { z } from "zod"; -import type { ApiError } from "~/classes/errors/api-error"; +import type { z } from "zod/v4"; import type { HonoEnv } from "~/types/api"; // Not exported by hono-rate-limiter diff --git a/middlewares/url-check.ts b/packages/api/middlewares/url-check.ts similarity index 91% rename from middlewares/url-check.ts rename to packages/api/middlewares/url-check.ts index c405b890..f4e323bc 100644 --- a/middlewares/url-check.ts +++ b/packages/api/middlewares/url-check.ts @@ -1,5 +1,5 @@ +import { config } from "@versia-server/config"; import { createMiddleware } from "hono/factory"; -import { config } from "~/config.ts"; export const urlCheck = createMiddleware(async (context, next) => { // Check that request URL matches base_url diff --git a/packages/api/package.json b/packages/api/package.json new file mode 100644 index 00000000..667217f0 --- /dev/null +++ b/packages/api/package.json @@ -0,0 +1,82 @@ +{ + "name": "@versia-server/api", + "module": "index.ts", + "type": "module", + "version": "0.9.0-alpha.0", + "description": "Powerful, configurable and modular federated server using the Versia Protocol.", + "homepage": "https://versia.pub", + "author": { + "email": "contact@cpluspatch.com", + "name": "Jesse Wierzbinski", + "url": "https://cpluspatch.com" + }, + "bugs": { + "url": "https://github.com/versia-pub/server/issues" + }, + "icon": "https://cdn.versia.pub/branding/icon.svg", + "license": "AGPL-3.0-or-later", + "keywords": [ + "federated", + "activitypub", + "bun" + ], + "maintainers": [ + { + "email": "contact@cpluspatch.com", + "name": "Jesse Wierzbinski", + "url": "https://cpluspatch.com" + } + ], + "repository": { + "type": "git", + "url": "git+https://github.com/versia-pub/server.git", + "directory": "packages/api" + }, + "private": true, + "scripts": { + "dev": "bun run --hot index.ts", + "build": "bun run build.ts", + "schema:generate": "bun run classes/config/to-json-schema.ts > config/config.schema.json && bun run packages/kit/json-schema.ts > packages/kit/manifest.schema.json", + "docs:dev": "vitepress dev docs", + "docs:build": "vitepress build docs", + "docs:preview": "vitepress preview docs" + }, + "exports": { + ".": { + "import": "./app.ts" + }, + "./setup": { + "import": "./setup.ts" + } + }, + "dependencies": { + "@versia-server/config": "workspace:*", + "@versia-server/tests": "workspace:*", + "@versia-server/kit": "workspace:*", + "@versia-server/logging": "workspace:*", + "@versia/client": "workspace:*", + "@versia/sdk": "workspace:*", + "youch": "catalog:", + "hono": "catalog:", + "hono-openapi": "catalog:", + "zod": "catalog:", + "drizzle-orm": "catalog:", + "string-comparison": "catalog:", + "bun-bagel": "catalog:", + "chalk": "catalog:", + "unicode-emoji-json": "catalog:", + "sharp": "catalog:", + "iso-639-1": "catalog:", + "jose": "catalog:", + "zod-openapi": "catalog:", + "@scalar/hono-api-reference": "catalog:", + "hono-rate-limiter": "catalog:", + "ip-matching": "catalog:", + "qs": "catalog:", + "altcha-lib": "catalog:", + "@hono/standard-validator": "catalog:", + "zod-validation-error": "catalog:", + "confbox": "catalog:", + "oauth4webapi": "catalog:" + } +} diff --git a/classes/plugin/loader.ts b/packages/api/plugin-loader.ts similarity index 79% rename from classes/plugin/loader.ts rename to packages/api/plugin-loader.ts index 7f356962..213fe771 100644 --- a/classes/plugin/loader.ts +++ b/packages/api/plugin-loader.ts @@ -1,22 +1,19 @@ import { readdir } from "node:fs/promises"; -import { getLogger, type Logger } from "@logtape/logtape"; +import { config } from "@versia-server/config"; +import { type Manifest, manifestSchema, Plugin } from "@versia-server/kit"; +import { pluginLogger, serverLogger } from "@versia-server/logging"; import { file, sleep } from "bun"; import chalk from "chalk"; import { parseJSON5, parseJSONC } from "confbox"; import type { Hono } from "hono"; -import type { ZodTypeAny } from "zod"; +import type { ZodTypeAny } from "zod/v4"; import { fromZodError, type ValidationError } from "zod-validation-error"; -import { config } from "~/config.ts"; -import { Plugin } from "~/packages/plugin-kit/plugin"; -import { type Manifest, manifestSchema } from "~/packages/plugin-kit/schema"; import type { HonoEnv } from "~/types/api"; /** * Class to manage plugins. */ export class PluginLoader { - private logger = getLogger("plugin"); - /** * Get all directories in a given directory. * @param {string} dir - The directory to search. @@ -56,7 +53,7 @@ export class PluginLoader { * @returns {Promise} - The parsed manifest content. * @throws Will throw an error if the manifest file cannot be parsed. */ - private async parseManifestFile( + private static async parseManifestFile( manifestPath: string, manifestFile: string, ): Promise { @@ -75,8 +72,7 @@ export class PluginLoader { throw new Error(`Unsupported manifest file type: ${manifestFile}`); } catch (e) { - this.logger - .fatal`Could not parse plugin manifest ${chalk.blue(manifestPath)} as ${manifestFile.split(".").pop()?.toUpperCase()}.`; + pluginLogger.fatal`Could not parse plugin manifest ${chalk.blue(manifestPath)} as ${manifestFile.split(".").pop()?.toUpperCase()}.`; throw e; } } @@ -112,7 +108,10 @@ export class PluginLoader { * @returns {Promise} - The parsed manifest object. * @throws Will throw an error if the manifest file is missing or invalid. */ - public async parseManifest(dir: string, plugin: string): Promise { + public static async parseManifest( + dir: string, + plugin: string, + ): Promise { const manifestFile = await PluginLoader.findManifestFile( `${dir}/${plugin}`, ); @@ -122,7 +121,7 @@ export class PluginLoader { } const manifestPath = `${dir}/${plugin}/${manifestFile}`; - const manifest = await this.parseManifestFile( + const manifest = await PluginLoader.parseManifestFile( manifestPath, manifestFile, ); @@ -130,8 +129,7 @@ export class PluginLoader { const result = await manifestSchema.safeParseAsync(manifest); if (!result.success) { - this.logger - .fatal`Plugin manifest ${chalk.blue(manifestPath)} is invalid.`; + pluginLogger.fatal`Plugin manifest ${chalk.blue(manifestPath)} is invalid.`; throw fromZodError(result.error); } @@ -145,7 +143,7 @@ export class PluginLoader { * @returns {Promise>} - The loaded Plugin instance. * @throws Will throw an error if the entrypoint's default export is not a Plugin. */ - public async loadPlugin( + public static async loadPlugin( dir: string, entrypoint: string, ): Promise> { @@ -155,8 +153,7 @@ export class PluginLoader { return plugin; } - this.logger - .fatal`Default export of entrypoint ${chalk.blue(entrypoint)} at ${chalk.blue(dir)} is not a Plugin.`; + pluginLogger.fatal`Default export of entrypoint ${chalk.blue(entrypoint)} at ${chalk.blue(dir)} is not a Plugin.`; throw new Error("Entrypoint is not a Plugin"); } @@ -178,14 +175,13 @@ export class PluginLoader { const disabledOn = (disabled?.length ?? 0) > 0; if (enabledOn && disabledOn) { - this.logger - .fatal`Both enabled and disabled lists are specified. Only one of them can be used.`; + pluginLogger.fatal`Both enabled and disabled lists are specified. Only one of them can be used.`; throw new Error("Invalid configuration"); } return Promise.all( plugins.map(async (plugin) => { - const manifest = await this.parseManifest(dir, plugin); + const manifest = await PluginLoader.parseManifest(dir, plugin); // If autoload is disabled, only load plugins explicitly enabled if ( @@ -204,7 +200,7 @@ export class PluginLoader { return null; } - const pluginInstance = await this.loadPlugin( + const pluginInstance = await PluginLoader.loadPlugin( dir, `${plugin}/index`, ); @@ -220,10 +216,9 @@ export class PluginLoader { plugin: Plugin; }[], app: Hono, - logger: Logger, ): Promise { for (const data of plugins) { - logger.info`Loading plugin ${chalk.blueBright(data.manifest.name)} ${chalk.blueBright(data.manifest.version)} ${chalk.gray(`[${plugins.indexOf(data) + 1}/${plugins.length}]`)}`; + serverLogger.info`Loading plugin ${chalk.blueBright(data.manifest.name)} ${chalk.blueBright(data.manifest.version)} ${chalk.gray(`[${plugins.indexOf(data) + 1}/${plugins.length}]`)}`; const time1 = performance.now(); @@ -233,13 +228,13 @@ export class PluginLoader { config.plugins?.config?.[data.manifest.name], ); } catch (e) { - logger.fatal`Error encountered while loading plugin ${chalk.blueBright(data.manifest.name)} ${chalk.blueBright(data.manifest.version)} configuration.`; - logger.fatal`This is due to invalid, missing or incomplete configuration.`; - logger.fatal`Put your configuration at ${chalk.blueBright( + serverLogger.fatal`Error encountered while loading plugin ${chalk.blueBright(data.manifest.name)} ${chalk.blueBright(data.manifest.version)} configuration.`; + serverLogger.fatal`This is due to invalid, missing or incomplete configuration.`; + serverLogger.fatal`Put your configuration at ${chalk.blueBright( "plugins.config.", )}`; - logger.fatal`Here is the error message, please fix the configuration file accordingly:`; - logger.fatal`${(e as ValidationError).message}`; + serverLogger.fatal`Here is the error message, please fix the configuration file accordingly:`; + serverLogger.fatal`${(e as ValidationError).message}`; await sleep(Number.POSITIVE_INFINITY); } @@ -251,7 +246,7 @@ export class PluginLoader { const time3 = performance.now(); - logger.info`Plugin ${chalk.blueBright(data.manifest.name)} ${chalk.blueBright( + serverLogger.info`Plugin ${chalk.blueBright(data.manifest.name)} ${chalk.blueBright( data.manifest.version, )} loaded in ${chalk.gray( `${(time2 - time1).toFixed(2)}ms`, diff --git a/plugins/openid/errors.ts b/packages/api/plugins/openid/errors.ts similarity index 100% rename from plugins/openid/errors.ts rename to packages/api/plugins/openid/errors.ts diff --git a/plugins/openid/index.ts b/packages/api/plugins/openid/index.ts similarity index 92% rename from plugins/openid/index.ts rename to packages/api/plugins/openid/index.ts index 5bfaf555..b7daed32 100644 --- a/plugins/openid/index.ts +++ b/packages/api/plugins/openid/index.ts @@ -1,12 +1,11 @@ import { RolePermission } from "@versia/client/schemas"; -import { Hooks, Plugin } from "@versia/kit"; -import { User } from "@versia/kit/db"; +import { keyPair, sensitiveString, url } from "@versia-server/config"; +import { ApiError, Hooks, Plugin } from "@versia-server/kit"; +import { User } from "@versia-server/kit/db"; import { getCookie } from "hono/cookie"; import { jwtVerify } from "jose"; import { JOSEError, JWTExpired } from "jose/errors"; -import { z } from "zod"; -import { keyPair, sensitiveString, url } from "~/classes/config/schema.ts"; -import { ApiError } from "~/classes/errors/api-error.ts"; +import { z } from "zod/v4"; import authorizeRoute from "./routes/authorize.ts"; import jwksRoute from "./routes/jwks.ts"; import ssoLoginCallbackRoute from "./routes/oauth/callback.ts"; diff --git a/plugins/openid/manifest.json b/packages/api/plugins/openid/manifest.json similarity index 87% rename from plugins/openid/manifest.json rename to packages/api/plugins/openid/manifest.json index e6ac5dff..f6219620 100644 --- a/plugins/openid/manifest.json +++ b/packages/api/plugins/openid/manifest.json @@ -1,5 +1,5 @@ { - "$schema": "https://raw.githubusercontent.com/versia-pub/server/refs/heads/main/packages/plugin-kit/manifest.schema.json", + "$schema": "https://raw.githubusercontent.com/versia-pub/server/refs/heads/main/packages/kit/manifest.schema.json", "name": "@versia/openid", "description": "OpenID authentication.", "version": "0.1.0", diff --git a/plugins/openid/routes/authorize.test.ts b/packages/api/plugins/openid/routes/authorize.test.ts similarity index 98% rename from plugins/openid/routes/authorize.test.ts rename to packages/api/plugins/openid/routes/authorize.test.ts index 22a62ccd..943ee586 100644 --- a/plugins/openid/routes/authorize.test.ts +++ b/packages/api/plugins/openid/routes/authorize.test.ts @@ -1,11 +1,11 @@ import { afterAll, describe, expect, test } from "bun:test"; import { RolePermission } from "@versia/client/schemas"; -import { Application } from "@versia/kit/db"; +import { config } from "@versia-server/config"; +import { Application } from "@versia-server/kit/db"; +import { fakeRequest, getTestUsers } from "@versia-server/tests"; import { randomUUIDv7 } from "bun"; import { SignJWT } from "jose"; import { randomString } from "@/math"; -import { config } from "~/config.ts"; -import { fakeRequest, getTestUsers } from "~/tests/utils"; const { deleteUsers, tokens, users } = await getTestUsers(1); const privateKey = await crypto.subtle.importKey( diff --git a/plugins/openid/routes/authorize.ts b/packages/api/plugins/openid/routes/authorize.ts similarity index 96% rename from plugins/openid/routes/authorize.ts rename to packages/api/plugins/openid/routes/authorize.ts index f75f5999..bb18310b 100644 --- a/plugins/openid/routes/authorize.ts +++ b/packages/api/plugins/openid/routes/authorize.ts @@ -1,12 +1,11 @@ import { RolePermission } from "@versia/client/schemas"; -import { Application, Token, User } from "@versia/kit/db"; +import { auth, handleZodError, jsonOrForm } from "@versia-server/kit/api"; +import { Application, Token, User } from "@versia-server/kit/db"; import { randomUUIDv7 } from "bun"; -import { describeRoute } from "hono-openapi"; -import { validator } from "hono-openapi/zod"; +import { describeRoute, validator } from "hono-openapi"; import { type JWTPayload, jwtVerify, SignJWT } from "jose"; import { JOSEError } from "jose/errors"; -import { z } from "zod"; -import { auth, handleZodError, jsonOrForm } from "@/api"; +import { z } from "zod/v4"; import { randomString } from "@/math"; import { errorRedirect, errors } from "../errors.ts"; import type { PluginType } from "../index.ts"; @@ -50,7 +49,6 @@ export default (plugin: PluginType): void => .object({ scope: z.string().optional(), redirect_uri: z - .string() .url() .optional() .or(z.literal("urn:ietf:wg:oauth:2.0:oob")), @@ -141,7 +139,7 @@ export default (plugin: PluginType): void => ); } - if (!z.string().uuid().safeParse(sub).success) { + if (!z.uuid().safeParse(sub).success) { return errorRedirect( context, errors.InvalidSub, diff --git a/plugins/openid/routes/jwks.test.ts b/packages/api/plugins/openid/routes/jwks.test.ts similarity index 90% rename from plugins/openid/routes/jwks.test.ts rename to packages/api/plugins/openid/routes/jwks.test.ts index 876769c1..2d97acf5 100644 --- a/plugins/openid/routes/jwks.test.ts +++ b/packages/api/plugins/openid/routes/jwks.test.ts @@ -1,7 +1,7 @@ import { afterAll, describe, expect, test } from "bun:test"; -import { Application } from "@versia/kit/db"; +import { Application } from "@versia-server/kit/db"; +import { fakeRequest } from "@versia-server/tests"; import { randomUUIDv7 } from "bun"; -import { fakeRequest } from "~/tests/utils"; const application = await Application.insert({ id: randomUUIDv7(), diff --git a/plugins/openid/routes/jwks.ts b/packages/api/plugins/openid/routes/jwks.ts similarity index 94% rename from plugins/openid/routes/jwks.ts rename to packages/api/plugins/openid/routes/jwks.ts index 02751413..31896563 100644 --- a/plugins/openid/routes/jwks.ts +++ b/packages/api/plugins/openid/routes/jwks.ts @@ -1,8 +1,7 @@ -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; +import { auth } from "@versia-server/kit/api"; +import { describeRoute, resolver } from "hono-openapi"; import { exportJWK } from "jose"; -import { z } from "zod"; -import { auth } from "@/api"; +import { z } from "zod/v4"; import type { PluginType } from "../index.ts"; export default (plugin: PluginType): void => { diff --git a/plugins/openid/routes/oauth/callback.ts b/packages/api/plugins/openid/routes/oauth/callback.ts similarity index 95% rename from plugins/openid/routes/oauth/callback.ts rename to packages/api/plugins/openid/routes/oauth/callback.ts index a73bd9d8..60db45e7 100644 --- a/plugins/openid/routes/oauth/callback.ts +++ b/packages/api/plugins/openid/routes/oauth/callback.ts @@ -1,19 +1,20 @@ import { Account as AccountSchema, RolePermission, + zBoolean, } from "@versia/client/schemas"; -import { db, Media, Token, User } from "@versia/kit/db"; -import { and, eq, isNull, type SQL } from "@versia/kit/drizzle"; -import { OpenIdAccounts, Users } from "@versia/kit/tables"; +import { ApiError } from "@versia-server/kit"; +import { handleZodError } from "@versia-server/kit/api"; +import { db, Media, Token, User } from "@versia-server/kit/db"; +import { searchManager } from "@versia-server/kit/search"; +import { OpenIdAccounts, Users } from "@versia-server/kit/tables"; import { randomUUIDv7 } from "bun"; +import { and, eq, isNull, type SQL } from "drizzle-orm"; import { setCookie } from "hono/cookie"; -import { describeRoute } from "hono-openapi"; -import { validator } from "hono-openapi/zod"; +import { describeRoute, validator } from "hono-openapi"; import { SignJWT } from "jose"; -import { z } from "zod"; -import { handleZodError } from "@/api"; +import { z } from "zod/v4"; import { randomString } from "@/math.ts"; -import { ApiError } from "~/classes/errors/api-error.ts"; import type { PluginType } from "../../index.ts"; import { automaticOidcFlow } from "../../utils.ts"; @@ -46,13 +47,8 @@ export default (plugin: PluginType): void => { z.object({ client_id: z.string().optional(), flow: z.string(), - link: z - .string() - .transform((v) => - ["true", "1", "on"].includes(v.toLowerCase()), - ) - .optional(), - user_id: z.string().uuid().optional(), + link: zBoolean.optional(), + user_id: z.uuid().optional(), }), handleZodError, ), @@ -242,6 +238,9 @@ export default (plugin: PluginType): void => { avatar: avatar ?? undefined, }); + // Add to search index + await searchManager.addUser(user); + // Link account await db.insert(OpenIdAccounts).values({ id: randomUUIDv7(), diff --git a/plugins/openid/routes/oauth/revoke.test.ts b/packages/api/plugins/openid/routes/oauth/revoke.test.ts similarity index 97% rename from plugins/openid/routes/oauth/revoke.test.ts rename to packages/api/plugins/openid/routes/oauth/revoke.test.ts index 14a74d45..4d469b94 100644 --- a/plugins/openid/routes/oauth/revoke.test.ts +++ b/packages/api/plugins/openid/routes/oauth/revoke.test.ts @@ -1,7 +1,7 @@ import { afterAll, describe, expect, test } from "bun:test"; -import { Application, Token } from "@versia/kit/db"; +import { Application, Token } from "@versia-server/kit/db"; +import { fakeRequest, getTestUsers } from "@versia-server/tests"; import { randomUUIDv7 } from "bun"; -import { fakeRequest, getTestUsers } from "~/tests/utils"; const { deleteUsers, users } = await getTestUsers(1); diff --git a/plugins/openid/routes/oauth/revoke.ts b/packages/api/plugins/openid/routes/oauth/revoke.ts similarity index 90% rename from plugins/openid/routes/oauth/revoke.ts rename to packages/api/plugins/openid/routes/oauth/revoke.ts index d9480e7c..958cd2b5 100644 --- a/plugins/openid/routes/oauth/revoke.ts +++ b/packages/api/plugins/openid/routes/oauth/revoke.ts @@ -1,10 +1,9 @@ -import { db, Token } from "@versia/kit/db"; -import { and, eq } from "@versia/kit/drizzle"; -import { Tokens } from "@versia/kit/tables"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { handleZodError, jsonOrForm } from "@/api"; +import { handleZodError, jsonOrForm } from "@versia-server/kit/api"; +import { db, Token } from "@versia-server/kit/db"; +import { Tokens } from "@versia-server/kit/tables"; +import { and, eq } from "drizzle-orm"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; import type { PluginType } from "../../index.ts"; export default (plugin: PluginType): void => { diff --git a/plugins/openid/routes/oauth/sso.ts b/packages/api/plugins/openid/routes/oauth/sso.ts similarity index 93% rename from plugins/openid/routes/oauth/sso.ts rename to packages/api/plugins/openid/routes/oauth/sso.ts index 9918998f..73f7ea35 100644 --- a/plugins/openid/routes/oauth/sso.ts +++ b/packages/api/plugins/openid/routes/oauth/sso.ts @@ -1,16 +1,15 @@ -import { Application, db } from "@versia/kit/db"; -import { OpenIdLoginFlows } from "@versia/kit/tables"; +import { handleZodError } from "@versia-server/kit/api"; +import { Application, db } from "@versia-server/kit/db"; +import { OpenIdLoginFlows } from "@versia-server/kit/tables"; import { randomUUIDv7 } from "bun"; -import { describeRoute } from "hono-openapi"; -import { validator } from "hono-openapi/zod"; +import { describeRoute, validator } from "hono-openapi"; import { calculatePKCECodeChallenge, discoveryRequest, generateRandomCodeVerifier, processDiscoveryResponse, } from "oauth4webapi"; -import { z } from "zod"; -import { handleZodError } from "@/api.ts"; +import { z } from "zod/v4"; import type { PluginType } from "../../index.ts"; import { oauthRedirectUri } from "../../utils.ts"; @@ -34,7 +33,7 @@ export default (plugin: PluginType): void => { z.object({ issuer: z.string(), client_id: z.string().optional(), - redirect_uri: z.string().url().optional(), + redirect_uri: z.url().optional(), scope: z.string().optional(), response_type: z.enum(["code"]).optional(), }), diff --git a/plugins/openid/routes/oauth/token.test.ts b/packages/api/plugins/openid/routes/oauth/token.test.ts similarity index 98% rename from plugins/openid/routes/oauth/token.test.ts rename to packages/api/plugins/openid/routes/oauth/token.test.ts index f30d5ab6..cb53156e 100644 --- a/plugins/openid/routes/oauth/token.test.ts +++ b/packages/api/plugins/openid/routes/oauth/token.test.ts @@ -1,7 +1,7 @@ import { afterAll, describe, expect, test } from "bun:test"; -import { Application, Token } from "@versia/kit/db"; +import { Application, Token } from "@versia-server/kit/db"; +import { fakeRequest, getTestUsers } from "@versia-server/tests"; import { randomUUIDv7 } from "bun"; -import { fakeRequest, getTestUsers } from "~/tests/utils"; const { deleteUsers, users } = await getTestUsers(1); diff --git a/plugins/openid/routes/oauth/token.ts b/packages/api/plugins/openid/routes/oauth/token.ts similarity index 95% rename from plugins/openid/routes/oauth/token.ts rename to packages/api/plugins/openid/routes/oauth/token.ts index 636832ee..9fe2ebf6 100644 --- a/plugins/openid/routes/oauth/token.ts +++ b/packages/api/plugins/openid/routes/oauth/token.ts @@ -1,10 +1,9 @@ -import { Application, Token } from "@versia/kit/db"; -import { and, eq } from "@versia/kit/drizzle"; -import { Tokens } from "@versia/kit/tables"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { handleZodError, jsonOrForm } from "@/api"; +import { handleZodError, jsonOrForm } from "@versia-server/kit/api"; +import { Application, Token } from "@versia-server/kit/db"; +import { Tokens } from "@versia-server/kit/tables"; +import { and, eq } from "drizzle-orm"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; import type { PluginType } from "../../index.ts"; export default (plugin: PluginType): void => { @@ -80,7 +79,7 @@ export default (plugin: PluginType): void => { client_secret: z.string().optional(), username: z.string().trim().optional(), password: z.string().trim().optional(), - redirect_uri: z.string().url().optional(), + redirect_uri: z.url().optional(), refresh_token: z.string().optional(), scope: z.string().optional(), assertion: z.string().optional(), diff --git a/plugins/openid/routes/sso/:id/index.test.ts b/packages/api/plugins/openid/routes/sso/:id/index.test.ts similarity index 94% rename from plugins/openid/routes/sso/:id/index.test.ts rename to packages/api/plugins/openid/routes/sso/:id/index.test.ts index 0bf270d3..2e2ccf5e 100644 --- a/plugins/openid/routes/sso/:id/index.test.ts +++ b/packages/api/plugins/openid/routes/sso/:id/index.test.ts @@ -1,5 +1,5 @@ import { afterAll, describe, expect, test } from "bun:test"; -import { fakeRequest, getTestUsers } from "~/tests/utils"; +import { fakeRequest, getTestUsers } from "@versia-server/tests"; const { deleteUsers, tokens } = await getTestUsers(1); diff --git a/plugins/openid/routes/sso/:id/index.ts b/packages/api/plugins/openid/routes/sso/:id/index.ts similarity index 92% rename from plugins/openid/routes/sso/:id/index.ts rename to packages/api/plugins/openid/routes/sso/:id/index.ts index 85c152fe..7669882a 100644 --- a/plugins/openid/routes/sso/:id/index.ts +++ b/packages/api/plugins/openid/routes/sso/:id/index.ts @@ -1,13 +1,12 @@ import { RolePermission } from "@versia/client/schemas"; -import { db } from "@versia/kit/db"; -import { and, eq, type SQL } from "@versia/kit/drizzle"; -import { OpenIdAccounts } from "@versia/kit/tables"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { auth, handleZodError } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; -import type { PluginType } from "~/plugins/openid"; +import { ApiError } from "@versia-server/kit"; +import { auth, handleZodError } from "@versia-server/kit/api"; +import { db } from "@versia-server/kit/db"; +import { OpenIdAccounts } from "@versia-server/kit/tables"; +import { and, eq, type SQL } from "drizzle-orm"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; +import type { PluginType } from "../../../index.ts"; export default (plugin: PluginType): void => { plugin.registerRoute("/api/v1/sso/:id", (app) => { diff --git a/plugins/openid/routes/sso/index.test.ts b/packages/api/plugins/openid/routes/sso/index.test.ts similarity index 95% rename from plugins/openid/routes/sso/index.test.ts rename to packages/api/plugins/openid/routes/sso/index.test.ts index 56e717e2..4f35bcd0 100644 --- a/plugins/openid/routes/sso/index.test.ts +++ b/packages/api/plugins/openid/routes/sso/index.test.ts @@ -1,5 +1,5 @@ import { afterAll, describe, expect, test } from "bun:test"; -import { fakeRequest, getTestUsers } from "~/tests/utils"; +import { fakeRequest, getTestUsers } from "@versia-server/tests"; const { deleteUsers, tokens } = await getTestUsers(1); diff --git a/plugins/openid/routes/sso/index.ts b/packages/api/plugins/openid/routes/sso/index.ts similarity index 94% rename from plugins/openid/routes/sso/index.ts rename to packages/api/plugins/openid/routes/sso/index.ts index 00d3942c..b2746db7 100644 --- a/plugins/openid/routes/sso/index.ts +++ b/packages/api/plugins/openid/routes/sso/index.ts @@ -1,16 +1,15 @@ import { RolePermission } from "@versia/client/schemas"; -import { Application, db } from "@versia/kit/db"; -import { OpenIdLoginFlows } from "@versia/kit/tables"; +import { ApiError } from "@versia-server/kit"; +import { auth, handleZodError } from "@versia-server/kit/api"; +import { Application, db } from "@versia-server/kit/db"; +import { OpenIdLoginFlows } from "@versia-server/kit/tables"; import { randomUUIDv7 } from "bun"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; +import { describeRoute, resolver, validator } from "hono-openapi"; import { calculatePKCECodeChallenge, generateRandomCodeVerifier, } from "oauth4webapi"; -import { z } from "zod"; -import { auth, handleZodError } from "@/api"; -import { ApiError } from "~/classes/errors/api-error.ts"; +import { z } from "zod/v4"; import type { PluginType } from "../../index.ts"; import { oauthDiscoveryRequest, oauthRedirectUri } from "../../utils.ts"; diff --git a/plugins/openid/utils.ts b/packages/api/plugins/openid/utils.ts similarity index 95% rename from plugins/openid/utils.ts rename to packages/api/plugins/openid/utils.ts index b924088d..4f7afdc9 100644 --- a/plugins/openid/utils.ts +++ b/packages/api/plugins/openid/utils.ts @@ -1,6 +1,6 @@ -import { type Application, db } from "@versia/kit/db"; -import { eq, type InferSelectModel, type SQL } from "@versia/kit/drizzle"; -import type { OpenIdLoginFlows } from "@versia/kit/tables"; +import { type Application, db } from "@versia-server/kit/db"; +import type { OpenIdLoginFlows } from "@versia-server/kit/tables"; +import { eq, type InferSelectModel, type SQL } from "drizzle-orm"; import { type AuthorizationResponseError, type AuthorizationServer, @@ -165,7 +165,7 @@ export const automaticOidcFlow = async ( const authServer = await getAuthServer(issuerUrl); - const parameters = await getParameters( + const parameters = getParameters( authServer, issuer.client_id, currentUrl, diff --git a/routes.ts b/packages/api/routes.ts similarity index 84% rename from routes.ts rename to packages/api/routes.ts index aa0a7f3a..ccc79e7b 100644 --- a/routes.ts +++ b/packages/api/routes.ts @@ -1,8 +1,10 @@ +import { join } from "node:path"; import { FileSystemRouter } from "bun"; + // Returns the route filesystem path when given a URL export const routeMatcher = new FileSystemRouter({ style: "nextjs", - dir: "api", + dir: join(import.meta.dir, "routes"), fileExtensions: [".ts", ".js"], }); diff --git a/api/api/auth/login/index.test.ts b/packages/api/routes/api/auth/login/index.test.ts similarity index 98% rename from api/api/auth/login/index.test.ts rename to packages/api/routes/api/auth/login/index.test.ts index 69c591bf..1fe79f5d 100644 --- a/api/api/auth/login/index.test.ts +++ b/packages/api/routes/api/auth/login/index.test.ts @@ -1,9 +1,9 @@ import { afterAll, describe, expect, test } from "bun:test"; -import { Application } from "@versia/kit/db"; +import { config } from "@versia-server/config"; +import { Application } from "@versia-server/kit/db"; +import { fakeRequest, getTestUsers } from "@versia-server/tests"; import { randomUUIDv7 } from "bun"; import { randomString } from "@/math"; -import { config } from "~/config.ts"; -import { fakeRequest, getTestUsers } from "~/tests/utils"; const { users, deleteUsers, passwords } = await getTestUsers(1); diff --git a/api/api/auth/login/index.ts b/packages/api/routes/api/auth/login/index.ts similarity index 94% rename from api/api/auth/login/index.ts rename to packages/api/routes/api/auth/login/index.ts index 3913547a..dd32644e 100644 --- a/api/api/auth/login/index.ts +++ b/packages/api/routes/api/auth/login/index.ts @@ -1,16 +1,15 @@ -import { Application, User } from "@versia/kit/db"; -import { Users } from "@versia/kit/tables"; +import { config } from "@versia-server/config"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, handleZodError } from "@versia-server/kit/api"; +import { Application, User } from "@versia-server/kit/db"; +import { Users } from "@versia-server/kit/tables"; import { password as bunPassword } from "bun"; import { eq, or } from "drizzle-orm"; import type { Context } from "hono"; import { setCookie } from "hono/cookie"; -import { describeRoute } from "hono-openapi"; -import { validator } from "hono-openapi/zod"; +import { describeRoute, validator } from "hono-openapi"; import { SignJWT } from "jose"; -import { z } from "zod"; -import { apiRoute, handleZodError } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; -import { config } from "~/config.ts"; +import { z } from "zod/v4"; const returnError = ( context: Context, @@ -59,7 +58,7 @@ export default apiRoute((app) => "query", z.object({ scope: z.string().optional(), - redirect_uri: z.string().url().optional(), + redirect_uri: z.url().optional(), response_type: z.enum([ "code", "token", @@ -90,7 +89,6 @@ export default apiRoute((app) => "form", z.object({ identifier: z - .string() .email() .toLowerCase() .or(z.string().toLowerCase()), diff --git a/api/api/auth/redirect/index.ts b/packages/api/routes/api/auth/redirect/index.ts similarity index 85% rename from api/api/auth/redirect/index.ts rename to packages/api/routes/api/auth/redirect/index.ts index 7dc95a6f..5e43431d 100644 --- a/api/api/auth/redirect/index.ts +++ b/packages/api/routes/api/auth/redirect/index.ts @@ -1,11 +1,10 @@ -import { db } from "@versia/kit/db"; -import { Applications, Tokens } from "@versia/kit/tables"; +import { config } from "@versia-server/config"; +import { apiRoute, handleZodError } from "@versia-server/kit/api"; +import { db } from "@versia-server/kit/db"; +import { Applications, Tokens } from "@versia-server/kit/tables"; import { and, eq } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, handleZodError } from "@/api"; -import { config } from "~/config.ts"; +import { describeRoute, validator } from "hono-openapi"; +import { z } from "zod/v4"; /** * OAuth Code flow @@ -28,7 +27,7 @@ export default apiRoute((app) => validator( "query", z.object({ - redirect_uri: z.string().url(), + redirect_uri: z.url(), client_id: z.string(), code: z.string(), }), diff --git a/api/api/auth/reset/index.test.ts b/packages/api/routes/api/auth/reset/index.test.ts similarity index 96% rename from api/api/auth/reset/index.test.ts rename to packages/api/routes/api/auth/reset/index.test.ts index d61eeae0..2bb3ed5d 100644 --- a/api/api/auth/reset/index.test.ts +++ b/packages/api/routes/api/auth/reset/index.test.ts @@ -1,9 +1,9 @@ import { afterAll, describe, expect, test } from "bun:test"; -import { Application } from "@versia/kit/db"; +import { config } from "@versia-server/config"; +import { Application } from "@versia-server/kit/db"; +import { fakeRequest, getTestUsers } from "@versia-server/tests"; import { randomUUIDv7 } from "bun"; import { randomString } from "@/math"; -import { config } from "~/config.ts"; -import { fakeRequest, getTestUsers } from "~/tests/utils"; const { users, deleteUsers, passwords } = await getTestUsers(1); const token = randomString(32, "hex"); diff --git a/api/api/auth/reset/index.ts b/packages/api/routes/api/auth/reset/index.ts similarity index 87% rename from api/api/auth/reset/index.ts rename to packages/api/routes/api/auth/reset/index.ts index 86dc15dd..f45a12ab 100644 --- a/api/api/auth/reset/index.ts +++ b/packages/api/routes/api/auth/reset/index.ts @@ -1,13 +1,12 @@ -import { User } from "@versia/kit/db"; -import { Users } from "@versia/kit/tables"; +import { config } from "@versia-server/config"; +import { apiRoute, handleZodError } from "@versia-server/kit/api"; +import { User } from "@versia-server/kit/db"; +import { Users } from "@versia-server/kit/tables"; import { password as bunPassword } from "bun"; import { eq } from "drizzle-orm"; import type { Context } from "hono"; -import { describeRoute } from "hono-openapi"; -import { validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, handleZodError } from "@/api"; -import { config } from "~/config.ts"; +import { describeRoute, validator } from "hono-openapi"; +import { z } from "zod/v4"; const returnError = ( context: Context, diff --git a/api/api/v1/accounts/[id]/block.test.ts b/packages/api/routes/api/v1/accounts/[id]/block.test.ts similarity index 95% rename from api/api/v1/accounts/[id]/block.test.ts rename to packages/api/routes/api/v1/accounts/[id]/block.test.ts index bf4a16c1..38cedf46 100644 --- a/api/api/v1/accounts/[id]/block.test.ts +++ b/packages/api/routes/api/v1/accounts/[id]/block.test.ts @@ -1,5 +1,5 @@ import { afterAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestUsers } from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(2); diff --git a/api/api/v1/accounts/[id]/block.ts b/packages/api/routes/api/v1/accounts/[id]/block.ts similarity index 88% rename from api/api/v1/accounts/[id]/block.ts rename to packages/api/routes/api/v1/accounts/[id]/block.ts index 2b1c22e0..d9e98a05 100644 --- a/api/api/v1/accounts/[id]/block.ts +++ b/packages/api/routes/api/v1/accounts/[id]/block.ts @@ -2,11 +2,10 @@ import { Relationship as RelationshipSchema, RolePermission, } from "@versia/client/schemas"; -import { Relationship } from "@versia/kit/db"; -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { apiRoute, auth, withUserParam } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, withUserParam } from "@versia-server/kit/api"; +import { Relationship } from "@versia-server/kit/db"; +import { describeRoute, resolver } from "hono-openapi"; export default apiRoute((app) => app.post( diff --git a/api/api/v1/accounts/[id]/feed.atom.ts b/packages/api/routes/api/v1/accounts/[id]/feed.atom.ts similarity index 84% rename from api/api/v1/accounts/[id]/feed.atom.ts rename to packages/api/routes/api/v1/accounts/[id]/feed.atom.ts index 55eb3868..53bcb81d 100644 --- a/api/api/v1/accounts/[id]/feed.atom.ts +++ b/packages/api/routes/api/v1/accounts/[id]/feed.atom.ts @@ -1,10 +1,14 @@ import { RolePermission } from "@versia/client/schemas"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError, withUserParam } from "@/api"; +import { ApiError } from "@versia-server/kit"; +import { + apiRoute, + auth, + handleZodError, + withUserParam, +} from "@versia-server/kit/api"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; import { getFeed } from "@/rss"; -import { ApiError } from "~/classes/errors/api-error"; export default apiRoute((app) => app.get( @@ -34,12 +38,13 @@ export default apiRoute((app) => RolePermission.ViewNotes, RolePermission.ViewAccounts, ], + scopes: ["read:statuses"], }), validator( "query", z.object({ - page: z.coerce.number().default(0).openapi({ + page: z.coerce.number().default(0).meta({ description: "Page number to fetch. Defaults to 0.", example: 2, }), diff --git a/api/api/v1/accounts/[id]/feed.rss.ts b/packages/api/routes/api/v1/accounts/[id]/feed.rss.ts similarity index 84% rename from api/api/v1/accounts/[id]/feed.rss.ts rename to packages/api/routes/api/v1/accounts/[id]/feed.rss.ts index 5e9bc314..296389e5 100644 --- a/api/api/v1/accounts/[id]/feed.rss.ts +++ b/packages/api/routes/api/v1/accounts/[id]/feed.rss.ts @@ -1,10 +1,14 @@ import { RolePermission } from "@versia/client/schemas"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError, withUserParam } from "@/api"; +import { ApiError } from "@versia-server/kit"; +import { + apiRoute, + auth, + handleZodError, + withUserParam, +} from "@versia-server/kit/api"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; import { getFeed } from "@/rss"; -import { ApiError } from "~/classes/errors/api-error"; export default apiRoute((app) => app.get( @@ -33,12 +37,13 @@ export default apiRoute((app) => RolePermission.ViewNotes, RolePermission.ViewAccounts, ], + scopes: ["read:statuses"], }), validator( "query", z.object({ - page: z.coerce.number().default(0).openapi({ + page: z.coerce.number().default(0).meta({ description: "Page number to fetch. Defaults to 0.", example: 2, }), diff --git a/api/api/v1/accounts/[id]/follow.test.ts b/packages/api/routes/api/v1/accounts/[id]/follow.test.ts similarity index 95% rename from api/api/v1/accounts/[id]/follow.test.ts rename to packages/api/routes/api/v1/accounts/[id]/follow.test.ts index 6331816e..4289139e 100644 --- a/api/api/v1/accounts/[id]/follow.test.ts +++ b/packages/api/routes/api/v1/accounts/[id]/follow.test.ts @@ -1,5 +1,5 @@ import { afterAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestUsers } from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(3); diff --git a/api/api/v1/accounts/[id]/follow.ts b/packages/api/routes/api/v1/accounts/[id]/follow.ts similarity index 87% rename from api/api/v1/accounts/[id]/follow.ts rename to packages/api/routes/api/v1/accounts/[id]/follow.ts index ae2c7da0..086cd894 100644 --- a/api/api/v1/accounts/[id]/follow.ts +++ b/packages/api/routes/api/v1/accounts/[id]/follow.ts @@ -3,12 +3,16 @@ import { Relationship as RelationshipSchema, RolePermission, } from "@versia/client/schemas"; -import { Relationship } from "@versia/kit/db"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError, withUserParam } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { ApiError } from "@versia-server/kit"; +import { + apiRoute, + auth, + handleZodError, + withUserParam, +} from "@versia-server/kit/api"; +import { Relationship } from "@versia-server/kit/db"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.post( @@ -57,12 +61,12 @@ export default apiRoute((app) => validator( "json", z.object({ - reblogs: z.boolean().default(true).openapi({ + reblogs: z.boolean().default(true).meta({ description: "Receive this account’s reblogs in home timeline?", example: true, }), - notify: z.boolean().default(false).openapi({ + notify: z.boolean().default(false).meta({ description: "Receive notifications when this account posts a status?", example: false, @@ -70,7 +74,7 @@ export default apiRoute((app) => languages: z .array(iso631) .default([]) - .openapi({ + .meta({ description: "Array of String (ISO 639-1 language two-letter code). Filter received statuses for these languages. If not provided, you will receive this account’s posts in all languages.", example: ["en", "fr"], diff --git a/api/api/v1/accounts/[id]/followers.test.ts b/packages/api/routes/api/v1/accounts/[id]/followers.test.ts similarity index 96% rename from api/api/v1/accounts/[id]/followers.test.ts rename to packages/api/routes/api/v1/accounts/[id]/followers.test.ts index ec8f4c6c..cd5d93fd 100644 --- a/api/api/v1/accounts/[id]/followers.test.ts +++ b/packages/api/routes/api/v1/accounts/[id]/followers.test.ts @@ -1,5 +1,5 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestUsers } from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(5); diff --git a/api/api/v1/accounts/[id]/followers.ts b/packages/api/routes/api/v1/accounts/[id]/followers.ts similarity index 88% rename from api/api/v1/accounts/[id]/followers.ts rename to packages/api/routes/api/v1/accounts/[id]/followers.ts index 225d5238..dc132a05 100644 --- a/api/api/v1/accounts/[id]/followers.ts +++ b/packages/api/routes/api/v1/accounts/[id]/followers.ts @@ -2,14 +2,18 @@ import { Account as AccountSchema, RolePermission, } from "@versia/client/schemas"; -import { Timeline } from "@versia/kit/db"; -import { Users } from "@versia/kit/tables"; +import { ApiError } from "@versia-server/kit"; +import { + apiRoute, + auth, + handleZodError, + withUserParam, +} from "@versia-server/kit/api"; +import { Timeline } from "@versia-server/kit/db"; +import { Users } from "@versia-server/kit/tables"; import { and, gt, gte, lt, sql } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError, withUserParam } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.get( @@ -34,7 +38,7 @@ export default apiRoute((app) => link: z .string() .optional() - .openapi({ + .meta({ description: "Links to the next and previous pages", example: @@ -61,22 +65,22 @@ export default apiRoute((app) => validator( "query", z.object({ - max_id: AccountSchema.shape.id.optional().openapi({ + max_id: AccountSchema.shape.id.optional().meta({ description: "All results returned will be lesser than this ID. In effect, sets an upper bound on results.", example: "8d35243d-b959-43e2-8bac-1a9d4eaea2aa", }), - since_id: AccountSchema.shape.id.optional().openapi({ + since_id: AccountSchema.shape.id.optional().meta({ description: "All results returned will be greater than this ID. In effect, sets a lower bound on results.", example: undefined, }), - min_id: AccountSchema.shape.id.optional().openapi({ + min_id: AccountSchema.shape.id.optional().meta({ description: "Returns results immediately newer than this ID. In effect, sets a cursor at this ID and paginates forward.", example: undefined, }), - limit: z.number().int().min(1).max(40).default(20).openapi({ + limit: z.number().int().min(1).max(40).default(20).meta({ description: "Maximum number of results to return.", }), }), diff --git a/api/api/v1/accounts/[id]/following.test.ts b/packages/api/routes/api/v1/accounts/[id]/following.test.ts similarity index 96% rename from api/api/v1/accounts/[id]/following.test.ts rename to packages/api/routes/api/v1/accounts/[id]/following.test.ts index 7d3a0134..6a78418a 100644 --- a/api/api/v1/accounts/[id]/following.test.ts +++ b/packages/api/routes/api/v1/accounts/[id]/following.test.ts @@ -1,5 +1,5 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestUsers } from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(5); diff --git a/api/api/v1/accounts/[id]/following.ts b/packages/api/routes/api/v1/accounts/[id]/following.ts similarity index 88% rename from api/api/v1/accounts/[id]/following.ts rename to packages/api/routes/api/v1/accounts/[id]/following.ts index c39cbafe..6fa5d021 100644 --- a/api/api/v1/accounts/[id]/following.ts +++ b/packages/api/routes/api/v1/accounts/[id]/following.ts @@ -2,14 +2,18 @@ import { Account as AccountSchema, RolePermission, } from "@versia/client/schemas"; -import { Timeline } from "@versia/kit/db"; -import { Users } from "@versia/kit/tables"; +import { ApiError } from "@versia-server/kit"; +import { + apiRoute, + auth, + handleZodError, + withUserParam, +} from "@versia-server/kit/api"; +import { Timeline } from "@versia-server/kit/db"; +import { Users } from "@versia-server/kit/tables"; import { and, gt, gte, lt, sql } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError, withUserParam } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.get( @@ -35,7 +39,7 @@ export default apiRoute((app) => link: z .string() .optional() - .openapi({ + .meta({ description: "Links to the next and previous pages", example: @@ -62,22 +66,22 @@ export default apiRoute((app) => validator( "query", z.object({ - max_id: AccountSchema.shape.id.optional().openapi({ + max_id: AccountSchema.shape.id.optional().meta({ description: "All results returned will be lesser than this ID. In effect, sets an upper bound on results.", example: "8d35243d-b959-43e2-8bac-1a9d4eaea2aa", }), - since_id: AccountSchema.shape.id.optional().openapi({ + since_id: AccountSchema.shape.id.optional().meta({ description: "All results returned will be greater than this ID. In effect, sets a lower bound on results.", example: undefined, }), - min_id: AccountSchema.shape.id.optional().openapi({ + min_id: AccountSchema.shape.id.optional().meta({ description: "Returns results immediately newer than this ID. In effect, sets a cursor at this ID and paginates forward.", example: undefined, }), - limit: z.number().int().min(1).max(40).default(20).openapi({ + limit: z.number().int().min(1).max(40).default(20).meta({ description: "Maximum number of results to return.", }), }), diff --git a/api/api/v1/accounts/[id]/index.test.ts b/packages/api/routes/api/v1/accounts/[id]/index.test.ts similarity index 95% rename from api/api/v1/accounts/[id]/index.test.ts rename to packages/api/routes/api/v1/accounts/[id]/index.test.ts index d0aff8ef..b4539209 100644 --- a/api/api/v1/accounts/[id]/index.test.ts +++ b/packages/api/routes/api/v1/accounts/[id]/index.test.ts @@ -1,5 +1,9 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestStatuses, getTestUsers } from "~/tests/utils"; +import { + generateClient, + getTestStatuses, + getTestUsers, +} from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(5); const timeline = (await getTestStatuses(5, users[0])).toReversed(); diff --git a/api/api/v1/accounts/[id]/index.ts b/packages/api/routes/api/v1/accounts/[id]/index.ts similarity index 87% rename from api/api/v1/accounts/[id]/index.ts rename to packages/api/routes/api/v1/accounts/[id]/index.ts index 3eca1fb7..771ebd4c 100644 --- a/api/api/v1/accounts/[id]/index.ts +++ b/packages/api/routes/api/v1/accounts/[id]/index.ts @@ -2,10 +2,9 @@ import { Account as AccountSchema, RolePermission, } from "@versia/client/schemas"; -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { apiRoute, auth, withUserParam } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, withUserParam } from "@versia-server/kit/api"; +import { describeRoute, resolver } from "hono-openapi"; export default apiRoute((app) => app.get( diff --git a/api/api/v1/accounts/[id]/mute.test.ts b/packages/api/routes/api/v1/accounts/[id]/mute.test.ts similarity index 96% rename from api/api/v1/accounts/[id]/mute.test.ts rename to packages/api/routes/api/v1/accounts/[id]/mute.test.ts index 92816afe..88fb0889 100644 --- a/api/api/v1/accounts/[id]/mute.test.ts +++ b/packages/api/routes/api/v1/accounts/[id]/mute.test.ts @@ -1,5 +1,5 @@ import { afterAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestUsers } from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(2); diff --git a/api/api/v1/accounts/[id]/mute.ts b/packages/api/routes/api/v1/accounts/[id]/mute.ts similarity index 87% rename from api/api/v1/accounts/[id]/mute.ts rename to packages/api/routes/api/v1/accounts/[id]/mute.ts index ab2680ca..90993ab3 100644 --- a/api/api/v1/accounts/[id]/mute.ts +++ b/packages/api/routes/api/v1/accounts/[id]/mute.ts @@ -2,16 +2,20 @@ import { Relationship as RelationshipSchema, RolePermission, } from "@versia/client/schemas"; -import { Relationship } from "@versia/kit/db"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError, withUserParam } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { ApiError } from "@versia-server/kit"; +import { + apiRoute, + auth, + handleZodError, + withUserParam, +} from "@versia-server/kit/api"; +import { Relationship } from "@versia-server/kit/db"; import { RelationshipJobType, relationshipQueue, -} from "~/classes/queues/relationships"; +} from "@versia-server/kit/queues/relationships"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.post( @@ -51,7 +55,7 @@ export default apiRoute((app) => validator( "json", z.object({ - notifications: z.boolean().default(true).openapi({ + notifications: z.boolean().default(true).meta({ description: "Mute notifications in addition to statuses?", }), duration: z @@ -60,7 +64,7 @@ export default apiRoute((app) => .min(0) .max(60 * 60 * 24 * 365 * 5) .default(0) - .openapi({ + .meta({ description: "How long the mute should last, in seconds. 0 means indefinite.", }), diff --git a/api/api/v1/accounts/[id]/note.test.ts b/packages/api/routes/api/v1/accounts/[id]/note.test.ts similarity index 96% rename from api/api/v1/accounts/[id]/note.test.ts rename to packages/api/routes/api/v1/accounts/[id]/note.test.ts index 83f26c21..70603f17 100644 --- a/api/api/v1/accounts/[id]/note.test.ts +++ b/packages/api/routes/api/v1/accounts/[id]/note.test.ts @@ -1,5 +1,5 @@ import { afterAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestUsers } from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(2); diff --git a/api/api/v1/accounts/[id]/note.ts b/packages/api/routes/api/v1/accounts/[id]/note.ts similarity index 87% rename from api/api/v1/accounts/[id]/note.ts rename to packages/api/routes/api/v1/accounts/[id]/note.ts index 04f44bcd..5c835eb3 100644 --- a/api/api/v1/accounts/[id]/note.ts +++ b/packages/api/routes/api/v1/accounts/[id]/note.ts @@ -2,12 +2,16 @@ import { Relationship as RelationshipSchema, RolePermission, } from "@versia/client/schemas"; -import { Relationship } from "@versia/kit/db"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError, withUserParam } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { ApiError } from "@versia-server/kit"; +import { + apiRoute, + auth, + handleZodError, + withUserParam, +} from "@versia-server/kit/api"; +import { Relationship } from "@versia-server/kit/db"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.post( @@ -45,7 +49,7 @@ export default apiRoute((app) => validator( "json", z.object({ - comment: RelationshipSchema.shape.note.optional().openapi({ + comment: RelationshipSchema.shape.note.optional().meta({ description: "The comment to be set on that user. Provide an empty string or leave out this parameter to clear the currently set note.", }), diff --git a/api/api/v1/accounts/[id]/pin.test.ts b/packages/api/routes/api/v1/accounts/[id]/pin.test.ts similarity index 95% rename from api/api/v1/accounts/[id]/pin.test.ts rename to packages/api/routes/api/v1/accounts/[id]/pin.test.ts index 1cb446bd..a8867c49 100644 --- a/api/api/v1/accounts/[id]/pin.test.ts +++ b/packages/api/routes/api/v1/accounts/[id]/pin.test.ts @@ -1,5 +1,5 @@ import { afterAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestUsers } from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(2); diff --git a/api/api/v1/accounts/[id]/pin.ts b/packages/api/routes/api/v1/accounts/[id]/pin.ts similarity index 89% rename from api/api/v1/accounts/[id]/pin.ts rename to packages/api/routes/api/v1/accounts/[id]/pin.ts index 649e66b2..48870a2c 100644 --- a/api/api/v1/accounts/[id]/pin.ts +++ b/packages/api/routes/api/v1/accounts/[id]/pin.ts @@ -2,10 +2,9 @@ import { Relationship as RelationshipSchema, RolePermission, } from "@versia/client/schemas"; -import { Relationship } from "@versia/kit/db"; -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { apiRoute, auth, withUserParam } from "@/api"; +import { apiRoute, auth, withUserParam } from "@versia-server/kit/api"; +import { Relationship } from "@versia-server/kit/db"; +import { describeRoute, resolver } from "hono-openapi"; export default apiRoute((app) => app.post( diff --git a/api/api/v1/accounts/[id]/refetch.ts b/packages/api/routes/api/v1/accounts/[id]/refetch.ts similarity index 87% rename from api/api/v1/accounts/[id]/refetch.ts rename to packages/api/routes/api/v1/accounts/[id]/refetch.ts index 825380ac..c817269d 100644 --- a/api/api/v1/accounts/[id]/refetch.ts +++ b/packages/api/routes/api/v1/accounts/[id]/refetch.ts @@ -2,11 +2,10 @@ import { Account as AccountSchema, RolePermission, } from "@versia/client/schemas"; -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { apiRoute, auth, withUserParam } from "@/api"; -import { User } from "~/classes/database/user"; -import { ApiError } from "~/classes/errors/api-error"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, withUserParam } from "@versia-server/kit/api"; +import { User } from "@versia-server/kit/db"; +import { describeRoute, resolver } from "hono-openapi"; export default apiRoute((app) => app.post( diff --git a/api/api/v1/accounts/[id]/remove_from_followers.test.ts b/packages/api/routes/api/v1/accounts/[id]/remove_from_followers.test.ts similarity index 96% rename from api/api/v1/accounts/[id]/remove_from_followers.test.ts rename to packages/api/routes/api/v1/accounts/[id]/remove_from_followers.test.ts index 434efc4e..c8563bee 100644 --- a/api/api/v1/accounts/[id]/remove_from_followers.test.ts +++ b/packages/api/routes/api/v1/accounts/[id]/remove_from_followers.test.ts @@ -1,5 +1,5 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestUsers } from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(2); diff --git a/api/api/v1/accounts/[id]/remove_from_followers.ts b/packages/api/routes/api/v1/accounts/[id]/remove_from_followers.ts similarity index 89% rename from api/api/v1/accounts/[id]/remove_from_followers.ts rename to packages/api/routes/api/v1/accounts/[id]/remove_from_followers.ts index f76adc3c..205836d4 100644 --- a/api/api/v1/accounts/[id]/remove_from_followers.ts +++ b/packages/api/routes/api/v1/accounts/[id]/remove_from_followers.ts @@ -2,11 +2,10 @@ import { Relationship as RelationshipSchema, RolePermission, } from "@versia/client/schemas"; -import { Relationship } from "@versia/kit/db"; -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { apiRoute, auth, withUserParam } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, withUserParam } from "@versia-server/kit/api"; +import { Relationship } from "@versia-server/kit/db"; +import { describeRoute, resolver } from "hono-openapi"; export default apiRoute((app) => app.post( diff --git a/api/api/v1/accounts/[id]/roles/[role_id]/index.test.ts b/packages/api/routes/api/v1/accounts/[id]/roles/[role_id]/index.test.ts similarity index 97% rename from api/api/v1/accounts/[id]/roles/[role_id]/index.test.ts rename to packages/api/routes/api/v1/accounts/[id]/roles/[role_id]/index.test.ts index c1ed1401..866b427a 100644 --- a/api/api/v1/accounts/[id]/roles/[role_id]/index.test.ts +++ b/packages/api/routes/api/v1/accounts/[id]/roles/[role_id]/index.test.ts @@ -1,8 +1,8 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import { RolePermission } from "@versia/client/schemas"; -import { Role } from "@versia/kit/db"; +import { Role } from "@versia-server/kit/db"; +import { generateClient, getTestUsers } from "@versia-server/tests"; import { randomUUIDv7 } from "bun"; -import { generateClient, getTestUsers } from "~/tests/utils"; const { users, deleteUsers } = await getTestUsers(2); let role: Role; diff --git a/api/api/v1/accounts/[id]/roles/[role_id]/index.ts b/packages/api/routes/api/v1/accounts/[id]/roles/[role_id]/index.ts similarity index 92% rename from api/api/v1/accounts/[id]/roles/[role_id]/index.ts rename to packages/api/routes/api/v1/accounts/[id]/roles/[role_id]/index.ts index d7cdad92..18d7a98b 100644 --- a/api/api/v1/accounts/[id]/roles/[role_id]/index.ts +++ b/packages/api/routes/api/v1/accounts/[id]/roles/[role_id]/index.ts @@ -3,12 +3,16 @@ import { RolePermission, Role as RoleSchema, } from "@versia/client/schemas"; -import { Role } from "@versia/kit/db"; -import { describeRoute } from "hono-openapi"; -import { validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError, withUserParam } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { ApiError } from "@versia-server/kit"; +import { + apiRoute, + auth, + handleZodError, + withUserParam, +} from "@versia-server/kit/api"; +import { Role } from "@versia-server/kit/db"; +import { describeRoute, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => { app.post( diff --git a/api/api/v1/accounts/[id]/roles/index.test.ts b/packages/api/routes/api/v1/accounts/[id]/roles/index.test.ts similarity index 93% rename from api/api/v1/accounts/[id]/roles/index.test.ts rename to packages/api/routes/api/v1/accounts/[id]/roles/index.test.ts index b85cbf98..d6a80a49 100644 --- a/api/api/v1/accounts/[id]/roles/index.test.ts +++ b/packages/api/routes/api/v1/accounts/[id]/roles/index.test.ts @@ -1,8 +1,8 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import { RolePermission } from "@versia/client/schemas"; -import { Role } from "@versia/kit/db"; +import { Role } from "@versia-server/kit/db"; +import { generateClient, getTestUsers } from "@versia-server/tests"; import { randomUUIDv7 } from "bun"; -import { generateClient, getTestUsers } from "~/tests/utils"; const { users, deleteUsers } = await getTestUsers(2); let role: Role; diff --git a/api/api/v1/accounts/[id]/roles/index.ts b/packages/api/routes/api/v1/accounts/[id]/roles/index.ts similarity index 82% rename from api/api/v1/accounts/[id]/roles/index.ts rename to packages/api/routes/api/v1/accounts/[id]/roles/index.ts index 3bd8b18a..eaf28716 100644 --- a/api/api/v1/accounts/[id]/roles/index.ts +++ b/packages/api/routes/api/v1/accounts/[id]/roles/index.ts @@ -1,9 +1,8 @@ import { Role as RoleSchema } from "@versia/client/schemas"; -import { Role } from "@versia/kit/db"; -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, withUserParam } from "@/api"; +import { apiRoute, auth, withUserParam } from "@versia-server/kit/api"; +import { Role } from "@versia-server/kit/db"; +import { describeRoute, resolver } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => { app.get( diff --git a/api/api/v1/accounts/[id]/statuses.test.ts b/packages/api/routes/api/v1/accounts/[id]/statuses.test.ts similarity index 98% rename from api/api/v1/accounts/[id]/statuses.test.ts rename to packages/api/routes/api/v1/accounts/[id]/statuses.test.ts index 012e133a..06675be4 100644 --- a/api/api/v1/accounts/[id]/statuses.test.ts +++ b/packages/api/routes/api/v1/accounts/[id]/statuses.test.ts @@ -3,7 +3,7 @@ import { generateClient, getTestStatuses, getTestUsers, -} from "~/tests/utils.ts"; +} from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(5); const timeline = (await getTestStatuses(5, users[1])).toReversed(); diff --git a/api/api/v1/accounts/[id]/statuses.ts b/packages/api/routes/api/v1/accounts/[id]/statuses.ts similarity index 81% rename from api/api/v1/accounts/[id]/statuses.ts rename to packages/api/routes/api/v1/accounts/[id]/statuses.ts index fbce144f..25f97ffd 100644 --- a/api/api/v1/accounts/[id]/statuses.ts +++ b/packages/api/routes/api/v1/accounts/[id]/statuses.ts @@ -3,14 +3,18 @@ import { Status as StatusSchema, zBoolean, } from "@versia/client/schemas"; -import { Timeline } from "@versia/kit/db"; -import { Notes } from "@versia/kit/tables"; +import { ApiError } from "@versia-server/kit"; +import { + apiRoute, + auth, + handleZodError, + withUserParam, +} from "@versia-server/kit/api"; +import { Timeline } from "@versia-server/kit/db"; +import { Notes } from "@versia-server/kit/tables"; import { and, eq, gt, gte, inArray, isNull, lt, or, sql } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError, withUserParam } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.get( @@ -42,50 +46,45 @@ export default apiRoute((app) => RolePermission.ViewNotes, RolePermission.ViewAccounts, ], + scopes: ["read:statuses"], }), validator( "query", z.object({ - max_id: StatusSchema.shape.id.optional().openapi({ + max_id: StatusSchema.shape.id.optional().meta({ description: "All results returned will be lesser than this ID. In effect, sets an upper bound on results.", example: "8d35243d-b959-43e2-8bac-1a9d4eaea2aa", }), - since_id: StatusSchema.shape.id.optional().openapi({ + since_id: StatusSchema.shape.id.optional().meta({ description: "All results returned will be greater than this ID. In effect, sets a lower bound on results.", example: undefined, }), - min_id: StatusSchema.shape.id.optional().openapi({ + min_id: StatusSchema.shape.id.optional().meta({ description: "Returns results immediately newer than this ID. In effect, sets a cursor at this ID and paginates forward.", example: undefined, }), - limit: z.coerce - .number() - .int() - .min(1) - .max(40) - .default(20) - .openapi({ - description: "Maximum number of results to return.", - }), - only_media: zBoolean.default(false).openapi({ + limit: z.coerce.number().int().min(1).max(40).default(20).meta({ + description: "Maximum number of results to return.", + }), + only_media: zBoolean.default(false).meta({ description: "Filter out statuses without attachments.", }), - exclude_replies: zBoolean.default(false).openapi({ + exclude_replies: zBoolean.default(false).meta({ description: "Filter out statuses in reply to a different account.", }), - exclude_reblogs: zBoolean.default(false).openapi({ + exclude_reblogs: zBoolean.default(false).meta({ description: "Filter out boosts from the response.", }), - pinned: zBoolean.default(false).openapi({ + pinned: zBoolean.default(false).meta({ description: "Filter for pinned statuses only. Pinned statuses do not receive special priority in the order of the returned results.", }), - tagged: z.string().optional().openapi({ + tagged: z.string().optional().meta({ description: "Filter for statuses using a specific hashtag.", }), diff --git a/api/api/v1/accounts/[id]/unblock.test.ts b/packages/api/routes/api/v1/accounts/[id]/unblock.test.ts similarity index 95% rename from api/api/v1/accounts/[id]/unblock.test.ts rename to packages/api/routes/api/v1/accounts/[id]/unblock.test.ts index 8515bfb9..952cdc51 100644 --- a/api/api/v1/accounts/[id]/unblock.test.ts +++ b/packages/api/routes/api/v1/accounts/[id]/unblock.test.ts @@ -1,5 +1,5 @@ import { afterAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestUsers } from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(2); diff --git a/api/api/v1/accounts/[id]/unblock.ts b/packages/api/routes/api/v1/accounts/[id]/unblock.ts similarity index 88% rename from api/api/v1/accounts/[id]/unblock.ts rename to packages/api/routes/api/v1/accounts/[id]/unblock.ts index 3a3215bb..9e2b0ef3 100644 --- a/api/api/v1/accounts/[id]/unblock.ts +++ b/packages/api/routes/api/v1/accounts/[id]/unblock.ts @@ -2,11 +2,10 @@ import { Relationship as RelationshipSchema, RolePermission, } from "@versia/client/schemas"; -import { Relationship } from "@versia/kit/db"; -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { apiRoute, auth, withUserParam } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, withUserParam } from "@versia-server/kit/api"; +import { Relationship } from "@versia-server/kit/db"; +import { describeRoute, resolver } from "hono-openapi"; export default apiRoute((app) => app.post( diff --git a/api/api/v1/accounts/[id]/unfollow.test.ts b/packages/api/routes/api/v1/accounts/[id]/unfollow.test.ts similarity index 96% rename from api/api/v1/accounts/[id]/unfollow.test.ts rename to packages/api/routes/api/v1/accounts/[id]/unfollow.test.ts index 754c3fa1..8b8e08db 100644 --- a/api/api/v1/accounts/[id]/unfollow.test.ts +++ b/packages/api/routes/api/v1/accounts/[id]/unfollow.test.ts @@ -1,5 +1,5 @@ import { afterAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestUsers } from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(3); diff --git a/api/api/v1/accounts/[id]/unfollow.ts b/packages/api/routes/api/v1/accounts/[id]/unfollow.ts similarity index 87% rename from api/api/v1/accounts/[id]/unfollow.ts rename to packages/api/routes/api/v1/accounts/[id]/unfollow.ts index 8791adec..493dbe3b 100644 --- a/api/api/v1/accounts/[id]/unfollow.ts +++ b/packages/api/routes/api/v1/accounts/[id]/unfollow.ts @@ -2,11 +2,10 @@ import { Relationship as RelationshipSchema, RolePermission, } from "@versia/client/schemas"; -import { Relationship } from "@versia/kit/db"; -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { apiRoute, auth, withUserParam } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, withUserParam } from "@versia-server/kit/api"; +import { Relationship } from "@versia-server/kit/db"; +import { describeRoute, resolver } from "hono-openapi"; export default apiRoute((app) => app.post( diff --git a/api/api/v1/accounts/[id]/unmute.test.ts b/packages/api/routes/api/v1/accounts/[id]/unmute.test.ts similarity index 95% rename from api/api/v1/accounts/[id]/unmute.test.ts rename to packages/api/routes/api/v1/accounts/[id]/unmute.test.ts index f4e5ce95..f70f2606 100644 --- a/api/api/v1/accounts/[id]/unmute.test.ts +++ b/packages/api/routes/api/v1/accounts/[id]/unmute.test.ts @@ -1,5 +1,5 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestUsers } from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(2); diff --git a/api/api/v1/accounts/[id]/unmute.ts b/packages/api/routes/api/v1/accounts/[id]/unmute.ts similarity index 88% rename from api/api/v1/accounts/[id]/unmute.ts rename to packages/api/routes/api/v1/accounts/[id]/unmute.ts index 3abc7b9a..03c0fc3c 100644 --- a/api/api/v1/accounts/[id]/unmute.ts +++ b/packages/api/routes/api/v1/accounts/[id]/unmute.ts @@ -2,11 +2,10 @@ import { Relationship as RelationshipSchema, RolePermission, } from "@versia/client/schemas"; -import { Relationship } from "@versia/kit/db"; -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { apiRoute, auth, withUserParam } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, withUserParam } from "@versia-server/kit/api"; +import { Relationship } from "@versia-server/kit/db"; +import { describeRoute, resolver } from "hono-openapi"; export default apiRoute((app) => app.post( diff --git a/api/api/v1/accounts/[id]/unpin.test.ts b/packages/api/routes/api/v1/accounts/[id]/unpin.test.ts similarity index 95% rename from api/api/v1/accounts/[id]/unpin.test.ts rename to packages/api/routes/api/v1/accounts/[id]/unpin.test.ts index eddcfd12..53a8963a 100644 --- a/api/api/v1/accounts/[id]/unpin.test.ts +++ b/packages/api/routes/api/v1/accounts/[id]/unpin.test.ts @@ -1,5 +1,5 @@ import { afterAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestUsers } from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(2); diff --git a/api/api/v1/accounts/[id]/unpin.ts b/packages/api/routes/api/v1/accounts/[id]/unpin.ts similarity index 88% rename from api/api/v1/accounts/[id]/unpin.ts rename to packages/api/routes/api/v1/accounts/[id]/unpin.ts index 91af9ba3..c7676b57 100644 --- a/api/api/v1/accounts/[id]/unpin.ts +++ b/packages/api/routes/api/v1/accounts/[id]/unpin.ts @@ -2,11 +2,10 @@ import { Relationship as RelationshipSchema, RolePermission, } from "@versia/client/schemas"; -import { Relationship } from "@versia/kit/db"; -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { apiRoute, auth, withUserParam } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, withUserParam } from "@versia-server/kit/api"; +import { Relationship } from "@versia-server/kit/db"; +import { describeRoute, resolver } from "hono-openapi"; export default apiRoute((app) => app.post( diff --git a/api/api/v1/accounts/familiar_followers/index.test.ts b/packages/api/routes/api/v1/accounts/familiar_followers/index.test.ts similarity index 97% rename from api/api/v1/accounts/familiar_followers/index.test.ts rename to packages/api/routes/api/v1/accounts/familiar_followers/index.test.ts index 5ee5d2f1..f3a887ad 100644 --- a/api/api/v1/accounts/familiar_followers/index.test.ts +++ b/packages/api/routes/api/v1/accounts/familiar_followers/index.test.ts @@ -1,5 +1,5 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestUsers } from "~/tests/utils.ts"; +import { generateClient, getTestUsers } from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(5); diff --git a/api/api/v1/accounts/familiar_followers/index.ts b/packages/api/routes/api/v1/accounts/familiar_followers/index.ts similarity index 84% rename from api/api/v1/accounts/familiar_followers/index.ts rename to packages/api/routes/api/v1/accounts/familiar_followers/index.ts index 77147b22..7d002ae6 100644 --- a/api/api/v1/accounts/familiar_followers/index.ts +++ b/packages/api/routes/api/v1/accounts/familiar_followers/index.ts @@ -3,15 +3,19 @@ import { FamiliarFollowers as FamiliarFollowersSchema, RolePermission, } from "@versia/client/schemas"; -import { db, User } from "@versia/kit/db"; -import type { Users } from "@versia/kit/tables"; +import { ApiError } from "@versia-server/kit"; +import { + apiRoute, + auth, + handleZodError, + qsQuery, +} from "@versia-server/kit/api"; +import { db, User } from "@versia-server/kit/db"; +import type { Users } from "@versia-server/kit/tables"; import { type InferSelectModel, sql } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError, qsQuery } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; -import { rateLimit } from "~/middlewares/rate-limit"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; +import { rateLimit } from "../../../../../middlewares/rate-limit.ts"; export default apiRoute((app) => app.get( @@ -51,8 +55,8 @@ export default apiRoute((app) => .array(AccountSchema.shape.id) .min(1) .max(10) - .or(AccountSchema.shape.id.transform((v) => [v])) - .openapi({ + .or(AccountSchema.shape.id) + .meta({ description: "Find familiar followers for the provided account IDs.", example: [ @@ -65,11 +69,11 @@ export default apiRoute((app) => ), async (context) => { const { user } = context.get("auth"); - const { id: ids } = context.req.valid("query"); + const { id } = context.req.valid("query"); // Find followers of the accounts in "ids", that you also follow const finalUsers = await Promise.all( - ids.map(async (id) => ({ + (Array.isArray(id) ? id : [id]).map(async (id) => ({ id, accounts: await User.fromIds( ( diff --git a/api/api/v1/accounts/index.get.test.ts b/packages/api/routes/api/v1/accounts/index.get.test.ts similarity index 95% rename from api/api/v1/accounts/index.get.test.ts rename to packages/api/routes/api/v1/accounts/index.get.test.ts index 3b82790a..4eb97a4e 100644 --- a/api/api/v1/accounts/index.get.test.ts +++ b/packages/api/routes/api/v1/accounts/index.get.test.ts @@ -1,5 +1,5 @@ import { afterAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestUsers } from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(5); diff --git a/api/api/v1/accounts/index.post.test.ts b/packages/api/routes/api/v1/accounts/index.post.test.ts similarity index 97% rename from api/api/v1/accounts/index.post.test.ts rename to packages/api/routes/api/v1/accounts/index.post.test.ts index 1938ca12..db412fc3 100644 --- a/api/api/v1/accounts/index.post.test.ts +++ b/packages/api/routes/api/v1/accounts/index.post.test.ts @@ -1,9 +1,9 @@ import { afterEach, describe, expect, test } from "bun:test"; -import { db } from "@versia/kit/db"; -import { Users } from "@versia/kit/tables"; +import { db } from "@versia-server/kit/db"; +import { Users } from "@versia-server/kit/tables"; +import { generateClient, getSolvedChallenge } from "@versia-server/tests"; import { eq } from "drizzle-orm"; import { randomString } from "@/math"; -import { generateClient, getSolvedChallenge } from "~/tests/utils"; const username = randomString(10, "hex"); const username2 = randomString(10, "hex"); diff --git a/api/api/v1/accounts/index.ts b/packages/api/routes/api/v1/accounts/index.ts similarity index 93% rename from api/api/v1/accounts/index.ts rename to packages/api/routes/api/v1/accounts/index.ts index 6db5f327..d16d8793 100644 --- a/api/api/v1/accounts/index.ts +++ b/packages/api/routes/api/v1/accounts/index.ts @@ -1,42 +1,48 @@ import { Account as AccountSchema, zBoolean } from "@versia/client/schemas"; -import { User } from "@versia/kit/db"; -import { Users } from "@versia/kit/tables"; +import { config } from "@versia-server/config"; +import { ApiError } from "@versia-server/kit"; +import { + apiRoute, + auth, + handleZodError, + jsonOrForm, + qsQuery, +} from "@versia-server/kit/api"; +import { User } from "@versia-server/kit/db"; +import { searchManager } from "@versia-server/kit/search"; +import { Users } from "@versia-server/kit/tables"; import { and, eq, isNull } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; +import { describeRoute, resolver, validator } from "hono-openapi"; import ISO6391 from "iso-639-1"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError, jsonOrForm, qsQuery } from "@/api"; +import { z } from "zod/v4"; import { tempmailDomains } from "@/tempmail"; -import { ApiError } from "~/classes/errors/api-error"; -import { config } from "~/config.ts"; -import { rateLimit } from "~/middlewares/rate-limit"; +import { rateLimit } from "../../../../middlewares/rate-limit.ts"; const schema = z.object({ - username: z.string().openapi({ + username: z.string().meta({ description: "The desired username for the account", example: "alice", }), - email: z.string().toLowerCase().openapi({ + email: z.string().toLowerCase().meta({ description: "The email address to be used for login. Transformed to lowercase.", example: "alice@gmail.com", }), - password: z.string().openapi({ + password: z.string().meta({ description: "The password to be used for login", example: "hunter2", }), - agreement: zBoolean.openapi({ + agreement: zBoolean.meta({ description: "Whether the user agrees to the local rules, terms, and policies. These should be presented to the user in order to allow them to consent before setting this parameter to TRUE.", example: true, }), - locale: z.string().openapi({ + locale: z.string().meta({ description: "The language of the confirmation email that will be sent. ISO 639-1 code.", example: "en", }), - reason: z.string().optional().openapi({ + reason: z.string().optional().meta({ description: "If registrations require manual approval, this text will be reviewed by moderators.", }), @@ -79,8 +85,8 @@ export default apiRoute((app) => { .array(AccountSchema.shape.id) .min(1) .max(40) - .or(AccountSchema.shape.id.transform((v) => [v])) - .openapi({ + .or(AccountSchema.shape.id) + .meta({ description: "The IDs of the Accounts in the database.", example: [ "f137ce6f-ff5e-4998-b20f-0361ba9be007", @@ -91,10 +97,10 @@ export default apiRoute((app) => { handleZodError, ), async (context) => { - const { id: ids } = context.req.valid("query"); + const { id } = context.req.valid("query"); // Find accounts by IDs - const accounts = await User.fromIds(ids); + const accounts = await User.fromIds(Array.isArray(id) ? id : [id]); return context.json( accounts.map((account) => account.toApi()), @@ -413,11 +419,14 @@ export default apiRoute((app) => { ); } - await User.register(username, { + const user = await User.register(username, { password, email, }); + // Add to search index + await searchManager.addUser(user); + return context.text("", 200); }, ); diff --git a/api/api/v1/accounts/lookup/index.test.ts b/packages/api/routes/api/v1/accounts/lookup/index.test.ts similarity index 94% rename from api/api/v1/accounts/lookup/index.test.ts rename to packages/api/routes/api/v1/accounts/lookup/index.test.ts index ce3a69a6..7745c34c 100644 --- a/api/api/v1/accounts/lookup/index.test.ts +++ b/packages/api/routes/api/v1/accounts/lookup/index.test.ts @@ -1,5 +1,5 @@ import { afterAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestUsers } from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(5); diff --git a/api/api/v1/accounts/lookup/index.ts b/packages/api/routes/api/v1/accounts/lookup/index.ts similarity index 85% rename from api/api/v1/accounts/lookup/index.ts rename to packages/api/routes/api/v1/accounts/lookup/index.ts index 7869002b..3cdb5d39 100644 --- a/api/api/v1/accounts/lookup/index.ts +++ b/packages/api/routes/api/v1/accounts/lookup/index.ts @@ -2,16 +2,16 @@ import { Account as AccountSchema, RolePermission, } from "@versia/client/schemas"; -import { Instance, User } from "@versia/kit/db"; -import { Users } from "@versia/kit/tables"; +import { config } from "@versia-server/config"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, handleZodError } from "@versia-server/kit/api"; +import { Instance, User } from "@versia-server/kit/db"; +import { parseUserAddress } from "@versia-server/kit/parsers"; +import { Users } from "@versia-server/kit/tables"; import { and, eq, isNull } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError, parseUserAddress } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; -import { config } from "~/config.ts"; -import { rateLimit } from "~/middlewares/rate-limit"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; +import { rateLimit } from "../../../../../middlewares/rate-limit.ts"; export default apiRoute((app) => app.get( @@ -42,7 +42,7 @@ export default apiRoute((app) => validator( "query", z.object({ - acct: AccountSchema.shape.acct.openapi({ + acct: AccountSchema.shape.acct.meta({ description: "The username or Webfinger address to lookup.", example: "lexi@beta.versia.social", }), diff --git a/api/api/v1/accounts/relationships/index.test.ts b/packages/api/routes/api/v1/accounts/relationships/index.test.ts similarity index 94% rename from api/api/v1/accounts/relationships/index.test.ts rename to packages/api/routes/api/v1/accounts/relationships/index.test.ts index 5de24560..3a6d8999 100644 --- a/api/api/v1/accounts/relationships/index.test.ts +++ b/packages/api/routes/api/v1/accounts/relationships/index.test.ts @@ -1,8 +1,8 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; -import { db } from "@versia/kit/db"; -import { Users } from "@versia/kit/tables"; +import { db } from "@versia-server/kit/db"; +import { Users } from "@versia-server/kit/tables"; +import { generateClient, getTestUsers } from "@versia-server/tests"; import { eq } from "drizzle-orm"; -import { generateClient, getTestUsers } from "~/tests/utils"; const { users, deleteUsers } = await getTestUsers(5); diff --git a/api/api/v1/accounts/relationships/index.ts b/packages/api/routes/api/v1/accounts/relationships/index.ts similarity index 77% rename from api/api/v1/accounts/relationships/index.ts rename to packages/api/routes/api/v1/accounts/relationships/index.ts index 7704361b..7abb3c9b 100644 --- a/api/api/v1/accounts/relationships/index.ts +++ b/packages/api/routes/api/v1/accounts/relationships/index.ts @@ -4,13 +4,17 @@ import { RolePermission, zBoolean, } from "@versia/client/schemas"; -import { Relationship } from "@versia/kit/db"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError, qsQuery } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; -import { rateLimit } from "~/middlewares/rate-limit"; +import { ApiError } from "@versia-server/kit"; +import { + apiRoute, + auth, + handleZodError, + qsQuery, +} from "@versia-server/kit/api"; +import { Relationship } from "@versia-server/kit/db"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; +import { rateLimit } from "../../../../../middlewares/rate-limit.ts"; export default apiRoute((app) => app.get( @@ -50,8 +54,8 @@ export default apiRoute((app) => .array(AccountSchema.shape.id) .min(1) .max(10) - .or(AccountSchema.shape.id.transform((v) => [v])) - .openapi({ + .or(AccountSchema.shape.id) + .meta({ description: "Check relationships for the provided account IDs.", example: [ @@ -59,7 +63,7 @@ export default apiRoute((app) => "8424c654-5d03-4a1b-bec8-4e87db811b5d", ], }), - with_suspended: zBoolean.default(false).openapi({ + with_suspended: zBoolean.default(false).meta({ description: "Whether relationships should be returned for suspended users", example: false, @@ -71,17 +75,16 @@ export default apiRoute((app) => const { user } = context.get("auth"); // TODO: Implement with_suspended - const { id: ids } = context.req.valid("query"); + const { id } = context.req.valid("query"); const relationships = await Relationship.fromOwnerAndSubjects( user, - ids, + Array.isArray(id) ? id : [id], ); relationships.sort( (a, b) => - ids.indexOf(a.data.subjectId) - - ids.indexOf(b.data.subjectId), + id.indexOf(a.data.subjectId) - id.indexOf(b.data.subjectId), ); return context.json( diff --git a/api/api/v1/accounts/search/index.test.ts b/packages/api/routes/api/v1/accounts/search/index.test.ts similarity index 92% rename from api/api/v1/accounts/search/index.test.ts rename to packages/api/routes/api/v1/accounts/search/index.test.ts index 6aeb39b6..ee07269a 100644 --- a/api/api/v1/accounts/search/index.test.ts +++ b/packages/api/routes/api/v1/accounts/search/index.test.ts @@ -1,5 +1,5 @@ import { afterAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestUsers } from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(5); diff --git a/api/api/v1/accounts/search/index.ts b/packages/api/routes/api/v1/accounts/search/index.ts similarity index 80% rename from api/api/v1/accounts/search/index.ts rename to packages/api/routes/api/v1/accounts/search/index.ts index 5edd1a38..fb97d929 100644 --- a/api/api/v1/accounts/search/index.ts +++ b/packages/api/routes/api/v1/accounts/search/index.ts @@ -3,16 +3,16 @@ import { RolePermission, zBoolean, } from "@versia/client/schemas"; -import { User } from "@versia/kit/db"; -import { Users } from "@versia/kit/tables"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, handleZodError } from "@versia-server/kit/api"; +import { User } from "@versia-server/kit/db"; +import { parseUserAddress } from "@versia-server/kit/parsers"; +import { Users } from "@versia-server/kit/tables"; import { eq, ilike, not, or, sql } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; +import { describeRoute, resolver, validator } from "hono-openapi"; import stringComparison from "string-comparison"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError, parseUserAddress } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; -import { rateLimit } from "~/middlewares/rate-limit"; +import { z } from "zod/v4"; +import { rateLimit } from "../../../../../middlewares/rate-limit.ts"; export default apiRoute((app) => app.get( @@ -47,30 +47,24 @@ export default apiRoute((app) => z.object({ q: AccountSchema.shape.username .or(AccountSchema.shape.acct) - .openapi({ + .meta({ description: "Search query for accounts.", example: "username", }), - limit: z.coerce - .number() - .int() - .min(1) - .max(80) - .default(40) - .openapi({ - description: "Maximum number of results.", - example: 40, - }), - offset: z.coerce.number().int().default(0).openapi({ + limit: z.coerce.number().int().min(1).max(80).default(40).meta({ + description: "Maximum number of results.", + example: 40, + }), + offset: z.coerce.number().int().default(0).meta({ description: "Skip the first n results.", example: 0, }), - resolve: zBoolean.default(false).openapi({ + resolve: zBoolean.default(false).meta({ description: "Attempt WebFinger lookup. Use this when q is an exact address.", example: false, }), - following: zBoolean.default(false).openapi({ + following: zBoolean.default(false).meta({ description: "Limit the search to users you are following.", example: false, }), diff --git a/api/api/v1/accounts/update_credentials/index.test.ts b/packages/api/routes/api/v1/accounts/update_credentials/index.test.ts similarity index 93% rename from api/api/v1/accounts/update_credentials/index.test.ts rename to packages/api/routes/api/v1/accounts/update_credentials/index.test.ts index ee2264a5..7420c83b 100644 --- a/api/api/v1/accounts/update_credentials/index.test.ts +++ b/packages/api/routes/api/v1/accounts/update_credentials/index.test.ts @@ -1,6 +1,6 @@ import { afterAll, describe, expect, test } from "bun:test"; -import { config } from "~/config.ts"; -import { generateClient, getTestUsers } from "~/tests/utils"; +import { config } from "@versia-server/config"; +import { generateClient, getTestUsers } from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(1); diff --git a/api/api/v1/accounts/update_credentials/index.ts b/packages/api/routes/api/v1/accounts/update_credentials/index.ts similarity index 84% rename from api/api/v1/accounts/update_credentials/index.ts rename to packages/api/routes/api/v1/accounts/update_credentials/index.ts index 6c48c04f..47ede0b2 100644 --- a/api/api/v1/accounts/update_credentials/index.ts +++ b/packages/api/routes/api/v1/accounts/update_credentials/index.ts @@ -3,20 +3,24 @@ import { RolePermission, zBoolean, } from "@versia/client/schemas"; -import { Emoji, Media, User } from "@versia/kit/db"; -import { Users } from "@versia/kit/tables"; +import * as VersiaEntities from "@versia/sdk/entities"; +import { config } from "@versia-server/config"; +import { ApiError } from "@versia-server/kit"; +import { + apiRoute, + auth, + handleZodError, + jsonOrForm, +} from "@versia-server/kit/api"; +import { Emoji, Media, User } from "@versia-server/kit/db"; +import { versiaTextToHtml } from "@versia-server/kit/parsers"; +import { Users } from "@versia-server/kit/tables"; import { and, eq, isNull } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError, jsonOrForm } from "@/api"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; import { mergeAndDeduplicate } from "@/lib"; import { sanitizedHtmlStrip } from "@/sanitization"; -import { ApiError } from "~/classes/errors/api-error"; -import { contentToHtml } from "~/classes/functions/status"; -import { config } from "~/config.ts"; -import { rateLimit } from "~/middlewares/rate-limit"; -import * as VersiaEntities from "~/packages/sdk/entities"; +import { rateLimit } from "../../../../../middlewares/rate-limit.ts"; export default apiRoute((app) => app.patch( @@ -53,7 +57,7 @@ export default apiRoute((app) => z .object({ display_name: AccountSchema.shape.display_name - .openapi({ + .meta({ description: "The display name to use for the profile.", example: "Lexi", @@ -70,7 +74,7 @@ export default apiRoute((app) => "Display name contains blocked words", ), username: AccountSchema.shape.username - .openapi({ + .meta({ description: "The username to use for the profile.", example: "lexi", }) @@ -90,7 +94,7 @@ export default apiRoute((app) => "Username is disallowed", ), note: AccountSchema.shape.note - .openapi({ + .meta({ description: "The account bio. Markdown is supported.", }) @@ -103,72 +107,60 @@ export default apiRoute((app) => "Bio contains blocked words", ), avatar: z - .string() .url() - .transform((a) => new URL(a)) - .openapi({ + .meta({ description: "Avatar image URL", }) .or( z - .instanceof(File) - .refine( - (v) => - v.size <= - config.validation.accounts - .max_avatar_bytes, - `Avatar must be less than ${config.validation.accounts.max_avatar_bytes} bytes`, + .file() + .max( + config.validation.accounts.max_avatar_bytes, ) - .openapi({ + .meta({ description: "Avatar image encoded using multipart/form-data", }), ), header: z - .string() .url() - .transform((v) => new URL(v)) - .openapi({ + .meta({ description: "Header image URL", }) .or( z - .instanceof(File) - .refine( - (v) => - v.size <= - config.validation.accounts - .max_header_bytes, - `Header must be less than ${config.validation.accounts.max_header_bytes} bytes`, + .file() + .max( + config.validation.accounts.max_header_bytes, ) - .openapi({ + .meta({ description: "Header image encoded using multipart/form-data", }), ), - locked: AccountSchema.shape.locked.openapi({ + locked: AccountSchema.shape.locked.meta({ description: "Whether manual approval of follow requests is required.", }), - bot: AccountSchema.shape.bot.openapi({ + bot: AccountSchema.shape.bot.meta({ description: "Whether the account has a bot flag.", }), discoverable: AccountSchema.shape.discoverable .unwrap() - .openapi({ + .meta({ description: "Whether the account should be shown in the profile directory.", }), - hide_collections: zBoolean.openapi({ + hide_collections: zBoolean.meta({ description: "Whether to hide followers and followed accounts.", }), - indexable: zBoolean.openapi({ + indexable: zBoolean.meta({ description: "Whether public posts should be searchable to anyone.", }), // TODO: Implement :( - attribution_domains: z.array(z.string()).openapi({ + attribution_domains: z.array(z.string()).meta({ description: "Domains of websites allowed to credit the account.", example: ["cnn.com", "myblog.com"], @@ -242,7 +234,7 @@ export default apiRoute((app) => if (note) { self.source.note = note; - self.note = await contentToHtml( + self.note = await versiaTextToHtml( new VersiaEntities.TextContentFormat({ "text/markdown": { content: note, @@ -282,9 +274,9 @@ export default apiRoute((app) => user.avatar = await Media.fromFile(avatar); } } else if (user.avatar) { - await user.avatar.updateFromUrl(avatar); + await user.avatar.updateFromUrl(new URL(avatar)); } else { - user.avatar = await Media.fromUrl(avatar); + user.avatar = await Media.fromUrl(new URL(avatar)); } } @@ -296,9 +288,9 @@ export default apiRoute((app) => user.header = await Media.fromFile(header); } } else if (user.header) { - await user.header.updateFromUrl(header); + await user.header.updateFromUrl(new URL(header)); } else { - user.header = await Media.fromUrl(header); + user.header = await Media.fromUrl(new URL(header)); } } @@ -329,7 +321,7 @@ export default apiRoute((app) => self.source.fields = []; for (const field of fields_attributes) { // Can be Markdown or plaintext, also has emojis - const parsedName = await contentToHtml( + const parsedName = await versiaTextToHtml( new VersiaEntities.TextContentFormat({ "text/markdown": { content: field.name, @@ -340,7 +332,7 @@ export default apiRoute((app) => true, ); - const parsedValue = await contentToHtml( + const parsedValue = await versiaTextToHtml( new VersiaEntities.TextContentFormat({ "text/markdown": { content: field.value, diff --git a/api/api/v1/accounts/verify_credentials/index.test.ts b/packages/api/routes/api/v1/accounts/verify_credentials/index.test.ts similarity index 95% rename from api/api/v1/accounts/verify_credentials/index.test.ts rename to packages/api/routes/api/v1/accounts/verify_credentials/index.test.ts index 79b015f4..3af99a9d 100644 --- a/api/api/v1/accounts/verify_credentials/index.test.ts +++ b/packages/api/routes/api/v1/accounts/verify_credentials/index.test.ts @@ -1,6 +1,6 @@ import { afterAll, describe, expect, test } from "bun:test"; -import { config } from "~/config"; -import { generateClient, getTestUsers } from "~/tests/utils"; +import { config } from "@versia-server/config"; +import { generateClient, getTestUsers } from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(1); diff --git a/api/api/v1/accounts/verify_credentials/index.ts b/packages/api/routes/api/v1/accounts/verify_credentials/index.ts similarity index 88% rename from api/api/v1/accounts/verify_credentials/index.ts rename to packages/api/routes/api/v1/accounts/verify_credentials/index.ts index d2f016e8..7da9b0fa 100644 --- a/api/api/v1/accounts/verify_credentials/index.ts +++ b/packages/api/routes/api/v1/accounts/verify_credentials/index.ts @@ -1,8 +1,7 @@ import { Account } from "@versia/client/schemas"; -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { apiRoute, auth } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth } from "@versia-server/kit/api"; +import { describeRoute, resolver } from "hono-openapi"; export default apiRoute((app) => app.get( diff --git a/api/api/v1/apps/index.ts b/packages/api/routes/api/v1/apps/index.ts similarity index 70% rename from api/api/v1/apps/index.ts rename to packages/api/routes/api/v1/apps/index.ts index 593f3f2d..0f2dbd2b 100644 --- a/api/api/v1/apps/index.ts +++ b/packages/api/routes/api/v1/apps/index.ts @@ -2,15 +2,14 @@ import { Application as ApplicationSchema, CredentialApplication as CredentialApplicationSchema, } from "@versia/client/schemas"; -import { Application } from "@versia/kit/db"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, handleZodError, jsonOrForm } from "@versia-server/kit/api"; +import { Application } from "@versia-server/kit/db"; import { randomUUIDv7 } from "bun"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, handleZodError, jsonOrForm } from "@/api"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; import { randomString } from "@/math"; -import { ApiError } from "~/classes/errors/api-error"; -import { rateLimit } from "~/middlewares/rate-limit"; +import { rateLimit } from "../../../../middlewares/rate-limit.ts"; export default apiRoute((app) => app.post( @@ -43,22 +42,20 @@ export default apiRoute((app) => z.object({ client_name: ApplicationSchema.shape.name, redirect_uris: ApplicationSchema.shape.redirect_uris.or( - ApplicationSchema.shape.redirect_uri.transform((u) => - u.split("\n"), - ), + ApplicationSchema.shape.redirect_uri, ), - scopes: z - .string() - .default("read") - .transform((s) => s.split(" ")) - .openapi({ - description: "Space separated list of scopes.", - }), + scopes: z.string().default("read").meta({ + description: "Space separated list of scopes.", + type: "string", + }), // Allow empty websites because Traewelling decides to give an empty // value instead of not providing anything at all website: ApplicationSchema.shape.website .optional() - .or(z.literal("").transform(() => undefined)), + .or(z.literal("")) + .meta({ + type: "string", + }), }), handleZodError, ), @@ -69,9 +66,11 @@ export default apiRoute((app) => const app = await Application.insert({ id: randomUUIDv7(), name: client_name, - redirectUri: redirect_uris.join("\n"), - scopes: scopes.join(" "), - website, + redirectUri: Array.isArray(redirect_uris) + ? redirect_uris.join("\n") + : redirect_uris, + scopes, + website: website || undefined, clientId: randomString(32, "base64url"), secret: randomString(64, "base64url"), }); diff --git a/api/api/v1/apps/verify_credentials/index.ts b/packages/api/routes/api/v1/apps/verify_credentials/index.ts similarity index 87% rename from api/api/v1/apps/verify_credentials/index.ts rename to packages/api/routes/api/v1/apps/verify_credentials/index.ts index 30e77bb9..9f42f8dd 100644 --- a/api/api/v1/apps/verify_credentials/index.ts +++ b/packages/api/routes/api/v1/apps/verify_credentials/index.ts @@ -2,11 +2,10 @@ import { Application as ApplicationSchema, RolePermission, } from "@versia/client/schemas"; -import { Application } from "@versia/kit/db"; -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { apiRoute, auth } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth } from "@versia-server/kit/api"; +import { Application } from "@versia-server/kit/db"; +import { describeRoute, resolver } from "hono-openapi"; export default apiRoute((app) => app.get( diff --git a/api/api/v1/blocks/index.test.ts b/packages/api/routes/api/v1/blocks/index.test.ts similarity index 95% rename from api/api/v1/blocks/index.test.ts rename to packages/api/routes/api/v1/blocks/index.test.ts index 71c8f512..f1d50a10 100644 --- a/api/api/v1/blocks/index.test.ts +++ b/packages/api/routes/api/v1/blocks/index.test.ts @@ -1,5 +1,5 @@ import { afterAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestUsers } from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(3); diff --git a/api/api/v1/blocks/index.ts b/packages/api/routes/api/v1/blocks/index.ts similarity index 80% rename from api/api/v1/blocks/index.ts rename to packages/api/routes/api/v1/blocks/index.ts index dd2b6512..efe26d62 100644 --- a/api/api/v1/blocks/index.ts +++ b/packages/api/routes/api/v1/blocks/index.ts @@ -2,14 +2,13 @@ import { Account as AccountSchema, RolePermission, } from "@versia/client/schemas"; -import { Timeline } from "@versia/kit/db"; -import { Users } from "@versia/kit/tables"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, handleZodError } from "@versia-server/kit/api"; +import { Timeline } from "@versia-server/kit/db"; +import { Users } from "@versia-server/kit/tables"; import { and, gt, gte, lt, sql } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.get( @@ -33,7 +32,7 @@ export default apiRoute((app) => link: z .string() .optional() - .openapi({ + .meta({ description: "Links to the next and previous pages", example: @@ -56,30 +55,24 @@ export default apiRoute((app) => validator( "query", z.object({ - max_id: AccountSchema.shape.id.optional().openapi({ + max_id: AccountSchema.shape.id.optional().meta({ description: "All results returned will be lesser than this ID. In effect, sets an upper bound on results.", example: "8d35243d-b959-43e2-8bac-1a9d4eaea2aa", }), - since_id: AccountSchema.shape.id.optional().openapi({ + since_id: AccountSchema.shape.id.optional().meta({ description: "All results returned will be greater than this ID. In effect, sets a lower bound on results.", example: undefined, }), - min_id: AccountSchema.shape.id.optional().openapi({ + min_id: AccountSchema.shape.id.optional().meta({ description: "Returns results immediately newer than this ID. In effect, sets a cursor at this ID and paginates forward.", example: undefined, }), - limit: z.coerce - .number() - .int() - .min(1) - .max(80) - .default(40) - .openapi({ - description: "Maximum number of results to return.", - }), + limit: z.coerce.number().int().min(1).max(80).default(40).meta({ + description: "Maximum number of results to return.", + }), }), handleZodError, ), diff --git a/api/api/v1/challenges/index.test.ts b/packages/api/routes/api/v1/challenges/index.test.ts similarity index 91% rename from api/api/v1/challenges/index.test.ts rename to packages/api/routes/api/v1/challenges/index.test.ts index 1f856379..6c528537 100644 --- a/api/api/v1/challenges/index.test.ts +++ b/packages/api/routes/api/v1/challenges/index.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "bun:test"; -import { generateClient } from "~/tests/utils"; +import { generateClient } from "@versia-server/tests"; // /api/v1/challenges describe("/api/v1/challenges", () => { diff --git a/api/api/v1/challenges/index.ts b/packages/api/routes/api/v1/challenges/index.ts similarity index 86% rename from api/api/v1/challenges/index.ts rename to packages/api/routes/api/v1/challenges/index.ts index 87cf76ac..b80be92c 100644 --- a/api/api/v1/challenges/index.ts +++ b/packages/api/routes/api/v1/challenges/index.ts @@ -1,10 +1,9 @@ import { Challenge } from "@versia/client/schemas"; -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { apiRoute, auth } from "@/api"; +import { config } from "@versia-server/config"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth } from "@versia-server/kit/api"; +import { describeRoute, resolver } from "hono-openapi"; import { generateChallenge } from "@/challenges"; -import { ApiError } from "~/classes/errors/api-error"; -import { config } from "~/config.ts"; export default apiRoute((app) => app.post( diff --git a/api/api/v1/custom_emojis/index.test.ts b/packages/api/routes/api/v1/custom_emojis/index.test.ts similarity index 95% rename from api/api/v1/custom_emojis/index.test.ts rename to packages/api/routes/api/v1/custom_emojis/index.test.ts index eca78e36..6fb13b1e 100644 --- a/api/api/v1/custom_emojis/index.test.ts +++ b/packages/api/routes/api/v1/custom_emojis/index.test.ts @@ -1,8 +1,8 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; -import { db } from "@versia/kit/db"; -import { Emojis } from "@versia/kit/tables"; +import { db } from "@versia-server/kit/db"; +import { Emojis } from "@versia-server/kit/tables"; +import { generateClient, getTestUsers } from "@versia-server/tests"; import { inArray } from "drizzle-orm"; -import { generateClient, getTestUsers } from "~/tests/utils"; const { users, deleteUsers } = await getTestUsers(2); diff --git a/api/api/v1/custom_emojis/index.ts b/packages/api/routes/api/v1/custom_emojis/index.ts similarity index 84% rename from api/api/v1/custom_emojis/index.ts rename to packages/api/routes/api/v1/custom_emojis/index.ts index f1e3d256..e8b8b91c 100644 --- a/api/api/v1/custom_emojis/index.ts +++ b/packages/api/routes/api/v1/custom_emojis/index.ts @@ -2,14 +2,13 @@ import { CustomEmoji as CustomEmojiSchema, RolePermission, } from "@versia/client/schemas"; -import { Emoji } from "@versia/kit/db"; -import { Emojis } from "@versia/kit/tables"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth } from "@versia-server/kit/api"; +import { Emoji } from "@versia-server/kit/db"; +import { Emojis } from "@versia-server/kit/tables"; import { and, eq, isNull, or } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { describeRoute, resolver } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.get( diff --git a/api/api/v1/emojis/[id]/index.test.ts b/packages/api/routes/api/v1/emojis/[id]/index.test.ts similarity index 95% rename from api/api/v1/emojis/[id]/index.test.ts rename to packages/api/routes/api/v1/emojis/[id]/index.test.ts index 098633bd..4f289b17 100644 --- a/api/api/v1/emojis/[id]/index.test.ts +++ b/packages/api/routes/api/v1/emojis/[id]/index.test.ts @@ -1,8 +1,8 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; -import { db } from "@versia/kit/db"; -import { Emojis } from "@versia/kit/tables"; +import { db } from "@versia-server/kit/db"; +import { Emojis } from "@versia-server/kit/tables"; +import { generateClient, getTestUsers } from "@versia-server/tests"; import { inArray } from "drizzle-orm"; -import { generateClient, getTestUsers } from "~/tests/utils"; const { users, deleteUsers } = await getTestUsers(2); let id = ""; diff --git a/api/api/v1/emojis/[id]/index.ts b/packages/api/routes/api/v1/emojis/[id]/index.ts similarity index 89% rename from api/api/v1/emojis/[id]/index.ts rename to packages/api/routes/api/v1/emojis/[id]/index.ts index bb8e022a..2a563b0b 100644 --- a/api/api/v1/emojis/[id]/index.ts +++ b/packages/api/routes/api/v1/emojis/[id]/index.ts @@ -2,19 +2,18 @@ import { CustomEmoji as CustomEmojiSchema, RolePermission, } from "@versia/client/schemas"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; +import { config } from "@versia-server/config"; +import { ApiError } from "@versia-server/kit"; import { apiRoute, auth, handleZodError, jsonOrForm, withEmojiParam, -} from "@/api"; +} from "@versia-server/kit/api"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; import { mimeLookup } from "@/content_types"; -import { ApiError } from "~/classes/errors/api-error"; -import { config } from "~/config.ts"; export default apiRoute((app) => { app.get( @@ -123,25 +122,18 @@ export default apiRoute((app) => { "Shortcode contains blocked words", ), element: z - .string() .url() - .transform((a) => new URL(a)) - .openapi({ + .meta({ description: "Emoji image URL", }) .or( z - .instanceof(File) - .openapi({ + .file() + .max(config.validation.emojis.max_bytes) + .meta({ description: "Emoji image encoded using multipart/form-data", - }) - .refine( - (v) => - v.size <= - config.validation.emojis.max_bytes, - `Emoji must be less than ${config.validation.emojis.max_bytes} bytes`, - ), + }), ), category: CustomEmojiSchema.shape.category.optional(), alt: CustomEmojiSchema.shape.description @@ -195,7 +187,7 @@ export default apiRoute((app) => { const contentType = element instanceof File ? element.type - : await mimeLookup(element); + : await mimeLookup(new URL(element)); if (!contentType.startsWith("image/")) { throw new ApiError( @@ -208,7 +200,7 @@ export default apiRoute((app) => { if (element instanceof File) { await emoji.media.updateFromFile(element); } else { - await emoji.media.updateFromUrl(element); + await emoji.media.updateFromUrl(new URL(element)); } } diff --git a/api/api/v1/emojis/index.test.ts b/packages/api/routes/api/v1/emojis/index.test.ts similarity index 96% rename from api/api/v1/emojis/index.test.ts rename to packages/api/routes/api/v1/emojis/index.test.ts index 3bff570f..317a2dd1 100644 --- a/api/api/v1/emojis/index.test.ts +++ b/packages/api/routes/api/v1/emojis/index.test.ts @@ -1,9 +1,9 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; -import { db } from "@versia/kit/db"; -import { Emojis } from "@versia/kit/tables"; +import { db } from "@versia-server/kit/db"; +import { Emojis } from "@versia-server/kit/tables"; +import { generateClient, getTestUsers } from "@versia-server/tests"; import { inArray } from "drizzle-orm"; import sharp from "sharp"; -import { generateClient, getTestUsers } from "~/tests/utils"; const { users, deleteUsers } = await getTestUsers(3); diff --git a/api/api/v1/emojis/index.ts b/packages/api/routes/api/v1/emojis/index.ts similarity index 78% rename from api/api/v1/emojis/index.ts rename to packages/api/routes/api/v1/emojis/index.ts index 141e6b3a..2fa8e4d6 100644 --- a/api/api/v1/emojis/index.ts +++ b/packages/api/routes/api/v1/emojis/index.ts @@ -2,17 +2,21 @@ import { CustomEmoji as CustomEmojiSchema, RolePermission, } from "@versia/client/schemas"; -import { Emoji, Media } from "@versia/kit/db"; -import { Emojis } from "@versia/kit/tables"; +import { config } from "@versia-server/config"; +import { ApiError } from "@versia-server/kit"; +import { + apiRoute, + auth, + handleZodError, + jsonOrForm, +} from "@versia-server/kit/api"; +import { Emoji, Media } from "@versia-server/kit/db"; +import { Emojis } from "@versia-server/kit/tables"; import { randomUUIDv7 } from "bun"; import { and, eq, isNull, or } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError, jsonOrForm } from "@/api"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; import { mimeLookup } from "@/content_types"; -import { ApiError } from "~/classes/errors/api-error"; -import { config } from "~/config.ts"; export default apiRoute((app) => app.post( @@ -55,25 +59,15 @@ export default apiRoute((app) => "Shortcode contains blocked words", ), element: z - .string() .url() - .transform((a) => new URL(a)) - .openapi({ + .meta({ description: "Emoji image URL", }) .or( - z - .instanceof(File) - .openapi({ - description: - "Emoji image encoded using multipart/form-data", - }) - .refine( - (v) => - v.size <= - config.validation.emojis.max_bytes, - `Emoji must be less than ${config.validation.emojis.max_bytes} bytes`, - ), + z.file().max(config.validation.emojis.max_bytes).meta({ + description: + "Emoji image encoded using multipart/form-data", + }), ), category: CustomEmojiSchema.shape.category.optional(), alt: CustomEmojiSchema.shape.description @@ -118,7 +112,7 @@ export default apiRoute((app) => const contentType = element instanceof File ? element.type - : await mimeLookup(element); + : await mimeLookup(new URL(element)); if (!contentType.startsWith("image/")) { throw new ApiError( @@ -133,7 +127,7 @@ export default apiRoute((app) => ? await Media.fromFile(element, { description: alt ?? undefined, }) - : await Media.fromUrl(element, { + : await Media.fromUrl(new URL(element), { description: alt ?? undefined, }); diff --git a/api/api/v1/favourites/index.ts b/packages/api/routes/api/v1/favourites/index.ts similarity index 80% rename from api/api/v1/favourites/index.ts rename to packages/api/routes/api/v1/favourites/index.ts index 3cfadbd4..38cc3173 100644 --- a/api/api/v1/favourites/index.ts +++ b/packages/api/routes/api/v1/favourites/index.ts @@ -1,12 +1,11 @@ import { RolePermission, Status as StatusSchema } from "@versia/client/schemas"; -import { Timeline } from "@versia/kit/db"; -import { Notes } from "@versia/kit/tables"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, handleZodError } from "@versia-server/kit/api"; +import { Timeline } from "@versia-server/kit/db"; +import { Notes } from "@versia-server/kit/tables"; import { and, gt, gte, lt, sql } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.get( @@ -30,7 +29,7 @@ export default apiRoute((app) => link: z .string() .optional() - .openapi({ + .meta({ description: "Links to the next and previous pages", example: @@ -52,30 +51,24 @@ export default apiRoute((app) => validator( "query", z.object({ - max_id: StatusSchema.shape.id.optional().openapi({ + max_id: StatusSchema.shape.id.optional().meta({ description: "All results returned will be lesser than this ID. In effect, sets an upper bound on results.", example: "8d35243d-b959-43e2-8bac-1a9d4eaea2aa", }), - since_id: StatusSchema.shape.id.optional().openapi({ + since_id: StatusSchema.shape.id.optional().meta({ description: "All results returned will be greater than this ID. In effect, sets a lower bound on results.", example: undefined, }), - min_id: StatusSchema.shape.id.optional().openapi({ + min_id: StatusSchema.shape.id.optional().meta({ description: "Returns results immediately newer than this ID. In effect, sets a cursor at this ID and paginates forward.", example: undefined, }), - limit: z.coerce - .number() - .int() - .min(1) - .max(80) - .default(40) - .openapi({ - description: "Maximum number of results to return.", - }), + limit: z.coerce.number().int().min(1).max(80).default(40).meta({ + description: "Maximum number of results to return.", + }), }), handleZodError, ), diff --git a/api/api/v1/follow_requests/[account_id]/authorize.ts b/packages/api/routes/api/v1/follow_requests/[account_id]/authorize.ts similarity index 89% rename from api/api/v1/follow_requests/[account_id]/authorize.ts rename to packages/api/routes/api/v1/follow_requests/[account_id]/authorize.ts index 4073571e..c7177704 100644 --- a/api/api/v1/follow_requests/[account_id]/authorize.ts +++ b/packages/api/routes/api/v1/follow_requests/[account_id]/authorize.ts @@ -3,12 +3,11 @@ import { Relationship as RelationshipSchema, RolePermission, } from "@versia/client/schemas"; -import { Relationship, User } from "@versia/kit/db"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, handleZodError } from "@versia-server/kit/api"; +import { Relationship, User } from "@versia-server/kit/db"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.post( diff --git a/api/api/v1/follow_requests/[account_id]/reject.ts b/packages/api/routes/api/v1/follow_requests/[account_id]/reject.ts similarity index 88% rename from api/api/v1/follow_requests/[account_id]/reject.ts rename to packages/api/routes/api/v1/follow_requests/[account_id]/reject.ts index 4a00502a..c7c797bf 100644 --- a/api/api/v1/follow_requests/[account_id]/reject.ts +++ b/packages/api/routes/api/v1/follow_requests/[account_id]/reject.ts @@ -3,12 +3,11 @@ import { Relationship as RelationshipSchema, RolePermission, } from "@versia/client/schemas"; -import { Relationship, User } from "@versia/kit/db"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, handleZodError } from "@versia-server/kit/api"; +import { Relationship, User } from "@versia-server/kit/db"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.post( diff --git a/api/api/v1/follow_requests/index.ts b/packages/api/routes/api/v1/follow_requests/index.ts similarity index 81% rename from api/api/v1/follow_requests/index.ts rename to packages/api/routes/api/v1/follow_requests/index.ts index 664f03e3..2e25f3af 100644 --- a/api/api/v1/follow_requests/index.ts +++ b/packages/api/routes/api/v1/follow_requests/index.ts @@ -2,14 +2,13 @@ import { Account as AccountSchema, RolePermission, } from "@versia/client/schemas"; -import { Timeline } from "@versia/kit/db"; -import { Users } from "@versia/kit/tables"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, handleZodError } from "@versia-server/kit/api"; +import { Timeline } from "@versia-server/kit/db"; +import { Users } from "@versia-server/kit/tables"; import { and, gt, gte, lt, sql } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.get( @@ -35,7 +34,7 @@ export default apiRoute((app) => link: z .string() .optional() - .openapi({ + .meta({ description: "Links to the next and previous pages", example: @@ -57,30 +56,24 @@ export default apiRoute((app) => validator( "query", z.object({ - max_id: AccountSchema.shape.id.optional().openapi({ + max_id: AccountSchema.shape.id.optional().meta({ description: "All results returned will be lesser than this ID. In effect, sets an upper bound on results.", example: "8d35243d-b959-43e2-8bac-1a9d4eaea2aa", }), - since_id: AccountSchema.shape.id.optional().openapi({ + since_id: AccountSchema.shape.id.optional().meta({ description: "All results returned will be greater than this ID. In effect, sets a lower bound on results.", example: undefined, }), - min_id: AccountSchema.shape.id.optional().openapi({ + min_id: AccountSchema.shape.id.optional().meta({ description: "Returns results immediately newer than this ID. In effect, sets a cursor at this ID and paginates forward.", example: undefined, }), - limit: z.coerce - .number() - .int() - .min(1) - .max(80) - .default(40) - .openapi({ - description: "Maximum number of results to return.", - }), + limit: z.coerce.number().int().min(1).max(80).default(40).meta({ + description: "Maximum number of results to return.", + }), }), handleZodError, ), diff --git a/api/api/v1/frontend/config/index.ts b/packages/api/routes/api/v1/frontend/config/index.ts similarity index 78% rename from api/api/v1/frontend/config/index.ts rename to packages/api/routes/api/v1/frontend/config/index.ts index 2383af47..bcbb016e 100644 --- a/api/api/v1/frontend/config/index.ts +++ b/packages/api/routes/api/v1/frontend/config/index.ts @@ -1,8 +1,7 @@ -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute } from "@/api"; -import { config } from "~/config.ts"; +import { config } from "@versia-server/config"; +import { apiRoute } from "@versia-server/kit/api"; +import { describeRoute, resolver } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.get( diff --git a/api/api/v1/instance/extended_description.test.ts b/packages/api/routes/api/v1/instance/extended_description.test.ts similarity index 91% rename from api/api/v1/instance/extended_description.test.ts rename to packages/api/routes/api/v1/instance/extended_description.test.ts index c5b847ce..838b63f5 100644 --- a/api/api/v1/instance/extended_description.test.ts +++ b/packages/api/routes/api/v1/instance/extended_description.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "bun:test"; -import { generateClient } from "~/tests/utils"; +import { generateClient } from "@versia-server/tests"; // /api/v1/instance/extended_description describe("/api/v1/instance/extended_description", () => { diff --git a/api/api/v1/instance/extended_description.ts b/packages/api/routes/api/v1/instance/extended_description.ts similarity index 84% rename from api/api/v1/instance/extended_description.ts rename to packages/api/routes/api/v1/instance/extended_description.ts index d01985d4..2a2fd9b5 100644 --- a/api/api/v1/instance/extended_description.ts +++ b/packages/api/routes/api/v1/instance/extended_description.ts @@ -1,9 +1,8 @@ import { ExtendedDescription as ExtendedDescriptionSchema } from "@versia/client/schemas"; -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { apiRoute } from "@/api"; -import { markdownParse } from "~/classes/functions/status"; -import { config } from "~/config.ts"; +import { config } from "@versia-server/config"; +import { apiRoute } from "@versia-server/kit/api"; +import { markdownToHtml } from "@versia-server/kit/markdown"; +import { describeRoute, resolver } from "hono-openapi"; export default apiRoute((app) => app.get( @@ -27,7 +26,7 @@ export default apiRoute((app) => }, }), async (context) => { - const content = await markdownParse( + const content = await markdownToHtml( config.instance.extended_description_path?.content ?? "This is a [Versia](https://versia.pub) server with the default extended description.", ); diff --git a/api/api/v1/instance/index.ts b/packages/api/routes/api/v1/instance/index.ts similarity index 91% rename from api/api/v1/instance/index.ts rename to packages/api/routes/api/v1/instance/index.ts index 36ed1655..7abf34c0 100644 --- a/api/api/v1/instance/index.ts +++ b/packages/api/routes/api/v1/instance/index.ts @@ -1,14 +1,13 @@ import { InstanceV1 as InstanceV1Schema } from "@versia/client/schemas"; -import { Instance, Note, User } from "@versia/kit/db"; -import { Users } from "@versia/kit/tables"; +import { config } from "@versia-server/config"; +import { apiRoute } from "@versia-server/kit/api"; +import { Instance, Note, User } from "@versia-server/kit/db"; +import { markdownToHtml } from "@versia-server/kit/markdown"; +import { Users } from "@versia-server/kit/tables"; import { and, eq, isNull } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import type { z } from "zod"; -import { apiRoute } from "@/api"; -import { markdownParse } from "~/classes/functions/status"; -import { config } from "~/config.ts"; -import manifest from "~/package.json" with { type: "json" }; +import { describeRoute, resolver } from "hono-openapi"; +import type { z } from "zod/v4"; +import manifest from "../../../../../../package.json" with { type: "json" }; export default apiRoute((app) => app.get( @@ -60,7 +59,7 @@ export default apiRoute((app) => } | undefined; - const content = await markdownParse( + const content = await markdownToHtml( config.instance.extended_description_path?.content ?? "This is a [Versia](https://versia.pub) server with the default extended description.", ); diff --git a/api/api/v1/instance/privacy_policy.test.ts b/packages/api/routes/api/v1/instance/privacy_policy.test.ts similarity index 91% rename from api/api/v1/instance/privacy_policy.test.ts rename to packages/api/routes/api/v1/instance/privacy_policy.test.ts index 396f982c..260c02ab 100644 --- a/api/api/v1/instance/privacy_policy.test.ts +++ b/packages/api/routes/api/v1/instance/privacy_policy.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "bun:test"; -import { generateClient } from "~/tests/utils"; +import { generateClient } from "@versia-server/tests"; // /api/v1/instance/privacy_policy describe("/api/v1/instance/privacy_policy", () => { diff --git a/api/api/v1/instance/privacy_policy.ts b/packages/api/routes/api/v1/instance/privacy_policy.ts similarity index 82% rename from api/api/v1/instance/privacy_policy.ts rename to packages/api/routes/api/v1/instance/privacy_policy.ts index 6af7596d..4f3c1925 100644 --- a/api/api/v1/instance/privacy_policy.ts +++ b/packages/api/routes/api/v1/instance/privacy_policy.ts @@ -1,9 +1,8 @@ import { PrivacyPolicy as PrivacyPolicySchema } from "@versia/client/schemas"; -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { apiRoute } from "@/api"; -import { markdownParse } from "~/classes/functions/status"; -import { config } from "~/config.ts"; +import { config } from "@versia-server/config"; +import { apiRoute } from "@versia-server/kit/api"; +import { markdownToHtml } from "@versia-server/kit/markdown"; +import { describeRoute, resolver } from "hono-openapi"; export default apiRoute((app) => app.get( @@ -27,7 +26,7 @@ export default apiRoute((app) => }, }), async (context) => { - const content = await markdownParse( + const content = await markdownToHtml( config.instance.privacy_policy_path?.content ?? "This instance has not provided any privacy policy.", ); diff --git a/api/api/v1/instance/rules.test.ts b/packages/api/routes/api/v1/instance/rules.test.ts similarity index 83% rename from api/api/v1/instance/rules.test.ts rename to packages/api/routes/api/v1/instance/rules.test.ts index bdd84da9..3bbfdedf 100644 --- a/api/api/v1/instance/rules.test.ts +++ b/packages/api/routes/api/v1/instance/rules.test.ts @@ -1,6 +1,6 @@ import { describe, expect, test } from "bun:test"; -import { config } from "~/config.ts"; -import { generateClient } from "~/tests/utils"; +import { config } from "@versia-server/config"; +import { generateClient } from "@versia-server/tests"; // /api/v1/instance/rules describe("/api/v1/instance/rules", () => { diff --git a/api/api/v1/instance/rules.ts b/packages/api/routes/api/v1/instance/rules.ts similarity index 84% rename from api/api/v1/instance/rules.ts rename to packages/api/routes/api/v1/instance/rules.ts index 783ec424..085746a5 100644 --- a/api/api/v1/instance/rules.ts +++ b/packages/api/routes/api/v1/instance/rules.ts @@ -1,9 +1,8 @@ import { Rule as RuleSchema } from "@versia/client/schemas"; -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute } from "@/api"; -import { config } from "~/config.ts"; +import { config } from "@versia-server/config"; +import { apiRoute } from "@versia-server/kit/api"; +import { describeRoute, resolver } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.get( diff --git a/api/api/v1/instance/terms_of_service.test.ts b/packages/api/routes/api/v1/instance/terms_of_service.test.ts similarity index 92% rename from api/api/v1/instance/terms_of_service.test.ts rename to packages/api/routes/api/v1/instance/terms_of_service.test.ts index eb529365..6c31a8dc 100644 --- a/api/api/v1/instance/terms_of_service.test.ts +++ b/packages/api/routes/api/v1/instance/terms_of_service.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "bun:test"; -import { generateClient } from "~/tests/utils"; +import { generateClient } from "@versia-server/tests"; // /api/v1/instance/terms_of_service describe("/api/v1/instance/terms_of_service", () => { diff --git a/api/api/v1/instance/terms_of_service.ts b/packages/api/routes/api/v1/instance/terms_of_service.ts similarity index 82% rename from api/api/v1/instance/terms_of_service.ts rename to packages/api/routes/api/v1/instance/terms_of_service.ts index 91e0a781..71927530 100644 --- a/api/api/v1/instance/terms_of_service.ts +++ b/packages/api/routes/api/v1/instance/terms_of_service.ts @@ -1,9 +1,8 @@ import { TermsOfService as TermsOfServiceSchema } from "@versia/client/schemas"; -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { apiRoute } from "@/api"; -import { markdownParse } from "~/classes/functions/status"; -import { config } from "~/config.ts"; +import { config } from "@versia-server/config"; +import { apiRoute } from "@versia-server/kit/api"; +import { markdownToHtml } from "@versia-server/kit/markdown"; +import { describeRoute, resolver } from "hono-openapi"; export default apiRoute((app) => app.get( @@ -28,7 +27,7 @@ export default apiRoute((app) => }, }), async (context) => { - const content = await markdownParse( + const content = await markdownToHtml( config.instance.tos_path?.content ?? "This instance has not provided any terms of service.", ); diff --git a/api/api/v1/markers/index.test.ts b/packages/api/routes/api/v1/markers/index.test.ts similarity index 94% rename from api/api/v1/markers/index.test.ts rename to packages/api/routes/api/v1/markers/index.test.ts index 0180a623..50826138 100644 --- a/api/api/v1/markers/index.test.ts +++ b/packages/api/routes/api/v1/markers/index.test.ts @@ -1,5 +1,9 @@ import { afterAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestStatuses, getTestUsers } from "~/tests/utils"; +import { + generateClient, + getTestStatuses, + getTestUsers, +} from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(1); const timeline = await getTestStatuses(10, users[0]); diff --git a/api/api/v1/markers/index.ts b/packages/api/routes/api/v1/markers/index.ts similarity index 91% rename from api/api/v1/markers/index.ts rename to packages/api/routes/api/v1/markers/index.ts index 354831fb..155c0450 100644 --- a/api/api/v1/markers/index.ts +++ b/packages/api/routes/api/v1/markers/index.ts @@ -4,15 +4,14 @@ import { RolePermission, Status as StatusSchema, } from "@versia/client/schemas"; -import { db } from "@versia/kit/db"; -import { Markers } from "@versia/kit/tables"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, handleZodError } from "@versia-server/kit/api"; +import { db } from "@versia-server/kit/db"; +import { Markers } from "@versia-server/kit/tables"; import { randomUUIDv7 } from "bun"; import { and, eq, type SQL } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; const MarkerResponseSchema = z.object({ notifications: MarkerSchema.optional(), @@ -52,9 +51,9 @@ export default apiRoute((app) => { "timeline[]": z .array(z.enum(["home", "notifications"])) .max(2) - .or(z.enum(["home", "notifications"]).transform((t) => [t])) + .or(z.enum(["home", "notifications"])) .optional() - .openapi({ + .meta({ description: "Specify the timeline(s) for which markers should be fetched. Possible values: home, notifications. If not provided, an empty object will be returned.", }), @@ -62,13 +61,17 @@ export default apiRoute((app) => { handleZodError, ), async (context) => { - const { "timeline[]": timeline } = context.req.valid("query"); + const { "timeline[]": queryTimeline } = context.req.valid("query"); const { user } = context.get("auth"); - if (!timeline) { + if (!queryTimeline) { return context.json({}, 200); } + const timeline = Array.isArray(queryTimeline) + ? queryTimeline + : [queryTimeline]; + const markers: z.infer = { home: undefined, notifications: undefined, @@ -160,13 +163,13 @@ export default apiRoute((app) => { "query", z .object({ - "home[last_read_id]": StatusSchema.shape.id.openapi({ + "home[last_read_id]": StatusSchema.shape.id.meta({ description: "ID of the last status read in the home timeline.", example: "c62aa212-8198-4ce5-a388-2cc8344a84ef", }), "notifications[last_read_id]": - NotificationSchema.shape.id.openapi({ + NotificationSchema.shape.id.meta({ description: "ID of the last notification read.", }), }) diff --git a/api/api/v1/media/[id]/index.ts b/packages/api/routes/api/v1/media/[id]/index.ts similarity index 92% rename from api/api/v1/media/[id]/index.ts rename to packages/api/routes/api/v1/media/[id]/index.ts index 947dc701..8c5a9978 100644 --- a/api/api/v1/media/[id]/index.ts +++ b/packages/api/routes/api/v1/media/[id]/index.ts @@ -2,13 +2,12 @@ import { Attachment as AttachmentSchema, RolePermission, } from "@versia/client/schemas"; -import { Media } from "@versia/kit/db"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; -import { config } from "~/config"; +import { config } from "@versia-server/config"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, handleZodError } from "@versia-server/kit/api"; +import { Media } from "@versia-server/kit/db"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => { app.get( @@ -106,7 +105,7 @@ export default apiRoute((app) => { "form", z .object({ - thumbnail: z.instanceof(File).openapi({ + thumbnail: z.file().meta({ description: "The custom thumbnail of the media to be attached, encoded using multipart form data.", }), @@ -114,7 +113,7 @@ export default apiRoute((app) => { .unwrap() .max(config.validation.media.max_description_characters) .optional(), - focus: z.string().openapi({ + focus: z.string().meta({ description: "Two floating points (x,y), comma-delimited, ranging from -1.0 to 1.0. Used for media cropping on clients.", externalDocs: { diff --git a/api/api/v1/media/index.ts b/packages/api/routes/api/v1/media/index.ts similarity index 87% rename from api/api/v1/media/index.ts rename to packages/api/routes/api/v1/media/index.ts index 711a2fe1..55f5cb58 100644 --- a/api/api/v1/media/index.ts +++ b/packages/api/routes/api/v1/media/index.ts @@ -2,13 +2,12 @@ import { Attachment as AttachmentSchema, RolePermission, } from "@versia/client/schemas"; -import { Media } from "@versia/kit/db"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; -import { config } from "~/config"; +import { config } from "@versia-server/config"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, handleZodError } from "@versia-server/kit/api"; +import { Media } from "@versia-server/kit/db"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.post( @@ -60,11 +59,11 @@ export default apiRoute((app) => validator( "form", z.object({ - file: z.instanceof(File).openapi({ + file: z.file().meta({ description: "The file to be attached, encoded using multipart form data. The file must have a MIME type.", }), - thumbnail: z.instanceof(File).optional().openapi({ + thumbnail: z.file().optional().meta({ description: "The custom thumbnail of the media to be attached, encoded using multipart form data.", }), @@ -75,7 +74,7 @@ export default apiRoute((app) => focus: z .string() .optional() - .openapi({ + .meta({ description: "Two floating points (x,y), comma-delimited, ranging from -1.0 to 1.0. Used for media cropping on clients.", externalDocs: { diff --git a/api/api/v1/mutes/index.test.ts b/packages/api/routes/api/v1/mutes/index.test.ts similarity index 95% rename from api/api/v1/mutes/index.test.ts rename to packages/api/routes/api/v1/mutes/index.test.ts index 5206d3be..09d6029e 100644 --- a/api/api/v1/mutes/index.test.ts +++ b/packages/api/routes/api/v1/mutes/index.test.ts @@ -1,5 +1,5 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestUsers } from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(3); diff --git a/api/api/v1/mutes/index.ts b/packages/api/routes/api/v1/mutes/index.ts similarity index 80% rename from api/api/v1/mutes/index.ts rename to packages/api/routes/api/v1/mutes/index.ts index 4f34f57b..df5e46d4 100644 --- a/api/api/v1/mutes/index.ts +++ b/packages/api/routes/api/v1/mutes/index.ts @@ -2,14 +2,13 @@ import { Account as AccountSchema, RolePermission, } from "@versia/client/schemas"; -import { Timeline } from "@versia/kit/db"; -import { Users } from "@versia/kit/tables"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, handleZodError } from "@versia-server/kit/api"; +import { Timeline } from "@versia-server/kit/db"; +import { Users } from "@versia-server/kit/tables"; import { and, gt, gte, lt, sql } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.get( @@ -33,7 +32,7 @@ export default apiRoute((app) => link: z .string() .optional() - .openapi({ + .meta({ description: "Links to the next and previous pages", example: @@ -56,30 +55,24 @@ export default apiRoute((app) => validator( "query", z.object({ - max_id: AccountSchema.shape.id.optional().openapi({ + max_id: AccountSchema.shape.id.optional().meta({ description: "All results returned will be lesser than this ID. In effect, sets an upper bound on results.", example: "8d35243d-b959-43e2-8bac-1a9d4eaea2aa", }), - since_id: AccountSchema.shape.id.optional().openapi({ + since_id: AccountSchema.shape.id.optional().meta({ description: "All results returned will be greater than this ID. In effect, sets a lower bound on results.", example: undefined, }), - min_id: AccountSchema.shape.id.optional().openapi({ + min_id: AccountSchema.shape.id.optional().meta({ description: "Returns results immediately newer than this ID. In effect, sets a cursor at this ID and paginates forward.", example: undefined, }), - limit: z.coerce - .number() - .int() - .min(1) - .max(80) - .default(40) - .openapi({ - description: "Maximum number of results to return.", - }), + limit: z.coerce.number().int().min(1).max(80).default(40).meta({ + description: "Maximum number of results to return.", + }), }), handleZodError, ), diff --git a/api/api/v1/notifications/[id]/dismiss.test.ts b/packages/api/routes/api/v1/notifications/[id]/dismiss.test.ts similarity index 94% rename from api/api/v1/notifications/[id]/dismiss.test.ts rename to packages/api/routes/api/v1/notifications/[id]/dismiss.test.ts index a9035dff..6d12ecd6 100644 --- a/api/api/v1/notifications/[id]/dismiss.test.ts +++ b/packages/api/routes/api/v1/notifications/[id]/dismiss.test.ts @@ -1,7 +1,7 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import type { Notification } from "@versia/client/schemas"; -import type { z } from "zod"; -import { generateClient, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestUsers } from "@versia-server/tests"; +import type { z } from "zod/v4"; const { users, deleteUsers } = await getTestUsers(2); let notifications: z.infer[] = []; diff --git a/api/api/v1/notifications/[id]/dismiss.ts b/packages/api/routes/api/v1/notifications/[id]/dismiss.ts similarity index 85% rename from api/api/v1/notifications/[id]/dismiss.ts rename to packages/api/routes/api/v1/notifications/[id]/dismiss.ts index 5fbedd60..acf837ac 100644 --- a/api/api/v1/notifications/[id]/dismiss.ts +++ b/packages/api/routes/api/v1/notifications/[id]/dismiss.ts @@ -2,12 +2,11 @@ import { Notification as NotificationSchema, RolePermission, } from "@versia/client/schemas"; -import { Notification } from "@versia/kit/db"; -import { describeRoute } from "hono-openapi"; -import { validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, handleZodError } from "@versia-server/kit/api"; +import { Notification } from "@versia-server/kit/db"; +import { describeRoute, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.post( diff --git a/api/api/v1/notifications/[id]/index.test.ts b/packages/api/routes/api/v1/notifications/[id]/index.test.ts similarity index 95% rename from api/api/v1/notifications/[id]/index.test.ts rename to packages/api/routes/api/v1/notifications/[id]/index.test.ts index 7bda7499..9428b51f 100644 --- a/api/api/v1/notifications/[id]/index.test.ts +++ b/packages/api/routes/api/v1/notifications/[id]/index.test.ts @@ -1,7 +1,7 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import type { Notification } from "@versia/client/schemas"; -import type { z } from "zod"; -import { generateClient, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestUsers } from "@versia-server/tests"; +import type { z } from "zod/v4"; const { users, deleteUsers } = await getTestUsers(2); let notifications: z.infer[] = []; diff --git a/api/api/v1/notifications/[id]/index.ts b/packages/api/routes/api/v1/notifications/[id]/index.ts similarity index 86% rename from api/api/v1/notifications/[id]/index.ts rename to packages/api/routes/api/v1/notifications/[id]/index.ts index 255c3d32..d48b1206 100644 --- a/api/api/v1/notifications/[id]/index.ts +++ b/packages/api/routes/api/v1/notifications/[id]/index.ts @@ -2,12 +2,11 @@ import { Notification as NotificationSchema, RolePermission, } from "@versia/client/schemas"; -import { Notification } from "@versia/kit/db"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, handleZodError } from "@versia-server/kit/api"; +import { Notification } from "@versia-server/kit/db"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.get( diff --git a/api/api/v1/notifications/clear/index.test.ts b/packages/api/routes/api/v1/notifications/clear/index.test.ts similarity index 94% rename from api/api/v1/notifications/clear/index.test.ts rename to packages/api/routes/api/v1/notifications/clear/index.test.ts index 7950cdbd..39a8c449 100644 --- a/api/api/v1/notifications/clear/index.test.ts +++ b/packages/api/routes/api/v1/notifications/clear/index.test.ts @@ -1,5 +1,5 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestUsers } from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(2); diff --git a/api/api/v1/notifications/clear/index.ts b/packages/api/routes/api/v1/notifications/clear/index.ts similarity index 91% rename from api/api/v1/notifications/clear/index.ts rename to packages/api/routes/api/v1/notifications/clear/index.ts index 747a03ba..65ac6e09 100644 --- a/api/api/v1/notifications/clear/index.ts +++ b/packages/api/routes/api/v1/notifications/clear/index.ts @@ -1,7 +1,7 @@ import { RolePermission } from "@versia/client/schemas"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth } from "@versia-server/kit/api"; import { describeRoute } from "hono-openapi"; -import { apiRoute, auth } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; export default apiRoute((app) => app.post( diff --git a/api/api/v1/notifications/destroy_multiple/index.test.ts b/packages/api/routes/api/v1/notifications/destroy_multiple/index.test.ts similarity index 93% rename from api/api/v1/notifications/destroy_multiple/index.test.ts rename to packages/api/routes/api/v1/notifications/destroy_multiple/index.test.ts index cb292b6b..558ad1b5 100644 --- a/api/api/v1/notifications/destroy_multiple/index.test.ts +++ b/packages/api/routes/api/v1/notifications/destroy_multiple/index.test.ts @@ -1,7 +1,11 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import type { Notification } from "@versia/client/schemas"; -import type { z } from "zod"; -import { generateClient, getTestStatuses, getTestUsers } from "~/tests/utils"; +import { + generateClient, + getTestStatuses, + getTestUsers, +} from "@versia-server/tests"; +import type { z } from "zod/v4"; const { users, deleteUsers } = await getTestUsers(2); const statuses = await getTestStatuses(5, users[0]); diff --git a/api/api/v1/notifications/destroy_multiple/index.ts b/packages/api/routes/api/v1/notifications/destroy_multiple/index.ts similarity index 78% rename from api/api/v1/notifications/destroy_multiple/index.ts rename to packages/api/routes/api/v1/notifications/destroy_multiple/index.ts index 9a17213f..23dd2dba 100644 --- a/api/api/v1/notifications/destroy_multiple/index.ts +++ b/packages/api/routes/api/v1/notifications/destroy_multiple/index.ts @@ -1,9 +1,13 @@ import { RolePermission } from "@versia/client/schemas"; -import { describeRoute } from "hono-openapi"; -import { validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError, qsQuery } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { ApiError } from "@versia-server/kit"; +import { + apiRoute, + auth, + handleZodError, + qsQuery, +} from "@versia-server/kit/api"; +import { describeRoute, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.delete( @@ -27,7 +31,7 @@ export default apiRoute((app) => validator( "query", z.object({ - ids: z.array(z.string().uuid()), + ids: z.array(z.uuid()), }), handleZodError, ), diff --git a/api/api/v1/notifications/index.test.ts b/packages/api/routes/api/v1/notifications/index.test.ts similarity index 96% rename from api/api/v1/notifications/index.test.ts rename to packages/api/routes/api/v1/notifications/index.test.ts index d03ebb89..84acbdcd 100644 --- a/api/api/v1/notifications/index.test.ts +++ b/packages/api/routes/api/v1/notifications/index.test.ts @@ -1,5 +1,9 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestStatuses, getTestUsers } from "~/tests/utils"; +import { + generateClient, + getTestStatuses, + getTestUsers, +} from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(2); const timeline = (await getTestStatuses(5, users[0])).toReversed(); diff --git a/api/api/v1/notifications/index.ts b/packages/api/routes/api/v1/notifications/index.ts similarity index 91% rename from api/api/v1/notifications/index.ts rename to packages/api/routes/api/v1/notifications/index.ts index 362974af..bd4ea34a 100644 --- a/api/api/v1/notifications/index.ts +++ b/packages/api/routes/api/v1/notifications/index.ts @@ -4,14 +4,13 @@ import { RolePermission, zBoolean, } from "@versia/client/schemas"; -import { Timeline } from "@versia/kit/db"; -import { Notifications } from "@versia/kit/tables"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, handleZodError } from "@versia-server/kit/api"; +import { Timeline } from "@versia-server/kit/db"; +import { Notifications } from "@versia-server/kit/tables"; import { and, eq, gt, gte, inArray, lt, not, sql } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.get( @@ -47,17 +46,17 @@ export default apiRoute((app) => "query", z .object({ - max_id: NotificationSchema.shape.id.optional().openapi({ + max_id: NotificationSchema.shape.id.optional().meta({ description: "All results returned will be lesser than this ID. In effect, sets an upper bound on results.", example: "8d35243d-b959-43e2-8bac-1a9d4eaea2aa", }), - since_id: NotificationSchema.shape.id.optional().openapi({ + since_id: NotificationSchema.shape.id.optional().meta({ description: "All results returned will be greater than this ID. In effect, sets a lower bound on results.", example: undefined, }), - min_id: NotificationSchema.shape.id.optional().openapi({ + min_id: NotificationSchema.shape.id.optional().meta({ description: "Returns results immediately newer than this ID. In effect, sets a cursor at this ID and paginates forward.", example: undefined, @@ -68,27 +67,27 @@ export default apiRoute((app) => .min(1) .max(80) .default(40) - .openapi({ + .meta({ description: "Maximum number of results to return.", }), types: z .array(NotificationSchema.shape.type) .optional() - .openapi({ + .meta({ description: "Types to include in the result.", }), exclude_types: z .array(NotificationSchema.shape.type) .optional() - .openapi({ + .meta({ description: "Types to exclude from the results.", }), - account_id: AccountSchema.shape.id.optional().openapi({ + account_id: AccountSchema.shape.id.optional().meta({ description: "Return only notifications received from the specified account.", }), // TODO: Implement - include_filtered: zBoolean.default(false).openapi({ + include_filtered: zBoolean.default(false).meta({ description: "Whether to include notifications filtered by the user's NotificationPolicy.", }), diff --git a/api/api/v1/profile/avatar.test.ts b/packages/api/routes/api/v1/profile/avatar.test.ts similarity index 94% rename from api/api/v1/profile/avatar.test.ts rename to packages/api/routes/api/v1/profile/avatar.test.ts index 65d0a282..dda97144 100644 --- a/api/api/v1/profile/avatar.test.ts +++ b/packages/api/routes/api/v1/profile/avatar.test.ts @@ -1,5 +1,5 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestUsers } from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(2); diff --git a/api/api/v1/profile/avatar.ts b/packages/api/routes/api/v1/profile/avatar.ts similarity index 89% rename from api/api/v1/profile/avatar.ts rename to packages/api/routes/api/v1/profile/avatar.ts index 06038c51..2f9cbb2e 100644 --- a/api/api/v1/profile/avatar.ts +++ b/packages/api/routes/api/v1/profile/avatar.ts @@ -1,8 +1,7 @@ import { Account, RolePermission } from "@versia/client/schemas"; -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { apiRoute, auth } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth } from "@versia-server/kit/api"; +import { describeRoute, resolver } from "hono-openapi"; export default apiRoute((app) => app.delete( diff --git a/api/api/v1/profile/header.test.ts b/packages/api/routes/api/v1/profile/header.test.ts similarity index 93% rename from api/api/v1/profile/header.test.ts rename to packages/api/routes/api/v1/profile/header.test.ts index 3bb348fe..c3277f45 100644 --- a/api/api/v1/profile/header.test.ts +++ b/packages/api/routes/api/v1/profile/header.test.ts @@ -1,5 +1,5 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestUsers } from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(3); diff --git a/api/api/v1/profile/header.ts b/packages/api/routes/api/v1/profile/header.ts similarity index 88% rename from api/api/v1/profile/header.ts rename to packages/api/routes/api/v1/profile/header.ts index facaa7df..630f89cd 100644 --- a/api/api/v1/profile/header.ts +++ b/packages/api/routes/api/v1/profile/header.ts @@ -1,8 +1,7 @@ import { Account, RolePermission } from "@versia/client/schemas"; -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { apiRoute, auth } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth } from "@versia-server/kit/api"; +import { describeRoute, resolver } from "hono-openapi"; export default apiRoute((app) => app.delete( diff --git a/api/api/v1/push/subscription/index.delete.ts b/packages/api/routes/api/v1/push/subscription/index.delete.ts similarity index 84% rename from api/api/v1/push/subscription/index.delete.ts rename to packages/api/routes/api/v1/push/subscription/index.delete.ts index 773274be..7661aa74 100644 --- a/api/api/v1/push/subscription/index.delete.ts +++ b/packages/api/routes/api/v1/push/subscription/index.delete.ts @@ -1,10 +1,9 @@ import { RolePermission } from "@versia/client/schemas"; -import { PushSubscription } from "@versia/kit/db"; -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth } from "@versia-server/kit/api"; +import { PushSubscription } from "@versia-server/kit/db"; +import { describeRoute, resolver } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.delete( diff --git a/api/api/v1/push/subscription/index.get.ts b/packages/api/routes/api/v1/push/subscription/index.get.ts similarity index 86% rename from api/api/v1/push/subscription/index.get.ts rename to packages/api/routes/api/v1/push/subscription/index.get.ts index 84705032..8b4e3b7f 100644 --- a/api/api/v1/push/subscription/index.get.ts +++ b/packages/api/routes/api/v1/push/subscription/index.get.ts @@ -2,11 +2,10 @@ import { RolePermission, WebPushSubscription as WebPushSubscriptionSchema, } from "@versia/client/schemas"; -import { PushSubscription } from "@versia/kit/db"; -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { apiRoute, auth } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth } from "@versia-server/kit/api"; +import { PushSubscription } from "@versia-server/kit/db"; +import { describeRoute, resolver } from "hono-openapi"; export default apiRoute((app) => app.get( diff --git a/api/api/v1/push/subscription/index.post.ts b/packages/api/routes/api/v1/push/subscription/index.post.ts similarity index 90% rename from api/api/v1/push/subscription/index.post.ts rename to packages/api/routes/api/v1/push/subscription/index.post.ts index 814b8ee5..cdd7fbac 100644 --- a/api/api/v1/push/subscription/index.post.ts +++ b/packages/api/routes/api/v1/push/subscription/index.post.ts @@ -3,12 +3,16 @@ import { WebPushSubscriptionInput, WebPushSubscription as WebPushSubscriptionSchema, } from "@versia/client/schemas"; -import { PushSubscription } from "@versia/kit/db"; +import { ApiError } from "@versia-server/kit"; +import { + apiRoute, + auth, + handleZodError, + jsonOrForm, +} from "@versia-server/kit/api"; +import { PushSubscription } from "@versia-server/kit/db"; import { randomUUIDv7 } from "bun"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { apiRoute, auth, handleZodError, jsonOrForm } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { describeRoute, resolver, validator } from "hono-openapi"; export default apiRoute((app) => app.post( diff --git a/api/api/v1/push/subscription/index.put.ts b/packages/api/routes/api/v1/push/subscription/index.put.ts similarity index 90% rename from api/api/v1/push/subscription/index.put.ts rename to packages/api/routes/api/v1/push/subscription/index.put.ts index 9952f326..54d5a5a0 100644 --- a/api/api/v1/push/subscription/index.put.ts +++ b/packages/api/routes/api/v1/push/subscription/index.put.ts @@ -3,11 +3,15 @@ import { WebPushSubscriptionInput, WebPushSubscription as WebPushSubscriptionSchema, } from "@versia/client/schemas"; -import { PushSubscription } from "@versia/kit/db"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { apiRoute, auth, handleZodError, jsonOrForm } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { ApiError } from "@versia-server/kit"; +import { + apiRoute, + auth, + handleZodError, + jsonOrForm, +} from "@versia-server/kit/api"; +import { PushSubscription } from "@versia-server/kit/db"; +import { describeRoute, resolver, validator } from "hono-openapi"; export default apiRoute((app) => app.put( diff --git a/api/api/v1/push/subscription/index.test.ts b/packages/api/routes/api/v1/push/subscription/index.test.ts similarity index 98% rename from api/api/v1/push/subscription/index.test.ts rename to packages/api/routes/api/v1/push/subscription/index.test.ts index 0485c2df..fc78238f 100644 --- a/api/api/v1/push/subscription/index.test.ts +++ b/packages/api/routes/api/v1/push/subscription/index.test.ts @@ -1,7 +1,7 @@ import { afterAll, beforeEach, describe, expect, test } from "bun:test"; -import { PushSubscription } from "@versia/kit/db"; +import { PushSubscription } from "@versia-server/kit/db"; +import { generateClient, getTestUsers } from "@versia-server/tests"; import { randomUUIDv7 } from "bun"; -import { generateClient, getTestUsers } from "~/tests/utils"; const { users, tokens, deleteUsers } = await getTestUsers(2); diff --git a/api/api/v1/roles/[id]/index.test.ts b/packages/api/routes/api/v1/roles/[id]/index.test.ts similarity index 98% rename from api/api/v1/roles/[id]/index.test.ts rename to packages/api/routes/api/v1/roles/[id]/index.test.ts index f18f6f11..a5ff6794 100644 --- a/api/api/v1/roles/[id]/index.test.ts +++ b/packages/api/routes/api/v1/roles/[id]/index.test.ts @@ -1,8 +1,8 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import { RolePermission } from "@versia/client/schemas"; -import { Role } from "@versia/kit/db"; +import { Role } from "@versia-server/kit/db"; +import { generateClient, getTestUsers } from "@versia-server/tests"; import { randomUUIDv7 } from "bun"; -import { generateClient, getTestUsers } from "~/tests/utils"; const { users, deleteUsers } = await getTestUsers(2); let role: Role; diff --git a/api/api/v1/roles/[id]/index.ts b/packages/api/routes/api/v1/roles/[id]/index.ts similarity index 91% rename from api/api/v1/roles/[id]/index.ts rename to packages/api/routes/api/v1/roles/[id]/index.ts index 6890e435..c642d82e 100644 --- a/api/api/v1/roles/[id]/index.ts +++ b/packages/api/routes/api/v1/roles/[id]/index.ts @@ -1,10 +1,9 @@ import { RolePermission, Role as RoleSchema } from "@versia/client/schemas"; -import { Role } from "@versia/kit/db"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, handleZodError } from "@versia-server/kit/api"; +import { Role } from "@versia-server/kit/db"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => { app.get( @@ -28,7 +27,7 @@ export default apiRoute((app) => { auth({ auth: true, }), - validator("param", z.object({ id: z.string().uuid() }), handleZodError), + validator("param", z.object({ id: z.uuid() }), handleZodError), async (context) => { const { id } = context.req.valid("param"); @@ -62,7 +61,7 @@ export default apiRoute((app) => { validator( "param", z.object({ - id: z.string().uuid(), + id: z.uuid(), }), handleZodError, ), @@ -118,7 +117,7 @@ export default apiRoute((app) => { } await role.update({ - permissions: permissions as unknown as RolePermission[], + permissions, priority, description, icon, @@ -150,7 +149,7 @@ export default apiRoute((app) => { validator( "param", z.object({ - id: z.string().uuid(), + id: z.uuid(), }), handleZodError, ), diff --git a/api/api/v1/roles/index.test.ts b/packages/api/routes/api/v1/roles/index.test.ts similarity index 96% rename from api/api/v1/roles/index.test.ts rename to packages/api/routes/api/v1/roles/index.test.ts index ae8cf568..f42764fa 100644 --- a/api/api/v1/roles/index.test.ts +++ b/packages/api/routes/api/v1/roles/index.test.ts @@ -1,9 +1,9 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import { RolePermission } from "@versia/client/schemas"; -import { Role } from "@versia/kit/db"; +import { config } from "@versia-server/config"; +import { Role } from "@versia-server/kit/db"; +import { generateClient, getTestUsers } from "@versia-server/tests"; import { randomUUIDv7 } from "bun"; -import { config } from "~/config.ts"; -import { generateClient, getTestUsers } from "~/tests/utils"; const { users, deleteUsers } = await getTestUsers(1); let role: Role; diff --git a/api/api/v1/roles/index.ts b/packages/api/routes/api/v1/roles/index.ts similarity index 92% rename from api/api/v1/roles/index.ts rename to packages/api/routes/api/v1/roles/index.ts index cf229f8f..4c71eb4f 100644 --- a/api/api/v1/roles/index.ts +++ b/packages/api/routes/api/v1/roles/index.ts @@ -1,11 +1,10 @@ import { RolePermission, Role as RoleSchema } from "@versia/client/schemas"; -import { Role } from "@versia/kit/db"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, handleZodError } from "@versia-server/kit/api"; +import { Role } from "@versia-server/kit/db"; import { randomUUIDv7 } from "bun"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => { app.get( diff --git a/api/api/v1/statuses/[id]/context.test.ts b/packages/api/routes/api/v1/statuses/[id]/context.test.ts similarity index 96% rename from api/api/v1/statuses/[id]/context.test.ts rename to packages/api/routes/api/v1/statuses/[id]/context.test.ts index a7386c55..dc1c4508 100644 --- a/api/api/v1/statuses/[id]/context.test.ts +++ b/packages/api/routes/api/v1/statuses/[id]/context.test.ts @@ -1,5 +1,9 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestStatuses, getTestUsers } from "~/tests/utils"; +import { + generateClient, + getTestStatuses, + getTestUsers, +} from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(3); const statuses = await getTestStatuses(1, users[0]); diff --git a/api/api/v1/statuses/[id]/context.ts b/packages/api/routes/api/v1/statuses/[id]/context.ts similarity index 89% rename from api/api/v1/statuses/[id]/context.ts rename to packages/api/routes/api/v1/statuses/[id]/context.ts index 298edc05..ec6b3fe1 100644 --- a/api/api/v1/statuses/[id]/context.ts +++ b/packages/api/routes/api/v1/statuses/[id]/context.ts @@ -2,10 +2,9 @@ import { Context as ContextSchema, RolePermission, } from "@versia/client/schemas"; -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { apiRoute, auth, withNoteParam } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, withNoteParam } from "@versia-server/kit/api"; +import { describeRoute, resolver } from "hono-openapi"; export default apiRoute((app) => app.get( diff --git a/api/api/v1/statuses/[id]/favourite.test.ts b/packages/api/routes/api/v1/statuses/[id]/favourite.test.ts similarity index 92% rename from api/api/v1/statuses/[id]/favourite.test.ts rename to packages/api/routes/api/v1/statuses/[id]/favourite.test.ts index 84a30665..b696fb29 100644 --- a/api/api/v1/statuses/[id]/favourite.test.ts +++ b/packages/api/routes/api/v1/statuses/[id]/favourite.test.ts @@ -1,5 +1,9 @@ import { afterAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestStatuses, getTestUsers } from "~/tests/utils"; +import { + generateClient, + getTestStatuses, + getTestUsers, +} from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(5); const timeline = (await getTestStatuses(2, users[0])).toReversed(); diff --git a/api/api/v1/statuses/[id]/favourite.ts b/packages/api/routes/api/v1/statuses/[id]/favourite.ts similarity index 85% rename from api/api/v1/statuses/[id]/favourite.ts rename to packages/api/routes/api/v1/statuses/[id]/favourite.ts index 2ba66ab8..120e7c49 100644 --- a/api/api/v1/statuses/[id]/favourite.ts +++ b/packages/api/routes/api/v1/statuses/[id]/favourite.ts @@ -1,8 +1,7 @@ import { RolePermission, Status as StatusSchema } from "@versia/client/schemas"; -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { apiRoute, auth, withNoteParam } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, withNoteParam } from "@versia-server/kit/api"; +import { describeRoute, resolver } from "hono-openapi"; export default apiRoute((app) => app.post( @@ -39,7 +38,7 @@ export default apiRoute((app) => const { user } = context.get("auth"); const note = context.get("note"); - await user.like(note); + await note.like(user); await note.reload(user.id); diff --git a/api/api/v1/statuses/[id]/favourited_by.test.ts b/packages/api/routes/api/v1/statuses/[id]/favourited_by.test.ts similarity index 92% rename from api/api/v1/statuses/[id]/favourited_by.test.ts rename to packages/api/routes/api/v1/statuses/[id]/favourited_by.test.ts index f8521545..b75a995f 100644 --- a/api/api/v1/statuses/[id]/favourited_by.test.ts +++ b/packages/api/routes/api/v1/statuses/[id]/favourited_by.test.ts @@ -1,5 +1,9 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestStatuses, getTestUsers } from "~/tests/utils"; +import { + generateClient, + getTestStatuses, + getTestUsers, +} from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(5); const timeline = (await getTestStatuses(5, users[0])).toReversed(); diff --git a/api/api/v1/statuses/[id]/favourited_by.ts b/packages/api/routes/api/v1/statuses/[id]/favourited_by.ts similarity index 81% rename from api/api/v1/statuses/[id]/favourited_by.ts rename to packages/api/routes/api/v1/statuses/[id]/favourited_by.ts index ed8b31fd..70c501b0 100644 --- a/api/api/v1/statuses/[id]/favourited_by.ts +++ b/packages/api/routes/api/v1/statuses/[id]/favourited_by.ts @@ -2,14 +2,18 @@ import { Account as AccountSchema, RolePermission, } from "@versia/client/schemas"; -import { Timeline } from "@versia/kit/db"; -import { Users } from "@versia/kit/tables"; +import { ApiError } from "@versia-server/kit"; +import { + apiRoute, + auth, + handleZodError, + withNoteParam, +} from "@versia-server/kit/api"; +import { Timeline } from "@versia-server/kit/db"; +import { Users } from "@versia-server/kit/tables"; import { and, gt, gte, lt, sql } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError, withNoteParam } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.get( @@ -33,7 +37,7 @@ export default apiRoute((app) => link: z .string() .optional() - .openapi({ + .meta({ description: "Links to the next and previous pages", example: @@ -60,30 +64,24 @@ export default apiRoute((app) => validator( "query", z.object({ - max_id: AccountSchema.shape.id.optional().openapi({ + max_id: AccountSchema.shape.id.optional().meta({ description: "All results returned will be lesser than this ID. In effect, sets an upper bound on results.", example: "8d35243d-b959-43e2-8bac-1a9d4eaea2aa", }), - since_id: AccountSchema.shape.id.optional().openapi({ + since_id: AccountSchema.shape.id.optional().meta({ description: "All results returned will be greater than this ID. In effect, sets a lower bound on results.", example: undefined, }), - min_id: AccountSchema.shape.id.optional().openapi({ + min_id: AccountSchema.shape.id.optional().meta({ description: "Returns results immediately newer than this ID. In effect, sets a cursor at this ID and paginates forward.", example: undefined, }), - limit: z.coerce - .number() - .int() - .min(1) - .max(80) - .default(40) - .openapi({ - description: "Maximum number of results to return.", - }), + limit: z.coerce.number().int().min(1).max(80).default(40).meta({ + description: "Maximum number of results to return.", + }), }), handleZodError, ), diff --git a/api/api/v1/statuses/[id]/index.test.ts b/packages/api/routes/api/v1/statuses/[id]/index.test.ts similarity index 94% rename from api/api/v1/statuses/[id]/index.test.ts rename to packages/api/routes/api/v1/statuses/[id]/index.test.ts index 6a98f25a..55331a85 100644 --- a/api/api/v1/statuses/[id]/index.test.ts +++ b/packages/api/routes/api/v1/statuses/[id]/index.test.ts @@ -1,5 +1,9 @@ import { afterAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestStatuses, getTestUsers } from "~/tests/utils"; +import { + generateClient, + getTestStatuses, + getTestUsers, +} from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(2); const statuses = await getTestStatuses(1, users[0]); diff --git a/api/api/v1/statuses/[id]/index.ts b/packages/api/routes/api/v1/statuses/[id]/index.ts similarity index 91% rename from api/api/v1/statuses/[id]/index.ts rename to packages/api/routes/api/v1/statuses/[id]/index.ts index 350a2862..03bafe88 100644 --- a/api/api/v1/statuses/[id]/index.ts +++ b/packages/api/routes/api/v1/statuses/[id]/index.ts @@ -6,22 +6,24 @@ import { StatusSource as StatusSourceSchema, zBoolean, } from "@versia/client/schemas"; -import { Emoji, Media } from "@versia/kit/db"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; +import * as VersiaEntities from "@versia/sdk/entities"; +import { config } from "@versia-server/config"; +import { ApiError } from "@versia-server/kit"; import { apiRoute, auth, handleZodError, jsonOrForm, withNoteParam, -} from "@/api"; +} from "@versia-server/kit/api"; +import { Emoji, Media } from "@versia-server/kit/db"; +import { + parseMentionsFromText, + versiaTextToHtml, +} from "@versia-server/kit/parsers"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; import { sanitizedHtmlStrip } from "@/sanitization"; -import { ApiError } from "~/classes/errors/api-error"; -import { contentToHtml, parseTextMentions } from "~/classes/functions/status"; -import { config } from "~/config.ts"; -import * as VersiaEntities from "~/packages/sdk/entities"; const schema = z .object({ @@ -35,7 +37,7 @@ const schema = z "Status contains blocked words", ) .optional() - .openapi({ + .meta({ description: "The text content of the status. If media_ids is provided, this becomes optional. Attaching a poll is optional while status is provided.", }), @@ -43,7 +45,7 @@ const schema = z content_type: z .enum(["text/plain", "text/html", "text/markdown"]) .default("text/plain") - .openapi({ + .meta({ description: "Content-Type of the status text.", example: "text/markdown", }), @@ -51,15 +53,15 @@ const schema = z .array(AttachmentSchema.shape.id) .max(config.validation.notes.max_attachments) .default([]) - .openapi({ + .meta({ description: "Include Attachment IDs to be attached as media. If provided, status becomes optional, and poll cannot be used.", }), - spoiler_text: StatusSourceSchema.shape.spoiler_text.optional().openapi({ + spoiler_text: StatusSourceSchema.shape.spoiler_text.optional().meta({ description: "Text to be shown as a warning or subject before the actual content. Statuses are generally collapsed behind this field.", }), - sensitive: zBoolean.default(false).openapi({ + sensitive: zBoolean.default(false).meta({ description: "Mark status and attached media as sensitive?", }), language: StatusSchema.shape.language.optional(), @@ -71,7 +73,7 @@ const schema = z ) .max(config.validation.polls.max_options) .optional() - .openapi({ + .meta({ description: "Possible answers to the poll. If provided, media_ids cannot be used, and poll[expires_in] must be provided.", }), @@ -81,14 +83,14 @@ const schema = z .min(config.validation.polls.min_duration_seconds) .max(config.validation.polls.max_duration_seconds) .optional() - .openapi({ + .meta({ description: "Duration that the poll should be open, in seconds. If provided, media_ids cannot be used, and poll[options] must be provided.", }), - "poll[multiple]": zBoolean.optional().openapi({ + "poll[multiple]": zBoolean.optional().meta({ description: "Allow multiple choices?", }), - "poll[hide_totals]": zBoolean.optional().openapi({ + "poll[hide_totals]": zBoolean.optional().meta({ description: "Hide vote counts until the poll ends?", }), }) @@ -256,7 +258,7 @@ export default apiRoute((app) => { : undefined; const parsedMentions = statusText - ? await parseTextMentions(statusText) + ? await parseMentionsFromText(statusText) : []; const parsedEmojis = statusText @@ -267,7 +269,7 @@ export default apiRoute((app) => { spoilerText: sanitizedSpoilerText, sensitive, content: content - ? await contentToHtml(content, parsedMentions) + ? await versiaTextToHtml(content, parsedMentions) : undefined, }); diff --git a/api/api/v1/statuses/[id]/pin.ts b/packages/api/routes/api/v1/statuses/[id]/pin.ts similarity index 89% rename from api/api/v1/statuses/[id]/pin.ts rename to packages/api/routes/api/v1/statuses/[id]/pin.ts index 3b9e9fa6..cc9f832b 100644 --- a/api/api/v1/statuses/[id]/pin.ts +++ b/packages/api/routes/api/v1/statuses/[id]/pin.ts @@ -1,10 +1,9 @@ import { RolePermission, Status as StatusSchema } from "@versia/client/schemas"; -import { db } from "@versia/kit/db"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, withNoteParam } from "@versia-server/kit/api"; +import { db } from "@versia-server/kit/db"; import { and, eq, type SQL } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { apiRoute, auth, withNoteParam } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { describeRoute, resolver } from "hono-openapi"; export default apiRoute((app) => app.post( diff --git a/api/api/v1/statuses/[id]/reactions/[name].test.ts b/packages/api/routes/api/v1/statuses/[id]/reactions/[name].test.ts similarity index 97% rename from api/api/v1/statuses/[id]/reactions/[name].test.ts rename to packages/api/routes/api/v1/statuses/[id]/reactions/[name].test.ts index e2042e10..ee644e28 100644 --- a/api/api/v1/statuses/[id]/reactions/[name].test.ts +++ b/packages/api/routes/api/v1/statuses/[id]/reactions/[name].test.ts @@ -1,8 +1,11 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; +import { Emoji, Media } from "@versia-server/kit/db"; +import { + generateClient, + getTestStatuses, + getTestUsers, +} from "@versia-server/tests"; import { randomUUIDv7 } from "bun"; -import { Emoji } from "~/classes/database/emoji"; -import { Media } from "~/classes/database/media"; -import { generateClient, getTestStatuses, getTestUsers } from "~/tests/utils"; const { users, deleteUsers } = await getTestUsers(3); const timeline = (await getTestStatuses(2, users[0])).toReversed(); diff --git a/api/api/v1/statuses/[id]/reactions/[name].ts b/packages/api/routes/api/v1/statuses/[id]/reactions/[name].ts similarity index 93% rename from api/api/v1/statuses/[id]/reactions/[name].ts rename to packages/api/routes/api/v1/statuses/[id]/reactions/[name].ts index 4070637b..5130502a 100644 --- a/api/api/v1/statuses/[id]/reactions/[name].ts +++ b/packages/api/routes/api/v1/statuses/[id]/reactions/[name].ts @@ -1,15 +1,19 @@ import { RolePermission, Status as StatusSchema } from "@versia/client/schemas"; -import { Emoji } from "@versia/kit/db"; -import { Emojis } from "@versia/kit/tables"; +import { ApiError } from "@versia-server/kit"; +import { + apiRoute, + auth, + handleZodError, + withNoteParam, +} from "@versia-server/kit/api"; +import { Emoji } from "@versia-server/kit/db"; +import { Emojis } from "@versia-server/kit/tables"; import { and, eq, isNull } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; +import { describeRoute, resolver, validator } from "hono-openapi"; import emojis from "unicode-emoji-json/data-ordered-emoji.json" with { type: "json", }; -import { z } from "zod"; -import { apiRoute, auth, handleZodError, withNoteParam } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { z } from "zod/v4"; export default apiRoute((app) => { app.put( @@ -105,7 +109,7 @@ export default apiRoute((app) => { emoji = unicodeEmoji; } - await user.react(note, emoji); + await note.react(user, emoji); // Reload note to get updated reactions await note.reload(user.id); @@ -199,7 +203,7 @@ export default apiRoute((app) => { emoji = unicodeEmoji; } - await user.unreact(note, emoji); + await note.unreact(user, emoji); // Reload note to get updated reactions await note.reload(user.id); diff --git a/api/api/v1/statuses/[id]/reactions/index.test.ts b/packages/api/routes/api/v1/statuses/[id]/reactions/index.test.ts similarity index 96% rename from api/api/v1/statuses/[id]/reactions/index.test.ts rename to packages/api/routes/api/v1/statuses/[id]/reactions/index.test.ts index c214f8d5..2666971f 100644 --- a/api/api/v1/statuses/[id]/reactions/index.test.ts +++ b/packages/api/routes/api/v1/statuses/[id]/reactions/index.test.ts @@ -1,5 +1,9 @@ import { afterAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestStatuses, getTestUsers } from "~/tests/utils"; +import { + generateClient, + getTestStatuses, + getTestUsers, +} from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(3); const timeline = (await getTestStatuses(2, users[0])).toReversed(); diff --git a/api/api/v1/statuses/[id]/reactions/index.ts b/packages/api/routes/api/v1/statuses/[id]/reactions/index.ts similarity index 86% rename from api/api/v1/statuses/[id]/reactions/index.ts rename to packages/api/routes/api/v1/statuses/[id]/reactions/index.ts index e1b372f9..8436dd5b 100644 --- a/api/api/v1/statuses/[id]/reactions/index.ts +++ b/packages/api/routes/api/v1/statuses/[id]/reactions/index.ts @@ -2,11 +2,10 @@ import { NoteReactionWithAccounts, RolePermission, } from "@versia/client/schemas"; -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, withNoteParam } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, withNoteParam } from "@versia-server/kit/api"; +import { describeRoute, resolver } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.get( diff --git a/api/api/v1/statuses/[id]/reblog.test.ts b/packages/api/routes/api/v1/statuses/[id]/reblog.test.ts similarity index 95% rename from api/api/v1/statuses/[id]/reblog.test.ts rename to packages/api/routes/api/v1/statuses/[id]/reblog.test.ts index 26c447a7..ac75523e 100644 --- a/api/api/v1/statuses/[id]/reblog.test.ts +++ b/packages/api/routes/api/v1/statuses/[id]/reblog.test.ts @@ -1,5 +1,9 @@ import { afterAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestStatuses, getTestUsers } from "~/tests/utils"; +import { + generateClient, + getTestStatuses, + getTestUsers, +} from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(2); const statuses = await getTestStatuses(1, users[0]); diff --git a/api/api/v1/statuses/[id]/reblog.ts b/packages/api/routes/api/v1/statuses/[id]/reblog.ts similarity index 85% rename from api/api/v1/statuses/[id]/reblog.ts rename to packages/api/routes/api/v1/statuses/[id]/reblog.ts index bd6185bd..5325ce46 100644 --- a/api/api/v1/statuses/[id]/reblog.ts +++ b/packages/api/routes/api/v1/statuses/[id]/reblog.ts @@ -1,9 +1,13 @@ import { RolePermission, Status as StatusSchema } from "@versia/client/schemas"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, jsonOrForm, withNoteParam } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { ApiError } from "@versia-server/kit"; +import { + apiRoute, + auth, + jsonOrForm, + withNoteParam, +} from "@versia-server/kit/api"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.post( @@ -50,7 +54,7 @@ export default apiRoute((app) => const { user } = context.get("auth"); const note = context.get("note"); - const reblog = await user.reblog(note, visibility); + const reblog = await note.reblog(user, visibility); return context.json(await reblog.toApi(user), 200); }, diff --git a/api/api/v1/statuses/[id]/reblogged_by.test.ts b/packages/api/routes/api/v1/statuses/[id]/reblogged_by.test.ts similarity index 92% rename from api/api/v1/statuses/[id]/reblogged_by.test.ts rename to packages/api/routes/api/v1/statuses/[id]/reblogged_by.test.ts index 6d1e0f78..5f999ab1 100644 --- a/api/api/v1/statuses/[id]/reblogged_by.test.ts +++ b/packages/api/routes/api/v1/statuses/[id]/reblogged_by.test.ts @@ -1,5 +1,9 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestStatuses, getTestUsers } from "~/tests/utils"; +import { + generateClient, + getTestStatuses, + getTestUsers, +} from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(5); const timeline = (await getTestStatuses(5, users[0])).toReversed(); diff --git a/api/api/v1/statuses/[id]/reblogged_by.ts b/packages/api/routes/api/v1/statuses/[id]/reblogged_by.ts similarity index 81% rename from api/api/v1/statuses/[id]/reblogged_by.ts rename to packages/api/routes/api/v1/statuses/[id]/reblogged_by.ts index 3d0aee6f..12ad42c3 100644 --- a/api/api/v1/statuses/[id]/reblogged_by.ts +++ b/packages/api/routes/api/v1/statuses/[id]/reblogged_by.ts @@ -2,14 +2,18 @@ import { Account as AccountSchema, RolePermission, } from "@versia/client/schemas"; -import { Timeline } from "@versia/kit/db"; -import { Users } from "@versia/kit/tables"; +import { ApiError } from "@versia-server/kit"; +import { + apiRoute, + auth, + handleZodError, + withNoteParam, +} from "@versia-server/kit/api"; +import { Timeline } from "@versia-server/kit/db"; +import { Users } from "@versia-server/kit/tables"; import { and, gt, gte, lt, sql } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError, withNoteParam } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.get( @@ -33,7 +37,7 @@ export default apiRoute((app) => link: z .string() .optional() - .openapi({ + .meta({ description: "Links to the next and previous pages", example: @@ -60,30 +64,24 @@ export default apiRoute((app) => validator( "query", z.object({ - max_id: AccountSchema.shape.id.optional().openapi({ + max_id: AccountSchema.shape.id.optional().meta({ description: "All results returned will be lesser than this ID. In effect, sets an upper bound on results.", example: "8d35243d-b959-43e2-8bac-1a9d4eaea2aa", }), - since_id: AccountSchema.shape.id.optional().openapi({ + since_id: AccountSchema.shape.id.optional().meta({ description: "All results returned will be greater than this ID. In effect, sets a lower bound on results.", example: undefined, }), - min_id: AccountSchema.shape.id.optional().openapi({ + min_id: AccountSchema.shape.id.optional().meta({ description: "Returns results immediately newer than this ID. In effect, sets a cursor at this ID and paginates forward.", example: undefined, }), - limit: z.coerce - .number() - .int() - .min(1) - .max(80) - .default(40) - .openapi({ - description: "Maximum number of results to return.", - }), + limit: z.coerce.number().int().min(1).max(80).default(40).meta({ + description: "Maximum number of results to return.", + }), }), handleZodError, ), diff --git a/api/api/v1/statuses/[id]/source.ts b/packages/api/routes/api/v1/statuses/[id]/source.ts similarity index 88% rename from api/api/v1/statuses/[id]/source.ts rename to packages/api/routes/api/v1/statuses/[id]/source.ts index c5a9f69c..8f82b24c 100644 --- a/api/api/v1/statuses/[id]/source.ts +++ b/packages/api/routes/api/v1/statuses/[id]/source.ts @@ -2,10 +2,9 @@ import { RolePermission, StatusSource as StatusSourceSchema, } from "@versia/client/schemas"; -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { apiRoute, auth, withNoteParam } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, withNoteParam } from "@versia-server/kit/api"; +import { describeRoute, resolver } from "hono-openapi"; export default apiRoute((app) => app.get( diff --git a/api/api/v1/statuses/[id]/unfavourite.test.ts b/packages/api/routes/api/v1/statuses/[id]/unfavourite.test.ts similarity index 94% rename from api/api/v1/statuses/[id]/unfavourite.test.ts rename to packages/api/routes/api/v1/statuses/[id]/unfavourite.test.ts index 7b13db21..aa50ccfa 100644 --- a/api/api/v1/statuses/[id]/unfavourite.test.ts +++ b/packages/api/routes/api/v1/statuses/[id]/unfavourite.test.ts @@ -1,5 +1,9 @@ import { afterAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestStatuses, getTestUsers } from "~/tests/utils"; +import { + generateClient, + getTestStatuses, + getTestUsers, +} from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(5); const timeline = (await getTestStatuses(2, users[0])).toReversed(); diff --git a/api/api/v1/statuses/[id]/unfavourite.ts b/packages/api/routes/api/v1/statuses/[id]/unfavourite.ts similarity index 85% rename from api/api/v1/statuses/[id]/unfavourite.ts rename to packages/api/routes/api/v1/statuses/[id]/unfavourite.ts index 49f569cb..49837b2f 100644 --- a/api/api/v1/statuses/[id]/unfavourite.ts +++ b/packages/api/routes/api/v1/statuses/[id]/unfavourite.ts @@ -1,8 +1,7 @@ import { RolePermission, Status as StatusSchema } from "@versia/client/schemas"; -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { apiRoute, auth, withNoteParam } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, withNoteParam } from "@versia-server/kit/api"; +import { describeRoute, resolver } from "hono-openapi"; export default apiRoute((app) => app.post( @@ -40,7 +39,7 @@ export default apiRoute((app) => const { user } = context.get("auth"); const note = context.get("note"); - await user.unlike(note); + await note.unlike(user); await note.reload(user.id); diff --git a/api/api/v1/statuses/[id]/unpin.ts b/packages/api/routes/api/v1/statuses/[id]/unpin.ts similarity index 89% rename from api/api/v1/statuses/[id]/unpin.ts rename to packages/api/routes/api/v1/statuses/[id]/unpin.ts index f47ced42..3e219710 100644 --- a/api/api/v1/statuses/[id]/unpin.ts +++ b/packages/api/routes/api/v1/statuses/[id]/unpin.ts @@ -1,8 +1,7 @@ import { RolePermission, Status as StatusSchema } from "@versia/client/schemas"; -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { apiRoute, auth, withNoteParam } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, withNoteParam } from "@versia-server/kit/api"; +import { describeRoute, resolver } from "hono-openapi"; export default apiRoute((app) => app.post( diff --git a/api/api/v1/statuses/[id]/unreblog.test.ts b/packages/api/routes/api/v1/statuses/[id]/unreblog.test.ts similarity index 95% rename from api/api/v1/statuses/[id]/unreblog.test.ts rename to packages/api/routes/api/v1/statuses/[id]/unreblog.test.ts index 3d89db0a..26e40f95 100644 --- a/api/api/v1/statuses/[id]/unreblog.test.ts +++ b/packages/api/routes/api/v1/statuses/[id]/unreblog.test.ts @@ -1,5 +1,9 @@ import { afterAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestStatuses, getTestUsers } from "~/tests/utils"; +import { + generateClient, + getTestStatuses, + getTestUsers, +} from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(2); const statuses = await getTestStatuses(1, users[0]); diff --git a/api/api/v1/statuses/[id]/unreblog.ts b/packages/api/routes/api/v1/statuses/[id]/unreblog.ts similarity index 84% rename from api/api/v1/statuses/[id]/unreblog.ts rename to packages/api/routes/api/v1/statuses/[id]/unreblog.ts index 37f2d8eb..89bcc1f6 100644 --- a/api/api/v1/statuses/[id]/unreblog.ts +++ b/packages/api/routes/api/v1/statuses/[id]/unreblog.ts @@ -1,9 +1,8 @@ import { RolePermission, Status as StatusSchema } from "@versia/client/schemas"; -import { Note } from "@versia/kit/db"; -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { apiRoute, auth, withNoteParam } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, withNoteParam } from "@versia-server/kit/api"; +import { Note } from "@versia-server/kit/db"; +import { describeRoute, resolver } from "hono-openapi"; export default apiRoute((app) => app.post( @@ -40,7 +39,7 @@ export default apiRoute((app) => const { user } = context.get("auth"); const note = context.get("note"); - await user.unreblog(note); + await note.unreblog(user); const newNote = await Note.fromId(note.data.id, user.id); diff --git a/api/api/v1/statuses/index.test.ts b/packages/api/routes/api/v1/statuses/index.test.ts similarity index 93% rename from api/api/v1/statuses/index.test.ts rename to packages/api/routes/api/v1/statuses/index.test.ts index a1f7008a..52f2c633 100644 --- a/api/api/v1/statuses/index.test.ts +++ b/packages/api/routes/api/v1/statuses/index.test.ts @@ -1,12 +1,12 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import type { Status } from "@versia/client/schemas"; -import { db, Media } from "@versia/kit/db"; -import { Emojis } from "@versia/kit/tables"; +import { config } from "@versia-server/config"; +import { db, Media } from "@versia-server/kit/db"; +import { Emojis } from "@versia-server/kit/tables"; +import { generateClient, getTestUsers } from "@versia-server/tests"; import { randomUUIDv7 } from "bun"; import { eq } from "drizzle-orm"; -import type { z } from "zod"; -import { config } from "~/config.ts"; -import { generateClient, getTestUsers } from "~/tests/utils"; +import type { z } from "zod/v4"; const { users, deleteUsers } = await getTestUsers(5); let media: Media; @@ -87,7 +87,7 @@ describe("/api/v1/statuses", () => { expect(raw.status).toBe(422); expect(data).toMatchObject({ error: expect.stringContaining( - "must be at least 5 minutes in the future", + "Must be at least 5 minutes in the future", ), }); }); @@ -246,6 +246,20 @@ describe("/api/v1/statuses", () => { expect(ok3).toBe(false); }); + test("should work with an empty spoiler_text", async () => { + await using client = await generateClient(users[0]); + + const { data, ok } = await client.postStatus("Hello, world!", { + spoiler_text: "", + }); + + expect(ok).toBe(true); + expect(data).toMatchObject({ + content: "

Hello, world!

", + spoiler_text: "", + }); + }); + describe("mentions testing", () => { test("should correctly parse @mentions", async () => { await using client = await generateClient(users[0]); diff --git a/api/api/v1/statuses/index.ts b/packages/api/routes/api/v1/statuses/index.ts similarity index 86% rename from api/api/v1/statuses/index.ts rename to packages/api/routes/api/v1/statuses/index.ts index 7bcc0dd5..7b2a52ca 100644 --- a/api/api/v1/statuses/index.ts +++ b/packages/api/routes/api/v1/statuses/index.ts @@ -6,17 +6,24 @@ import { StatusSource as StatusSourceSchema, zBoolean, } from "@versia/client/schemas"; -import { Emoji, Media, Note } from "@versia/kit/db"; +import * as VersiaEntities from "@versia/sdk/entities"; +import { config } from "@versia-server/config"; +import { ApiError } from "@versia-server/kit"; +import { + apiRoute, + auth, + handleZodError, + jsonOrForm, +} from "@versia-server/kit/api"; +import { Emoji, Media, Note } from "@versia-server/kit/db"; +import { + parseMentionsFromText, + versiaTextToHtml, +} from "@versia-server/kit/parsers"; import { randomUUIDv7 } from "bun"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError, jsonOrForm } from "@/api"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; import { sanitizedHtmlStrip } from "@/sanitization"; -import { ApiError } from "~/classes/errors/api-error"; -import { contentToHtml, parseTextMentions } from "~/classes/functions/status"; -import { config } from "~/config.ts"; -import * as VersiaEntities from "~/packages/sdk/entities"; const schema = z .object({ @@ -30,7 +37,7 @@ const schema = z "Status contains blocked words", ) .optional() - .openapi({ + .meta({ description: "The text content of the status. If media_ids is provided, this becomes optional. Attaching a poll is optional while status is provided.", }), @@ -38,7 +45,7 @@ const schema = z content_type: z .enum(["text/plain", "text/html", "text/markdown"]) .default("text/plain") - .openapi({ + .meta({ description: "Content-Type of the status text.", example: "text/markdown", }), @@ -46,15 +53,15 @@ const schema = z .array(AttachmentSchema.shape.id) .max(config.validation.notes.max_attachments) .default([]) - .openapi({ + .meta({ description: "Include Attachment IDs to be attached as media. If provided, status becomes optional, and poll cannot be used.", }), - spoiler_text: StatusSourceSchema.shape.spoiler_text.optional().openapi({ + spoiler_text: StatusSourceSchema.shape.spoiler_text.optional().meta({ description: "Text to be shown as a warning or subject before the actual content. Statuses are generally collapsed behind this field.", }), - sensitive: zBoolean.default(false).openapi({ + sensitive: zBoolean.default(false).meta({ description: "Mark status and attached media as sensitive?", }), language: StatusSchema.shape.language.optional(), @@ -66,7 +73,7 @@ const schema = z ) .max(config.validation.polls.max_options) .optional() - .openapi({ + .meta({ description: "Possible answers to the poll. If provided, media_ids cannot be used, and poll[expires_in] must be provided.", }), @@ -76,39 +83,43 @@ const schema = z .min(config.validation.polls.min_duration_seconds) .max(config.validation.polls.max_duration_seconds) .optional() - .openapi({ + .meta({ description: "Duration that the poll should be open, in seconds. If provided, media_ids cannot be used, and poll[options] must be provided.", }), - "poll[multiple]": zBoolean.optional().openapi({ + "poll[multiple]": zBoolean.optional().meta({ description: "Allow multiple choices?", }), - "poll[hide_totals]": zBoolean.optional().openapi({ + "poll[hide_totals]": zBoolean.optional().meta({ description: "Hide vote counts until the poll ends?", }), - in_reply_to_id: StatusSchema.shape.id.optional().nullable().openapi({ + in_reply_to_id: StatusSchema.shape.id.optional().nullable().meta({ description: "ID of the status being replied to, if status is a reply.", }), /* Versia Server API Extension */ - quote_id: StatusSchema.shape.id.optional().nullable().openapi({ + quote_id: StatusSchema.shape.id.optional().nullable().meta({ description: "ID of the status being quoted, if status is a quote.", }), visibility: StatusSchema.shape.visibility.default("public"), - scheduled_at: z.coerce - .date() - .min( - new Date(Date.now() + 5 * 60 * 1000), - "must be at least 5 minutes in the future.", + scheduled_at: z.iso + .datetime() + .refine( + (date) => + new Date(date).getTime() >= + new Date(Date.now() + 5 * 60 * 1000).getTime(), + { + message: "must be at least 5 minutes in the future.", + }, ) .optional() .nullable() - .openapi({ + .meta({ description: "Datetime at which to schedule a status. Providing this parameter will cause ScheduledStatus to be returned instead of Status. Must be at least 5 minutes in the future.", }), /* Versia Server API Extension */ - local_only: zBoolean.default(false).openapi({ + local_only: zBoolean.default(false).meta({ description: "If true, this status will not be federated.", }), }) @@ -220,7 +231,7 @@ export default apiRoute((app) => : undefined; const parsedMentions = status - ? await parseTextMentions(status) + ? await parseMentionsFromText(status) : []; const parsedEmojis = status @@ -232,7 +243,7 @@ export default apiRoute((app) => authorId: user.id, visibility, content: content - ? await contentToHtml(content, parsedMentions) + ? await versiaTextToHtml(content, parsedMentions) : undefined, sensitive, spoilerText: sanitizedSpoilerText, diff --git a/api/api/v1/timelines/home.test.ts b/packages/api/routes/api/v1/timelines/home.test.ts similarity index 97% rename from api/api/v1/timelines/home.test.ts rename to packages/api/routes/api/v1/timelines/home.test.ts index 4b39cd9f..77ade702 100644 --- a/api/api/v1/timelines/home.test.ts +++ b/packages/api/routes/api/v1/timelines/home.test.ts @@ -1,6 +1,10 @@ import { afterAll, describe, expect, test } from "bun:test"; -import { config } from "~/config.ts"; -import { generateClient, getTestStatuses, getTestUsers } from "~/tests/utils"; +import { config } from "@versia-server/config"; +import { + generateClient, + getTestStatuses, + getTestUsers, +} from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(5); const timeline = (await getTestStatuses(10, users[0])).toReversed(); diff --git a/api/api/v1/timelines/home.ts b/packages/api/routes/api/v1/timelines/home.ts similarity index 84% rename from api/api/v1/timelines/home.ts rename to packages/api/routes/api/v1/timelines/home.ts index 24214226..a56bb7ca 100644 --- a/api/api/v1/timelines/home.ts +++ b/packages/api/routes/api/v1/timelines/home.ts @@ -1,12 +1,11 @@ import { RolePermission, Status as StatusSchema } from "@versia/client/schemas"; -import { Timeline } from "@versia/kit/db"; -import { Notes } from "@versia/kit/tables"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, handleZodError } from "@versia-server/kit/api"; +import { Timeline } from "@versia-server/kit/db"; +import { Notes } from "@versia-server/kit/tables"; import { and, eq, gt, gte, inArray, lt, or, sql } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.get( @@ -31,7 +30,7 @@ export default apiRoute((app) => link: z .string() .optional() - .openapi({ + .meta({ description: "Links to the next and previous pages", example: @@ -57,30 +56,24 @@ export default apiRoute((app) => validator( "query", z.object({ - max_id: StatusSchema.shape.id.optional().openapi({ + max_id: StatusSchema.shape.id.optional().meta({ description: "All results returned will be lesser than this ID. In effect, sets an upper bound on results.", example: "8d35243d-b959-43e2-8bac-1a9d4eaea2aa", }), - since_id: StatusSchema.shape.id.optional().openapi({ + since_id: StatusSchema.shape.id.optional().meta({ description: "All results returned will be greater than this ID. In effect, sets a lower bound on results.", example: undefined, }), - min_id: StatusSchema.shape.id.optional().openapi({ + min_id: StatusSchema.shape.id.optional().meta({ description: "Returns results immediately newer than this ID. In effect, sets a cursor at this ID and paginates forward.", example: undefined, }), - limit: z.coerce - .number() - .int() - .min(1) - .max(40) - .default(20) - .openapi({ - description: "Maximum number of results to return.", - }), + limit: z.coerce.number().int().min(1).max(40).default(20).meta({ + description: "Maximum number of results to return.", + }), }), handleZodError, ), diff --git a/api/api/v1/timelines/public.test.ts b/packages/api/routes/api/v1/timelines/public.test.ts similarity index 97% rename from api/api/v1/timelines/public.test.ts rename to packages/api/routes/api/v1/timelines/public.test.ts index 677c509a..bf43ec76 100644 --- a/api/api/v1/timelines/public.test.ts +++ b/packages/api/routes/api/v1/timelines/public.test.ts @@ -1,6 +1,10 @@ import { afterAll, describe, expect, test } from "bun:test"; -import { config } from "~/config.ts"; -import { generateClient, getTestStatuses, getTestUsers } from "~/tests/utils"; +import { config } from "@versia-server/config"; +import { + generateClient, + getTestStatuses, + getTestUsers, +} from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(5); const timeline = (await getTestStatuses(10, users[0])).toReversed(); diff --git a/api/api/v1/timelines/public.ts b/packages/api/routes/api/v1/timelines/public.ts similarity index 88% rename from api/api/v1/timelines/public.ts rename to packages/api/routes/api/v1/timelines/public.ts index 9cd4c257..8224fa0e 100644 --- a/api/api/v1/timelines/public.ts +++ b/packages/api/routes/api/v1/timelines/public.ts @@ -3,14 +3,13 @@ import { Status as StatusSchema, zBoolean, } from "@versia/client/schemas"; -import { Timeline } from "@versia/kit/db"; -import { Notes } from "@versia/kit/tables"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, handleZodError } from "@versia-server/kit/api"; +import { Timeline } from "@versia-server/kit/db"; +import { Notes } from "@versia-server/kit/tables"; import { and, eq, gt, gte, inArray, lt, or, sql } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.get( @@ -34,7 +33,7 @@ export default apiRoute((app) => link: z .string() .optional() - .openapi({ + .meta({ description: "Links to the next and previous pages", example: @@ -60,28 +59,28 @@ export default apiRoute((app) => "query", z .object({ - max_id: StatusSchema.shape.id.optional().openapi({ + max_id: StatusSchema.shape.id.optional().meta({ description: "All results returned will be lesser than this ID. In effect, sets an upper bound on results.", example: "8d35243d-b959-43e2-8bac-1a9d4eaea2aa", }), - since_id: StatusSchema.shape.id.optional().openapi({ + since_id: StatusSchema.shape.id.optional().meta({ description: "All results returned will be greater than this ID. In effect, sets a lower bound on results.", example: undefined, }), - min_id: StatusSchema.shape.id.optional().openapi({ + min_id: StatusSchema.shape.id.optional().meta({ description: "Returns results immediately newer than this ID. In effect, sets a cursor at this ID and paginates forward.", example: undefined, }), - local: zBoolean.default(false).openapi({ + local: zBoolean.default(false).meta({ description: "Show only local statuses?", }), - remote: zBoolean.default(false).openapi({ + remote: zBoolean.default(false).meta({ description: "Show only remote statuses?", }), - only_media: zBoolean.default(false).openapi({ + only_media: zBoolean.default(false).meta({ description: "Show only statuses with media attached?", }), limit: z.coerce @@ -90,7 +89,7 @@ export default apiRoute((app) => .min(1) .max(40) .default(20) - .openapi({ + .meta({ description: "Maximum number of results to return.", }), }) diff --git a/api/api/v2/filters/[id]/index.test.ts b/packages/api/routes/api/v2/filters/[id]/index.test.ts similarity index 98% rename from api/api/v2/filters/[id]/index.test.ts rename to packages/api/routes/api/v2/filters/[id]/index.test.ts index 9e3ff960..1a7c7223 100644 --- a/api/api/v2/filters/[id]/index.test.ts +++ b/packages/api/routes/api/v2/filters/[id]/index.test.ts @@ -1,5 +1,5 @@ import { afterAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestUsers } from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(2); diff --git a/api/api/v2/filters/[id]/index.ts b/packages/api/routes/api/v2/filters/[id]/index.ts similarity index 96% rename from api/api/v2/filters/[id]/index.ts rename to packages/api/routes/api/v2/filters/[id]/index.ts index 5cf1fdaf..da296aee 100644 --- a/api/api/v2/filters/[id]/index.ts +++ b/packages/api/routes/api/v2/filters/[id]/index.ts @@ -4,14 +4,18 @@ import { RolePermission, zBoolean, } from "@versia/client/schemas"; -import { db } from "@versia/kit/db"; -import { FilterKeywords, Filters } from "@versia/kit/tables"; +import { ApiError } from "@versia-server/kit"; +import { + apiRoute, + auth, + handleZodError, + jsonOrForm, +} from "@versia-server/kit/api"; +import { db } from "@versia-server/kit/db"; +import { FilterKeywords, Filters } from "@versia-server/kit/tables"; import { and, eq, inArray, type SQL } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError, jsonOrForm } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => { app.get( @@ -140,7 +144,7 @@ export default apiRoute((app) => { .int() .min(60) .max(60 * 60 * 24 * 365 * 5) - .openapi({ + .meta({ description: "How many seconds from now should the filter expire?", }), @@ -152,7 +156,7 @@ export default apiRoute((app) => { }) .extend({ // biome-ignore lint/style/useNamingConvention: _destroy is a Mastodon API imposed variable name - _destroy: zBoolean.default(false).openapi({ + _destroy: zBoolean.default(false).meta({ description: "If true, will remove the keyword with the given ID.", }), diff --git a/api/api/v2/filters/index.test.ts b/packages/api/routes/api/v2/filters/index.test.ts similarity index 96% rename from api/api/v2/filters/index.test.ts rename to packages/api/routes/api/v2/filters/index.test.ts index 06b6fc75..9a14716d 100644 --- a/api/api/v2/filters/index.test.ts +++ b/packages/api/routes/api/v2/filters/index.test.ts @@ -1,5 +1,5 @@ import { afterAll, describe, expect, test } from "bun:test"; -import { generateClient, getTestUsers } from "~/tests/utils"; +import { generateClient, getTestUsers } from "@versia-server/tests"; const { users, deleteUsers } = await getTestUsers(2); diff --git a/api/api/v2/filters/index.ts b/packages/api/routes/api/v2/filters/index.ts similarity index 94% rename from api/api/v2/filters/index.ts rename to packages/api/routes/api/v2/filters/index.ts index 84797a38..fe96d954 100644 --- a/api/api/v2/filters/index.ts +++ b/packages/api/routes/api/v2/filters/index.ts @@ -3,15 +3,19 @@ import { Filter as FilterSchema, RolePermission, } from "@versia/client/schemas"; -import { db } from "@versia/kit/db"; -import { FilterKeywords, Filters } from "@versia/kit/tables"; +import { ApiError } from "@versia-server/kit"; +import { + apiRoute, + auth, + handleZodError, + jsonOrForm, +} from "@versia-server/kit/api"; +import { db } from "@versia-server/kit/db"; +import { FilterKeywords, Filters } from "@versia-server/kit/tables"; import { randomUUIDv7 } from "bun"; import { eq, type SQL } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError, jsonOrForm } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => { app.get( @@ -110,7 +114,7 @@ export default apiRoute((app) => { .min(60) .max(60 * 60 * 24 * 365 * 5) .optional() - .openapi({ + .meta({ description: "How many seconds from now should the filter expire?", }), diff --git a/api/api/v2/instance/index.test.ts b/packages/api/routes/api/v2/instance/index.test.ts similarity index 92% rename from api/api/v2/instance/index.test.ts rename to packages/api/routes/api/v2/instance/index.test.ts index 90238e4d..7a32dae6 100644 --- a/api/api/v2/instance/index.test.ts +++ b/packages/api/routes/api/v2/instance/index.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "bun:test"; -import { generateClient } from "~/tests/utils"; +import { generateClient } from "@versia-server/tests"; // /api/v2/instance describe("/api/v2/instance", () => { diff --git a/api/api/v2/instance/index.ts b/packages/api/routes/api/v2/instance/index.ts similarity index 95% rename from api/api/v2/instance/index.ts rename to packages/api/routes/api/v2/instance/index.ts index 9ff4cddd..c464bc53 100644 --- a/api/api/v2/instance/index.ts +++ b/packages/api/routes/api/v2/instance/index.ts @@ -1,12 +1,11 @@ import { Instance as InstanceSchema } from "@versia/client/schemas"; -import { User } from "@versia/kit/db"; -import { Users } from "@versia/kit/tables"; +import { config } from "@versia-server/config"; +import { apiRoute } from "@versia-server/kit/api"; +import { User } from "@versia-server/kit/db"; +import { Users } from "@versia-server/kit/tables"; import { and, eq, isNull } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { apiRoute } from "@/api"; -import { config } from "~/config.ts"; -import pkg from "~/package.json" with { type: "json" }; +import { describeRoute, resolver } from "hono-openapi"; +import pkg from "../../../../../../package.json" with { type: "json" }; export default apiRoute((app) => app.get( diff --git a/api/api/v2/media/index.ts b/packages/api/routes/api/v2/media/index.ts similarity index 89% rename from api/api/v2/media/index.ts rename to packages/api/routes/api/v2/media/index.ts index 9f42a6ee..5388e58a 100644 --- a/api/api/v2/media/index.ts +++ b/packages/api/routes/api/v2/media/index.ts @@ -2,13 +2,12 @@ import { Attachment as AttachmentSchema, RolePermission, } from "@versia/client/schemas"; -import { Media } from "@versia/kit/db"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; -import { config } from "~/config"; +import { config } from "@versia-server/config"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, handleZodError } from "@versia-server/kit/api"; +import { Media } from "@versia-server/kit/db"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.post( @@ -69,11 +68,11 @@ export default apiRoute((app) => validator( "form", z.object({ - file: z.instanceof(File).openapi({ + file: z.file().meta({ description: "The file to be attached, encoded using multipart form data. The file must have a MIME type.", }), - thumbnail: z.instanceof(File).optional().openapi({ + thumbnail: z.file().optional().meta({ description: "The custom thumbnail of the media to be attached, encoded using multipart form data.", }), @@ -84,7 +83,7 @@ export default apiRoute((app) => focus: z .string() .optional() - .openapi({ + .meta({ description: "Two floating points (x,y), comma-delimited, ranging from -1.0 to 1.0. Used for media cropping on clients.", externalDocs: { diff --git a/api/api/v2/search/index.ts b/packages/api/routes/api/v2/search/index.ts similarity index 88% rename from api/api/v2/search/index.ts rename to packages/api/routes/api/v2/search/index.ts index 1e24cbd7..cf679e08 100644 --- a/api/api/v2/search/index.ts +++ b/packages/api/routes/api/v2/search/index.ts @@ -6,16 +6,16 @@ import { userAddressRegex, zBoolean, } from "@versia/client/schemas"; -import { db, Note, User } from "@versia/kit/db"; -import { Instances, Notes, Users } from "@versia/kit/tables"; +import { config } from "@versia-server/config"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, auth, handleZodError } from "@versia-server/kit/api"; +import { db, Note, User } from "@versia-server/kit/db"; +import { parseUserAddress } from "@versia-server/kit/parsers"; +import { searchManager } from "@versia-server/kit/search"; +import { Instances, Notes, Users } from "@versia-server/kit/tables"; import { and, eq, inArray, isNull, sql } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, auth, handleZodError, parseUserAddress } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; -import { searchManager } from "~/classes/search/search-manager"; -import { config } from "~/config.ts"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.get( @@ -58,59 +58,53 @@ export default apiRoute((app) => validator( "query", z.object({ - q: z.string().trim().openapi({ + q: z.string().trim().meta({ description: "The search query.", example: "versia", }), type: z .enum(["accounts", "hashtags", "statuses"]) .optional() - .openapi({ + .meta({ description: "Specify whether to search for only accounts, hashtags, statuses", example: "accounts", }), - resolve: zBoolean.default(false).openapi({ + resolve: zBoolean.default(false).meta({ description: "Only relevant if type includes accounts. If true and (a) the search query is for a remote account (e.g., someaccount@someother.server) and (b) the local server does not know about the account, WebFinger is used to try and resolve the account at someother.server. This provides the best recall at higher latency. If false only accounts the server knows about are returned.", }), - following: zBoolean.default(false).openapi({ + following: zBoolean.default(false).meta({ description: "Only include accounts that the user is following?", }), - account_id: AccountSchema.shape.id.optional().openapi({ + account_id: AccountSchema.shape.id.optional().meta({ description: " If provided, will only return statuses authored by this account.", }), - exclude_unreviewed: zBoolean.default(false).openapi({ + exclude_unreviewed: zBoolean.default(false).meta({ description: "Filter out unreviewed tags? Use true when trying to find trending tags.", }), - max_id: Id.optional().openapi({ + max_id: Id.optional().meta({ description: "All results returned will be lesser than this ID. In effect, sets an upper bound on results.", example: "8d35243d-b959-43e2-8bac-1a9d4eaea2aa", }), - since_id: Id.optional().openapi({ + since_id: Id.optional().meta({ description: "All results returned will be greater than this ID. In effect, sets a lower bound on results.", example: undefined, }), - min_id: Id.optional().openapi({ + min_id: Id.optional().meta({ description: "Returns results immediately newer than this ID. In effect, sets a cursor at this ID and paginates forward.", example: undefined, }), - limit: z.coerce - .number() - .int() - .min(1) - .max(40) - .default(20) - .openapi({ - description: "Maximum number of results to return.", - }), - offset: z.coerce.number().int().min(0).default(0).openapi({ + limit: z.coerce.number().int().min(1).max(40).default(20).meta({ + description: "Maximum number of results to return.", + }), + offset: z.coerce.number().int().min(0).default(0).meta({ description: "Skip the first n results.", }), }), diff --git a/api/inbox/index.test.ts b/packages/api/routes/inbox/index.test.ts similarity index 96% rename from api/inbox/index.test.ts rename to packages/api/routes/inbox/index.test.ts index 329b8334..608c3c7e 100644 --- a/api/inbox/index.test.ts +++ b/packages/api/routes/inbox/index.test.ts @@ -1,4 +1,14 @@ import { afterAll, describe, expect, test } from "bun:test"; +import { sign } from "@versia/sdk/crypto"; +import * as VersiaEntities from "@versia/sdk/entities"; +import { config } from "@versia-server/config"; +import { Instance, Note, Reaction, User } from "@versia-server/kit/db"; +import { Notes, Reactions, Users } from "@versia-server/kit/tables"; +import { + fakeRequest, + generateClient, + getTestUsers, +} from "@versia-server/tests"; import { randomUUIDv7, sleep } from "bun"; import { clearMocks, @@ -7,15 +17,6 @@ import { mock, } from "bun-bagel"; import { and, eq, isNull } from "drizzle-orm"; -import { Instance } from "~/classes/database/instance"; -import { Note } from "~/classes/database/note"; -import { Reaction } from "~/classes/database/reaction"; -import { User } from "~/classes/database/user"; -import { config } from "~/config"; -import { Notes, Reactions, Users } from "~/drizzle/schema"; -import { sign } from "~/packages/sdk/crypto"; -import * as VersiaEntities from "~/packages/sdk/entities"; -import { fakeRequest, generateClient, getTestUsers } from "~/tests/utils"; const instanceUrl = new URL("https://versia.example.com"); const noteId = randomUUIDv7(); diff --git a/api/inbox/index.ts b/packages/api/routes/inbox/index.ts similarity index 87% rename from api/inbox/index.ts rename to packages/api/routes/inbox/index.ts index 43567d88..ffcc0266 100644 --- a/api/inbox/index.ts +++ b/packages/api/routes/inbox/index.ts @@ -1,8 +1,7 @@ -import { describeRoute } from "hono-openapi"; -import { validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, handleZodError } from "@/api"; -import { InboxJobType, inboxQueue } from "~/classes/queues/inbox"; +import { apiRoute, handleZodError } from "@versia-server/kit/api"; +import { InboxJobType, inboxQueue } from "@versia-server/kit/queues/inbox"; +import { describeRoute, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.post( @@ -22,7 +21,6 @@ export default apiRoute((app) => "versia-signature": z.string().optional(), "versia-signed-at": z.coerce.number().optional(), "versia-signed-by": z - .string() .url() .or(z.string().startsWith("instance ")) .optional(), diff --git a/api/likes/[uuid]/index.ts b/packages/api/routes/likes/[uuid]/index.ts similarity index 85% rename from api/likes/[uuid]/index.ts rename to packages/api/routes/likes/[uuid]/index.ts index 4ecea34e..51e67065 100644 --- a/api/likes/[uuid]/index.ts +++ b/packages/api/routes/likes/[uuid]/index.ts @@ -1,14 +1,13 @@ import { Status as StatusSchema } from "@versia/client/schemas"; -import { Like, User } from "@versia/kit/db"; -import { Likes } from "@versia/kit/tables"; +import { LikeSchema } from "@versia/sdk/schemas"; +import { config } from "@versia-server/config"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, handleZodError } from "@versia-server/kit/api"; +import { Like, User } from "@versia-server/kit/db"; +import { Likes } from "@versia-server/kit/tables"; import { and, eq, sql } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, handleZodError } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; -import { config } from "~/config.ts"; -import { LikeSchema } from "~/packages/sdk/schemas"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.get( diff --git a/api/media/[hash]/[name]/index.ts b/packages/api/routes/media/[hash]/[name]/index.ts similarity index 91% rename from api/media/[hash]/[name]/index.ts rename to packages/api/routes/media/[hash]/[name]/index.ts index a5e69a9a..071bb517 100644 --- a/api/media/[hash]/[name]/index.ts +++ b/packages/api/routes/media/[hash]/[name]/index.ts @@ -1,9 +1,8 @@ +import { ApiError } from "@versia-server/kit"; +import { apiRoute, handleZodError } from "@versia-server/kit/api"; import { file as bunFile } from "bun"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, handleZodError } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.get( diff --git a/api/media/proxy/[id].ts b/packages/api/routes/media/proxy/[id].ts similarity index 79% rename from api/media/proxy/[id].ts rename to packages/api/routes/media/proxy/[id].ts index 41b2c489..3d3128a3 100644 --- a/api/media/proxy/[id].ts +++ b/packages/api/routes/media/proxy/[id].ts @@ -1,11 +1,10 @@ +import { config } from "@versia-server/config"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, handleZodError } from "@versia-server/kit/api"; import { proxy } from "hono/proxy"; import type { ContentfulStatusCode, StatusCode } from "hono/utils/http-status"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, handleZodError } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; -import { config } from "~/config.ts"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.get( @@ -34,19 +33,19 @@ export default apiRoute((app) => validator( "param", z.object({ - id: z - .string() - .transform((val) => - Buffer.from(val, "base64url").toString(), - ), + id: z.base64url().meta({ + description: "Base64url encoded URL to proxy", + type: "string", + }), }), handleZodError, ), async (context) => { const { id } = context.req.valid("param"); + const url = Buffer.from(id, "base64url").toString(); // Check if URL is valid - if (!URL.canParse(id)) { + if (!URL.canParse(url)) { throw new ApiError( 400, "Invalid URL", @@ -54,7 +53,7 @@ export default apiRoute((app) => ); } - const media = await proxy(id, { + const media = await proxy(url, { // @ts-expect-error Proxy is a Bun-specific feature proxy: config.http.proxy_address, }); @@ -63,7 +62,7 @@ export default apiRoute((app) => // Cloudflare R2 serves those as application/xml if ( media.headers.get("Content-Type") === "application/xml" && - id.endsWith(".svg") + url.endsWith(".svg") ) { media.headers.set("Content-Type", "image/svg+xml"); } @@ -71,7 +70,7 @@ export default apiRoute((app) => const realFilename = media.headers .get("Content-Disposition") - ?.match(/filename="(.+)"/)?.[1] || id.split("/").pop(); + ?.match(/filename="(.+)"/)?.[1] || url.split("/").pop(); if (!media.body) { return context.body(null, media.status as StatusCode); diff --git a/api/messaging/index.ts b/packages/api/routes/messaging/index.ts similarity index 73% rename from api/messaging/index.ts rename to packages/api/routes/messaging/index.ts index 677576c0..ecc383dd 100644 --- a/api/messaging/index.ts +++ b/packages/api/routes/messaging/index.ts @@ -1,7 +1,7 @@ -import { getLogger } from "@logtape/logtape"; +import { apiRoute } from "@versia-server/kit/api"; +import { federationMessagingLogger } from "@versia-server/logging"; import chalk from "chalk"; import { describeRoute } from "hono-openapi"; -import { apiRoute } from "@/api"; export default apiRoute((app) => app.post( @@ -19,8 +19,7 @@ export default apiRoute((app) => async (context) => { const content = await context.req.text(); - getLogger(["federation", "messaging"]) - .info`Received message via ${chalk.bold("Instance Messaging")}:\n${content}`; + federationMessagingLogger.info`Received message via ${chalk.bold("Instance Messaging")}:\n${content}`; return context.text("", 200); }, diff --git a/api/notes/[uuid]/index.ts b/packages/api/routes/notes/[uuid]/index.ts similarity index 84% rename from api/notes/[uuid]/index.ts rename to packages/api/routes/notes/[uuid]/index.ts index 6c6a3781..a538d0af 100644 --- a/api/notes/[uuid]/index.ts +++ b/packages/api/routes/notes/[uuid]/index.ts @@ -1,14 +1,13 @@ import { Status as StatusSchema } from "@versia/client/schemas"; -import { Note } from "@versia/kit/db"; -import { Notes } from "@versia/kit/tables"; +import { NoteSchema } from "@versia/sdk/schemas"; +import { config } from "@versia-server/config"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, handleZodError } from "@versia-server/kit/api"; +import { Note } from "@versia-server/kit/db"; +import { Notes } from "@versia-server/kit/tables"; import { and, eq, inArray } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, handleZodError } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; -import { config } from "~/config.ts"; -import { NoteSchema } from "~/packages/sdk/schemas"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.get( diff --git a/api/notes/[uuid]/quotes.ts b/packages/api/routes/notes/[uuid]/quotes.ts similarity index 90% rename from api/notes/[uuid]/quotes.ts rename to packages/api/routes/notes/[uuid]/quotes.ts index ec65d503..0ebfee27 100644 --- a/api/notes/[uuid]/quotes.ts +++ b/packages/api/routes/notes/[uuid]/quotes.ts @@ -1,15 +1,14 @@ import { Status as StatusSchema } from "@versia/client/schemas"; -import { db, Note } from "@versia/kit/db"; -import { Notes } from "@versia/kit/tables"; +import * as VersiaEntities from "@versia/sdk/entities"; +import { URICollectionSchema } from "@versia/sdk/schemas"; +import { config } from "@versia-server/config"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, handleZodError } from "@versia-server/kit/api"; +import { db, Note } from "@versia-server/kit/db"; +import { Notes } from "@versia-server/kit/tables"; import { and, eq, inArray } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, handleZodError } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; -import { config } from "~/config.ts"; -import * as VersiaEntities from "~/packages/sdk/entities"; -import { URICollectionSchema } from "~/packages/sdk/schemas"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.get( diff --git a/api/notes/[uuid]/replies.ts b/packages/api/routes/notes/[uuid]/replies.ts similarity index 90% rename from api/notes/[uuid]/replies.ts rename to packages/api/routes/notes/[uuid]/replies.ts index 49465e5a..88cdb2f9 100644 --- a/api/notes/[uuid]/replies.ts +++ b/packages/api/routes/notes/[uuid]/replies.ts @@ -1,15 +1,14 @@ import { Status as StatusSchema } from "@versia/client/schemas"; -import { db, Note } from "@versia/kit/db"; -import { Notes } from "@versia/kit/tables"; +import * as VersiaEntities from "@versia/sdk/entities"; +import { URICollectionSchema } from "@versia/sdk/schemas"; +import { config } from "@versia-server/config"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, handleZodError } from "@versia-server/kit/api"; +import { db, Note } from "@versia-server/kit/db"; +import { Notes } from "@versia-server/kit/tables"; import { and, eq, inArray } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, handleZodError } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; -import { config } from "~/config.ts"; -import * as VersiaEntities from "~/packages/sdk/entities"; -import { URICollectionSchema } from "~/packages/sdk/schemas"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.get( diff --git a/api/notes/[uuid]/shares.ts b/packages/api/routes/notes/[uuid]/shares.ts similarity index 91% rename from api/notes/[uuid]/shares.ts rename to packages/api/routes/notes/[uuid]/shares.ts index 3bf131af..ec34cb56 100644 --- a/api/notes/[uuid]/shares.ts +++ b/packages/api/routes/notes/[uuid]/shares.ts @@ -1,15 +1,14 @@ import { Status as StatusSchema } from "@versia/client/schemas"; -import { db, Note } from "@versia/kit/db"; -import { Notes } from "@versia/kit/tables"; +import * as VersiaEntities from "@versia/sdk/entities"; +import { URICollectionSchema } from "@versia/sdk/schemas"; +import { config } from "@versia-server/config"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, handleZodError } from "@versia-server/kit/api"; +import { db, Note } from "@versia-server/kit/db"; +import { Notes } from "@versia-server/kit/tables"; import { and, eq, inArray } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, handleZodError } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; -import { config } from "~/config.ts"; -import * as VersiaEntities from "~/packages/sdk/entities"; -import { URICollectionSchema } from "~/packages/sdk/schemas"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.get( diff --git a/tests/oauth.test.ts b/packages/api/routes/oauth.test.ts similarity index 68% rename from tests/oauth.test.ts rename to packages/api/routes/oauth.test.ts index e466a1e8..ec96a0bc 100644 --- a/tests/oauth.test.ts +++ b/packages/api/routes/oauth.test.ts @@ -1,10 +1,11 @@ -/** - * @deprecated - */ import { afterAll, describe, expect, test } from "bun:test"; -import type { Application, Token } from "@versia/client/schemas"; -import type { z } from "zod"; -import { fakeRequest, getTestUsers } from "./utils.ts"; +import type { Token } from "@versia/client/schemas"; +import { + fakeRequest, + generateClient, + getTestUsers, +} from "@versia-server/tests"; +import type { z } from "zod/v4"; let clientId: string; let clientSecret: string; @@ -17,36 +18,18 @@ afterAll(async () => { await deleteUsers(); }); -describe("POST /api/v1/apps/", () => { +describe("Login flow", () => { test("should create an application", async () => { - const formData = new FormData(); + const client = await generateClient(users[0]); - formData.append("client_name", "Test Application"); - formData.append("website", "https://example.com"); - formData.append("redirect_uris", "https://example.com"); - formData.append("scopes", "read write"); - - const response = await fakeRequest("/api/v1/apps", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - client_name: "Test Application", - website: "https://example.com", - redirect_uris: "https://example.com", - scopes: "read write", - }), + const { ok, data } = await client.createApp("Test Application", { + redirect_uris: "https://example.com", + website: "https://example.com", + scopes: ["read", "write"], }); - expect(response.status).toBe(200); - expect(response.headers.get("content-type")).toContain( - "application/json", - ); - - const json = await response.json(); - - expect(json).toEqual({ + expect(ok).toBe(true); + expect(data).toEqual({ name: "Test Application", website: "https://example.com", client_id: expect.any(String), @@ -57,12 +40,10 @@ describe("POST /api/v1/apps/", () => { scopes: ["read", "write"], }); - clientId = json.client_id; - clientSecret = json.client_secret; + clientId = data.client_id; + clientSecret = data.client_secret; }); -}); -describe("POST /api/auth/login/", () => { test("should get a JWT", async () => { const formData = new FormData(); @@ -83,9 +64,7 @@ describe("POST /api/auth/login/", () => { response.headers.get("Set-Cookie")?.match(/jwt=([^;]+);/)?.[1] ?? ""; }); -}); -describe("GET /oauth/authorize/", () => { test("should get a code", async () => { const response = await fakeRequest("/oauth/authorize", { method: "POST", @@ -113,9 +92,7 @@ describe("GET /oauth/authorize/", () => { code = locationHeader.searchParams.get("code") ?? ""; }); -}); -describe("POST /oauth/token/", () => { test("should get an access token", async () => { const response = await fakeRequest("/oauth/token", { method: "POST", @@ -151,24 +128,19 @@ describe("POST /oauth/token/", () => { token = json; }); -}); -describe("GET /api/v1/apps/verify_credentials", () => { test("should return the authenticated application's credentials", async () => { - const response = await fakeRequest("/api/v1/apps/verify_credentials", { + const client = await generateClient(users[0]); + + const { ok, data } = await client.verifyAppCredentials({ headers: { Authorization: `Bearer ${token.access_token}`, }, }); - expect(response.status).toBe(200); - expect(response.headers.get("content-type")).toContain( - "application/json", - ); + expect(ok).toBe(true); - const credentials = (await response.json()) as Partial< - z.infer - >; + const credentials = data; expect(credentials.name).toBe("Test Application"); expect(credentials.website).toBe("https://example.com"); diff --git a/api/shares/[uuid]/index.ts b/packages/api/routes/shares/[uuid]/index.ts similarity index 84% rename from api/shares/[uuid]/index.ts rename to packages/api/routes/shares/[uuid]/index.ts index 84023fd1..ecb500cb 100644 --- a/api/shares/[uuid]/index.ts +++ b/packages/api/routes/shares/[uuid]/index.ts @@ -1,14 +1,13 @@ import { Status as StatusSchema } from "@versia/client/schemas"; -import { Note } from "@versia/kit/db"; -import { Notes } from "@versia/kit/tables"; +import { ShareSchema } from "@versia/sdk/schemas"; +import { config } from "@versia-server/config"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, handleZodError } from "@versia-server/kit/api"; +import { Note } from "@versia-server/kit/db"; +import { Notes } from "@versia-server/kit/tables"; import { and, eq, inArray } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, handleZodError } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; -import { config } from "~/config.ts"; -import { ShareSchema } from "~/packages/sdk/schemas"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.get( diff --git a/api/users/[uuid]/inbox/index.ts b/packages/api/routes/users/[uuid]/inbox/index.ts similarity index 91% rename from api/users/[uuid]/inbox/index.ts rename to packages/api/routes/users/[uuid]/inbox/index.ts index 34106569..2456ee0b 100644 --- a/api/users/[uuid]/inbox/index.ts +++ b/packages/api/routes/users/[uuid]/inbox/index.ts @@ -1,9 +1,8 @@ -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, handleZodError } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; -import { InboxJobType, inboxQueue } from "~/classes/queues/inbox"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, handleZodError } from "@versia-server/kit/api"; +import { InboxJobType, inboxQueue } from "@versia-server/kit/queues/inbox"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.post( @@ -68,7 +67,7 @@ export default apiRoute((app) => validator( "param", z.object({ - uuid: z.string().uuid(), + uuid: z.uuid(), }), handleZodError, ), @@ -78,7 +77,6 @@ export default apiRoute((app) => "versia-signature": z.string().optional(), "versia-signed-at": z.coerce.number().optional(), "versia-signed-by": z - .string() .url() .or(z.string().startsWith("instance ")) .optional(), diff --git a/api/users/[uuid]/index.ts b/packages/api/routes/users/[uuid]/index.ts similarity index 85% rename from api/users/[uuid]/index.ts rename to packages/api/routes/users/[uuid]/index.ts index dc1aea76..773632b8 100644 --- a/api/users/[uuid]/index.ts +++ b/packages/api/routes/users/[uuid]/index.ts @@ -1,10 +1,9 @@ -import { User } from "@versia/kit/db"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, handleZodError } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; -import { UserSchema } from "~/packages/sdk/schemas"; +import { UserSchema } from "@versia/sdk/schemas"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, handleZodError } from "@versia-server/kit/api"; +import { User } from "@versia-server/kit/db"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.get( @@ -39,7 +38,7 @@ export default apiRoute((app) => validator( "param", z.object({ - uuid: z.string().uuid(), + uuid: z.uuid(), }), handleZodError, ), diff --git a/api/users/[uuid]/outbox/index.ts b/packages/api/routes/users/[uuid]/outbox/index.ts similarity index 88% rename from api/users/[uuid]/outbox/index.ts rename to packages/api/routes/users/[uuid]/outbox/index.ts index 2094c7aa..bd49b1b3 100644 --- a/api/users/[uuid]/outbox/index.ts +++ b/packages/api/routes/users/[uuid]/outbox/index.ts @@ -1,14 +1,13 @@ -import { db, Note, User } from "@versia/kit/db"; -import { Notes } from "@versia/kit/tables"; +import * as VersiaEntities from "@versia/sdk/entities"; +import { CollectionSchema, NoteSchema } from "@versia/sdk/schemas"; +import { config } from "@versia-server/config"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, handleZodError } from "@versia-server/kit/api"; +import { db, Note, User } from "@versia-server/kit/db"; +import { Notes } from "@versia-server/kit/tables"; import { and, eq, inArray } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute, handleZodError } from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; -import { config } from "~/config.ts"; -import * as VersiaEntities from "~/packages/sdk/entities"; -import { CollectionSchema, NoteSchema } from "~/packages/sdk/schemas"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; const NOTES_PER_PAGE = 20; @@ -52,7 +51,7 @@ export default apiRoute((app) => validator( "param", z.object({ - uuid: z.string().uuid(), + uuid: z.uuid(), }), handleZodError, ), diff --git a/api/well-known/host-meta/index.ts b/packages/api/routes/well-known/host-meta/index.ts similarity index 85% rename from api/well-known/host-meta/index.ts rename to packages/api/routes/well-known/host-meta/index.ts index 6531e3d2..81be321c 100644 --- a/api/well-known/host-meta/index.ts +++ b/packages/api/routes/well-known/host-meta/index.ts @@ -1,8 +1,7 @@ -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute } from "@/api"; -import { config } from "~/config.ts"; +import { config } from "@versia-server/config"; +import { apiRoute } from "@versia-server/kit/api"; +import { describeRoute, resolver } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.get( diff --git a/api/well-known/nodeinfo/2.0/index.ts b/packages/api/routes/well-known/nodeinfo/2.0/index.ts similarity index 90% rename from api/well-known/nodeinfo/2.0/index.ts rename to packages/api/routes/well-known/nodeinfo/2.0/index.ts index e046432f..589775a7 100644 --- a/api/well-known/nodeinfo/2.0/index.ts +++ b/packages/api/routes/well-known/nodeinfo/2.0/index.ts @@ -1,10 +1,9 @@ -import { Note, User } from "@versia/kit/db"; -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute } from "@/api"; -import { config } from "~/config.ts"; -import manifest from "~/package.json" with { type: "json" }; +import { config } from "@versia-server/config"; +import { apiRoute } from "@versia-server/kit/api"; +import { Note, User } from "@versia-server/kit/db"; +import { describeRoute, resolver } from "hono-openapi"; +import { z } from "zod/v4"; +import manifest from "../../../../../../package.json" with { type: "json" }; export default apiRoute((app) => app.get( diff --git a/api/well-known/nodeinfo/index.ts b/packages/api/routes/well-known/nodeinfo/index.ts similarity index 87% rename from api/well-known/nodeinfo/index.ts rename to packages/api/routes/well-known/nodeinfo/index.ts index 2ef92880..fcc1657f 100644 --- a/api/well-known/nodeinfo/index.ts +++ b/packages/api/routes/well-known/nodeinfo/index.ts @@ -1,8 +1,7 @@ -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute } from "@/api"; -import { config } from "~/config.ts"; +import { config } from "@versia-server/config"; +import { apiRoute } from "@versia-server/kit/api"; +import { describeRoute, resolver } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.get( diff --git a/api/well-known/openid-configuration/index.ts b/packages/api/routes/well-known/openid-configuration/index.ts similarity index 93% rename from api/well-known/openid-configuration/index.ts rename to packages/api/routes/well-known/openid-configuration/index.ts index 225063fe..9917997b 100644 --- a/api/well-known/openid-configuration/index.ts +++ b/packages/api/routes/well-known/openid-configuration/index.ts @@ -1,8 +1,7 @@ -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { z } from "zod"; -import { apiRoute } from "@/api"; -import { config } from "~/config.ts"; +import { config } from "@versia-server/config"; +import { apiRoute } from "@versia-server/kit/api"; +import { describeRoute, resolver } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.get( diff --git a/api/well-known/versia.ts b/packages/api/routes/well-known/versia.ts similarity index 89% rename from api/well-known/versia.ts rename to packages/api/routes/well-known/versia.ts index 03c5a251..85598640 100644 --- a/api/well-known/versia.ts +++ b/packages/api/routes/well-known/versia.ts @@ -1,13 +1,12 @@ -import { User } from "@versia/kit/db"; -import { Users } from "@versia/kit/tables"; +import { InstanceMetadataSchema } from "@versia/sdk/schemas"; +import { config } from "@versia-server/config"; +import { apiRoute } from "@versia-server/kit/api"; +import { User } from "@versia-server/kit/db"; +import { Users } from "@versia-server/kit/tables"; import { asc } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { apiRoute } from "@/api"; +import { describeRoute, resolver } from "hono-openapi"; import { urlToContentFormat } from "@/content_types"; -import { config } from "~/config.ts"; -import pkg from "~/package.json" with { type: "json" }; -import { InstanceMetadataSchema } from "~/packages/sdk/schemas"; +import pkg from "../../../../package.json" with { type: "json" }; export default apiRoute((app) => app.get( diff --git a/api/well-known/webfinger/index.ts b/packages/api/routes/well-known/webfinger/index.ts similarity index 85% rename from api/well-known/webfinger/index.ts rename to packages/api/routes/well-known/webfinger/index.ts index 03e712d7..9b34783c 100644 --- a/api/well-known/webfinger/index.ts +++ b/packages/api/routes/well-known/webfinger/index.ts @@ -1,21 +1,16 @@ -import { getLogger } from "@logtape/logtape"; -import { User } from "@versia/kit/db"; -import { Users } from "@versia/kit/tables"; import { FederationRequester } from "@versia/sdk/http"; +import { WebFingerSchema } from "@versia/sdk/schemas"; +import { config } from "@versia-server/config"; +import { ApiError } from "@versia-server/kit"; +import { apiRoute, handleZodError } from "@versia-server/kit/api"; +import { User } from "@versia-server/kit/db"; +import { parseUserAddress } from "@versia-server/kit/parsers"; +import { uuid, webfingerMention } from "@versia-server/kit/regex"; +import { Users } from "@versia-server/kit/tables"; +import { federationBridgeLogger } from "@versia-server/logging"; import { and, eq, isNull } from "drizzle-orm"; -import { describeRoute } from "hono-openapi"; -import { resolver, validator } from "hono-openapi/zod"; -import { z } from "zod"; -import { - apiRoute, - handleZodError, - idValidator, - parseUserAddress, - webfingerMention, -} from "@/api"; -import { ApiError } from "~/classes/errors/api-error"; -import { config } from "~/config.ts"; -import { WebFingerSchema } from "~/packages/sdk/schemas"; +import { describeRoute, resolver, validator } from "hono-openapi"; +import { z } from "zod/v4"; export default apiRoute((app) => app.get( @@ -68,7 +63,7 @@ export default apiRoute((app) => ); } - const isUuid = username.match(idValidator); + const isUuid = username.match(uuid); const user = await User.fromSql( and( @@ -94,8 +89,7 @@ export default apiRoute((app) => } catch (e) { const error = e as ApiError; - getLogger(["federation", "bridge"]) - .error`Error from bridge: ${error.message}`; + federationBridgeLogger.error`Error from bridge: ${error.message}`; } } diff --git a/entrypoints/api/setup.ts b/packages/api/setup.ts similarity index 76% rename from entrypoints/api/setup.ts rename to packages/api/setup.ts index f2daf095..80dfc4cf 100644 --- a/entrypoints/api/setup.ts +++ b/packages/api/setup.ts @@ -1,17 +1,11 @@ -import { getLogger } from "@logtape/logtape"; -import { Note } from "@versia/kit/db"; -import { configureLoggers } from "@/loggers"; -import { connection } from "@/redis.ts"; -import { config } from "~/config.ts"; -import { setupDatabase } from "~/drizzle/db"; -import { searchManager } from "../../classes/search/search-manager.ts"; +import { config } from "@versia-server/config"; +import { Note, setupDatabase } from "@versia-server/kit/db"; +import { connection } from "@versia-server/kit/redis"; +import { searchManager } from "@versia-server/kit/search"; +import { serverLogger } from "@versia-server/logging"; const timeAtStart = performance.now(); -await configureLoggers(); - -const serverLogger = getLogger("server"); - console.info(` ██╗ ██╗███████╗██████╗ ███████╗██╗ █████╗ ██║ ██║██╔════╝██╔══██╗██╔════╝██║██╔══██╗ diff --git a/packages/api/util.ts b/packages/api/util.ts new file mode 100644 index 00000000..e69de29b diff --git a/packages/client/build.ts b/packages/client/build.ts new file mode 100644 index 00000000..908e5d50 --- /dev/null +++ b/packages/client/build.ts @@ -0,0 +1,19 @@ +import { $, build } from "bun"; +import manifest from "./package.json" with { type: "json" }; + +console.log("Building..."); + +await $`rm -rf dist && mkdir dist`; + +await build({ + entrypoints: Object.values(manifest.exports).map((entry) => entry.import), + outdir: "dist", + target: "bun", + splitting: true, + minify: true, + external: [ + ...Object.keys(manifest.dependencies).filter((dep) => + dep.startsWith("@versia"), + ), + ], +}); diff --git a/packages/client/jsr.jsonc b/packages/client/jsr.jsonc index cebfe30f..6bec7337 100644 --- a/packages/client/jsr.jsonc +++ b/packages/client/jsr.jsonc @@ -2,6 +2,7 @@ "$schema": "https://jsr.io/schema/config-file.v1.json", "name": "@versia/client", "version": "0.2.0-alpha.4", + "license": "MIT", "exports": { ".": "./index.ts", "./schemas": "./schemas.ts" diff --git a/packages/client/package.json b/packages/client/package.json index e8a5c44e..54d0b718 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -7,6 +7,9 @@ "name": "Jesse Wierzbinski (CPlusPatch)", "url": "https://cpluspatch.com" }, + "scripts": { + "build": "bun run build.ts" + }, "readme": "README.md", "repository": { "type": "git", @@ -41,12 +44,10 @@ }, "exports": { ".": { - "import": "./index.ts", - "default": "./index.ts" + "import": "./index.ts" }, "./schemas": { - "import": "./schemas.ts", - "default": "./schemas.ts" + "import": "./schemas.ts" } }, "funding": { @@ -64,9 +65,9 @@ "packageManager": "bun@1.2.5", "dependencies": { "@badgateway/oauth2-client": "^3.0.0", - "iso-639-1": "^3.1.5", - "magic-regexp": "^0.10.0", - "zod": "^3.24.2", - "zod-openapi": "^4.2.4" + "iso-639-1": "catalog:", + "magic-regexp": "catalog:", + "zod": "catalog:", + "zod-openapi": "catalog:" } } diff --git a/packages/client/schemas/account-warning.ts b/packages/client/schemas/account-warning.ts index 1447b12c..40c34924 100644 --- a/packages/client/schemas/account-warning.ts +++ b/packages/client/schemas/account-warning.ts @@ -1,11 +1,11 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { Account } from "./account.ts"; import { Appeal } from "./appeal.ts"; import { Id } from "./common.ts"; export const AccountWarning = z .object({ - id: Id.openapi({ + id: Id.meta({ description: "The ID of the account warning in the database.", example: "0968680e-fd64-4525-b818-6e1c46fbdb28", }), @@ -19,40 +19,40 @@ export const AccountWarning = z "silence", "suspend", ]) - .openapi({ + .meta({ description: "Action taken against the account. 'none' = No action was taken, this is a simple warning; 'disable' = The account has been disabled; 'mark_statuses_as_sensitive' = Specific posts from the target account have been marked as sensitive; 'delete_statuses' = Specific statuses from the target account have been deleted; 'sensitive' = All posts from the target account are marked as sensitive; 'silence' = The target account has been limited; 'suspend' = The target account has been suspended.", example: "none", }), - text: z.string().openapi({ + text: z.string().meta({ description: "Message from the moderator to the target account.", example: "Please adhere to our community guidelines.", }), status_ids: z .array(Id) .nullable() - .openapi({ + .meta({ description: "List of status IDs that are relevant to the warning. When action is mark_statuses_as_sensitive or delete_statuses, those are the affected statuses.", example: ["5ee59275-c308-4173-bb1f-58646204579b"], }), - target_account: Account.openapi({ + target_account: Account.meta({ description: "Account against which a moderation decision has been taken.", }), - appeal: Appeal.nullable().openapi({ + appeal: Appeal.nullable().meta({ description: "Appeal submitted by the target account, if any.", example: null, }), - created_at: z.string().datetime().openapi({ + created_at: z.iso.datetime().meta({ description: "When the event took place.", example: "2025-01-04T14:11:00Z", }), }) - .openapi({ + .meta({ description: "Moderation warning against a particular account.", externalDocs: { url: "https://docs.joinmastodon.org/entities/AccountWarning", }, - ref: "AccountWarning", + id: "AccountWarning", }); diff --git a/packages/client/schemas/account.ts b/packages/client/schemas/account.ts index 84f9c83c..f04141be 100644 --- a/packages/client/schemas/account.ts +++ b/packages/client/schemas/account.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { userAddressRegex } from "../regex.ts"; import { iso631, zBoolean } from "./common.ts"; import { CustomEmoji } from "./emoji.ts"; @@ -10,7 +10,7 @@ export const Field = z .string() .trim() .min(1) - .openapi({ + .meta({ description: "The key of a given field’s key-value pair.", example: "Freak level", externalDocs: { @@ -21,18 +21,17 @@ export const Field = z .string() .trim() .min(1) - .openapi({ + .meta({ description: "The value associated with the name key.", example: "

High

", externalDocs: { url: "https://docs.joinmastodon.org/entities/Account/#value", }, }), - verified_at: z - .string() + verified_at: z.iso .datetime() .nullable() - .openapi({ + .meta({ description: "Timestamp of when the server verified a URL value for a rel=“me” link.", example: null, @@ -41,11 +40,11 @@ export const Field = z }, }), }) - .openapi({ ref: "AccountField" }); + .meta({ id: "AccountField" }); export const Source = z .object({ - privacy: z.enum(["public", "unlisted", "private", "direct"]).openapi({ + privacy: z.enum(["public", "unlisted", "private", "direct"]).meta({ description: "The default post privacy to be used for new statuses.", example: "unlisted", @@ -53,7 +52,7 @@ export const Source = z url: "https://docs.joinmastodon.org/entities/Account/#source-privacy", }, }), - sensitive: zBoolean.openapi({ + sensitive: zBoolean.meta({ description: "Whether new statuses should be marked sensitive by default.", example: false, @@ -61,7 +60,7 @@ export const Source = z url: "https://docs.joinmastodon.org/entities/Account/#source-sensitive", }, }), - language: iso631.openapi({ + language: iso631.meta({ description: "The default posting language for new statuses.", example: "en", externalDocs: { @@ -73,7 +72,7 @@ export const Source = z .int() .nonnegative() .optional() - .openapi({ + .meta({ description: "The number of pending follow requests.", example: 3, externalDocs: { @@ -84,39 +83,35 @@ export const Source = z .string() .trim() .min(0) - .openapi({ + .meta({ description: "Profile bio, in plain-text instead of in HTML.", example: "ermmm what the meow meow", externalDocs: { url: "https://docs.joinmastodon.org/entities/Account/#source-note", }, }), - fields: z.array(Field).openapi({ + fields: z.array(Field).meta({ description: "Metadata about the account.", }), }) - .openapi({ + .meta({ description: "An extra attribute that contains source values to be used with API methods that verify credentials and update credentials.", externalDocs: { url: "https://docs.joinmastodon.org/entities/Account/#source", }, - ref: "AccountSource", + id: "AccountSource", }); -// Because Account has some recursive references, we need to define it like this -const BaseAccount = z +export const Account = z .object({ - id: z - .string() - .uuid() - .openapi({ - description: "The account ID in the database.", - example: "9e84842b-4db6-4a9b-969d-46ab408278da", - externalDocs: { - url: "https://docs.joinmastodon.org/entities/Account/#id", - }, - }), + id: z.uuid().meta({ + description: "The account ID in the database.", + example: "9e84842b-4db6-4a9b-969d-46ab408278da", + externalDocs: { + url: "https://docs.joinmastodon.org/entities/Account/#id", + }, + }), username: z .string() .min(3) @@ -125,7 +120,7 @@ const BaseAccount = z /^[a-z0-9_-]+$/, "Username can only contain letters, numbers, underscores and hyphens", ) - .openapi({ + .meta({ description: "The username of the account, not including domain.", example: "lexi", @@ -138,7 +133,7 @@ const BaseAccount = z .min(1) .trim() .regex(userAddressRegex, "Invalid user address") - .openapi({ + .meta({ description: "The Webfinger account URI. Equal to username for local users, or username@domain for remote users.", example: "lexi@beta.versia.social", @@ -146,21 +141,18 @@ const BaseAccount = z url: "https://docs.joinmastodon.org/entities/Account/#acct", }, }), - url: z - .string() - .url() - .openapi({ - description: "The location of the user’s profile page.", - example: "https://beta.versia.social/@lexi", - externalDocs: { - url: "https://docs.joinmastodon.org/entities/Account/#url", - }, - }), + url: z.url().meta({ + description: "The location of the user’s profile page.", + example: "https://beta.versia.social/@lexi", + externalDocs: { + url: "https://docs.joinmastodon.org/entities/Account/#url", + }, + }), display_name: z .string() .min(3) .trim() - .openapi({ + .meta({ description: "The profile’s display name.", example: "Lexi :flower:", externalDocs: { @@ -171,29 +163,26 @@ const BaseAccount = z .string() .min(0) .trim() - .openapi({ + .meta({ description: "The profile’s bio or description.", example: "

ermmm what the meow meow

", externalDocs: { url: "https://docs.joinmastodon.org/entities/Account/#note", }, }), - avatar: z - .string() - .url() - .openapi({ - description: - "An image icon that is shown next to statuses and in the profile.", - example: - "https://cdn.versia.social/avatars/cff9aea0-0000-43fe-8b5e-e7c7ea69a488/lexi.webp", - externalDocs: { - url: "https://docs.joinmastodon.org/entities/Account/#avatar", - }, - }), + avatar: z.url().meta({ + description: + "An image icon that is shown next to statuses and in the profile.", + example: + "https://cdn.versia.social/avatars/cff9aea0-0000-43fe-8b5e-e7c7ea69a488/lexi.webp", + externalDocs: { + url: "https://docs.joinmastodon.org/entities/Account/#avatar", + }, + }), avatar_static: z .string() .url() - .openapi({ + .meta({ description: "A static version of the avatar. Equal to avatar if its value is a static image; different if avatar is an animated GIF.", example: @@ -202,31 +191,25 @@ const BaseAccount = z url: "https://docs.joinmastodon.org/entities/Account/#avatar_static", }, }), - header: z - .string() - .url() - .openapi({ - description: - "An image banner that is shown above the profile and in profile cards.", - example: - "https://cdn.versia.social/headers/a049f8e3-878c-4faa-ae4c-a6bcceddbd9d/femboy_2.webp", - externalDocs: { - url: "https://docs.joinmastodon.org/entities/Account/#header", - }, - }), - header_static: z - .string() - .url() - .openapi({ - description: - "A static version of the header. Equal to header if its value is a static image; different if header is an animated GIF.", - example: - "https://cdn.versia.social/headers/a049f8e3-878c-4faa-ae4c-a6bcceddbd9d/femboy_2.webp", - externalDocs: { - url: "https://docs.joinmastodon.org/entities/Account/#header_static", - }, - }), - locked: zBoolean.openapi({ + header: z.url().meta({ + description: + "An image banner that is shown above the profile and in profile cards.", + example: + "https://cdn.versia.social/headers/a049f8e3-878c-4faa-ae4c-a6bcceddbd9d/femboy_2.webp", + externalDocs: { + url: "https://docs.joinmastodon.org/entities/Account/#header", + }, + }), + header_static: z.url().meta({ + description: + "A static version of the header. Equal to header if its value is a static image; different if header is an animated GIF.", + example: + "https://cdn.versia.social/headers/a049f8e3-878c-4faa-ae4c-a6bcceddbd9d/femboy_2.webp", + externalDocs: { + url: "https://docs.joinmastodon.org/entities/Account/#header_static", + }, + }), + locked: zBoolean.meta({ description: "Whether the account manually approves follow requests.", example: false, @@ -234,21 +217,21 @@ const BaseAccount = z url: "https://docs.joinmastodon.org/entities/Account/#locked", }, }), - fields: z.array(Field).openapi({ + fields: z.array(Field).meta({ description: "Additional metadata attached to a profile as name-value pairs.", externalDocs: { url: "https://docs.joinmastodon.org/entities/Account/#fields", }, }), - emojis: z.array(CustomEmoji).openapi({ + emojis: z.array(CustomEmoji).meta({ description: "Custom emoji entities to be used when rendering the profile.", externalDocs: { url: "https://docs.joinmastodon.org/entities/Account/#emojis", }, }), - bot: zBoolean.openapi({ + bot: zBoolean.meta({ description: "Indicates that the account may perform automated actions, may not be monitored, or identifies as a robot.", example: false, @@ -256,14 +239,14 @@ const BaseAccount = z url: "https://docs.joinmastodon.org/entities/Account/#bot", }, }), - group: z.literal(false).openapi({ + group: z.literal(false).meta({ description: "Indicates that the account represents a Group actor.", example: false, externalDocs: { url: "https://docs.joinmastodon.org/entities/Account/#group", }, }), - discoverable: zBoolean.nullable().openapi({ + discoverable: zBoolean.nullable().meta({ description: "Whether the account has opted into discovery features such as the profile directory.", example: true, @@ -274,7 +257,7 @@ const BaseAccount = z noindex: zBoolean .nullable() .optional() - .openapi({ + .meta({ description: "Whether the local user has opted out of being indexed by search engines.", example: false, @@ -282,7 +265,7 @@ const BaseAccount = z url: "https://docs.joinmastodon.org/entities/Account/#noindex", }, }), - suspended: zBoolean.optional().openapi({ + suspended: zBoolean.optional().meta({ description: "An extra attribute returned only when an account is suspended.", example: false, @@ -290,7 +273,7 @@ const BaseAccount = z url: "https://docs.joinmastodon.org/entities/Account/#suspended", }, }), - limited: zBoolean.optional().openapi({ + limited: zBoolean.optional().meta({ description: "An extra attribute returned only when an account is silenced. If true, indicates that the account should be hidden behind a warning screen.", example: false, @@ -298,20 +281,17 @@ const BaseAccount = z url: "https://docs.joinmastodon.org/entities/Account/#limited", }, }), - created_at: z - .string() - .datetime() - .openapi({ - description: "When the account was created.", - example: "2024-10-15T22:00:00.000Z", - externalDocs: { - url: "https://docs.joinmastodon.org/entities/Account/#created_at", - }, - }), + created_at: z.iso.datetime().meta({ + description: "When the account was created.", + example: "2024-10-15T22:00:00.000Z", + externalDocs: { + url: "https://docs.joinmastodon.org/entities/Account/#created_at", + }, + }), // TODO last_status_at: z .literal(null) - .openapi({ + .meta({ description: "When the most recent status was posted.", example: null, externalDocs: { @@ -323,7 +303,7 @@ const BaseAccount = z .number() .int() .nonnegative() - .openapi({ + .meta({ description: "How many statuses are attached to this account.", example: 42, externalDocs: { @@ -334,7 +314,7 @@ const BaseAccount = z .number() .int() .nonnegative() - .openapi({ + .meta({ description: "The reported followers of this profile.", example: 6, externalDocs: { @@ -345,7 +325,7 @@ const BaseAccount = z .number() .int() .nonnegative() - .openapi({ + .meta({ description: "The reported follows of this profile.", example: 23, externalDocs: { @@ -353,7 +333,7 @@ const BaseAccount = z }, }), /* Versia Server API extension */ - uri: z.string().url().openapi({ + uri: z.url().meta({ description: "The location of the user's Versia profile page, as opposed to the local representation.", example: @@ -365,26 +345,25 @@ const BaseAccount = z name: z.string(), }) .optional(), + get moved() { + return Account.nullable() + .optional() + .meta({ + description: + "Indicates that the profile is currently inactive and that its user has moved to a new account.", + example: null, + externalDocs: { + url: "https://docs.joinmastodon.org/entities/Account/#moved", + }, + }); + }, /* Versia Server API extension */ - roles: z.array(Role).openapi({ + roles: z.array(Role).meta({ description: "Roles assigned to the account.", }), - mute_expires_at: z.string().datetime().nullable().openapi({ + mute_expires_at: z.iso.datetime().nullable().meta({ description: "When a timed mute will expire, if applicable.", example: "2025-03-01T14:00:00.000Z", }), }) - .openapi({ ref: "BaseAccount" }); - -export const Account = BaseAccount.extend({ - moved: BaseAccount.nullable() - .optional() - .openapi({ - description: - "Indicates that the profile is currently inactive and that its user has moved to a new account.", - example: null, - externalDocs: { - url: "https://docs.joinmastodon.org/entities/Account/#moved", - }, - }), -}).openapi({ ref: "Account" }); + .meta({ id: "Account" }); diff --git a/packages/client/schemas/appeal.ts b/packages/client/schemas/appeal.ts index 4b626d6e..410b4c72 100644 --- a/packages/client/schemas/appeal.ts +++ b/packages/client/schemas/appeal.ts @@ -1,22 +1,22 @@ -import { z } from "zod"; +import { z } from "zod/v4"; export const Appeal = z .object({ - text: z.string().openapi({ + text: z.string().meta({ description: "Text of the appeal from the moderated account to the moderators.", example: "I believe this action was taken in error.", }), - state: z.enum(["approved", "rejected", "pending"]).openapi({ + state: z.enum(["approved", "rejected", "pending"]).meta({ description: "State of the appeal. 'approved' = The appeal has been approved by a moderator, 'rejected' = The appeal has been rejected by a moderator, 'pending' = The appeal has been submitted, but neither approved nor rejected yet.", example: "pending", }), }) - .openapi({ + .meta({ description: "Appeal against a moderation action.", externalDocs: { url: "https://docs.joinmastodon.org/entities/Appeal", }, - ref: "Appeal", + id: "Appeal", }); diff --git a/packages/client/schemas/application.ts b/packages/client/schemas/application.ts index afda6a91..685a6a00 100644 --- a/packages/client/schemas/application.ts +++ b/packages/client/schemas/application.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; export const Application = z .object({ @@ -7,7 +7,7 @@ export const Application = z .trim() .min(1) .max(200) - .openapi({ + .meta({ description: "The name of your application.", example: "Test Application", externalDocs: { @@ -17,7 +17,7 @@ export const Application = z website: z .string() .nullable() - .openapi({ + .meta({ description: "The website associated with your application.", example: "https://app.example", externalDocs: { @@ -27,7 +27,7 @@ export const Application = z scopes: z .array(z.string()) .default(["read"]) - .openapi({ + .meta({ description: "The scopes for your application. This is the registered scopes string split on whitespace.", example: ["read", "write", "push"], @@ -37,22 +37,18 @@ export const Application = z }), redirect_uris: z .array( - z - .string() - .url() - .or(z.literal("urn:ietf:wg:oauth:2.0:oob")) - .openapi({ - description: "URL or 'urn:ietf:wg:oauth:2.0:oob'", - }), + z.url().or(z.literal("urn:ietf:wg:oauth:2.0:oob")).meta({ + description: "URL or 'urn:ietf:wg:oauth:2.0:oob'", + }), ) - .openapi({ + .meta({ description: "The registered redirection URI(s) for your application.", externalDocs: { url: "https://docs.joinmastodon.org/entities/Application/#redirect_uris", }, }), - redirect_uri: z.string().openapi({ + redirect_uri: z.string().meta({ deprecated: true, description: "The registered redirection URI(s) for your application. May contain \\n characters when multiple redirect URIs are registered.", @@ -61,30 +57,30 @@ export const Application = z }, }), }) - .openapi({ - ref: "Application", + .meta({ + id: "Application", }); export const CredentialApplication = Application.extend({ - client_id: z.string().openapi({ + client_id: z.string().meta({ description: "Client ID key, to be used for obtaining OAuth tokens", externalDocs: { url: "https://docs.joinmastodon.org/entities/CredentialApplication/#client_id", }, }), - client_secret: z.string().openapi({ + client_secret: z.string().meta({ description: "Client secret key, to be used for obtaining OAuth tokens", externalDocs: { url: "https://docs.joinmastodon.org/entities/CredentialApplication/#client_secret", }, }), - client_secret_expires_at: z.string().openapi({ + client_secret_expires_at: z.string().meta({ description: "When the client secret key will expire at, presently this always returns 0 indicating that OAuth Clients do not expire", externalDocs: { url: "https://docs.joinmastodon.org/entities/CredentialApplication/#client_secret_expires_at", }, }), -}).openapi({ - ref: "CredentialApplication", +}).meta({ + id: "CredentialApplication", }); diff --git a/packages/client/schemas/attachment.ts b/packages/client/schemas/attachment.ts index 76c9ce7e..d149cf1d 100644 --- a/packages/client/schemas/attachment.ts +++ b/packages/client/schemas/attachment.ts @@ -1,34 +1,34 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { Id } from "./common.ts"; export const Attachment = z .object({ - id: Id.openapi({ + id: Id.meta({ description: "The ID of the attachment in the database.", example: "8c33d4c6-2292-4f4d-945d-261836e09647", }), - type: z.enum(["unknown", "image", "gifv", "video", "audio"]).openapi({ + type: z.enum(["unknown", "image", "gifv", "video", "audio"]).meta({ description: "The type of the attachment. 'unknown' = unsupported or unrecognized file type, 'image' = Static image, 'gifv' = Looping, soundless animation, 'video' = Video clip, 'audio' = Audio track.", example: "image", }), - url: z.string().url().openapi({ + url: z.url().meta({ description: "The location of the original full-size attachment.", example: "https://files.mastodon.social/media_attachments/files/022/345/792/original/57859aede991da25.jpeg", }), - preview_url: z.string().url().nullable().openapi({ + preview_url: z.url().nullable().meta({ description: "The location of a scaled-down preview of the attachment.", example: "https://files.mastodon.social/media_attachments/files/022/345/792/small/57859aede991da25.jpeg", }), - remote_url: z.string().url().nullable().openapi({ + remote_url: z.url().nullable().meta({ description: "The location of the full-size original attachment on the remote website, or null if the attachment is local.", example: null, }), - meta: z.record(z.any()).openapi({ + meta: z.any().meta({ description: "Metadata. May contain subtrees like 'small' and 'original', and possibly a 'focus' object for smart thumbnail cropping.", example: { @@ -50,22 +50,22 @@ export const Attachment = z }, }, }), - description: z.string().trim().nullable().openapi({ + description: z.string().trim().nullable().meta({ description: "Alternate text that describes what is in the media attachment, to be used for the visually impaired or when media attachments do not load.", example: "test media description", }), - blurhash: z.string().nullable().openapi({ + blurhash: z.string().nullable().meta({ description: "A hash computed by the BlurHash algorithm, for generating colorful preview thumbnails when media has not been downloaded yet.", example: "UFBWY:8_0Jxv4mx]t8t64.%M-:IUWGWAt6M}", }), }) - .openapi({ + .meta({ description: "Represents a file or media attachment that can be added to a status.", externalDocs: { url: "https://docs.joinmastodon.org/entities/Attachment", }, - ref: "Attachment", + id: "Attachment", }); diff --git a/packages/client/schemas/card.ts b/packages/client/schemas/card.ts index 3a7e804b..7389b0ff 100644 --- a/packages/client/schemas/card.ts +++ b/packages/client/schemas/card.ts @@ -1,97 +1,88 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { Account } from "./account.ts"; export const PreviewCardAuthor = z .object({ - name: z.string().openapi({ + name: z.string().meta({ description: "The original resource author’s name.", example: "The Doubleclicks", externalDocs: { url: "https://docs.joinmastodon.org/entities/PreviewCardAuthor/#name", }, }), - url: z - .string() - .url() - .openapi({ - description: "A link to the author of the original resource.", - example: "https://www.youtube.com/user/thedoubleclicks", - externalDocs: { - url: "https://docs.joinmastodon.org/entities/PreviewCardAuthor/#url", - }, - }), - account: Account.nullable().openapi({ + url: z.url().meta({ + description: "A link to the author of the original resource.", + example: "https://www.youtube.com/user/thedoubleclicks", + externalDocs: { + url: "https://docs.joinmastodon.org/entities/PreviewCardAuthor/#url", + }, + }), + account: Account.nullable().meta({ description: "The fediverse account of the author.", externalDocs: { url: "https://docs.joinmastodon.org/entities/PreviewCardAuthor/#account", }, }), }) - .openapi({ - ref: "PreviewCardAuthor", + .meta({ + id: "PreviewCardAuthor", }); export const PreviewCard = z .object({ - url: z - .string() - .url() - .openapi({ - description: "Location of linked resource.", - example: "https://www.youtube.com/watch?v=OMv_EPMED8Y", - externalDocs: { - url: "https://docs.joinmastodon.org/entities/PreviewCard/#url", - }, - }), + url: z.url().meta({ + description: "Location of linked resource.", + example: "https://www.youtube.com/watch?v=OMv_EPMED8Y", + externalDocs: { + url: "https://docs.joinmastodon.org/entities/PreviewCard/#url", + }, + }), title: z .string() .min(1) - .openapi({ + .meta({ description: "Title of linked resource.", example: "♪ Brand New Friend (Christmas Song!)", externalDocs: { url: "https://docs.joinmastodon.org/entities/PreviewCard/#title", }, }), - description: z.string().openapi({ + description: z.string().meta({ description: "Description of preview.", example: "", externalDocs: { url: "https://docs.joinmastodon.org/entities/PreviewCard/#description", }, }), - type: z.enum(["link", "photo", "video"]).openapi({ + type: z.enum(["link", "photo", "video"]).meta({ description: "The type of the preview card.", example: "video", externalDocs: { url: "https://docs.joinmastodon.org/entities/PreviewCard/#type", }, }), - authors: z.array(PreviewCardAuthor).openapi({ + authors: z.array(PreviewCardAuthor).meta({ description: "Fediverse account of the authors of the original resource.", externalDocs: { url: "https://docs.joinmastodon.org/entities/PreviewCard/#authors", }, }), - provider_name: z.string().openapi({ + provider_name: z.string().meta({ description: "The provider of the original resource.", example: "YouTube", externalDocs: { url: "https://docs.joinmastodon.org/entities/PreviewCard/#provider_name", }, }), - provider_url: z - .string() - .url() - .openapi({ - description: "A link to the provider of the original resource.", - example: "https://www.youtube.com/", - externalDocs: { - url: "https://docs.joinmastodon.org/entities/PreviewCard/#provider_url", - }, - }), - html: z.string().openapi({ + provider_url: z.url().meta({ + description: "A link to the provider of the original resource.", + example: "https://www.youtube.com/", + externalDocs: { + url: "https://docs.joinmastodon.org/entities/PreviewCard/#provider_url", + }, + }), + html: z.string().meta({ description: "HTML to be used for generating the preview card.", example: '', @@ -102,7 +93,7 @@ export const PreviewCard = z width: z .number() .int() - .openapi({ + .meta({ description: "Width of preview, in pixels.", example: 480, externalDocs: { @@ -112,7 +103,7 @@ export const PreviewCard = z height: z .number() .int() - .openapi({ + .meta({ description: "Height of preview, in pixels.", example: 270, externalDocs: { @@ -120,10 +111,9 @@ export const PreviewCard = z }, }), image: z - .string() .url() .nullable() - .openapi({ + .meta({ description: "Preview thumbnail.", example: "https://cdn.versia.social/preview_cards/images/014/179/145/original/9cf4b7cf5567b569.jpeg", @@ -131,21 +121,18 @@ export const PreviewCard = z url: "https://docs.joinmastodon.org/entities/PreviewCard/#image", }, }), - embed_url: z - .string() - .url() - .openapi({ - description: "Used for photo embeds, instead of custom html.", - example: - "https://live.staticflickr.com/65535/49088768431_6a4322b3bb_b.jpg", - externalDocs: { - url: "https://docs.joinmastodon.org/entities/PreviewCard/#embed_url", - }, - }), + embed_url: z.url().meta({ + description: "Used for photo embeds, instead of custom html.", + example: + "https://live.staticflickr.com/65535/49088768431_6a4322b3bb_b.jpg", + externalDocs: { + url: "https://docs.joinmastodon.org/entities/PreviewCard/#embed_url", + }, + }), blurhash: z .string() .nullable() - .openapi({ + .meta({ description: "A hash computed by the BlurHash algorithm, for generating colorful preview thumbnails when media has not been downloaded yet.", example: "UvK0HNkV,:s9xBR%njog0fo2W=WBS5ozofV@", @@ -154,11 +141,11 @@ export const PreviewCard = z }, }), }) - .openapi({ + .meta({ description: "Represents a rich preview card that is generated using OpenGraph tags from a URL.", externalDocs: { url: "https://docs.joinmastodon.org/entities/PreviewCard", }, - ref: "PreviewCard", + id: "PreviewCard", }); diff --git a/packages/client/schemas/common.ts b/packages/client/schemas/common.ts index e3eeef6f..b93c57c6 100644 --- a/packages/client/schemas/common.ts +++ b/packages/client/schemas/common.ts @@ -1,22 +1,17 @@ import ISO6391 from "iso-639-1"; -import { z } from "zod"; -import "zod-openapi/extend"; +import { z } from "zod/v4"; -export const Id = z.string().uuid(); +export const Id = z.uuid(); export const iso631 = z .enum(ISO6391.getAllCodes() as [string, ...string[]]) - .openapi({ + .meta({ description: "ISO 639-1 language code", example: "en", externalDocs: { url: "https://en.wikipedia.org/wiki/List_of_ISO_639-1_language_codes", }, - ref: "ISO631", + id: "ISO631", }); -export const zBoolean = z - .string() - .transform((v) => ["true", "1", "on"].includes(v.toLowerCase())) - .openapi({ type: "boolean" }) - .or(z.boolean()); +export const zBoolean = z.stringbool().or(z.boolean()); diff --git a/packages/client/schemas/context.ts b/packages/client/schemas/context.ts index 5916dd14..4344bec9 100644 --- a/packages/client/schemas/context.ts +++ b/packages/client/schemas/context.ts @@ -1,26 +1,26 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { Status } from "./status.ts"; export const Context = z .object({ - ancestors: z.array(Status).openapi({ + ancestors: z.array(Status).meta({ description: "Parents in the thread.", externalDocs: { url: "https://docs.joinmastodon.org/entities/Context/#ancestors", }, }), - descendants: z.array(Status).openapi({ + descendants: z.array(Status).meta({ description: "Children in the thread.", externalDocs: { url: "https://docs.joinmastodon.org/entities/Context/#descendants", }, }), }) - .openapi({ + .meta({ description: "Represents the tree around a given status. Used for reconstructing threads of statuses.", externalDocs: { url: "https://docs.joinmastodon.org/entities/Context/#context", }, - ref: "Context", + id: "Context", }); diff --git a/packages/client/schemas/emoji.ts b/packages/client/schemas/emoji.ts index 5a1f8aae..00e49cbc 100644 --- a/packages/client/schemas/emoji.ts +++ b/packages/client/schemas/emoji.ts @@ -1,11 +1,11 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { emojiRegex } from "../regex.ts"; import { Id, zBoolean } from "./common.ts"; export const CustomEmoji = z .object({ /* Versia Server API extension */ - id: Id.openapi({ + id: Id.meta({ description: "ID of the custom emoji in the database.", example: "af9ccd29-c689-477f-aa27-d7d95fd8fb05", }), @@ -17,36 +17,30 @@ export const CustomEmoji = z emojiRegex, "Shortcode must only contain letters (any case), numbers, dashes or underscores.", ) - .openapi({ + .meta({ description: "The name of the custom emoji.", example: "blobaww", externalDocs: { url: "https://docs.joinmastodon.org/entities/CustomEmoji/#shortcode", }, }), - url: z - .string() - .url() - .openapi({ - description: "A link to the custom emoji.", - example: - "https://cdn.versia.social/emojis/images/000/011/739/original/blobaww.png", - externalDocs: { - url: "https://docs.joinmastodon.org/entities/CustomEmoji/#url", - }, - }), - static_url: z - .string() - .url() - .openapi({ - description: "A link to a static copy of the custom emoji.", - example: - "https://cdn.versia.social/emojis/images/000/011/739/static/blobaww.png", - externalDocs: { - url: "https://docs.joinmastodon.org/entities/CustomEmoji/#static_url", - }, - }), - visible_in_picker: z.boolean().openapi({ + url: z.url().meta({ + description: "A link to the custom emoji.", + example: + "https://cdn.versia.social/emojis/images/000/011/739/original/blobaww.png", + externalDocs: { + url: "https://docs.joinmastodon.org/entities/CustomEmoji/#url", + }, + }), + static_url: z.url().meta({ + description: "A link to a static copy of the custom emoji.", + example: + "https://cdn.versia.social/emojis/images/000/011/739/static/blobaww.png", + externalDocs: { + url: "https://docs.joinmastodon.org/entities/CustomEmoji/#static_url", + }, + }), + visible_in_picker: z.boolean().meta({ description: "Whether this Emoji should be visible in the picker or unlisted.", example: true, @@ -59,7 +53,7 @@ export const CustomEmoji = z .trim() .max(64) .nullable() - .openapi({ + .meta({ description: "Used for sorting custom emoji in the picker.", example: "Blobs", externalDocs: { @@ -67,7 +61,7 @@ export const CustomEmoji = z }, }), /* Versia Server API extension */ - global: zBoolean.openapi({ + global: zBoolean.meta({ description: "Whether this emoji is visible to all users.", example: false, }), @@ -75,7 +69,7 @@ export const CustomEmoji = z description: z .string() .nullable() - .openapi({ + .meta({ description: "Emoji description for users using screen readers.", example: "A cute blob.", @@ -84,10 +78,10 @@ export const CustomEmoji = z }, }), }) - .openapi({ + .meta({ description: "Represents a custom emoji.", externalDocs: { url: "https://docs.joinmastodon.org/entities/CustomEmoji", }, - ref: "CustomEmoji", + id: "CustomEmoji", }); diff --git a/packages/client/schemas/extended-description.ts b/packages/client/schemas/extended-description.ts index a45c808e..7becfd2c 100644 --- a/packages/client/schemas/extended-description.ts +++ b/packages/client/schemas/extended-description.ts @@ -1,19 +1,16 @@ -import { z } from "zod"; +import { z } from "zod/v4"; export const ExtendedDescription = z .object({ - updated_at: z - .string() - .datetime() - .openapi({ - description: - "A timestamp of when the extended description was last updated.", - example: "2025-01-12T13:11:00Z", - externalDocs: { - url: "https://docs.joinmastodon.org/entities/ExtendedDescription/#updated_at", - }, - }), - content: z.string().openapi({ + updated_at: z.iso.datetime().meta({ + description: + "A timestamp of when the extended description was last updated.", + example: "2025-01-12T13:11:00Z", + externalDocs: { + url: "https://docs.joinmastodon.org/entities/ExtendedDescription/#updated_at", + }, + }), + content: z.string().meta({ description: "The rendered HTML content of the extended description.", example: "

We love casting spells.

", @@ -22,11 +19,11 @@ export const ExtendedDescription = z }, }), }) - .openapi({ + .meta({ description: "Represents an extended description for the instance, to be shown on its about page.", externalDocs: { url: "https://docs.joinmastodon.org/entities/ExtendedDescription", }, - ref: "ExtendedDescription", + id: "ExtendedDescription", }); diff --git a/packages/client/schemas/familiar-followers.ts b/packages/client/schemas/familiar-followers.ts index cfeeee15..92c6a7ca 100644 --- a/packages/client/schemas/familiar-followers.ts +++ b/packages/client/schemas/familiar-followers.ts @@ -1,27 +1,27 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { Account } from "./account.ts"; export const FamiliarFollowers = z .object({ - id: Account.shape.id.openapi({ + id: Account.shape.id.meta({ description: "The ID of the Account in the database.", example: "48214efb-1f3c-459a-abfa-618a5aeb2f7a", externalDocs: { url: "https://docs.joinmastodon.org/entities/FamiliarFollowers/#id", }, }), - accounts: z.array(Account).openapi({ + accounts: z.array(Account).meta({ description: "Accounts you follow that also follow this account.", externalDocs: { url: "https://docs.joinmastodon.org/entities/FamiliarFollowers/#accounts", }, }), }) - .openapi({ + .meta({ description: "Represents a subset of your follows who also follow some other user.", externalDocs: { url: "https://docs.joinmastodon.org/entities/FamiliarFollowers", }, - ref: "FamiliarFollowers", + id: "FamiliarFollowers", }); diff --git a/packages/client/schemas/filters.ts b/packages/client/schemas/filters.ts index 6e92ceb1..902f6f6f 100644 --- a/packages/client/schemas/filters.ts +++ b/packages/client/schemas/filters.ts @@ -1,16 +1,16 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { Id, zBoolean } from "./common.ts"; export const FilterStatus = z .object({ - id: Id.openapi({ + id: Id.meta({ description: "The ID of the FilterStatus in the database.", example: "3b19ed7c-0c4b-45e1-8c75-e21dfc8e86c3", externalDocs: { url: "https://docs.joinmastodon.org/entities/FilterStatus/#id", }, }), - status_id: Id.openapi({ + status_id: Id.meta({ description: "The ID of the Status that will be filtered.", example: "4f941ac8-295c-4c2d-9300-82c162ac8028", externalDocs: { @@ -18,32 +18,32 @@ export const FilterStatus = z }, }), }) - .openapi({ + .meta({ description: "Represents a status ID that, if matched, should cause the filter action to be taken.", externalDocs: { url: "https://docs.joinmastodon.org/entities/FilterStatus", }, - ref: "FilterStatus", + id: "FilterStatus", }); export const FilterKeyword = z .object({ - id: Id.openapi({ + id: Id.meta({ description: "The ID of the FilterKeyword in the database.", example: "ca921e60-5b96-4686-90f3-d7cc420d7391", externalDocs: { url: "https://docs.joinmastodon.org/entities/FilterKeyword/#id", }, }), - keyword: z.string().openapi({ + keyword: z.string().meta({ description: "The phrase to be matched against.", example: "badword", externalDocs: { url: "https://docs.joinmastodon.org/entities/FilterKeyword/#keyword", }, }), - whole_word: zBoolean.openapi({ + whole_word: zBoolean.meta({ description: "Should the filter consider word boundaries? See implementation guidelines for filters.", example: false, @@ -52,18 +52,18 @@ export const FilterKeyword = z }, }), }) - .openapi({ + .meta({ description: "Represents a keyword that, if matched, should cause the filter action to be taken.", externalDocs: { url: "https://docs.joinmastodon.org/entities/FilterKeyword", }, - ref: "FilterKeyword", + id: "FilterKeyword", }); export const Filter = z .object({ - id: Id.openapi({ + id: Id.meta({ description: "The ID of the Filter in the database.", example: "6b8fa22f-b128-43c2-9a1f-3c0499ef3a51", externalDocs: { @@ -75,7 +75,7 @@ export const Filter = z .trim() .min(1) .max(255) - .openapi({ + .meta({ description: "A title given by the user to name the filter.", example: "Test filter", externalDocs: { @@ -93,7 +93,7 @@ export const Filter = z ]), ) .default([]) - .openapi({ + .meta({ description: "The contexts in which the filter should be applied.", example: ["home"], @@ -104,7 +104,7 @@ export const Filter = z expires_at: z .string() .nullable() - .openapi({ + .meta({ description: "When the filter should no longer be applied.", example: "2026-09-20T17:27:39.296Z", externalDocs: { @@ -114,7 +114,7 @@ export const Filter = z filter_action: z .enum(["warn", "hide"]) .default("warn") - .openapi({ + .meta({ description: "The action to be taken when a status matches this filter.", example: "warn", @@ -122,31 +122,31 @@ export const Filter = z url: "https://docs.joinmastodon.org/entities/Filter/#filter_action", }, }), - keywords: z.array(FilterKeyword).openapi({ + keywords: z.array(FilterKeyword).meta({ description: "The keywords grouped under this filter.", externalDocs: { url: "https://docs.joinmastodon.org/entities/Filter/#keywords", }, }), - statuses: z.array(FilterStatus).openapi({ + statuses: z.array(FilterStatus).meta({ description: "The statuses grouped under this filter.", externalDocs: { url: "https://docs.joinmastodon.org/entities/Filter/#statuses", }, }), }) - .openapi({ + .meta({ description: "Represents a user-defined filter for determining which statuses should not be shown to the user.", externalDocs: { url: "https://docs.joinmastodon.org/entities/Filter", }, - ref: "Filter", + id: "Filter", }); export const FilterResult = z .object({ - filter: Filter.openapi({ + filter: Filter.meta({ description: "The filter that was matched.", externalDocs: { url: "https://docs.joinmastodon.org/entities/FilterResult/#filter", @@ -155,7 +155,7 @@ export const FilterResult = z keyword_matches: z .array(z.string()) .nullable() - .openapi({ + .meta({ description: "The keyword within the filter that was matched.", example: ["badword"], externalDocs: { @@ -165,7 +165,7 @@ export const FilterResult = z status_matches: z .array(Id) .nullable() - .openapi({ + .meta({ description: "The status ID within the filter that was matched.", example: ["3819515a-5ceb-4078-8524-c939e38dcf8f"], @@ -174,11 +174,11 @@ export const FilterResult = z }, }), }) - .openapi({ + .meta({ description: "Represents a filter whose keywords matched a given status.", externalDocs: { url: "https://docs.joinmastodon.org/entities/FilterResult", }, - ref: "FilterResult", + id: "FilterResult", }); diff --git a/packages/client/schemas/instance-v1.ts b/packages/client/schemas/instance-v1.ts index 75e058df..0e0cddee 100644 --- a/packages/client/schemas/instance-v1.ts +++ b/packages/client/schemas/instance-v1.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { Instance } from "./instance.ts"; import { SSOConfig } from "./versia.ts"; @@ -7,7 +7,7 @@ export const InstanceV1 = z uri: Instance.shape.domain, title: Instance.shape.title, short_description: Instance.shape.description, - description: z.string().openapi({ + description: z.string().meta({ description: "An HTML-permitted description of the site.", example: "

Join the world's smallest social network.

", }), @@ -20,29 +20,29 @@ export const InstanceV1 = z streaming_api: Instance.shape.configuration.shape.urls.shape.streaming, }) - .openapi({ + .meta({ description: "URLs of interest for clients apps.", }), stats: z .object({ - user_count: z.number().openapi({ + user_count: z.number().meta({ description: "Total users on this instance.", example: 812303, }), - status_count: z.number().openapi({ + status_count: z.number().meta({ description: "Total statuses on this instance.", example: 38151616, }), - domain_count: z.number().openapi({ + domain_count: z.number().meta({ description: "Total domains discovered by this instance.", example: 25255, }), }) - .openapi({ + .meta({ description: "Statistics about how much information the instance contains.", }), - thumbnail: z.string().url().nullable().openapi({ + thumbnail: z.url().nullable().meta({ description: "Banner image for the website.", example: "https://files.mastodon.social/site_uploads/files/000/000/001/original/vlcsnap-2018-08-27-16h43m11s127.png", @@ -50,7 +50,7 @@ export const InstanceV1 = z languages: Instance.shape.languages, registrations: Instance.shape.registrations.shape.enabled, approval_required: Instance.shape.registrations.shape.approval_required, - invites_enabled: z.boolean().openapi({ + invites_enabled: z.boolean().meta({ description: "Whether invites are enabled.", example: true, }), @@ -62,7 +62,7 @@ export const InstanceV1 = z Instance.shape.configuration.shape.accounts.shape .max_featured_tags, }) - .openapi({ + .meta({ description: "Limits related to accounts.", }), statuses: z @@ -77,7 +77,7 @@ export const InstanceV1 = z Instance.shape.configuration.shape.statuses.shape .characters_reserved_per_url, }) - .openapi({ + .meta({ description: "Limits related to authoring statuses.", }), media_attachments: z @@ -101,7 +101,7 @@ export const InstanceV1 = z Instance.shape.configuration.shape.media_attachments .shape.video_matrix_limit, }) - .openapi({ + .meta({ description: "Hints for which attachments will be accepted.", }), @@ -120,11 +120,11 @@ export const InstanceV1 = z Instance.shape.configuration.shape.polls.shape .max_expiration, }) - .openapi({ + .meta({ description: "Limits related to polls.", }), }) - .openapi({ + .meta({ description: "Configured values and limits for this website.", }), contact_account: Instance.shape.contact.shape.account, @@ -132,11 +132,11 @@ export const InstanceV1 = z /* Versia Server API extension */ sso: SSOConfig, }) - .openapi({ + .meta({ description: "Represents the software instance of Versia Server running on this domain.", externalDocs: { url: "https://docs.joinmastodon.org/entities/V1_Instance", }, - ref: "InstanceV1", + id: "InstanceV1", }); diff --git a/packages/client/schemas/instance.ts b/packages/client/schemas/instance.ts index 0914138b..b72d4031 100644 --- a/packages/client/schemas/instance.ts +++ b/packages/client/schemas/instance.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { Account } from "./account.ts"; import { iso631 } from "./common.ts"; import { Rule } from "./rule.ts"; @@ -6,50 +6,50 @@ import { SSOConfig } from "./versia.ts"; const InstanceIcon = z .object({ - src: z.string().url().openapi({ + src: z.url().meta({ description: "The URL of this icon.", example: "https://files.mastodon.social/site_uploads/files/000/000/003/36/accf17b0104f18e5.png", }), - size: z.string().openapi({ + size: z.string().meta({ description: "The size of this icon (in the form of 12x34, where 12 is the width and 34 is the height of the icon).", example: "36x36", }), }) - .openapi({ + .meta({ externalDocs: { url: "https://docs.joinmastodon.org/entities/InstanceIcon", }, - ref: "InstanceIcon", + id: "InstanceIcon", }); export const Instance = z .object({ - domain: z.string().openapi({ + domain: z.string().meta({ description: "The domain name of the instance.", example: "versia.social", }), - title: z.string().openapi({ + title: z.string().meta({ description: "The title of the website.", example: "Versia Social • Now with 100% more blobs!", }), - version: z.string().openapi({ + version: z.string().meta({ description: "Mastodon version that the API is compatible with. Used for compatibility with Mastodon clients.", example: "4.3.0+glitch", }), /* Versia Server API extension */ - versia_version: z.string().openapi({ + versia_version: z.string().meta({ description: "Versia Server version.", example: "0.8.0", }), - source_url: z.string().url().openapi({ + source_url: z.url().meta({ description: "The URL for the source code of the software running on this instance, in keeping with AGPL license requirements.", example: "https://github.com/versia-pub/server", }), - description: z.string().openapi({ + description: z.string().meta({ description: "A short, plain-text description defined by the admin.", example: "The flagship Versia Server instance. Join for free hugs!", @@ -58,74 +58,74 @@ export const Instance = z .object({ users: z .object({ - active_month: z.number().openapi({ + active_month: z.number().meta({ description: "The number of active users in the past 4 weeks.", example: 1_261, }), }) - .openapi({ + .meta({ description: "Usage data related to users on this instance.", }), }) - .openapi({ description: "Usage data for this instance." }), + .meta({ description: "Usage data for this instance." }), thumbnail: z .object({ - url: z.string().url().openapi({ + url: z.url().meta({ description: "The URL for the thumbnail image.", example: "https://files.mastodon.social/site_uploads/files/000/000/001/@1x/57c12f441d083cde.png", }), - blurhash: z.string().optional().openapi({ + blurhash: z.string().optional().meta({ description: "A hash computed by the BlurHash algorithm, for generating colorful preview thumbnails when media has not been downloaded yet.", example: "UUKJMXv|x]t7^*t7Rjaz^jazRjaz", }), versions: z .object({ - "@1x": z.string().url().optional().openapi({ + "@1x": z.url().optional().meta({ description: "The URL for the thumbnail image at 1x resolution.", }), - "@2x": z.string().url().optional().openapi({ + "@2x": z.url().optional().meta({ description: "The URL for the thumbnail image at 2x resolution.", }), }) .optional() - .openapi({ + .meta({ description: "Links to scaled resolution images, for high DPI screens.", }), }) - .openapi({ + .meta({ description: "An image used to represent this instance.", }), /* Versia Server API extension */ banner: z .object({ - url: z.string().url().openapi({ + url: z.url().meta({ description: "The URL for the banner image.", example: "https://files.mastodon.social/site_uploads/files/000/000/001/@1x/57c12f441d083cde.png", }), - blurhash: z.string().optional().openapi({ + blurhash: z.string().optional().meta({ description: "A hash computed by the BlurHash algorithm, for generating colorful preview thumbnails when media has not been downloaded yet.", example: "UUKJMXv|x]t7^*t7Rjaz^jazRjaz", }), }) .optional() - .openapi({ + .meta({ description: "A wide banner image used to represent this instance.", }), - icon: z.array(InstanceIcon).openapi({ + icon: z.array(InstanceIcon).meta({ description: "The list of available size variants for this instance configured icon.", }), - languages: z.array(iso631).openapi({ + languages: z.array(iso631).meta({ description: "Primary languages of the website and its staff.", example: ["en"], }), @@ -133,63 +133,63 @@ export const Instance = z .object({ urls: z .object({ - streaming: z.string().url().openapi({ + streaming: z.url().meta({ description: "The Websockets URL for connecting to the streaming API.", example: "wss://versia.social", }), }) - .openapi({ + .meta({ description: "URLs of interest for clients apps.", }), vapid: z .object({ - public_key: z.string().openapi({ + public_key: z.string().meta({ description: "The instance's VAPID public key, used for push notifications, the same as WebPushSubscription#server_key.", example: "BCkMmVdKDnKYwzVCDC99Iuc9GvId-x7-kKtuHnLgfF98ENiZp_aj-UNthbCdI70DqN1zUVis-x0Wrot2sBagkMc=", }), }) - .openapi({ description: "VAPID configuration." }), + .meta({ description: "VAPID configuration." }), accounts: z .object({ - max_featured_tags: z.number().openapi({ + max_featured_tags: z.number().meta({ description: "The maximum number of featured tags allowed for each account.", example: 10, }), - max_pinned_statuses: z.number().openapi({ + max_pinned_statuses: z.number().meta({ description: "The maximum number of pinned statuses for each account.", example: 4, }), /* Versia Server API extension */ - max_displayname_characters: z.number().openapi({ + max_displayname_characters: z.number().meta({ description: "The maximum number of characters allowed in a display name.", example: 30, }), /* Versia Server API extension */ - max_username_characters: z.number().openapi({ + max_username_characters: z.number().meta({ description: "The maximum number of characters allowed in a username.", example: 30, }), /* Versia Server API extension */ - max_note_characters: z.number().openapi({ + max_note_characters: z.number().meta({ description: "The maximum number of characters allowed in an account's bio/note.", example: 500, }), /* Versia Server API extension */ - avatar_limit: z.number().openapi({ + avatar_limit: z.number().meta({ description: "The maximum size of an avatar image, in bytes.", example: 1048576, }), /* Versia Server API extension */ - header_limit: z.number().openapi({ + header_limit: z.number().meta({ description: "The maximum size of a header image, in bytes.", example: 2097152, @@ -197,206 +197,206 @@ export const Instance = z /* Versia Server API extension */ fields: z .object({ - max_fields: z.number().openapi({ + max_fields: z.number().meta({ description: "The maximum number of fields allowed per account.", example: 4, }), - max_name_characters: z.number().openapi({ + max_name_characters: z.number().meta({ description: "The maximum number of characters allowed in a field name.", example: 30, }), - max_value_characters: z.number().openapi({ + max_value_characters: z.number().meta({ description: "The maximum number of characters allowed in a field value.", example: 100, }), }) - .openapi({ + .meta({ description: "Limits related to account fields.", }), }) - .openapi({ description: "Limits related to accounts." }), + .meta({ description: "Limits related to accounts." }), statuses: z .object({ - max_characters: z.number().openapi({ + max_characters: z.number().meta({ description: "The maximum number of allowed characters per status.", example: 500, }), - max_media_attachments: z.number().openapi({ + max_media_attachments: z.number().meta({ description: "The maximum number of media attachments that can be added to a status.", example: 4, }), - characters_reserved_per_url: z.number().openapi({ + characters_reserved_per_url: z.number().meta({ description: "Each URL in a status will be assumed to be exactly this many characters.", example: 23, }), }) - .openapi({ + .meta({ description: "Limits related to authoring statuses.", }), media_attachments: z .object({ - supported_mime_types: z.array(z.string()).openapi({ + supported_mime_types: z.array(z.string()).meta({ description: "Contains MIME types that can be uploaded.", example: ["image/jpeg", "image/png", "image/gif"], }), - description_limit: z.number().openapi({ + description_limit: z.number().meta({ description: "The maximum size of a description, in characters.", example: 1500, }), - image_size_limit: z.number().openapi({ + image_size_limit: z.number().meta({ description: "The maximum size of any uploaded image, in bytes.", example: 10485760, }), - image_matrix_limit: z.number().openapi({ + image_matrix_limit: z.number().meta({ description: "The maximum number of pixels (width times height) for image uploads.", example: 16777216, }), - video_size_limit: z.number().openapi({ + video_size_limit: z.number().meta({ description: "The maximum size of any uploaded video, in bytes.", example: 41943040, }), - video_frame_rate_limit: z.number().openapi({ + video_frame_rate_limit: z.number().meta({ description: "The maximum frame rate for any uploaded video.", example: 60, }), - video_matrix_limit: z.number().openapi({ + video_matrix_limit: z.number().meta({ description: "The maximum number of pixels (width times height) for video uploads.", example: 2304000, }), }) - .openapi({ + .meta({ description: "Hints for which attachments will be accepted.", }), /* Versia Server API extension */ emojis: z .object({ - emoji_size_limit: z.number().openapi({ + emoji_size_limit: z.number().meta({ description: "The maximum size of an emoji image, in bytes.", example: 1048576, }), - max_shortcode_characters: z.number().openapi({ + max_shortcode_characters: z.number().meta({ description: "The maximum number of characters allowed in an emoji shortcode.", example: 30, }), - max_description_characters: z.number().openapi({ + max_description_characters: z.number().meta({ description: "The maximum number of characters allowed in an emoji description.", example: 100, }), }) - .openapi({ + .meta({ description: "Limits related to custom emojis.", }), polls: z .object({ - max_options: z.number().openapi({ + max_options: z.number().meta({ description: "Each poll is allowed to have up to this many options.", example: 4, }), - max_characters_per_option: z.number().openapi({ + max_characters_per_option: z.number().meta({ description: "Each poll option is allowed to have this many characters.", example: 50, }), - min_expiration: z.number().openapi({ + min_expiration: z.number().meta({ description: "The shortest allowed poll duration, in seconds.", example: 300, }), - max_expiration: z.number().openapi({ + max_expiration: z.number().meta({ description: "The longest allowed poll duration, in seconds.", example: 2629746, }), }) - .openapi({ description: "Limits related to polls." }), + .meta({ description: "Limits related to polls." }), translation: z .object({ - enabled: z.boolean().openapi({ + enabled: z.boolean().meta({ description: "Whether the Translations API is available on this instance.", example: true, }), }) - .openapi({ description: "Hints related to translation." }), + .meta({ description: "Hints related to translation." }), }) - .openapi({ + .meta({ description: "Configured values and limits for this website.", }), registrations: z .object({ - enabled: z.boolean().openapi({ + enabled: z.boolean().meta({ description: "Whether registrations are enabled.", example: false, }), - approval_required: z.boolean().openapi({ + approval_required: z.boolean().meta({ description: "Whether registrations require moderator approval.", example: false, }), - message: z.string().nullable().openapi({ + message: z.string().nullable().meta({ description: "A custom message to be shown when registrations are closed.", }), }) - .openapi({ + .meta({ description: "Information about registering for this website.", }), api_versions: z .object({ - mastodon: z.number().openapi({ + mastodon: z.number().meta({ description: "API version number that this server implements.", example: 1, }), }) - .openapi({ + .meta({ description: "Information about which version of the API is implemented by this server.", }), contact: z .object({ - email: z.string().email().openapi({ + email: z.email().meta({ description: "An email address that can be messaged regarding inquiries or issues.", example: "contact@versia.social", }), - account: Account.nullable().openapi({ + account: Account.nullable().meta({ description: "An account that can be contacted regarding inquiries or issues.", }), }) - .openapi({ + .meta({ description: "Hints related to contacting a representative of the website.", }), - rules: z.array(Rule).openapi({ + rules: z.array(Rule).meta({ description: "An itemized list of rules for this website.", }), /* Versia Server API extension */ sso: SSOConfig, }) - .openapi({ + .meta({ externalDocs: { url: "https://docs.joinmastodon.org/entities/Instance", }, - ref: "Instance", + id: "Instance", }); diff --git a/packages/client/schemas/marker.ts b/packages/client/schemas/marker.ts index 74fe122e..99705921 100644 --- a/packages/client/schemas/marker.ts +++ b/packages/client/schemas/marker.ts @@ -1,27 +1,27 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { Id } from "./common.ts"; export const Marker = z .object({ - last_read_id: Id.openapi({ + last_read_id: Id.meta({ description: "The ID of the most recently viewed entity.", example: "ead15c9d-8eda-4b2c-9546-ecbf851f001c", }), - version: z.number().openapi({ + version: z.number().meta({ description: "An incrementing counter, used for locking to prevent write conflicts.", example: 462, }), - updated_at: z.string().datetime().openapi({ + updated_at: z.iso.datetime().meta({ description: "The timestamp of when the marker was set.", example: "2025-01-12T13:11:00Z", }), }) - .openapi({ + .meta({ description: "Represents the last read position within a user's timelines.", externalDocs: { url: "https://docs.joinmastodon.org/entities/Marker", }, - ref: "Marker", + id: "Marker", }); diff --git a/packages/client/schemas/notification.ts b/packages/client/schemas/notification.ts index 32f9318c..f7b3be22 100644 --- a/packages/client/schemas/notification.ts +++ b/packages/client/schemas/notification.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { Account } from "./account.ts"; import { AccountWarning } from "./account-warning.ts"; import { Id } from "./common.ts"; @@ -7,7 +7,7 @@ import { Status } from "./status.ts"; export const Notification = z .object({ - id: Id.openapi({ + id: Id.meta({ description: "The ID of the notification in the database.", example: "6405f495-da55-4ad7-b5d6-9a773360fc07", }), @@ -27,47 +27,46 @@ export const Notification = z "severed_relationships", "moderation_warning", ]) - .openapi({ + .meta({ description: "The type of event that resulted in the notification.", example: "mention", }), - group_key: z.string().openapi({ + group_key: z.string().meta({ description: "Group key shared by similar notifications, to be used in the grouped notifications feature.", example: "ungrouped-34975861", }), - created_at: z.string().datetime().openapi({ + created_at: z.iso.datetime().meta({ description: "The timestamp of the notification.", example: "2025-01-12T13:11:00Z", }), - account: Account.openapi({ + account: Account.meta({ description: "The account that performed the action that generated the notification.", }), - status: Status.optional().openapi({ + status: Status.optional().meta({ description: "Status that was the object of the notification. Attached when type of the notification is favourite, reblog, status, mention, poll, or update.", }), - report: Report.optional().openapi({ + report: Report.optional().meta({ description: "Report that was the object of the notification. Attached when type of the notification is admin.report.", }), - event: z.undefined().openapi({ + event: z.undefined().meta({ description: "Versia Server does not sever relationships, so this field is always empty.", - type: "null", }), - moderation_warning: AccountWarning.optional().openapi({ + moderation_warning: AccountWarning.optional().meta({ description: "Moderation warning that caused the notification. Attached when type of the notification is moderation_warning.", }), }) - .openapi({ + .meta({ description: "Represents a notification of an event relevant to the user.", externalDocs: { url: "https://docs.joinmastodon.org/entities/Notification", }, - ref: "Notification", + id: "Notification", }); diff --git a/packages/client/schemas/poll.ts b/packages/client/schemas/poll.ts index a2643727..b3fe20f9 100644 --- a/packages/client/schemas/poll.ts +++ b/packages/client/schemas/poll.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { Id } from "./common.ts"; import { CustomEmoji } from "./emoji.ts"; @@ -8,7 +8,7 @@ export const PollOption = z .string() .trim() .min(1) - .openapi({ + .meta({ description: "The text value of the poll option.", example: "yes", externalDocs: { @@ -20,7 +20,7 @@ export const PollOption = z .int() .nonnegative() .nullable() - .openapi({ + .meta({ description: "The total number of received votes for this option.", example: 6, @@ -29,41 +29,40 @@ export const PollOption = z }, }), }) - .openapi({ + .meta({ externalDocs: { url: "https://docs.joinmastodon.org/entities/Poll/#Option", }, - ref: "PollOption", + id: "PollOption", }); export const Poll = z .object({ - id: Id.openapi({ + id: Id.meta({ description: "ID of the poll in the database.", example: "d87d230f-e401-4282-80c7-2044ab989662", externalDocs: { url: "https://docs.joinmastodon.org/entities/Poll/#id", }, }), - expires_at: z - .string() + expires_at: z.iso .datetime() .nullable() - .openapi({ + .meta({ description: "When the poll ends.", example: "2025-01-07T14:11:00.000Z", externalDocs: { url: "https://docs.joinmastodon.org/entities/Poll/#expires_at", }, }), - expired: z.boolean().openapi({ + expired: z.boolean().meta({ description: "Is the poll currently expired?", example: false, externalDocs: { url: "https://docs.joinmastodon.org/entities/Poll/#expired", }, }), - multiple: z.boolean().openapi({ + multiple: z.boolean().meta({ description: "Does the poll allow multiple-choice answers?", example: false, externalDocs: { @@ -74,7 +73,7 @@ export const Poll = z .number() .int() .nonnegative() - .openapi({ + .meta({ description: "How many votes have been received.", example: 6, externalDocs: { @@ -86,7 +85,7 @@ export const Poll = z .int() .nonnegative() .nullable() - .openapi({ + .meta({ description: "How many unique accounts have voted on a multiple-choice poll.", example: 3, @@ -94,13 +93,13 @@ export const Poll = z url: "https://docs.joinmastodon.org/entities/Poll/#voters_count", }, }), - options: z.array(PollOption).openapi({ + options: z.array(PollOption).meta({ description: "Possible answers for the poll.", externalDocs: { url: "https://docs.joinmastodon.org/entities/Poll/#options", }, }), - emojis: z.array(CustomEmoji).openapi({ + emojis: z.array(CustomEmoji).meta({ description: "Custom emoji to be used for rendering poll options.", externalDocs: { url: "https://docs.joinmastodon.org/entities/Poll/#emojis", @@ -109,7 +108,7 @@ export const Poll = z voted: z .boolean() .optional() - .openapi({ + .meta({ description: "When called with a user token, has the authorized user voted?", example: true, @@ -120,7 +119,7 @@ export const Poll = z own_votes: z .array(z.number().int()) .optional() - .openapi({ + .meta({ description: "When called with a user token, which options has the authorized user chosen? Contains an array of index values for options.", example: [0], @@ -129,10 +128,10 @@ export const Poll = z }, }), }) - .openapi({ + .meta({ description: "Represents a poll attached to a status.", externalDocs: { url: "https://docs.joinmastodon.org/entities/Poll", }, - ref: "Poll", + id: "Poll", }); diff --git a/packages/client/schemas/preferences.ts b/packages/client/schemas/preferences.ts index 34fb609f..a5766ee4 100644 --- a/packages/client/schemas/preferences.ts +++ b/packages/client/schemas/preferences.ts @@ -1,23 +1,23 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { Source } from "./account.ts"; export const Preferences = z .object({ - "posting:default:visibility": Source.shape.privacy.openapi({ + "posting:default:visibility": Source.shape.privacy.meta({ description: "Default visibility for new posts.", example: "public", externalDocs: { url: "https://docs.joinmastodon.org/entities/Preferences/#posting-default-visibility", }, }), - "posting:default:sensitive": Source.shape.sensitive.openapi({ + "posting:default:sensitive": Source.shape.sensitive.meta({ description: "Default sensitivity flag for new posts.", example: false, externalDocs: { url: "https://docs.joinmastodon.org/entities/Preferences/#posting-default-sensitive", }, }), - "posting:default:language": Source.shape.language.nullable().openapi({ + "posting:default:language": Source.shape.language.nullable().meta({ description: "Default language for new posts.", example: null, externalDocs: { @@ -26,7 +26,7 @@ export const Preferences = z }), "reading:expand:media": z .enum(["default", "show_all", "hide_all"]) - .openapi({ + .meta({ description: "Whether media attachments should be automatically displayed or blurred/hidden.", example: "default", @@ -34,7 +34,7 @@ export const Preferences = z url: "https://docs.joinmastodon.org/entities/Preferences/#reading-expand-media", }, }), - "reading:expand:spoilers": z.boolean().openapi({ + "reading:expand:spoilers": z.boolean().meta({ description: "Whether CWs should be expanded by default.", example: false, externalDocs: { @@ -42,10 +42,10 @@ export const Preferences = z }, }), }) - .openapi({ + .meta({ description: "Represents a user's preferences.", externalDocs: { url: "https://docs.joinmastodon.org/entities/Preferences", }, - ref: "Preferences", + id: "Preferences", }); diff --git a/packages/client/schemas/privacy-policy.ts b/packages/client/schemas/privacy-policy.ts index c057249f..4b3fb9da 100644 --- a/packages/client/schemas/privacy-policy.ts +++ b/packages/client/schemas/privacy-policy.ts @@ -1,19 +1,16 @@ -import { z } from "zod"; +import { z } from "zod/v4"; export const PrivacyPolicy = z .object({ - updated_at: z - .string() - .datetime() - .openapi({ - description: - "A timestamp of when the privacy policy was last updated.", - example: "2025-01-12T13:11:00Z", - externalDocs: { - url: "https://docs.joinmastodon.org/entities/PrivacyPolicy/#updated_at", - }, - }), - content: z.string().openapi({ + updated_at: z.iso.datetime().meta({ + description: + "A timestamp of when the privacy policy was last updated.", + example: "2025-01-12T13:11:00Z", + externalDocs: { + url: "https://docs.joinmastodon.org/entities/PrivacyPolicy/#updated_at", + }, + }), + content: z.string().meta({ description: "The rendered HTML content of the privacy policy.", example: "

Privacy Policy

None, good luck.

", externalDocs: { @@ -21,10 +18,10 @@ export const PrivacyPolicy = z }, }), }) - .openapi({ + .meta({ description: "Represents the privacy policy of the instance.", externalDocs: { url: "https://docs.joinmastodon.org/entities/PrivacyPolicy", }, - ref: "PrivacyPolicy", + id: "PrivacyPolicy", }); diff --git a/packages/client/schemas/pushsubscription.ts b/packages/client/schemas/pushsubscription.ts index 5236ea7c..3ec03837 100644 --- a/packages/client/schemas/pushsubscription.ts +++ b/packages/client/schemas/pushsubscription.ts @@ -1,64 +1,64 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { Id } from "./common.ts"; export const WebPushSubscription = z .object({ - id: Id.openapi({ + id: Id.meta({ example: "24eb1891-accc-43b4-b213-478e37d525b4", description: "The ID of the Web Push subscription in the database.", }), - endpoint: z.string().url().openapi({ + endpoint: z.url().meta({ example: "https://yourdomain.example/listener", description: "Where push alerts will be sent to.", }), alerts: z .object({ - mention: z.boolean().optional().openapi({ + mention: z.boolean().optional().meta({ example: true, description: "Receive mention notifications?", }), - favourite: z.boolean().optional().openapi({ + favourite: z.boolean().optional().meta({ example: true, description: "Receive favourite notifications?", }), - reblog: z.boolean().optional().openapi({ + reblog: z.boolean().optional().meta({ example: true, description: "Receive reblog notifications?", }), - follow: z.boolean().optional().openapi({ + follow: z.boolean().optional().meta({ example: true, description: "Receive follow notifications?", }), - poll: z.boolean().optional().openapi({ + poll: z.boolean().optional().meta({ example: false, description: "Receive poll notifications?", }), - follow_request: z.boolean().optional().openapi({ + follow_request: z.boolean().optional().meta({ example: false, description: "Receive follow request notifications?", }), - status: z.boolean().optional().openapi({ + status: z.boolean().optional().meta({ example: false, description: "Receive new subscribed account notifications?", }), - update: z.boolean().optional().openapi({ + update: z.boolean().optional().meta({ example: false, description: "Receive status edited notifications?", }), - "admin.sign_up": z.boolean().optional().openapi({ + "admin.sign_up": z.boolean().optional().meta({ example: false, description: "Receive new user signup notifications? Must have a role with the appropriate permissions.", }), - "admin.report": z.boolean().optional().openapi({ + "admin.report": z.boolean().optional().meta({ example: false, description: "Receive new report notifications? Must have a role with the appropriate permissions.", }), }) .default({}) - .openapi({ + .meta({ example: { mention: true, favourite: true, @@ -74,62 +74,57 @@ export const WebPushSubscription = z description: "Which alerts should be delivered to the endpoint.", }), - server_key: z.string().openapi({ + server_key: z.string().meta({ example: "BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=", description: "The streaming server’s VAPID key.", }), }) - .openapi({ ref: "WebPushSubscription" }); + .meta({ id: "WebPushSubscription" }); -export const WebPushSubscriptionInput = z - .object({ - subscription: z.object({ - endpoint: z.string().url().openapi({ - example: "https://yourdomain.example/listener", - description: "Where push alerts will be sent to.", - }), - keys: z - .object({ - p256dh: z.string().base64url().openapi({ - description: - "User agent public key. Base64url encoded string of a public key from a ECDH keypair using the prime256v1 curve.", - example: - "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEoKCJeHCy69ywHcb3dAR/T8Sud5ljSFHJkuiR6it1ycqAjGTe5F1oZ0ef5QiMX/zdQ+d4jSKiO7RztIz+o/eGuQ==", - }), - auth: z.string().base64url().openapi({ - description: - "Auth secret. Base64url encoded string of 16 bytes of random data.", - example: "u67u09PXZW4ncK9l9mAXkA==", - }), - }) - .strict(), +export const WebPushSubscriptionInput = z.strictObject({ + subscription: z.object({ + endpoint: z.url().meta({ + example: "https://yourdomain.example/listener", + description: "Where push alerts will be sent to.", }), - policy: z - .enum(["all", "followed", "follower", "none"]) - .default("all") - .openapi({ + keys: z.strictObject({ + p256dh: z.base64url().meta({ description: - "Specify whether to receive push notifications from all, followed, follower, or none users.", + "User agent public key. Base64url encoded string of a public key from a ECDH keypair using the prime256v1 curve.", + example: + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEoKCJeHCy69ywHcb3dAR/T8Sud5ljSFHJkuiR6it1ycqAjGTe5F1oZ0ef5QiMX/zdQ+d4jSKiO7RztIz+o/eGuQ==", }), - data: z - .object({ - alerts: WebPushSubscription.shape.alerts, - }) - .strict() - .default({ - alerts: { - mention: false, - favourite: false, - reblog: false, - follow: false, - poll: false, - follow_request: false, - status: false, - update: false, - "admin.sign_up": false, - "admin.report": false, - }, + auth: z.base64url().meta({ + description: + "Auth secret. Base64url encoded string of 16 bytes of random data.", + example: "u67u09PXZW4ncK9l9mAXkA==", }), - }) - .strict(); + }), + }), + policy: z + .enum(["all", "followed", "follower", "none"]) + .default("all") + .meta({ + description: + "Specify whether to receive push notifications from all, followed, follower, or none users.", + }), + data: z + .strictObject({ + alerts: WebPushSubscription.shape.alerts, + }) + .default({ + alerts: { + mention: false, + favourite: false, + reblog: false, + follow: false, + poll: false, + follow_request: false, + status: false, + update: false, + "admin.sign_up": false, + "admin.report": false, + }, + }), +}); diff --git a/packages/client/schemas/relationship.ts b/packages/client/schemas/relationship.ts index f24c7cbe..f70707e2 100644 --- a/packages/client/schemas/relationship.ts +++ b/packages/client/schemas/relationship.ts @@ -1,75 +1,75 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { Id, iso631 } from "./common.ts"; export const Relationship = z .object({ - id: Id.openapi({ + id: Id.meta({ description: "The account ID.", example: "51f34c31-c8c6-4dc2-9df1-3704fcdde9b6", }), - following: z.boolean().openapi({ + following: z.boolean().meta({ description: "Are you following this user?", example: true, }), - showing_reblogs: z.boolean().openapi({ + showing_reblogs: z.boolean().meta({ description: "Are you receiving this user’s boosts in your home timeline?", example: true, }), - notifying: z.boolean().openapi({ + notifying: z.boolean().meta({ description: "Have you enabled notifications for this user?", example: false, }), - languages: z.array(iso631).openapi({ + languages: z.array(iso631).meta({ description: "Which languages are you following from this user?", example: ["en"], }), - followed_by: z.boolean().openapi({ + followed_by: z.boolean().meta({ description: "Are you followed by this user?", example: true, }), - blocking: z.boolean().openapi({ + blocking: z.boolean().meta({ description: "Are you blocking this user?", example: false, }), - blocked_by: z.boolean().openapi({ + blocked_by: z.boolean().meta({ description: "Is this user blocking you?", example: false, }), - muting: z.boolean().openapi({ + muting: z.boolean().meta({ description: "Are you muting this user?", example: false, }), - muting_notifications: z.boolean().openapi({ + muting_notifications: z.boolean().meta({ description: "Are you muting notifications from this user?", example: false, }), - requested: z.boolean().openapi({ + requested: z.boolean().meta({ description: "Do you have a pending follow request for this user?", example: false, }), - requested_by: z.boolean().openapi({ + requested_by: z.boolean().meta({ description: "Has this user requested to follow you?", example: false, }), - domain_blocking: z.boolean().openapi({ + domain_blocking: z.boolean().meta({ description: "Are you blocking this user’s domain?", example: false, }), - endorsed: z.boolean().openapi({ + endorsed: z.boolean().meta({ description: "Are you featuring this user on your profile?", example: false, }), - note: z.string().min(0).max(5000).trim().openapi({ + note: z.string().min(0).max(5000).trim().meta({ description: "This user’s profile bio", example: "they also like Kerbal Space Program", }), }) - .openapi({ + .meta({ description: "Represents the relationship between accounts, such as following / blocking / muting / etc.", externalDocs: { url: "https://docs.joinmastodon.org/entities/Relationship", }, - ref: "Relationship", + id: "Relationship", }); diff --git a/packages/client/schemas/report.ts b/packages/client/schemas/report.ts index adef106f..7c48a1d5 100644 --- a/packages/client/schemas/report.ts +++ b/packages/client/schemas/report.ts @@ -1,60 +1,60 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { Account } from "./account.ts"; import { Id } from "./common.ts"; export const Report = z .object({ - id: Id.openapi({ + id: Id.meta({ description: "The ID of the report in the database.", example: "9b0cd757-324b-4ea6-beab-f6226e138886", }), - action_taken: z.boolean().openapi({ + action_taken: z.boolean().meta({ description: "Whether an action was taken yet.", example: false, }), - action_taken_at: z.string().datetime().nullable().openapi({ + action_taken_at: z.iso.datetime().nullable().meta({ description: "When an action was taken against the report.", example: null, }), - category: z.enum(["spam", "violation", "other"]).openapi({ + category: z.enum(["spam", "violation", "other"]).meta({ description: "The generic reason for the report. 'spam' = Unwanted or repetitive content, 'violation' = A specific rule was violated, 'other' = Some other reason.", example: "spam", }), - comment: z.string().openapi({ + comment: z.string().meta({ description: "The reason for the report.", example: "Spam account", }), - forwarded: z.boolean().openapi({ + forwarded: z.boolean().meta({ description: "Whether the report was forwarded to a remote domain.", example: false, }), - created_at: z.string().datetime().openapi({ + created_at: z.iso.datetime().meta({ description: "When the report was created.", example: "2024-12-31T23:59:59.999Z", }), status_ids: z .array(Id) .nullable() - .openapi({ + .meta({ description: "IDs of statuses that have been attached to this report for additional context.", example: ["1abf027c-af03-46ff-8d17-9ee799a17ca7"], }), - rule_ids: z.array(z.string()).nullable().openapi({ + rule_ids: z.array(z.string()).nullable().meta({ description: "IDs of the rules that have been cited as a violation by this report.", example: null, }), - target_account: Account.openapi({ + target_account: Account.meta({ description: "The account that was reported.", }), }) - .openapi({ + .meta({ description: "Reports filed against users and/or statuses, to be taken action on by moderators.", externalDocs: { url: "https://docs.joinmastodon.org/entities/Report", }, - ref: "Report", + id: "Report", }); diff --git a/packages/client/schemas/rule.ts b/packages/client/schemas/rule.ts index 692a30a7..3131dc89 100644 --- a/packages/client/schemas/rule.ts +++ b/packages/client/schemas/rule.ts @@ -1,24 +1,24 @@ -import { z } from "zod"; +import { z } from "zod/v4"; export const Rule = z .object({ - id: z.string().openapi({ + id: z.string().meta({ description: "The identifier for the rule.", example: "1", }), - text: z.string().openapi({ + text: z.string().meta({ description: "The rule to be followed.", example: "Do not spam pictures of skibidi toilet.", }), - hint: z.string().optional().openapi({ + hint: z.string().optional().meta({ description: "Longer-form description of the rule.", example: "Please, we beg you.", }), }) - .openapi({ + .meta({ description: "Represents a rule that server users should follow.", externalDocs: { url: "https://docs.joinmastodon.org/entities/Rule", }, - ref: "Rule", + id: "Rule", }); diff --git a/packages/client/schemas/search.ts b/packages/client/schemas/search.ts index 46bd9664..cb9ffe7d 100644 --- a/packages/client/schemas/search.ts +++ b/packages/client/schemas/search.ts @@ -1,24 +1,24 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { Account } from "./account.ts"; import { Status } from "./status.ts"; import { Tag } from "./tag.ts"; export const Search = z .object({ - accounts: z.array(Account).openapi({ + accounts: z.array(Account).meta({ description: "Accounts which match the given query", }), - statuses: z.array(Status).openapi({ + statuses: z.array(Status).meta({ description: "Statuses which match the given query", }), - hashtags: z.array(Tag).openapi({ + hashtags: z.array(Tag).meta({ description: "Hashtags which match the given query", }), }) - .openapi({ + .meta({ description: "Represents the results of a search.", externalDocs: { url: "https://docs.joinmastodon.org/entities/Search", }, - ref: "Search", + id: "Search", }); diff --git a/packages/client/schemas/status.ts b/packages/client/schemas/status.ts index 60623f1c..50271f10 100644 --- a/packages/client/schemas/status.ts +++ b/packages/client/schemas/status.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { Account } from "./account.ts"; import { Attachment } from "./attachment.ts"; import { PreviewCard } from "./card.ts"; @@ -11,28 +11,28 @@ import { NoteReaction } from "./versia.ts"; export const Mention = z .object({ - id: Account.shape.id.openapi({ + id: Account.shape.id.meta({ description: "The account ID of the mentioned user.", example: "b9dcb548-bd4d-42af-8b48-3693e6d298e6", externalDocs: { url: "https://docs.joinmastodon.org/entities/Status/#Mention-id", }, }), - username: Account.shape.username.openapi({ + username: Account.shape.username.meta({ description: "The username of the mentioned user.", example: "lexi", externalDocs: { url: "https://docs.joinmastodon.org/entities/Status/#Mention-username", }, }), - url: Account.shape.url.openapi({ + url: Account.shape.url.meta({ description: "The location of the mentioned user’s profile.", example: "https://beta.versia.social/@lexi", externalDocs: { url: "https://docs.joinmastodon.org/entities/Status/#Mention-url", }, }), - acct: Account.shape.acct.openapi({ + acct: Account.shape.acct.meta({ description: "The webfinger acct: URI of the mentioned user. Equivalent to username for local users, or username@domain for remote users.", example: "lexi@beta.versia.social", @@ -41,62 +41,59 @@ export const Mention = z }, }), }) - .openapi({ + .meta({ externalDocs: { url: "https://docs.joinmastodon.org/entities/Status/#Mention", }, - ref: "Mention", + id: "Mention", }); export const StatusSource = z .object({ - id: Id.openapi({ + id: Id.meta({ description: "ID of the status in the database.", example: "c7db92a4-e472-4e94-a115-7411ee934ba1", }), - text: z.string().trim().openapi({ + text: z.string().trim().meta({ description: "The plain text used to compose the status.", example: "this is a status that will be edited", }), - spoiler_text: z.string().trim().min(1).max(1024).openapi({ + // min(0) because some masto-fe clients send empty spoiler_text + // when they don't want to set it. + spoiler_text: z.string().trim().min(0).max(1024).meta({ description: "The plain text used to compose the status’s subject or content warning.", example: "", }), }) - .openapi({ + .meta({ externalDocs: { url: "https://docs.joinmastodon.org/entities/StatusSource", }, - ref: "StatusSource", + id: "StatusSource", }); -// Because Status has some recursive references, we need to define it like this -const BaseStatus = z +export const Status = z .object({ - id: Id.openapi({ + id: Id.meta({ description: "ID of the status in the database.", example: "2de861d3-a3dd-42ee-ba38-2c7d3f4af588", externalDocs: { url: "https://docs.joinmastodon.org/entities/Status/#id", }, }), - uri: z - .string() - .url() - .openapi({ - description: "URI of the status used for federation.", - example: - "https://beta.versia.social/@lexi/2de861d3-a3dd-42ee-ba38-2c7d3f4af588", - externalDocs: { - url: "https://docs.joinmastodon.org/entities/Status/#uri", - }, - }), + uri: z.url().meta({ + description: "URI of the status used for federation.", + example: + "https://beta.versia.social/@lexi/2de861d3-a3dd-42ee-ba38-2c7d3f4af588", + externalDocs: { + url: "https://docs.joinmastodon.org/entities/Status/#uri", + }, + }), url: z - .string() .url() .nullable() - .openapi({ + .meta({ description: "A link to the status’s HTML representation.", example: "https://beta.versia.social/@lexi/2de861d3-a3dd-42ee-ba38-2c7d3f4af588", @@ -104,20 +101,20 @@ const BaseStatus = z url: "https://docs.joinmastodon.org/entities/Status/#url", }, }), - account: Account.openapi({ + account: Account.meta({ description: "The account that authored this status.", externalDocs: { url: "https://docs.joinmastodon.org/entities/Status/#account", }, }), - in_reply_to_id: Id.nullable().openapi({ + in_reply_to_id: Id.nullable().meta({ description: "ID of the status being replied to.", example: "c41c9fe9-919a-4d35-a921-d3e79a5c95f8", externalDocs: { url: "https://docs.joinmastodon.org/entities/Status/#in_reply_to_id", }, }), - in_reply_to_account_id: Account.shape.id.nullable().openapi({ + in_reply_to_account_id: Account.shape.id.nullable().meta({ description: "ID of the account that authored the status being replied to.", example: "7b9b3ec6-1013-4cc6-8902-94ad00cf2ccc", @@ -126,35 +123,31 @@ const BaseStatus = z }, }), - content: z.string().openapi({ + content: z.string().meta({ description: "HTML-encoded status content.", example: "

hello world

", externalDocs: { url: "https://docs.joinmastodon.org/entities/Status/#content", }, }), - created_at: z - .string() - .datetime() - .openapi({ - description: "The date when this status was created.", - example: "2025-01-07T14:11:00.000Z", - externalDocs: { - url: "https://docs.joinmastodon.org/entities/Status/#created_at", - }, - }), - edited_at: z - .string() + created_at: z.iso.datetime().meta({ + description: "The date when this status was created.", + example: "2025-01-07T14:11:00.000Z", + externalDocs: { + url: "https://docs.joinmastodon.org/entities/Status/#created_at", + }, + }), + edited_at: z.iso .datetime() .nullable() - .openapi({ + .meta({ description: "Timestamp of when the status was last edited.", example: "2025-01-07T14:11:00.000Z", externalDocs: { url: "https://docs.joinmastodon.org/entities/Status/#edited_at", }, }), - emojis: z.array(CustomEmoji).openapi({ + emojis: z.array(CustomEmoji).meta({ description: "Custom emoji to be used when rendering status content.", externalDocs: { @@ -165,7 +158,7 @@ const BaseStatus = z .number() .int() .nonnegative() - .openapi({ + .meta({ description: "How many replies this status has received.", example: 1, externalDocs: { @@ -176,7 +169,7 @@ const BaseStatus = z .number() .int() .nonnegative() - .openapi({ + .meta({ description: "How many boosts this status has received.", example: 6, externalDocs: { @@ -187,14 +180,14 @@ const BaseStatus = z .number() .int() .nonnegative() - .openapi({ + .meta({ description: "How many favourites this status has received.", example: 11, externalDocs: { url: "https://docs.joinmastodon.org/entities/Status/#favourites_count", }, }), - reblogged: zBoolean.optional().openapi({ + reblogged: zBoolean.optional().meta({ description: "If the current token has an authorized user: Have you boosted this status?", example: false, @@ -202,7 +195,7 @@ const BaseStatus = z url: "https://docs.joinmastodon.org/entities/Status/#reblogged", }, }), - favourited: zBoolean.optional().openapi({ + favourited: zBoolean.optional().meta({ description: "If the current token has an authorized user: Have you favourited this status?", example: true, @@ -210,7 +203,7 @@ const BaseStatus = z url: "https://docs.joinmastodon.org/entities/Status/#favourited", }, }), - muted: zBoolean.optional().openapi({ + muted: zBoolean.optional().meta({ description: "If the current token has an authorized user: Have you muted notifications for this status’s conversation?", example: false, @@ -218,14 +211,14 @@ const BaseStatus = z url: "https://docs.joinmastodon.org/entities/Status/#muted", }, }), - sensitive: zBoolean.openapi({ + sensitive: zBoolean.meta({ description: "Is this status marked as sensitive content?", example: false, externalDocs: { url: "https://docs.joinmastodon.org/entities/Status/#sensitive", }, }), - spoiler_text: z.string().openapi({ + spoiler_text: z.string().meta({ description: "Subject or summary line, below which status content is collapsed until expanded.", example: "lewd text", @@ -233,41 +226,39 @@ const BaseStatus = z url: "https://docs.joinmastodon.org/entities/Status/#spoiler_text", }, }), - visibility: z - .enum(["public", "unlisted", "private", "direct"]) - .openapi({ - description: "Visibility of this status.", - example: "public", - externalDocs: { - url: "https://docs.joinmastodon.org/entities/Status/#visibility", - }, - }), - media_attachments: z.array(Attachment).openapi({ + visibility: z.enum(["public", "unlisted", "private", "direct"]).meta({ + description: "Visibility of this status.", + example: "public", + externalDocs: { + url: "https://docs.joinmastodon.org/entities/Status/#visibility", + }, + }), + media_attachments: z.array(Attachment).meta({ description: "Media that is attached to this status.", externalDocs: { url: "https://docs.joinmastodon.org/entities/Status/#media_attachments", }, }), - mentions: z.array(Mention).openapi({ + mentions: z.array(Mention).meta({ description: "Mentions of users within the status content.", externalDocs: { url: "https://docs.joinmastodon.org/entities/Status/#mentions", }, }), - tags: z.array(Tag).openapi({ + tags: z.array(Tag).meta({ description: "Hashtags used within the status content.", externalDocs: { url: "https://docs.joinmastodon.org/entities/Status/#tags", }, }), - card: PreviewCard.nullable().openapi({ + card: PreviewCard.nullable().meta({ description: "Preview card for links included within status content.", externalDocs: { url: "https://docs.joinmastodon.org/entities/Status/#card", }, }), - poll: Poll.nullable().openapi({ + poll: Poll.nullable().meta({ description: "The poll attached to the status.", externalDocs: { url: "https://docs.joinmastodon.org/entities/Status/#poll", @@ -275,7 +266,7 @@ const BaseStatus = z }), application: z .object({ - name: z.string().openapi({ + name: z.string().meta({ description: "The name of the application that posted this status.", externalDocs: { @@ -283,10 +274,9 @@ const BaseStatus = z }, }), website: z - .string() .url() .nullable() - .openapi({ + .meta({ description: "The website associated with the application that posted this status.", externalDocs: { @@ -295,30 +285,41 @@ const BaseStatus = z }), }) .optional() - .openapi({ + .meta({ description: "The application used to post this status.", externalDocs: { url: "https://docs.joinmastodon.org/entities/Status/#application", }, }), - language: iso631.nullable().openapi({ + language: iso631.nullable().meta({ description: "Primary language of this status.", example: "en", externalDocs: { url: "https://docs.joinmastodon.org/entities/Status/#language", }, }), + get reblog() { + return Status.nullable().meta({ + description: "The status being reblogged.", + externalDocs: { + url: "https://docs.joinmastodon.org/entities/Status/#reblog", + }, + }); + }, + get quote() { + return Status.nullable(); + }, text: z .string() .nullable() - .openapi({ + .meta({ description: "Plain-text source of a status. Returned instead of content when status is deleted, so the user may redraft from the source text without the client having to reverse-engineer the original text from the HTML content.", externalDocs: { url: "https://docs.joinmastodon.org/entities/Status/#text", }, }), - pinned: zBoolean.optional().openapi({ + pinned: zBoolean.optional().meta({ description: "If the current token has an authorized user: Have you pinned this status? Only appears if the status is pinnable.", example: true, @@ -326,8 +327,8 @@ const BaseStatus = z url: "https://docs.joinmastodon.org/entities/Status/#pinned", }, }), - reactions: z.array(NoteReaction).openapi({}), - bookmarked: zBoolean.optional().openapi({ + reactions: z.array(NoteReaction).meta({}), + bookmarked: zBoolean.optional().meta({ description: "If the current token has an authorized user: Have you bookmarked this status?", example: false, @@ -338,7 +339,7 @@ const BaseStatus = z filtered: z .array(FilterResult) .optional() - .openapi({ + .meta({ description: "If the current token has an authorized user: The filter and keywords that matched this status.", externalDocs: { @@ -346,35 +347,23 @@ const BaseStatus = z }, }), }) - .openapi({ - ref: "BaseStatus", + .meta({ + id: "Status", }); -export const Status = BaseStatus.extend({ - reblog: BaseStatus.nullable().openapi({ - description: "The status being reblogged.", - externalDocs: { - url: "https://docs.joinmastodon.org/entities/Status/#reblog", - }, - }), - quote: BaseStatus.nullable(), -}).openapi({ - ref: "Status", -}); - export const ScheduledStatus = z .object({ - id: Id.openapi({ + id: Id.meta({ description: "ID of the scheduled status in the database.", example: "2de861d3-a3dd-42ee-ba38-2c7d3f4af588", }), - scheduled_at: z.string().datetime().openapi({ + scheduled_at: z.iso.datetime().meta({ description: "When the status will be scheduled.", example: "2025-01-07T14:11:00.000Z", }), media_attachments: Status.shape.media_attachments, params: z.object({ - text: z.string().openapi({ + text: z.string().meta({ description: "Text to be used as status content.", example: "Hello, world!", }), @@ -382,7 +371,7 @@ export const ScheduledStatus = z media_ids: z .array(Id) .nullable() - .openapi({ + .meta({ description: "IDs of the MediaAttachments that will be attached to the status.", example: ["1234567890", "1234567891"], @@ -392,22 +381,22 @@ export const ScheduledStatus = z visibility: Status.shape.visibility, in_reply_to_id: Status.shape.in_reply_to_id, /** Versia Server API Extension */ - quote_id: z.string().openapi({ + quote_id: z.string().meta({ description: "ID of the status being quoted.", example: "c5d62a13-f340-4e7d-8942-7fd14be688dc", }), language: Status.shape.language, - scheduled_at: z.null().openapi({ + scheduled_at: z.null().meta({ description: "When the status will be scheduled. This will be null because the status is only scheduled once.", example: null, }), - idempotency: z.string().nullable().openapi({ + idempotency: z.string().nullable().meta({ description: "Idempotency key to prevent duplicate statuses.", example: "1234567890", }), }), }) - .openapi({ - ref: "ScheduledStatus", + .meta({ + id: "ScheduledStatus", }); diff --git a/packages/client/schemas/tag.ts b/packages/client/schemas/tag.ts index b6cd1eba..cc91300f 100644 --- a/packages/client/schemas/tag.ts +++ b/packages/client/schemas/tag.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; export const Tag = z .object({ @@ -6,27 +6,24 @@ export const Tag = z .string() .min(1) .max(128) - .openapi({ + .meta({ description: "The value of the hashtag after the # sign.", example: "versia", externalDocs: { url: "https://docs.joinmastodon.org/entities/Status/#Tag-name", }, }), - url: z - .string() - .url() - .openapi({ - description: "A link to the hashtag on the instance.", - example: "https://beta.versia.social/tags/versia", - externalDocs: { - url: "https://docs.joinmastodon.org/entities/Status/#Tag-url", - }, - }), + url: z.url().meta({ + description: "A link to the hashtag on the instance.", + example: "https://beta.versia.social/tags/versia", + externalDocs: { + url: "https://docs.joinmastodon.org/entities/Status/#Tag-url", + }, + }), }) - .openapi({ + .meta({ externalDocs: { url: "https://docs.joinmastodon.org/entities/Status/#Tag", }, - ref: "Tag", + id: "Tag", }); diff --git a/packages/client/schemas/token.ts b/packages/client/schemas/token.ts index cc83657d..bbd80c79 100644 --- a/packages/client/schemas/token.ts +++ b/packages/client/schemas/token.ts @@ -1,30 +1,30 @@ -import { z } from "zod"; +import { z } from "zod/v4"; export const Token = z .object({ - access_token: z.string().openapi({ + access_token: z.string().meta({ description: "An OAuth token to be used for authorization.", example: "ZA-Yj3aBD8U8Cm7lKUp-lm9O9BmDgdhHzDeqsY8tlL0", }), - token_type: z.string().openapi({ + token_type: z.string().meta({ description: "The OAuth token type. Versia uses Bearer tokens.", example: "Bearer", }), - scope: z.string().openapi({ + scope: z.string().meta({ description: "The OAuth scopes granted by this token, space-separated.", example: "read write follow push", }), - created_at: z.number().nonnegative().openapi({ + created_at: z.number().nonnegative().meta({ description: "When the token was generated. UNIX timestamp.", example: 1573979017, }), }) - .openapi({ + .meta({ description: "Represents an OAuth token used for authenticating with the API and performing actions.", externalDocs: { url: "https://docs.joinmastodon.org/entities/Token", }, - ref: "Token", + id: "Token", }); diff --git a/packages/client/schemas/tos.ts b/packages/client/schemas/tos.ts index bf47ed98..36a1e96b 100644 --- a/packages/client/schemas/tos.ts +++ b/packages/client/schemas/tos.ts @@ -1,17 +1,17 @@ -import { z } from "zod"; +import { z } from "zod/v4"; export const TermsOfService = z .object({ - updated_at: z.string().datetime().openapi({ + updated_at: z.iso.datetime().meta({ description: "A timestamp of when the ToS was last updated.", example: "2025-01-12T13:11:00Z", }), - content: z.string().openapi({ + content: z.string().meta({ description: "The rendered HTML content of the ToS.", example: "

ToS

None, have fun.

", }), }) - .openapi({ + .meta({ description: "Represents the ToS of the instance.", - ref: "TermsOfService", + id: "TermsOfService", }); diff --git a/packages/client/schemas/versia.ts b/packages/client/schemas/versia.ts index 4606bfe8..120d8c32 100644 --- a/packages/client/schemas/versia.ts +++ b/packages/client/schemas/versia.ts @@ -1,86 +1,78 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { Id } from "./common.ts"; import { RolePermission } from "./permissions.ts"; /* Versia Server API extension */ export const Role = z .object({ - id: Id.openapi({ + id: Id.meta({ description: "The role ID in the database.", example: "b4a7e0f0-8f6a-479b-910b-9265c070d5bd", }), - name: z.string().min(1).max(128).trim().openapi({ + name: z.string().min(1).max(128).trim().meta({ description: "The name of the role.", example: "Moderator", }), - permissions: z - .array(z.nativeEnum(RolePermission)) - .transform( - // Deduplicate permissions - (permissions) => Array.from(new Set(permissions)), - ) - .default([]) - .openapi({ - description: "The permissions granted to the role.", - example: [ - RolePermission.ManageEmojis, - RolePermission.ManageAccounts, - ], - type: "array", - }), - priority: z.number().int().default(0).openapi({ + permissions: z.array(z.enum(RolePermission)).meta({ + description: "The permissions granted to the role.", + example: [ + RolePermission.ManageEmojis, + RolePermission.ManageAccounts, + ], + }), + priority: z.number().int().meta({ description: "Role priority. Higher priority roles allow overriding lower priority roles.", example: 100, }), - description: z.string().min(0).max(1024).trim().optional().openapi({ + description: z.string().min(0).max(1024).trim().optional().meta({ description: "Short role description.", example: "Allows managing emojis and accounts.", }), - visible: z.boolean().default(true).openapi({ + visible: z.boolean().default(true).meta({ description: "Whether the role should be shown in the UI.", }), - icon: z.string().url().optional().openapi({ + icon: z.url().optional().meta({ description: "URL to the role icon.", example: "https://example.com/role-icon.png", }), }) - .openapi({ + .meta({ description: "Information about a role in the system, as well as its permissions.", - ref: "Role", + id: "Role", }); /* Versia Server API extension */ export const NoteReaction = z .object({ - name: z.string().min(1).trim().openapi({ + name: z.string().min(1).trim().meta({ description: "Custom Emoji shortcode or Unicode emoji.", example: "blobfox_coffee", }), - count: z.number().int().nonnegative().openapi({ + count: z.number().int().nonnegative().meta({ description: "Number of users who reacted with this emoji.", example: 5, }), - remote: z.boolean().openapi({ + remote: z.boolean().meta({ description: "Whether this reaction is from a remote instance (federated).", example: false, }), - me: z.boolean().optional().openapi({ + me: z.boolean().optional().meta({ description: "Whether the current authenticated user reacted with this emoji.", example: true, }), }) - .openapi({ + .meta({ description: "Information about a reaction to a note.", - ref: "NoteReaction", + id: "NoteReaction", }); /* Versia Server API extension */ export const NoteReactionWithAccounts = NoteReaction.extend({ - account_ids: z.array(Id).openapi({ + account_ids: z.array(Id).meta({ description: "Array of user IDs who reacted with this emoji.", example: [ "1d0185bc-d949-4ff5-8a15-1d691b256489", @@ -88,14 +80,14 @@ export const NoteReactionWithAccounts = NoteReaction.extend({ "1f0c4eb9-a742-4c82-96c9-697a39831cd1", ], }), -}).openapi({ +}).meta({ description: "Information about a reaction to a note with account IDs.", - ref: "NoteReactionWithAccounts", + id: "NoteReactionWithAccounts", }); /* Versia Server API extension */ export const SSOConfig = z.object({ - forced: z.boolean().openapi({ + forced: z.boolean().meta({ description: "If this is enabled, normal identifier/password login is disabled and login must be done through SSO.", example: false, @@ -103,21 +95,21 @@ export const SSOConfig = z.object({ providers: z .array( z.object({ - id: z.string().min(1).openapi({ + id: z.string().min(1).meta({ description: "The ID of the provider.", example: "google", }), - name: z.string().min(1).openapi({ + name: z.string().min(1).meta({ description: "Human-readable provider name.", example: "Google", }), - icon: z.string().url().optional().openapi({ + icon: z.url().optional().meta({ description: "URL to the provider icon.", example: "https://cdn.versia.social/google-icon.png", }), }), ) - .openapi({ + .meta({ description: "An array of external OpenID Connect providers that users can link their accounts to.", }), @@ -126,32 +118,32 @@ export const SSOConfig = z.object({ /* Versia Server API extension */ export const Challenge = z .object({ - id: Id.openapi({}).openapi({ + id: Id.meta({}).meta({ description: "Challenge ID in the database.", example: "b4a7e0f0-8f6a-479b-910b-9265c070d5bd", }), - algorithm: z.enum(["SHA-1", "SHA-256", "SHA-512"]).openapi({ + algorithm: z.enum(["SHA-1", "SHA-256", "SHA-512"]).meta({ description: "Algorithm used to generate the challenge.", example: "SHA-1", }), - challenge: z.string().openapi({ + challenge: z.string().meta({ description: "Challenge to solve.", example: "1234567890", }), - maxnumber: z.number().int().nonnegative().optional().openapi({ + maxnumber: z.number().int().nonnegative().optional().meta({ description: "Maximum number to solve the challenge.", example: 100, }), - salt: z.string().openapi({ + salt: z.string().meta({ description: "Salt used to generate the challenge.", example: "1234567890", }), - signature: z.string().openapi({ + signature: z.string().meta({ description: "Signature of the challenge.", example: "1234567890", }), }) - .openapi({ + .meta({ description: "A cryptographic challenge to solve. Used for Captchas.", - ref: "Challenge", + id: "Challenge", }); diff --git a/packages/client/versia/base.ts b/packages/client/versia/base.ts index 91df1081..82b7e442 100644 --- a/packages/client/versia/base.ts +++ b/packages/client/versia/base.ts @@ -87,8 +87,8 @@ export class ResponseError< export class BaseClient { public constructor( protected baseUrl: URL, - private accessToken?: string, - private options: { + private readonly accessToken?: string, + private readonly options: { globalCatch?: (error: ResponseError) => void; throwOnError?: boolean; } = {}, @@ -103,6 +103,7 @@ export class BaseClient { } /** Overridable by testing */ + // biome-ignore lint/nursery/useReadonlyClassProperties: Overridable by testing private fetch = (...args: Parameters) => fetch(...args); private async request( diff --git a/packages/client/versia/client.ts b/packages/client/versia/client.ts index 70bf94fb..bb085443 100644 --- a/packages/client/versia/client.ts +++ b/packages/client/versia/client.ts @@ -1,5 +1,5 @@ import { OAuth2Client } from "@badgateway/oauth2-client"; -import type { z } from "zod"; +import type { z } from "zod/v4"; import type { Account } from "../schemas/account.ts"; import type { CredentialApplication } from "../schemas/application.ts"; import type { Attachment } from "../schemas/attachment.ts"; diff --git a/packages/config/build.ts b/packages/config/build.ts new file mode 100644 index 00000000..908e5d50 --- /dev/null +++ b/packages/config/build.ts @@ -0,0 +1,19 @@ +import { $, build } from "bun"; +import manifest from "./package.json" with { type: "json" }; + +console.log("Building..."); + +await $`rm -rf dist && mkdir dist`; + +await build({ + entrypoints: Object.values(manifest.exports).map((entry) => entry.import), + outdir: "dist", + target: "bun", + splitting: true, + minify: true, + external: [ + ...Object.keys(manifest.dependencies).filter((dep) => + dep.startsWith("@versia"), + ), + ], +}); diff --git a/classes/config/schema.ts b/packages/config/index.ts similarity index 80% rename from classes/config/schema.ts rename to packages/config/index.ts index ed73ad00..31b17271 100644 --- a/classes/config/schema.ts +++ b/packages/config/index.ts @@ -1,10 +1,38 @@ +import { RolePermission } from "@versia/client/schemas"; import { type BunFile, env, file } from "bun"; +import chalk from "chalk"; +import { parseTOML } from "confbox"; import ISO6391 from "iso-639-1"; import { types as mimeTypes } from "mime-types"; import { generateVAPIDKeys } from "web-push"; -import { z } from "zod"; -import { ProxiableUrl } from "~/classes/media/url.ts"; -import { RolePermission } from "~/packages/client/schemas/permissions.ts"; +import { z } from "zod/v4"; +import { fromZodError } from "zod-validation-error"; + +export class ProxiableUrl extends URL { + private isAllowedOrigin(): boolean { + const allowedOrigins: URL[] = [exportedConfig.http.base_url].concat( + exportedConfig.s3?.public_url ?? [], + ); + + return allowedOrigins.some((origin) => + this.hostname.endsWith(origin.hostname), + ); + } + + public get proxied(): string { + // Don't proxy from CDN and self, since those sources are trusted + if (this.isAllowedOrigin()) { + return this.href; + } + + const urlAsBase64Url = Buffer.from(this.href).toString("base64url"); + + return new URL( + `/media/proxy/${urlAsBase64Url}`, + exportedConfig.http.base_url, + ).href; + } +} export const DEFAULT_ROLES = [ RolePermission.ManageOwnNotes, @@ -105,11 +133,7 @@ export const sensitiveString = z .refine( (text) => text.startsWith("PATH:") ? fileFromPathString(text).exists() : true, - (text) => ({ - message: `Path ${ - fileFromPathString(text).name - } does not exist, is a directory or is not accessible`, - }), + "Path does not exist, is a directory or is not accessible", ) .transform((text) => text.startsWith("PATH:") ? fileFromPathString(text).text() : text, @@ -121,9 +145,7 @@ export const filePathString = z .transform((s) => file(s)) .refine( (file) => file.exists(), - (file) => ({ - message: `Path ${file.name} does not exist, is a directory or is not accessible`, - }), + "Path does not exist, is a directory or is not accessible", ) .transform(async (file) => ({ content: await file.text(), @@ -153,8 +175,8 @@ export const keyPair = z ).toString("base64"); ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: `Public and private keys are not set. Here are generated keys for you to copy.\n\nPublic: ${publicKey}\nPrivate: ${privateKey}`, + code: "custom", + error: `Public and private keys are not set. Here are generated keys for you to copy.\n\nPublic: ${publicKey}\nPrivate: ${privateKey}`, }); return z.NEVER; @@ -173,8 +195,8 @@ export const keyPair = z ); } catch { ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: "Public key is invalid", + code: "custom", + error: "Public key is invalid", }); return z.NEVER; @@ -190,8 +212,8 @@ export const keyPair = z ); } catch { ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: "Private key is invalid", + code: "custom", + error: "Private key is invalid", }); return z.NEVER; @@ -214,8 +236,8 @@ export const vapidKeyPair = z const keys = generateVAPIDKeys(); ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: `VAPID keys are not set. Here are generated keys for you to copy.\n\nPublic: ${keys.publicKey}\nPrivate: ${keys.privateKey}`, + code: "custom", + error: `VAPID keys are not set. Here are generated keys for you to copy.\n\nPublic: ${keys.publicKey}\nPrivate: ${keys.privateKey}`, }); return z.NEVER; @@ -240,8 +262,8 @@ export const hmacKey = sensitiveString.transform(async (text, ctx) => { const base64 = Buffer.from(exported).toString("base64"); ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: `HMAC key is not set. Here is a generated key for you to copy: ${base64}`, + code: "custom", + error: `HMAC key is not set. Here is a generated key for you to copy: ${base64}`, }); return z.NEVER; @@ -260,8 +282,8 @@ export const hmacKey = sensitiveString.transform(async (text, ctx) => { ); } catch { ctx.addIssue({ - code: z.ZodIssueCode.custom, - message: "HMAC key is invalid", + code: "custom", + error: "HMAC key is invalid", }); return z.NEVER; @@ -417,9 +439,7 @@ export const ConfigSchema = z "When send_emails is enabled, SMTP configuration must be set", ), media: z.strictObject({ - backend: z - .nativeEnum(MediaBackendType) - .default(MediaBackendType.Local), + backend: z.enum(MediaBackendType).default(MediaBackendType.Local), uploads_path: z.string().min(1).default("uploads"), conversion: z.strictObject({ convert_images: z.boolean().default(false), @@ -470,33 +490,35 @@ export const ConfigSchema = z .default(5_000_000), disallowed_usernames: z .array(regex) - .default([ - "well-known", - "about", - "activities", - "api", - "auth", - "dev", - "inbox", - "internal", - "main", - "media", - "nodeinfo", - "notice", - "oauth", - "objects", - "proxy", - "push", - "registration", - "relay", - "settings", - "status", - "tag", - "users", - "web", - "search", - "mfa", - ]), + .default( + [ + "well-known", + "about", + "activities", + "api", + "auth", + "dev", + "inbox", + "internal", + "main", + "media", + "nodeinfo", + "notice", + "oauth", + "objects", + "proxy", + "push", + "registration", + "relay", + "settings", + "status", + "tag", + "users", + "web", + "search", + "mfa", + ].map((s) => new RegExp(`^${s}$`, "i")), + ), max_field_count: z.number().int().default(10), max_field_name_characters: z.number().int().default(1000), max_field_value_characters: z.number().int().default(1000), @@ -681,7 +703,6 @@ export const ConfigSchema = z .describe("Primary instance languages. ISO 639-1 codes."), contact: z.strictObject({ email: z - .string() .email() .describe("Email to contact the instance administration"), }), @@ -707,43 +728,40 @@ export const ConfigSchema = z keys: keyPair, }), permissions: z.strictObject({ - anonymous: z - .array(z.nativeEnum(RolePermission)) - .default(DEFAULT_ROLES), - default: z - .array(z.nativeEnum(RolePermission)) - .default(DEFAULT_ROLES), - admin: z.array(z.nativeEnum(RolePermission)).default(ADMIN_ROLES), + anonymous: z.array(z.enum(RolePermission)).default(DEFAULT_ROLES), + default: z.array(z.enum(RolePermission)).default(DEFAULT_ROLES), + admin: z.array(z.enum(RolePermission)).default(ADMIN_ROLES), }), logging: z.strictObject({ - types: z.record( - z.enum([ - "requests", - "responses", - "requests_content", - "filters", - ]), - z - .boolean() - .default(false) - .or( - z.strictObject({ - level: z - .enum([ - "debug", - "info", - "warning", - "error", - "fatal", - ]) - .default("info"), - log_file_path: z.string().optional(), - }), - ), - ), - log_level: z - .enum(["debug", "info", "warning", "error", "fatal"]) - .default("info"), + file: z + .strictObject({ + path: z.string().default("logs/versia.log"), + rotation: z + .strictObject({ + max_size: z + .number() + .int() + .nonnegative() + .default(10_000_000), // 10 MB + max_files: z + .number() + .int() + .nonnegative() + .default(10), + }) + .optional(), + log_level: z + .enum([ + "trace", + "debug", + "info", + "warning", + "error", + "fatal", + ]) + .default("info"), + }) + .optional(), sentry: z .strictObject({ dsn: url, @@ -753,9 +771,21 @@ export const ConfigSchema = z trace_propagation_targets: z.array(z.string()).default([]), max_breadcrumbs: z.number().default(100), environment: z.string().optional(), + log_level: z + .enum([ + "trace", + "debug", + "info", + "warning", + "error", + "fatal", + ]) + .default("info"), }) .optional(), - log_file_path: z.string().default("logs/versia.log"), + log_level: z + .enum(["trace", "debug", "info", "warning", "error", "fatal"]) + .default("info"), }), debug: z .strictObject({ @@ -783,3 +813,38 @@ export const ConfigSchema = z (arg) => arg.media.backend === MediaBackendType.Local || !!arg.s3, "When media backend is S3, S3 configuration must be set", ); + +const CONFIG_LOCATION = env.CONFIG_LOCATION ?? "./config/config.toml"; +const configFile = file(CONFIG_LOCATION); + +if (!(await configFile.exists())) { + throw new Error( + `config file at "${CONFIG_LOCATION}" does not exist or is not accessible.`, + ); +} + +const configText = await configFile.text(); +const config = parseTOML>(configText); + +const parsed = await ConfigSchema.safeParseAsync(config); + +if (!parsed.success) { + console.error( + `⚠ Error encountered while loading ${chalk.gray(CONFIG_LOCATION)}.`, + ); + console.error( + "⚠ This is due to invalid, missing or incorrect values in the configuration file.", + ); + console.error( + "⚠ Here is the error message, please fix the configuration file accordingly:", + ); + const errorMessage = fromZodError(parsed.error).message; + + console.info(errorMessage); + + throw new Error("Configuration file is invalid."); +} + +const exportedConfig = parsed.data; + +export { exportedConfig as config }; diff --git a/packages/config/package.json b/packages/config/package.json new file mode 100644 index 00000000..7ef42bc5 --- /dev/null +++ b/packages/config/package.json @@ -0,0 +1,25 @@ +{ + "name": "@versia-server/config", + "module": "index.ts", + "type": "module", + "version": "0.0.1", + "private": true, + "scripts": { + "build": "bun run build.ts" + }, + "exports": { + ".": { + "import": "./index.ts" + } + }, + "dependencies": { + "chalk": "catalog:", + "zod": "catalog:", + "zod-validation-error": "catalog:", + "confbox": "catalog:", + "web-push": "catalog:", + "iso-639-1": "catalog:", + "mime-types": "catalog:", + "@versia/client": "workspace:*" + } +} diff --git a/packages/config/to-json-schema.ts b/packages/config/to-json-schema.ts new file mode 100644 index 00000000..38e8d246 --- /dev/null +++ b/packages/config/to-json-schema.ts @@ -0,0 +1,6 @@ +import * as z from "zod/v4"; +import { ConfigSchema } from "./index.ts"; + +const jsonSchema = z.toJSONSchema(ConfigSchema); + +console.write(`${JSON.stringify(jsonSchema, null, 4)}\n`); diff --git a/classes/errors/api-error.ts b/packages/kit/api-error.ts similarity index 98% rename from classes/errors/api-error.ts rename to packages/kit/api-error.ts index c4b89c16..8f63b97a 100644 --- a/classes/errors/api-error.ts +++ b/packages/kit/api-error.ts @@ -1,8 +1,8 @@ import type { ContentfulStatusCode } from "hono/utils/http-status"; import type { JSONObject } from "hono/utils/types"; import type { DescribeRouteOptions } from "hono-openapi"; -import { resolver } from "hono-openapi/zod"; -import { z } from "zod"; +import { resolver } from "hono-openapi"; +import { z } from "zod/v4"; /** * API Error diff --git a/utils/api.ts b/packages/kit/api.ts similarity index 77% rename from utils/api.ts rename to packages/kit/api.ts index 7842b16a..f31e6a7f 100644 --- a/utils/api.ts +++ b/packages/kit/api.ts @@ -1,91 +1,29 @@ -import type { Hook } from "@hono/zod-validator"; -import { getLogger } from "@logtape/logtape"; +import type { Hook } from "@hono/standard-validator"; import type { RolePermission } from "@versia/client/schemas"; -import { Application, db, Emoji, Note, Token, User } from "@versia/kit/db"; -import { Challenges } from "@versia/kit/tables"; +import { config } from "@versia-server/config"; +import { serverLogger } from "@versia-server/logging"; import { extractParams, verifySolution } from "altcha-lib"; -import { SHA256 } from "bun"; import chalk from "chalk"; import { eq, type SQL } from "drizzle-orm"; import type { Context, Hono, MiddlewareHandler, ValidationTargets } from "hono"; import { every } from "hono/combine"; import { createMiddleware } from "hono/factory"; -import { validator } from "hono-openapi/zod"; -import { - anyOf, - caseInsensitive, - charIn, - createRegExp, - digit, - exactly, - global, - letter, - maybe, - oneOrMore, -} from "magic-regexp"; +import { validator } from "hono-openapi"; import { type ParsedQs, parse } from "qs"; -import { type ZodAny, type ZodError, z } from "zod"; +import { type ZodAny, ZodError, z } from "zod/v4"; import { fromZodError } from "zod-validation-error"; -import { ApiError } from "~/classes/errors/api-error"; -import type { AuthData } from "~/classes/functions/user"; -import { config } from "~/config.ts"; -import type { HonoEnv } from "~/types/api"; +import type { AuthData, HonoEnv } from "~/types/api"; +import { ApiError } from "./api-error.ts"; +import { Application } from "./db/application.ts"; +import { Emoji } from "./db/emoji.ts"; +import { Note } from "./db/note.ts"; +import { Token } from "./db/token.ts"; +import { User } from "./db/user.ts"; +import { db } from "./tables/db.ts"; +import { Challenges } from "./tables/schema.ts"; export const apiRoute = (fn: (app: Hono) => void): typeof fn => fn; -export const idValidator = createRegExp( - anyOf(digit, charIn("ABCDEF")).times(8), - exactly("-"), - anyOf(digit, charIn("ABCDEF")).times(4), - exactly("-"), - exactly("7"), - anyOf(digit, charIn("ABCDEF")).times(3), - exactly("-"), - anyOf("8", "9", "A", "B").times(1), - anyOf(digit, charIn("ABCDEF")).times(3), - exactly("-"), - anyOf(digit, charIn("ABCDEF")).times(12), - [caseInsensitive], -); - -export const mentionValidator = createRegExp( - exactly("@"), - oneOrMore(anyOf(letter.lowercase, digit, charIn("-_"))).groupedAs( - "username", - ), - maybe( - exactly("@"), - oneOrMore(anyOf(letter, digit, charIn("_-.:"))).groupedAs("domain"), - ), - [global], -); - -export const webfingerMention = createRegExp( - exactly("acct:"), - oneOrMore(anyOf(letter, digit, charIn("-_"))).groupedAs("username"), - maybe( - exactly("@"), - oneOrMore(anyOf(letter, digit, charIn("_-.:"))).groupedAs("domain"), - ), - [], -); - -export const parseUserAddress = ( - address: string, -): { - username: string; - domain?: string; -} => { - let output = address; - // Remove leading @ if it exists - if (output.startsWith("@")) { - output = output.slice(1); - } - - const [username, domain] = output.split("@"); - return { username, domain }; -}; - export const handleZodError: Hook< z.infer, HonoEnv, @@ -93,9 +31,11 @@ export const handleZodError: Hook< keyof ValidationTargets > = (result, context): Response | undefined => { if (!result.success) { + const issues = result.error as z.core.$ZodIssue[]; + return context.json( { - error: fromZodError(result.error as ZodError).message, + error: fromZodError(new ZodError(issues)).message, }, 422, ); @@ -266,7 +206,7 @@ type WithIdParam = { * @returns MiddlewareHandler */ export const withNoteParam = every( - validator("param", z.object({ id: z.string().uuid() }), handleZodError), + validator("param", z.object({ id: z.uuid() }), handleZodError), createMiddleware< HonoEnv & { Variables: { @@ -304,7 +244,7 @@ export const withNoteParam = every( * @returns MiddlewareHandler */ export const withUserParam = every( - validator("param", z.object({ id: z.string().uuid() }), handleZodError), + validator("param", z.object({ id: z.uuid() }), handleZodError), createMiddleware< HonoEnv & { Variables: { @@ -340,7 +280,7 @@ export const withUserParam = every( * @returns */ export const withEmojiParam = every( - validator("param", z.object({ id: z.string().uuid() }), handleZodError), + validator("param", z.object({ id: z.uuid() }), handleZodError), createMiddleware< HonoEnv & { Variables: { @@ -478,34 +418,8 @@ export const jsonOrForm = (): MiddlewareHandler => { }); }; -export const debugRequest = async (req: Request): Promise => { - const body = await req.text(); - const logger = getLogger("server"); - - const urlAndMethod = `${chalk.green(req.method)} ${chalk.blue(req.url)}`; - - const hash = `${chalk.bold("Hash")}: ${chalk.yellow( - new SHA256().update(body).digest("hex"), - )}`; - - const headers = `${chalk.bold("Headers")}:\n${Array.from( - req.headers.entries(), - ) - .map(([key, value]) => ` - ${chalk.cyan(key)}: ${chalk.white(value)}`) - .join("\n")}`; - - const bodyLog = `${chalk.bold("Body")}: ${chalk.gray(body)}`; - - if (config.logging.types.requests_content) { - logger.debug`${urlAndMethod}\n${hash}\n${headers}\n${bodyLog}`; - } else { - logger.debug`${urlAndMethod}`; - } -}; - export const debugResponse = async (res: Response): Promise => { const body = await res.clone().text(); - const logger = getLogger("server"); const status = `${chalk.bold("Status")}: ${chalk.green(res.status)}`; @@ -517,9 +431,5 @@ export const debugResponse = async (res: Response): Promise => { const bodyLog = `${chalk.bold("Body")}: ${chalk.gray(body)}`; - if (config.logging.types.requests_content) { - logger.debug`${status}\n${headers}\n${bodyLog}`; - } else { - logger.debug`${status}`; - } + serverLogger.debug`${status}\n${headers}\n${bodyLog}`; }; diff --git a/packages/kit/build.ts b/packages/kit/build.ts new file mode 100644 index 00000000..df40150d --- /dev/null +++ b/packages/kit/build.ts @@ -0,0 +1,44 @@ +import { $, build } from "bun"; +import manifest from "./package.json" with { type: "json" }; + +console.log("Building..."); + +await $`rm -rf dist && mkdir dist`; + +await build({ + entrypoints: Object.values(manifest.exports).map((entry) => entry.import), + outdir: "dist", + target: "bun", + splitting: true, + minify: true, + external: [ + ...Object.keys(manifest.dependencies).filter((dep) => + dep.startsWith("@versia"), + ), + "acorn", + // Excluded because Standard Schema imports those, but the code is never executed + "@valibot/to-json-schema", + "effect", + ], +}); + +console.log("Copying files..."); + +// Copy Drizzle stuff +// Copy to dist instead of dist/tables because the built files are at the top-level +await $`cp -rL tables/migrations dist`; + +await $`mkdir -p dist/node_modules`; + +// Copy Sharp to dist +await $`mkdir -p dist/node_modules/@img`; +await $`cp -rL ../../node_modules/@img/sharp-libvips-linux* dist/node_modules/@img`; +await $`cp -rL ../../node_modules/@img/sharp-linux* dist/node_modules/@img`; + +// Copy acorn to dist +await $`cp -rL ../../node_modules/acorn dist/node_modules/acorn`; + +// Fixes issues with sharp +await $`cp -rL ../../node_modules/detect-libc dist/node_modules/`; + +console.log("Build complete!"); diff --git a/classes/database/application.ts b/packages/kit/db/application.ts similarity index 96% rename from classes/database/application.ts rename to packages/kit/db/application.ts index 27bb0980..15314c66 100644 --- a/classes/database/application.ts +++ b/packages/kit/db/application.ts @@ -2,8 +2,6 @@ import type { Application as ApplicationSchema, CredentialApplication, } from "@versia/client/schemas"; -import { db, Token } from "@versia/kit/db"; -import { Applications } from "@versia/kit/tables"; import { desc, eq, @@ -12,8 +10,11 @@ import { inArray, type SQL, } from "drizzle-orm"; -import type { z } from "zod"; +import type { z } from "zod/v4"; +import { db } from "../tables/db.ts"; +import { Applications } from "../tables/schema.ts"; import { BaseInterface } from "./base.ts"; +import { Token } from "./token.ts"; type ApplicationType = InferSelectModel; diff --git a/classes/database/base.ts b/packages/kit/db/base.ts similarity index 100% rename from classes/database/base.ts rename to packages/kit/db/base.ts diff --git a/classes/database/emoji.ts b/packages/kit/db/emoji.ts similarity index 94% rename from classes/database/emoji.ts rename to packages/kit/db/emoji.ts index 00715305..e4a3fc08 100644 --- a/classes/database/emoji.ts +++ b/packages/kit/db/emoji.ts @@ -3,8 +3,8 @@ import { emojiWithColonsRegex, emojiWithIdentifiersRegex, } from "@versia/client/schemas"; -import { db, type Instance, Media } from "@versia/kit/db"; -import { Emojis, type Instances, type Medias } from "@versia/kit/tables"; +import * as VersiaEntities from "@versia/sdk/entities"; +import type { ImageContentFormatSchema } from "@versia/sdk/schemas"; import { randomUUIDv7 } from "bun"; import { and, @@ -16,10 +16,12 @@ import { isNull, type SQL, } from "drizzle-orm"; -import type { z } from "zod"; -import * as VersiaEntities from "~/packages/sdk/entities/index.ts"; -import type { ImageContentFormatSchema } from "~/packages/sdk/schemas/index.ts"; +import type { z } from "zod/v4"; +import { db } from "../tables/db.ts"; +import { Emojis, type Instances, type Medias } from "../tables/schema.ts"; import { BaseInterface } from "./base.ts"; +import type { Instance } from "./instance.ts"; +import { Media } from "./media.ts"; type EmojiType = InferSelectModel & { media: InferSelectModel; @@ -192,7 +194,7 @@ export class Emoji extends BaseInterface { global: this.data.ownerId === null, description: this.media.data.content[this.media.getPreferredMimeType()] - .description ?? null, + ?.description ?? null, }; } diff --git a/packages/kit/db/index.ts b/packages/kit/db/index.ts new file mode 100644 index 00000000..1cc2359d --- /dev/null +++ b/packages/kit/db/index.ts @@ -0,0 +1,15 @@ +export { db, setupDatabase } from "../tables/db.ts"; +export { Application } from "./application.ts"; +export { Emoji } from "./emoji.ts"; +export { Instance } from "./instance.ts"; +export { Like } from "./like.ts"; +export { Media } from "./media.ts"; +export { Note } from "./note.ts"; +export { Notification } from "./notification.ts"; +export { PushSubscription } from "./pushsubscription.ts"; +export { Reaction } from "./reaction.ts"; +export { Relationship } from "./relationship.ts"; +export { Role } from "./role.ts"; +export { Timeline } from "./timeline.ts"; +export { Token } from "./token.ts"; +export { User } from "./user.ts"; diff --git a/classes/database/instance.ts b/packages/kit/db/instance.ts similarity index 84% rename from classes/database/instance.ts rename to packages/kit/db/instance.ts index 7033b5e7..c064a5b0 100644 --- a/classes/database/instance.ts +++ b/packages/kit/db/instance.ts @@ -1,6 +1,10 @@ -import { getLogger } from "@logtape/logtape"; -import { db } from "@versia/kit/db"; -import { Instances } from "@versia/kit/tables"; +import * as VersiaEntities from "@versia/sdk/entities"; +import { FederationRequester } from "@versia/sdk/http"; +import { config } from "@versia-server/config"; +import { + federationMessagingLogger, + federationResolversLogger, +} from "@versia-server/logging"; import { randomUUIDv7 } from "bun"; import chalk from "chalk"; import { @@ -11,11 +15,11 @@ import { inArray, type SQL, } from "drizzle-orm"; -import { config } from "~/config.ts"; -import * as VersiaEntities from "~/packages/sdk/entities/index.ts"; -import { ApiError } from "../errors/api-error.ts"; +import { ApiError } from "../api-error.ts"; +import { db } from "../tables/db.ts"; +import { Instances } from "../tables/schema.ts"; import { BaseInterface } from "./base.ts"; -import { User } from "./user.ts"; +import type { User } from "./user.ts"; type InstanceType = InferSelectModel; @@ -143,10 +147,10 @@ export class Instance extends BaseInterface { const wellKnownUrl = new URL("/.well-known/versia", origin); try { - const metadata = await User.federationRequester.fetchEntity( - wellKnownUrl, - VersiaEntities.InstanceMetadata, - ); + const metadata = await new FederationRequester( + config.instance.keys.private, + config.http.base_url, + ).fetchEntity(wellKnownUrl, VersiaEntities.InstanceMetadata); return { metadata, protocol: "versia" }; } catch { @@ -175,28 +179,25 @@ export class Instance extends BaseInterface { const wellKnownUrl = new URL("/.well-known/nodeinfo", origin); // Go to endpoint, then follow the links to the actual metadata - - const logger = getLogger(["federation", "resolvers"]); - try { - const { json, ok, status } = await fetch(wellKnownUrl, { + const result = await fetch(wellKnownUrl, { // @ts-expect-error Bun extension proxy: config.http.proxy_address, }); - if (!ok) { - logger.error`Failed to fetch ActivityPub metadata for instance ${chalk.bold( + if (!result.ok) { + federationResolversLogger.error`Failed to fetch ActivityPub metadata for instance ${chalk.bold( origin, - )} - HTTP ${status}`; + )} - HTTP ${result.status}`; return null; } - const wellKnown = (await json()) as { + const wellKnown = (await result.json()) as { links: { rel: string; href: string }[]; }; if (!wellKnown.links) { - logger.error`Failed to fetch ActivityPub metadata for instance ${chalk.bold( + federationResolversLogger.error`Failed to fetch ActivityPub metadata for instance ${chalk.bold( origin, )} - No links found`; return null; @@ -209,29 +210,25 @@ export class Instance extends BaseInterface { ); if (!metadataUrl) { - logger.error`Failed to fetch ActivityPub metadata for instance ${chalk.bold( + federationResolversLogger.error`Failed to fetch ActivityPub metadata for instance ${chalk.bold( origin, )} - No metadata URL found`; return null; } - const { - json: json2, - ok: ok2, - status: status2, - } = await fetch(metadataUrl.href, { + const result2 = await fetch(metadataUrl.href, { // @ts-expect-error Bun extension proxy: config.http.proxy_address, }); - if (!ok2) { - logger.error`Failed to fetch ActivityPub metadata for instance ${chalk.bold( + if (!result2.ok) { + federationResolversLogger.error`Failed to fetch ActivityPub metadata for instance ${chalk.bold( origin, - )} - HTTP ${status2}`; + )} - HTTP ${result2.status}`; return null; } - const metadata = (await json2()) as { + const metadata = (await result2.json()) as { metadata: { nodeName?: string; title?: string; @@ -264,7 +261,7 @@ export class Instance extends BaseInterface { }, }); } catch (error) { - logger.error`Failed to fetch ActivityPub metadata for instance ${chalk.bold( + federationResolversLogger.error`Failed to fetch ActivityPub metadata for instance ${chalk.bold( origin, )} - Error! ${error}`; return null; @@ -312,14 +309,12 @@ export class Instance extends BaseInterface { } public async updateFromRemote(): Promise { - const logger = getLogger(["federation", "resolvers"]); - const output = await Instance.fetchMetadata( new URL(`https://${this.data.baseUrl}`), ); if (!output) { - logger.error`Failed to update instance ${chalk.bold( + federationResolversLogger.error`Failed to update instance ${chalk.bold( this.data.baseUrl, )}`; throw new Error("Failed to update instance"); @@ -341,12 +336,10 @@ export class Instance extends BaseInterface { } public async sendMessage(content: string): Promise { - const logger = getLogger(["federation", "messaging"]); - if ( !this.data.extensions?.["pub.versia:instance_messaging"]?.endpoint ) { - logger.info`Instance ${chalk.gray( + federationMessagingLogger.info`Instance ${chalk.gray( this.data.baseUrl, )} does not support Instance Messaging, skipping message`; diff --git a/classes/database/like.ts b/packages/kit/db/like.ts similarity index 96% rename from classes/database/like.ts rename to packages/kit/db/like.ts index 9dc42277..041bfd04 100644 --- a/classes/database/like.ts +++ b/packages/kit/db/like.ts @@ -1,10 +1,5 @@ -import { db } from "@versia/kit/db"; -import { - Likes, - type Notes, - Notifications, - type Users, -} from "@versia/kit/tables"; +import * as VersiaEntities from "@versia/sdk/entities"; +import { config } from "@versia-server/config"; import { and, desc, @@ -14,8 +9,13 @@ import { inArray, type SQL, } from "drizzle-orm"; -import { config } from "~/config.ts"; -import * as VersiaEntities from "~/packages/sdk/entities/index.ts"; +import { db } from "../tables/db.ts"; +import { + Likes, + type Notes, + Notifications, + type Users, +} from "../tables/schema.ts"; import { BaseInterface } from "./base.ts"; import { User } from "./user.ts"; diff --git a/classes/database/media.ts b/packages/kit/db/media.ts similarity index 94% rename from classes/database/media.ts rename to packages/kit/db/media.ts index ae398c1d..a680c262 100644 --- a/classes/database/media.ts +++ b/packages/kit/db/media.ts @@ -1,7 +1,11 @@ import { join } from "node:path"; import type { Attachment as AttachmentSchema } from "@versia/client/schemas"; -import { db } from "@versia/kit/db"; -import { Medias } from "@versia/kit/tables"; +import * as VersiaEntities from "@versia/sdk/entities"; +import type { + ContentFormatSchema, + ImageContentFormatSchema, +} from "@versia/sdk/schemas"; +import { config, MediaBackendType, ProxiableUrl } from "@versia-server/config"; import { randomUUIDv7, S3Client, SHA256, write } from "bun"; import { desc, @@ -12,19 +16,13 @@ import { type SQL, } from "drizzle-orm"; import sharp from "sharp"; -import type { z } from "zod"; +import type { z } from "zod/v4"; import { mimeLookup } from "@/content_types.ts"; -import { MediaBackendType } from "~/classes/config/schema.ts"; -import { config } from "~/config.ts"; -import * as VersiaEntities from "~/packages/sdk/entities/index.ts"; -import type { - ContentFormatSchema, - ImageContentFormatSchema, -} from "~/packages/sdk/schemas/index.ts"; -import { ApiError } from "../errors/api-error.ts"; -import { getMediaHash } from "../media/media-hasher.ts"; -import { ProxiableUrl } from "../media/url.ts"; -import { MediaJobType, mediaQueue } from "../queues/media.ts"; +import { getMediaHash } from "../../../classes/media/media-hasher.ts"; +import { ApiError } from "../api-error.ts"; +import { MediaJobType, mediaQueue } from "../queues/media/queue.ts"; +import { db } from "../tables/db.ts"; +import { Medias } from "../tables/schema.ts"; import { BaseInterface } from "./base.ts"; type MediaType = InferSelectModel; @@ -299,7 +297,7 @@ export class Media extends BaseInterface { const content = await Media.fileToContentFormat(file, url, { description: this.data.content[Object.keys(this.data.content)[0]] - .description || undefined, + ?.description || undefined, }); await this.update({ @@ -321,7 +319,7 @@ export class Media extends BaseInterface { remote: true, description: this.data.content[Object.keys(this.data.content)[0]] - .description || undefined, + ?.description || undefined, }, }; @@ -365,7 +363,7 @@ export class Media extends BaseInterface { content[type] = { ...content[type], ...metadata, - }; + } as (typeof content)[keyof typeof content]; } await this.update({ @@ -492,6 +490,14 @@ export class Media extends BaseInterface { public toApiMeta(): z.infer { const type = this.getPreferredMimeType(); const data = this.data.content[type]; + + if (!data) { + throw new ApiError( + 500, + `No content for type ${type} in attachment ${this.id}`, + ); + } + const size = data.width && data.height ? `${data.width}x${data.height}` @@ -535,7 +541,7 @@ export class Media extends BaseInterface { ? new ProxiableUrl(thumbnailData.content).proxied : null, meta: this.toApiMeta(), - description: data.description || null, + description: data?.description || null, blurhash: this.data.blurhash, }; } diff --git a/classes/database/note.ts b/packages/kit/db/note.ts similarity index 64% rename from classes/database/note.ts rename to packages/kit/db/note.ts index 25fa185b..73589953 100644 --- a/classes/database/note.ts +++ b/packages/kit/db/note.ts @@ -1,13 +1,11 @@ -import type { NoteReactionWithAccounts, Status } from "@versia/client/schemas"; -import { db, Instance, type Reaction } from "@versia/kit/db"; -import { - EmojiToNote, - Likes, - MediasToNotes, - Notes, - NoteToMentions, - Users, -} from "@versia/kit/tables"; +import type { + NoteReactionWithAccounts, + Status as StatusSchema, +} from "@versia/client/schemas"; +import * as VersiaEntities from "@versia/sdk/entities"; +import { FederationRequester } from "@versia/sdk/http"; +import type { NonTextContentFormatSchema } from "@versia/sdk/schemas"; +import { config } from "@versia-server/config"; import { randomUUIDv7 } from "bun"; import { and, @@ -22,20 +20,211 @@ import { } from "drizzle-orm"; import { htmlToText } from "html-to-text"; import { createRegExp, exactly, global } from "magic-regexp"; -import type { z } from "zod"; -import { idValidator } from "@/api"; +import type { z } from "zod/v4"; import { mergeAndDeduplicate } from "@/lib.ts"; import { sanitizedHtmlStrip } from "@/sanitization"; -import { contentToHtml, findManyNotes } from "~/classes/functions/status"; -import { config } from "~/config.ts"; -import * as VersiaEntities from "~/packages/sdk/entities/index.ts"; -import type { NonTextContentFormatSchema } from "~/packages/sdk/schemas/contentformat.ts"; -import { DeliveryJobType, deliveryQueue } from "../queues/delivery.ts"; +import { versiaTextToHtml } from "../parsers.ts"; +import { DeliveryJobType, deliveryQueue } from "../queues/delivery/queue.ts"; +import { uuid } from "../regex.ts"; +import { db } from "../tables/db.ts"; +import { + EmojiToNote, + Likes, + MediasToNotes, + Notes, + NoteToMentions, + Notifications, + Users, +} from "../tables/schema.ts"; import { Application } from "./application.ts"; import { BaseInterface } from "./base.ts"; import { Emoji } from "./emoji.ts"; +import { Instance } from "./instance.ts"; +import { Like } from "./like.ts"; import { Media } from "./media.ts"; -import { User } from "./user.ts"; +import { Reaction } from "./reaction.ts"; +import { + transformOutputToUserWithRelations, + User, + userRelations, +} from "./user.ts"; + +/** + * Wrapper against the Status object to make it easier to work with + * @param query + * @returns + */ +const findManyNotes = async ( + query: Parameters[0], + userId?: string, +): Promise<(typeof Note.$type)[]> => { + const output = await db.query.Notes.findMany({ + ...query, + with: { + ...query?.with, + attachments: { + with: { + media: true, + }, + }, + reactions: { + with: { + emoji: { + with: { + instance: true, + media: true, + }, + }, + }, + }, + emojis: { + with: { + emoji: { + with: { + instance: true, + media: true, + }, + }, + }, + }, + author: { + with: { + ...userRelations, + }, + }, + mentions: { + with: { + user: { + with: { + instance: true, + }, + }, + }, + }, + reblog: { + with: { + attachments: { + with: { + media: true, + }, + }, + reactions: { + with: { + emoji: { + with: { + instance: true, + media: true, + }, + }, + }, + }, + emojis: { + with: { + emoji: { + with: { + instance: true, + media: true, + }, + }, + }, + }, + likes: true, + application: true, + mentions: { + with: { + user: { + with: userRelations, + }, + }, + }, + author: { + with: { + ...userRelations, + }, + }, + }, + extras: { + pinned: userId + ? sql`EXISTS (SELECT 1 FROM "UserToPinnedNotes" WHERE "UserToPinnedNotes"."noteId" = "Notes_reblog".id AND "UserToPinnedNotes"."userId" = ${userId})`.as( + "pinned", + ) + : sql`false`.as("pinned"), + reblogged: userId + ? sql`EXISTS (SELECT 1 FROM "Notes" WHERE "Notes"."authorId" = ${userId} AND "Notes"."reblogId" = "Notes_reblog".id)`.as( + "reblogged", + ) + : sql`false`.as("reblogged"), + muted: userId + ? sql`EXISTS (SELECT 1 FROM "Relationships" WHERE "Relationships"."ownerId" = ${userId} AND "Relationships"."subjectId" = "Notes_reblog"."authorId" AND "Relationships"."muting" = true)`.as( + "muted", + ) + : sql`false`.as("muted"), + liked: userId + ? sql`EXISTS (SELECT 1 FROM "Likes" WHERE "Likes"."likedId" = "Notes_reblog".id AND "Likes"."likerId" = ${userId})`.as( + "liked", + ) + : sql`false`.as("liked"), + }, + }, + reply: true, + quote: true, + }, + extras: { + pinned: userId + ? sql`EXISTS (SELECT 1 FROM "UserToPinnedNotes" WHERE "UserToPinnedNotes"."noteId" = "Notes".id AND "UserToPinnedNotes"."userId" = ${userId})`.as( + "pinned", + ) + : sql`false`.as("pinned"), + reblogged: userId + ? sql`EXISTS (SELECT 1 FROM "Notes" WHERE "Notes"."authorId" = ${userId} AND "Notes"."reblogId" = "Notes".id)`.as( + "reblogged", + ) + : sql`false`.as("reblogged"), + muted: userId + ? sql`EXISTS (SELECT 1 FROM "Relationships" WHERE "Relationships"."ownerId" = ${userId} AND "Relationships"."subjectId" = "Notes"."authorId" AND "Relationships"."muting" = true)`.as( + "muted", + ) + : sql`false`.as("muted"), + liked: userId + ? sql`EXISTS (SELECT 1 FROM "Likes" WHERE "Likes"."likedId" = "Notes".id AND "Likes"."likerId" = ${userId})`.as( + "liked", + ) + : sql`false`.as("liked"), + ...query?.extras, + }, + }); + + return output.map((post) => ({ + ...post, + author: transformOutputToUserWithRelations(post.author), + mentions: post.mentions.map((mention) => ({ + ...mention.user, + endpoints: mention.user.endpoints, + })), + attachments: post.attachments.map((attachment) => attachment.media), + emojis: (post.emojis ?? []).map((emoji) => emoji.emoji), + reblog: post.reblog && { + ...post.reblog, + author: transformOutputToUserWithRelations(post.reblog.author), + mentions: post.reblog.mentions.map((mention) => ({ + ...mention.user, + endpoints: mention.user.endpoints, + })), + attachments: post.reblog.attachments.map( + (attachment) => attachment.media, + ), + emojis: (post.reblog.emojis ?? []).map((emoji) => emoji.emoji), + pinned: Boolean(post.reblog.pinned), + reblogged: Boolean(post.reblog.reblogged), + muted: Boolean(post.reblog.muted), + liked: Boolean(post.reblog.liked), + }, + pinned: Boolean(post.pinned), + reblogged: Boolean(post.reblogged), + muted: Boolean(post.muted), + liked: Boolean(post.liked), + })); +}; type NoteType = InferSelectModel; @@ -294,6 +483,315 @@ export class Note extends BaseInterface { ); } + /** + * Reblog a note. + * + * If the note is already reblogged, it will return the existing reblog. Also creates a notification for the author of the note. + * @param reblogger The user reblogging the note + * @param visibility The visibility of the reblog + * @param uri The URI of the reblog, if it is remote + * @returns The reblog object created or the existing reblog + */ + public async reblog( + reblogger: User, + visibility: z.infer, + uri?: URL, + ): Promise { + const existingReblog = await Note.fromSql( + and(eq(Notes.authorId, reblogger.id), eq(Notes.reblogId, this.id)), + undefined, + reblogger.id, + ); + + if (existingReblog) { + return existingReblog; + } + + const newReblog = await Note.insert({ + id: randomUUIDv7(), + authorId: reblogger.id, + reblogId: this.id, + visibility, + sensitive: false, + updatedAt: new Date().toISOString(), + applicationId: null, + uri: uri?.href, + }); + + await this.recalculateReblogCount(); + + // Refetch the note *again* to get the proper value of .reblogged + await newReblog.reload(reblogger?.id); + + if (!newReblog) { + throw new Error("Failed to reblog"); + } + + if (this.author.local) { + // Notify the user that their post has been reblogged + await this.author.notify("reblog", reblogger, newReblog); + } + + if (reblogger.local) { + const federatedUsers = await reblogger.federateToFollowers( + newReblog.toVersiaShare(), + ); + + if ( + this.remote && + !federatedUsers.find((u) => u.id === this.author.id) + ) { + await reblogger.federateToUser( + newReblog.toVersiaShare(), + this.author, + ); + } + } + + return newReblog; + } + + /** + * Unreblog a note. + * + * If the note is not reblogged, it will return without doing anything. Also removes any notifications for this reblog. + * @param unreblogger The user unreblogging the note + * @returns + */ + public async unreblog(unreblogger: User): Promise { + const reblogToDelete = await Note.fromSql( + and( + eq(Notes.authorId, unreblogger.id), + eq(Notes.reblogId, this.id), + ), + undefined, + unreblogger.id, + ); + + if (!reblogToDelete) { + return; + } + + await reblogToDelete.delete(); + + await this.recalculateReblogCount(); + + if (this.author.local) { + // Remove any eventual notifications for this reblog + await db + .delete(Notifications) + .where( + and( + eq(Notifications.accountId, this.id), + eq(Notifications.type, "reblog"), + eq(Notifications.notifiedId, unreblogger.id), + eq(Notifications.noteId, this.id), + ), + ); + } + + if (this.local) { + const federatedUsers = await unreblogger.federateToFollowers( + reblogToDelete.toVersiaUnshare(), + ); + + if ( + this.remote && + !federatedUsers.find((u) => u.id === this.author.id) + ) { + await unreblogger.federateToUser( + reblogToDelete.toVersiaUnshare(), + this.author, + ); + } + } + } + + /** + * Like a note. + * + * If the note is already liked, it will return the existing like. Also creates a notification for the author of the note. + * @param liker The user liking the note + * @param uri The URI of the like, if it is remote + * @returns The like object created or the existing like + */ + public async like(liker: User, uri?: URL): Promise { + // Check if the user has already liked the note + const existingLike = await Like.fromSql( + and(eq(Likes.likerId, liker.id), eq(Likes.likedId, this.id)), + ); + + if (existingLike) { + return existingLike; + } + + const newLike = await Like.insert({ + id: randomUUIDv7(), + likerId: liker.id, + likedId: this.id, + uri: uri?.href, + }); + + await this.recalculateLikeCount(); + + if (this.author.local) { + // Notify the user that their post has been favourited + await this.author.notify("favourite", liker, this); + } + + if (liker.local) { + const federatedUsers = await liker.federateToFollowers( + newLike.toVersia(), + ); + + if ( + this.remote && + !federatedUsers.find((u) => u.id === this.author.id) + ) { + await liker.federateToUser(newLike.toVersia(), this.author); + } + } + + return newLike; + } + + /** + * Unlike a note. + * + * If the note is not liked, it will return without doing anything. Also removes any notifications for this like. + * @param unliker The user unliking the note + * @returns + */ + public async unlike(unliker: User): Promise { + const likeToDelete = await Like.fromSql( + and(eq(Likes.likerId, unliker.id), eq(Likes.likedId, this.id)), + ); + + if (!likeToDelete) { + return; + } + + await likeToDelete.delete(); + + await this.recalculateLikeCount(); + + if (this.author.local) { + // Remove any eventual notifications for this like + await likeToDelete.clearRelatedNotifications(); + } + + if (unliker.local) { + const federatedUsers = await unliker.federateToFollowers( + likeToDelete.unlikeToVersia(unliker), + ); + + if ( + this.remote && + !federatedUsers.find((u) => u.id === this.author.id) + ) { + await unliker.federateToUser( + likeToDelete.unlikeToVersia(unliker), + this.author, + ); + } + } + } + + /** + * Add an emoji reaction to a note + * @param reacter - The author of the reaction + * @param emoji - The emoji to react with (Emoji object for custom emojis, or Unicode emoji) + * @returns The created reaction + */ + public async react(reacter: User, emoji: Emoji | string): Promise { + const existingReaction = await Reaction.fromEmoji(emoji, reacter, this); + + if (existingReaction) { + return; // Reaction already exists, don't create duplicate + } + + // Create the reaction + const reaction = await Reaction.insert({ + id: randomUUIDv7(), + authorId: reacter.id, + noteId: this.id, + emojiText: emoji instanceof Emoji ? null : emoji, + emojiId: emoji instanceof Emoji ? emoji.id : null, + }); + + await this.reload(reacter.id); + + if (this.author.local) { + // Notify the user that their post has been reacted to + await this.author.notify("reaction", reacter, this); + } + + if (reacter.local) { + const federatedUsers = await reacter.federateToFollowers( + reaction.toVersia(), + ); + + if ( + this.remote && + !federatedUsers.find((u) => u.id === this.author.id) + ) { + await reacter.federateToUser(reaction.toVersia(), this.author); + } + } + } + + /** + * Remove an emoji reaction from a note + * @param unreacter - The author of the reaction + * @param emoji - The emoji to remove reaction for (Emoji object for custom emojis, or Unicode emoji) + */ + public async unreact( + unreacter: User, + emoji: Emoji | string, + ): Promise { + const reactionToDelete = await Reaction.fromEmoji( + emoji, + unreacter, + this, + ); + + if (!reactionToDelete) { + return; // Reaction doesn't exist, nothing to delete + } + + await reactionToDelete.delete(); + + if (this.author.local) { + // Remove any eventual notifications for this reaction + await db + .delete(Notifications) + .where( + and( + eq(Notifications.accountId, unreacter.id), + eq(Notifications.type, "reaction"), + eq(Notifications.notifiedId, this.data.authorId), + eq(Notifications.noteId, this.id), + ), + ); + } + + if (unreacter.local) { + const federatedUsers = await unreacter.federateToFollowers( + reactionToDelete.toVersiaUnreact(), + ); + + if ( + this.remote && + !federatedUsers.find((u) => u.id === this.author.id) + ) { + await unreacter.federateToUser( + reactionToDelete.toVersiaUnreact(), + this.author, + ); + } + } + } + /** * Get the children of this note (replies) * @param userId - The ID of the user requesting the note (used to check visibility of the note) @@ -420,15 +918,15 @@ export class Note extends BaseInterface { // Check if URI is of a local note if (uri.origin === config.http.base_url.origin) { - const uuid = uri.pathname.match(idValidator); + const noteUuid = uri.pathname.match(uuid); - if (!uuid?.[0]) { + if (!noteUuid?.[0]) { throw new Error( `URI ${uri} is of a local note, but it could not be parsed`, ); } - return await Note.fromId(uuid[0]); + return await Note.fromId(noteUuid[0]); } return Note.fromVersia(uri); @@ -456,10 +954,10 @@ export class Note extends BaseInterface { ): Promise { if (versiaNote instanceof URL) { // No bridge support for notes yet - const note = await User.federationRequester.fetchEntity( - versiaNote, - VersiaEntities.Note, - ); + const note = await new FederationRequester( + config.instance.keys.private, + config.http.base_url, + ).fetchEntity(versiaNote, VersiaEntities.Note); return Note.fromVersia(note); } @@ -532,7 +1030,7 @@ export class Note extends BaseInterface { await note.update({ content: versiaNote.content - ? await contentToHtml(versiaNote.content, mentions) + ? await versiaTextToHtml(versiaNote.content, mentions) : undefined, contentSource: versiaNote.content ? versiaNote.content.data["text/plain"]?.content || @@ -624,7 +1122,7 @@ export class Note extends BaseInterface { */ public async toApi( userFetching?: User | null, - ): Promise> { + ): Promise> { const data = this.data; // Convert mentions of local users from @username@host to @username diff --git a/classes/database/notification.ts b/packages/kit/db/notification.ts similarity index 96% rename from classes/database/notification.ts rename to packages/kit/db/notification.ts index bfaecf5f..ecfc4205 100644 --- a/classes/database/notification.ts +++ b/packages/kit/db/notification.ts @@ -1,6 +1,4 @@ import type { Notification as NotificationSchema } from "@versia/client/schemas"; -import { db, Note, User } from "@versia/kit/db"; -import { Notifications } from "@versia/kit/tables"; import { desc, eq, @@ -9,12 +7,16 @@ import { inArray, type SQL, } from "drizzle-orm"; -import type { z } from "zod"; +import type { z } from "zod/v4"; +import { db } from "../tables/db.ts"; +import { Notifications } from "../tables/schema.ts"; +import { BaseInterface } from "./base.ts"; +import { Note } from "./note.ts"; import { transformOutputToUserWithRelations, + User, userRelations, -} from "../functions/user.ts"; -import { BaseInterface } from "./base.ts"; +} from "./user.ts"; export type NotificationType = InferSelectModel & { status: typeof Note.$type | null; @@ -187,6 +189,7 @@ export class Notification extends BaseInterface< created_at: new Date(this.data.createdAt).toISOString(), id: this.data.id, type: this.data.type, + event: undefined, status: this.data.status ? await new Note(this.data.status).toApi(account) : undefined, diff --git a/classes/database/pushsubscription.ts b/packages/kit/db/pushsubscription.ts similarity index 96% rename from classes/database/pushsubscription.ts rename to packages/kit/db/pushsubscription.ts index 67460234..2f7b3654 100644 --- a/classes/database/pushsubscription.ts +++ b/packages/kit/db/pushsubscription.ts @@ -1,6 +1,4 @@ import type { WebPushSubscription as WebPushSubscriptionSchema } from "@versia/client/schemas"; -import { db, type Token, type User } from "@versia/kit/db"; -import { PushSubscriptions, Tokens } from "@versia/kit/tables"; import { desc, eq, @@ -9,8 +7,12 @@ import { inArray, type SQL, } from "drizzle-orm"; -import type { z } from "zod"; +import type { z } from "zod/v4"; +import { db } from "../tables/db.ts"; +import { PushSubscriptions, Tokens } from "../tables/schema.ts"; import { BaseInterface } from "./base.ts"; +import type { Token } from "./token.ts"; +import type { User } from "./user.ts"; type PushSubscriptionType = InferSelectModel; diff --git a/classes/database/reaction.ts b/packages/kit/db/reaction.ts similarity index 95% rename from classes/database/reaction.ts rename to packages/kit/db/reaction.ts index e08d73c6..1f55373e 100644 --- a/classes/database/reaction.ts +++ b/packages/kit/db/reaction.ts @@ -1,5 +1,5 @@ -import { db, Emoji, Instance, type Note, User } from "@versia/kit/db"; -import { type Notes, Reactions, type Users } from "@versia/kit/tables"; +import * as VersiaEntities from "@versia/sdk/entities"; +import { config } from "@versia-server/config"; import { randomUUIDv7 } from "bun"; import { and, @@ -11,9 +11,13 @@ import { isNull, type SQL, } from "drizzle-orm"; -import { config } from "~/config.ts"; -import * as VersiaEntities from "~/packages/sdk/entities/index.ts"; +import { db } from "../tables/db.ts"; +import { type Notes, Reactions, type Users } from "../tables/schema.ts"; import { BaseInterface } from "./base.ts"; +import { Emoji } from "./emoji.ts"; +import { Instance } from "./instance.ts"; +import type { Note } from "./note.ts"; +import { User } from "./user.ts"; type ReactionType = InferSelectModel & { emoji: typeof Emoji.$type | null; diff --git a/classes/database/relationship.ts b/packages/kit/db/relationship.ts similarity index 98% rename from classes/database/relationship.ts rename to packages/kit/db/relationship.ts index 107c1aec..4fd9b7ba 100644 --- a/classes/database/relationship.ts +++ b/packages/kit/db/relationship.ts @@ -1,6 +1,4 @@ import type { Relationship as RelationshipSchema } from "@versia/client/schemas"; -import { db } from "@versia/kit/db"; -import { Relationships, Users } from "@versia/kit/tables"; import { randomUUIDv7 } from "bun"; import { and, @@ -12,7 +10,9 @@ import { type SQL, sql, } from "drizzle-orm"; -import { z } from "zod"; +import { z } from "zod/v4"; +import { db } from "../tables/db.ts"; +import { Relationships, Users } from "../tables/schema.ts"; import { BaseInterface } from "./base.ts"; import type { User } from "./user.ts"; diff --git a/classes/database/role.ts b/packages/kit/db/role.ts similarity index 96% rename from classes/database/role.ts rename to packages/kit/db/role.ts index 356040ee..c813e6ac 100644 --- a/classes/database/role.ts +++ b/packages/kit/db/role.ts @@ -2,8 +2,7 @@ import type { RolePermission, Role as RoleSchema, } from "@versia/client/schemas"; -import { db } from "@versia/kit/db"; -import { Roles, RoleToUsers } from "@versia/kit/tables"; +import { config, ProxiableUrl } from "@versia-server/config"; import { and, desc, @@ -13,9 +12,9 @@ import { inArray, type SQL, } from "drizzle-orm"; -import type { z } from "zod"; -import { config } from "~/config.ts"; -import { ProxiableUrl } from "../media/url.ts"; +import type { z } from "zod/v4"; +import { db } from "../tables/db.ts"; +import { Roles, RoleToUsers } from "../tables/schema.ts"; import { BaseInterface } from "./base.ts"; type RoleType = InferSelectModel; diff --git a/classes/database/timeline.ts b/packages/kit/db/timeline.ts similarity index 97% rename from classes/database/timeline.ts rename to packages/kit/db/timeline.ts index 3d529b1a..6b1e825e 100644 --- a/classes/database/timeline.ts +++ b/packages/kit/db/timeline.ts @@ -1,6 +1,6 @@ -import { Notes, Notifications, Users } from "@versia/kit/tables"; +import { config } from "@versia-server/config"; import { gt, type SQL } from "drizzle-orm"; -import { config } from "~/config.ts"; +import { Notes, Notifications, Users } from "../tables/schema.ts"; import { Note } from "./note.ts"; import { Notification } from "./notification.ts"; import { User } from "./user.ts"; @@ -12,7 +12,7 @@ enum TimelineType { } export class Timeline { - public constructor(private type: TimelineType) {} + public constructor(private readonly type: TimelineType) {} public static getNoteTimeline( sql: SQL | undefined, diff --git a/classes/database/token.ts b/packages/kit/db/token.ts similarity index 95% rename from classes/database/token.ts rename to packages/kit/db/token.ts index a2113811..fdfd9e43 100644 --- a/classes/database/token.ts +++ b/packages/kit/db/token.ts @@ -1,6 +1,4 @@ import type { Token as TokenSchema } from "@versia/client/schemas"; -import { type Application, db, User } from "@versia/kit/db"; -import { Tokens } from "@versia/kit/tables"; import { desc, eq, @@ -9,8 +7,12 @@ import { inArray, type SQL, } from "drizzle-orm"; -import type { z } from "zod"; +import type { z } from "zod/v4"; +import { db } from "../tables/db.ts"; +import { Tokens } from "../tables/schema.ts"; +import type { Application } from "./application.ts"; import { BaseInterface } from "./base.ts"; +import { User } from "./user.ts"; type TokenType = InferSelectModel & { application: typeof Application.$type | null; diff --git a/classes/database/user.ts b/packages/kit/db/user.ts similarity index 74% rename from classes/database/user.ts rename to packages/kit/db/user.ts index 3486b548..30130e32 100644 --- a/classes/database/user.ts +++ b/packages/kit/db/user.ts @@ -1,28 +1,18 @@ -import { getLogger } from "@logtape/logtape"; import type { Account, Mention as MentionSchema, RolePermission, Source, - Status as StatusSchema, } from "@versia/client/schemas"; +import { sign } from "@versia/sdk/crypto"; +import * as VersiaEntities from "@versia/sdk/entities"; +import { FederationRequester } from "@versia/sdk/http"; +import type { ImageContentFormatSchema } from "@versia/sdk/schemas"; +import { config, ProxiableUrl } from "@versia-server/config"; import { - db, - Media, - Notification, - PushSubscription, - Reaction, -} from "@versia/kit/db"; -import { - EmojiToUser, - Likes, - Notes, - NoteToMentions, - Notifications, - Relationships, - Users, - UserToPinnedNotes, -} from "@versia/kit/tables"; + federationDeliveryLogger, + federationResolversLogger, +} from "@versia-server/logging"; import { password as bunPassword, randomUUIDv7 } from "bun"; import chalk from "chalk"; import { @@ -40,30 +30,115 @@ import { sql, } from "drizzle-orm"; import { htmlToText } from "html-to-text"; -import type { z } from "zod"; -import { idValidator } from "@/api"; +import type { z } from "zod/v4"; import { getBestContentType } from "@/content_types"; import { randomString } from "@/math"; -import { sentry } from "@/sentry"; -import { findManyUsers } from "~/classes/functions/user"; -import { searchManager } from "~/classes/search/search-manager"; -import { config } from "~/config.ts"; -import { sign } from "~/packages/sdk/crypto.ts"; -import * as VersiaEntities from "~/packages/sdk/entities/index.ts"; -import { FederationRequester } from "~/packages/sdk/http.ts"; -import type { ImageContentFormatSchema } from "~/packages/sdk/schemas/index.ts"; import type { HttpVerb, KnownEntity } from "~/types/api.ts"; -import { ProxiableUrl } from "../media/url.ts"; -import { DeliveryJobType, deliveryQueue } from "../queues/delivery.ts"; -import { PushJobType, pushQueue } from "../queues/push.ts"; +import { DeliveryJobType, deliveryQueue } from "../queues/delivery/queue.ts"; +import { PushJobType, pushQueue } from "../queues/push/queue.ts"; +import { uuid } from "../regex.ts"; +import { db } from "../tables/db.ts"; +import { + EmojiToUser, + Notes, + NoteToMentions, + Notifications, + Relationships, + Users, + UserToPinnedNotes, +} from "../tables/schema.ts"; import { BaseInterface } from "./base.ts"; import { Emoji } from "./emoji.ts"; import { Instance } from "./instance.ts"; -import { Like } from "./like.ts"; -import { Note } from "./note.ts"; +import { Media } from "./media.ts"; +import type { Note } from "./note.ts"; +import { PushSubscription } from "./pushsubscription.ts"; import { Relationship } from "./relationship.ts"; import { Role } from "./role.ts"; +export const userRelations = { + instance: true, + emojis: { + with: { + emoji: { + with: { + instance: true, + media: true, + }, + }, + }, + }, + avatar: true, + header: true, + roles: { + with: { + role: true, + }, + }, +} as const; + +export const transformOutputToUserWithRelations = ( + user: Omit, "endpoints"> & { + followerCount: unknown; + followingCount: unknown; + statusCount: unknown; + avatar: typeof Media.$type | null; + header: typeof Media.$type | null; + emojis: { + userId: string; + emojiId: string; + emoji?: typeof Emoji.$type; + }[]; + instance: typeof Instance.$type | null; + roles: { + userId: string; + roleId: string; + role?: typeof Role.$type; + }[]; + endpoints: unknown; + }, +): typeof User.$type => { + return { + ...user, + followerCount: Number(user.followerCount), + followingCount: Number(user.followingCount), + statusCount: Number(user.statusCount), + endpoints: + user.endpoints ?? + ({} as Partial<{ + dislikes: string; + featured: string; + likes: string; + followers: string; + following: string; + inbox: string; + outbox: string; + }>), + emojis: user.emojis.map( + (emoji) => + (emoji as unknown as Record) + .emoji as typeof Emoji.$type, + ), + roles: user.roles + .map((role) => role.role) + .filter(Boolean) as (typeof Role.$type)[], + }; +}; + +const findManyUsers = async ( + query: Parameters[0], +): Promise<(typeof User.$type)[]> => { + const output = await db.query.Users.findMany({ + ...query, + with: { + ...userRelations, + ...query?.with, + }, + }); + + return output.map((user) => transformOutputToUserWithRelations(user)); +}; + type UserWithInstance = InferSelectModel & { instance: typeof Instance.$type | null; }; @@ -488,127 +563,6 @@ export class User extends BaseInterface { .filter((x) => x !== null); } - /** - * Reblog a note. - * - * If the note is already reblogged, it will return the existing reblog. Also creates a notification for the author of the note. - * @param note The note to reblog - * @param visibility The visibility of the reblog - * @param uri The URI of the reblog, if it is remote - * @returns The reblog object created or the existing reblog - */ - public async reblog( - note: Note, - visibility: z.infer, - uri?: URL, - ): Promise { - const existingReblog = await Note.fromSql( - and(eq(Notes.authorId, this.id), eq(Notes.reblogId, note.id)), - undefined, - this.id, - ); - - if (existingReblog) { - return existingReblog; - } - - const newReblog = await Note.insert({ - id: randomUUIDv7(), - authorId: this.id, - reblogId: note.id, - visibility, - sensitive: false, - updatedAt: new Date().toISOString(), - applicationId: null, - uri: uri?.href, - }); - - await note.recalculateReblogCount(); - - // Refetch the note *again* to get the proper value of .reblogged - const finalNewReblog = await Note.fromId(newReblog.id, this?.id); - - if (!finalNewReblog) { - throw new Error("Failed to reblog"); - } - - if (note.author.local) { - // Notify the user that their post has been reblogged - await note.author.notify("reblog", this, finalNewReblog); - } - - if (this.local) { - const federatedUsers = await this.federateToFollowers( - finalNewReblog.toVersiaShare(), - ); - - if ( - note.remote && - !federatedUsers.find((u) => u.id === note.author.id) - ) { - await this.federateToUser( - finalNewReblog.toVersiaShare(), - note.author, - ); - } - } - - return finalNewReblog; - } - - /** - * Unreblog a note. - * - * If the note is not reblogged, it will return without doing anything. Also removes any notifications for this reblog. - * @param note The note to unreblog - * @returns - */ - public async unreblog(note: Note): Promise { - const reblogToDelete = await Note.fromSql( - and(eq(Notes.authorId, this.id), eq(Notes.reblogId, note.id)), - undefined, - this.id, - ); - - if (!reblogToDelete) { - return; - } - - await reblogToDelete.delete(); - - await note.recalculateReblogCount(); - - if (note.author.local) { - // Remove any eventual notifications for this reblog - await db - .delete(Notifications) - .where( - and( - eq(Notifications.accountId, this.id), - eq(Notifications.type, "reblog"), - eq(Notifications.notifiedId, note.data.authorId), - eq(Notifications.noteId, note.id), - ), - ); - } - - if (this.local) { - const federatedUsers = await this.federateToFollowers( - reblogToDelete.toVersiaUnshare(), - ); - - if ( - note.remote && - !federatedUsers.find((u) => u.id === note.author.id) - ) { - await this.federateToUser( - reblogToDelete.toVersiaUnshare(), - note.author, - ); - } - } - } - public async recalculateFollowerCount(): Promise { const followerCount = await db.$count( Relationships, @@ -648,188 +602,6 @@ export class User extends BaseInterface { }); } - /** - * Like a note. - * - * If the note is already liked, it will return the existing like. Also creates a notification for the author of the note. - * @param note The note to like - * @param uri The URI of the like, if it is remote - * @returns The like object created or the existing like - */ - public async like(note: Note, uri?: URL): Promise { - // Check if the user has already liked the note - const existingLike = await Like.fromSql( - and(eq(Likes.likerId, this.id), eq(Likes.likedId, note.id)), - ); - - if (existingLike) { - return existingLike; - } - - const newLike = await Like.insert({ - id: randomUUIDv7(), - likerId: this.id, - likedId: note.id, - uri: uri?.href, - }); - - await note.recalculateLikeCount(); - - if (note.author.local) { - // Notify the user that their post has been favourited - await note.author.notify("favourite", this, note); - } - - if (this.local) { - const federatedUsers = await this.federateToFollowers( - newLike.toVersia(), - ); - - if ( - note.remote && - !federatedUsers.find((u) => u.id === note.author.id) - ) { - await this.federateToUser(newLike.toVersia(), note.author); - } - } - - return newLike; - } - - /** - * Unlike a note. - * - * If the note is not liked, it will return without doing anything. Also removes any notifications for this like. - * @param note The note to unlike - * @returns - */ - public async unlike(note: Note): Promise { - const likeToDelete = await Like.fromSql( - and(eq(Likes.likerId, this.id), eq(Likes.likedId, note.id)), - ); - - if (!likeToDelete) { - return; - } - - await likeToDelete.delete(); - - await note.recalculateLikeCount(); - - if (note.author.local) { - // Remove any eventual notifications for this like - await likeToDelete.clearRelatedNotifications(); - } - - if (this.local) { - const federatedUsers = await this.federateToFollowers( - likeToDelete.unlikeToVersia(this), - ); - - if ( - note.remote && - !federatedUsers.find((u) => u.id === note.author.id) - ) { - await this.federateToUser( - likeToDelete.unlikeToVersia(this), - note.author, - ); - } - } - } - - /** - * Add an emoji reaction to a note - * @param note - The note to react to - * @param emoji - The emoji to react with (Emoji object for custom emojis, or Unicode emoji) - * @returns The created reaction - */ - public async react(note: Note, emoji: Emoji | string): Promise { - const existingReaction = await Reaction.fromEmoji(emoji, this, note); - - if (existingReaction) { - return; // Reaction already exists, don't create duplicate - } - - // Create the reaction - const reaction = await Reaction.insert({ - id: randomUUIDv7(), - authorId: this.id, - noteId: note.id, - emojiText: emoji instanceof Emoji ? null : emoji, - emojiId: emoji instanceof Emoji ? emoji.id : null, - }); - - const finalNote = await Note.fromId(note.id, this.id); - - if (!finalNote) { - throw new Error("Failed to fetch note after reaction"); - } - - if (note.author.local) { - // Notify the user that their post has been reacted to - await note.author.notify("reaction", this, finalNote); - } - - if (this.local) { - const federatedUsers = await this.federateToFollowers( - reaction.toVersia(), - ); - - if ( - note.remote && - !federatedUsers.find((u) => u.id === note.author.id) - ) { - await this.federateToUser(reaction.toVersia(), note.author); - } - } - } - - /** - * Remove an emoji reaction from a note - * @param note - The note to remove reaction from - * @param emoji - The emoji to remove reaction for (Emoji object for custom emojis, or Unicode emoji) - */ - public async unreact(note: Note, emoji: Emoji | string): Promise { - const reactionToDelete = await Reaction.fromEmoji(emoji, this, note); - - if (!reactionToDelete) { - return; // Reaction doesn't exist, nothing to delete - } - - await reactionToDelete.delete(); - - if (note.author.local) { - // Remove any eventual notifications for this reaction - await db - .delete(Notifications) - .where( - and( - eq(Notifications.accountId, this.id), - eq(Notifications.type, "reaction"), - eq(Notifications.notifiedId, note.data.authorId), - eq(Notifications.noteId, note.id), - ), - ); - } - - if (this.local) { - const federatedUsers = await this.federateToFollowers( - reactionToDelete.toVersiaUnreact(), - ); - - if ( - note.remote && - !federatedUsers.find((u) => u.id === note.author.id) - ) { - await this.federateToUser( - reactionToDelete.toVersiaUnreact(), - note.author, - ); - } - } - } - public async notify( type: | "mention" @@ -841,13 +613,18 @@ export class User extends BaseInterface { relatedUser: User, note?: Note, ): Promise { - const notification = await Notification.insert({ - id: randomUUIDv7(), - accountId: relatedUser.id, - type, - notifiedId: this.id, - noteId: note?.id ?? null, - }); + const notification = ( + await db + .insert(Notifications) + .values({ + id: randomUUIDv7(), + accountId: relatedUser.id, + type, + notifiedId: this.id, + noteId: note?.id ?? null, + }) + .returning() + )[0]; // Also do push notifications if (config.notifications.push) { @@ -963,10 +740,10 @@ export class User extends BaseInterface { ); } - const user = await User.federationRequester.fetchEntity( - uri, - VersiaEntities.User, - ); + const user = await new FederationRequester( + config.instance.keys.private, + config.http.base_url, + ).fetchEntity(uri, VersiaEntities.User); return User.fromVersia(user); } @@ -1081,8 +858,7 @@ export class User extends BaseInterface { } public static async resolve(uri: URL): Promise { - getLogger(["federation", "resolvers"]) - .debug`Resolving user ${chalk.gray(uri)}`; + federationResolversLogger.debug`Resolving user ${chalk.gray(uri)}`; // Check if user not already in database const foundUser = await User.fromSql(eq(Users.uri, uri.href)); @@ -1092,19 +868,18 @@ export class User extends BaseInterface { // Check if URI is of a local user if (uri.origin === config.http.base_url.origin) { - const uuid = uri.href.match(idValidator); + const userUuid = uri.href.match(uuid); - if (!uuid?.[0]) { + if (!userUuid?.[0]) { throw new Error( `URI ${uri} is of a local user, but it could not be parsed`, ); } - return await User.fromId(uuid[0]); + return await User.fromId(userUuid[0]); } - getLogger(["federation", "resolvers"]) - .debug`User not found in database, fetching from remote`; + federationResolversLogger.debug`User not found in database, fetching from remote`; return User.fromVersia(uri); } @@ -1185,9 +960,6 @@ export class User extends BaseInterface { } as z.infer, }); - // Add to search index - await searchManager.addUser(user); - return user; } @@ -1251,13 +1023,6 @@ export class User extends BaseInterface { return updated.data; } - public static get federationRequester(): FederationRequester { - return new FederationRequester( - config.instance.keys.private, - config.http.base_url, - ); - } - public get federationRequester(): Promise { return crypto.subtle .importKey( @@ -1335,11 +1100,10 @@ export class User extends BaseInterface { entity, ); } catch (e) { - getLogger(["federation", "delivery"]).error`Federating ${chalk.gray( + federationDeliveryLogger.error`Federating ${chalk.gray( entity.data.type, )} to ${user.uri} ${chalk.bold.red("failed")}`; - getLogger(["federation", "delivery"]).error`${e}`; - sentry?.captureException(e); + federationDeliveryLogger.error`${e}`; return { ok: false }; } diff --git a/packages/plugin-kit/example.ts b/packages/kit/example.ts similarity index 91% rename from packages/plugin-kit/example.ts rename to packages/kit/example.ts index 8ebb14cf..1b1119e6 100644 --- a/packages/plugin-kit/example.ts +++ b/packages/kit/example.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { Hooks } from "./hooks.ts"; import { Plugin } from "./plugin.ts"; diff --git a/packages/plugin-kit/hooks.ts b/packages/kit/hooks.ts similarity index 100% rename from packages/plugin-kit/hooks.ts rename to packages/kit/hooks.ts diff --git a/classes/inbox/processor.ts b/packages/kit/inbox-processor.ts similarity index 90% rename from classes/inbox/processor.ts rename to packages/kit/inbox-processor.ts index b4497bc3..f7c4a631 100644 --- a/classes/inbox/processor.ts +++ b/packages/kit/inbox-processor.ts @@ -1,26 +1,22 @@ -import { getLogger, type Logger } from "@logtape/logtape"; -import { - type Instance, - Like, - Note, - Reaction, - Relationship, - User, -} from "@versia/kit/db"; -import { Likes, Notes } from "@versia/kit/tables"; +import { EntitySorter, type JSONObject } from "@versia/sdk"; +import { verify } from "@versia/sdk/crypto"; +import * as VersiaEntities from "@versia/sdk/entities"; +import { config } from "@versia-server/config"; +import { federationInboxLogger } from "@versia-server/logging"; import type { SocketAddress } from "bun"; import { Glob } from "bun"; import chalk from "chalk"; import { and, eq } from "drizzle-orm"; import { matches } from "ip-matching"; import { isValidationError } from "zod-validation-error"; -import { sentry } from "@/sentry"; -import { config } from "~/config.ts"; -import { verify } from "~/packages/sdk/crypto.ts"; -import * as VersiaEntities from "~/packages/sdk/entities/index.ts"; -import { EntitySorter } from "~/packages/sdk/inbox-processor.ts"; -import type { JSONObject } from "~/packages/sdk/types.ts"; -import { ApiError } from "../errors/api-error.ts"; +import { ApiError } from "./api-error.ts"; +import type { Instance } from "./db/instance.ts"; +import { Like } from "./db/like.ts"; +import { Note } from "./db/note.ts"; +import { Reaction } from "./db/reaction.ts"; +import { Relationship } from "./db/relationship.ts"; +import { User } from "./db/user.ts"; +import { Likes, Notes } from "./tables/schema.ts"; /** * Checks if the hostname is defederated using glob matching. @@ -59,15 +55,14 @@ export class InboxProcessor { * @param requestIp Request IP address. Grabs it from the Hono context if not provided. */ public constructor( - private request: Request, - private body: JSONObject, - private sender: { + private readonly request: Request, + private readonly body: JSONObject, + private readonly sender: { instance: Instance; key: CryptoKey; } | null, - private authorizationHeader?: string, - private logger: Logger = getLogger(["federation", "inbox"]), - private requestIp: SocketAddress | null = null, + private readonly authorizationHeader?: string, + private readonly requestIp: SocketAddress | null = null, ) {} /** @@ -157,7 +152,7 @@ export class InboxProcessor { */ public async process(): Promise { !this.sender && - this.logger.debug`Processing request from potential bridge`; + federationInboxLogger.debug`Processing request from potential bridge`; if (this.sender && isDefederated(this.sender.instance.data.baseUrl)) { // Return 201 to avoid @@ -166,15 +161,15 @@ export class InboxProcessor { return; } - this.logger.debug`Instance ${chalk.gray( + federationInboxLogger.debug`Instance ${chalk.gray( this.sender?.instance.data.baseUrl, )} is not defederated`; const shouldCheckSignature = this.shouldCheckSignature(); shouldCheckSignature - ? this.logger.debug`Checking signature` - : this.logger.debug`Skipping signature check`; + ? federationInboxLogger.debug`Checking signature` + : federationInboxLogger.debug`Skipping signature check`; if (shouldCheckSignature) { const isValid = await this.isSignatureValid(); @@ -184,7 +179,7 @@ export class InboxProcessor { } } - shouldCheckSignature && this.logger.debug`Signature is valid`; + shouldCheckSignature && federationInboxLogger.debug`Signature is valid`; try { await new EntitySorter(this.body) @@ -213,7 +208,7 @@ export class InboxProcessor { throw new ApiError(400, "Unknown entity type"); }); } catch (e) { - return this.handleError(e as Error); + return InboxProcessor.handleError(e as Error); } } @@ -250,7 +245,8 @@ export class InboxProcessor { // If note has a blocked word if ( Object.values(note.content?.data ?? {}) - .flatMap((c) => c.content) + .flatMap((c) => c?.content) + .filter((content) => content !== undefined) .some((content) => config.validation.filters.note_content.some((filter) => filter.test(content), @@ -286,7 +282,8 @@ export class InboxProcessor { if ( Object.values(user.bio?.data ?? {}) - .flatMap((c) => c.content) + .flatMap((c) => c?.content) + .filter((content) => content !== undefined) .some((content) => config.validation.filters.bio.some((filter) => filter.test(content), @@ -442,7 +439,7 @@ export class InboxProcessor { throw new ApiError(404, "Shared Note not found"); } - await author.reblog(sharedNote, "public", new URL(share.data.uri)); + await sharedNote.reblog(author, "public", new URL(share.data.uri)); } /** @@ -518,7 +515,7 @@ export class InboxProcessor { throw new ApiError(404, "Like author not found"); } - await likeAuthor.unlike(liked); + await liked.unlike(likeAuthor); return; } @@ -550,7 +547,7 @@ export class InboxProcessor { ); } - await author.unreblog(reblogged); + await reblogged.unreblog(author); return; } default: { @@ -582,7 +579,7 @@ export class InboxProcessor { throw new ApiError(404, "Liked Note not found"); } - await author.like(likedNote, new URL(like.data.uri)); + await likedNote.like(author, new URL(like.data.uri)); } /** @@ -592,13 +589,12 @@ export class InboxProcessor { * @returns {void} * @throws {ApiError} - The error response. */ - private handleError(e: Error): void { + private static handleError(e: Error): void { if (isValidationError(e)) { throw new ApiError(400, "Failed to process request", e.message); } - this.logger.error`${e}`; - sentry?.captureException(e); + federationInboxLogger.error`${e}`; throw new ApiError(500, "Failed to process request", e.message); } diff --git a/packages/kit/index.ts b/packages/kit/index.ts new file mode 100644 index 00000000..a25e611b --- /dev/null +++ b/packages/kit/index.ts @@ -0,0 +1,4 @@ +export { ApiError } from "./api-error.ts"; +export { Hooks } from "./hooks.ts"; +export { Plugin } from "./plugin.ts"; +export { type Manifest, manifestSchema } from "./schema.ts"; diff --git a/packages/plugin-kit/json-schema.ts b/packages/kit/json-schema.ts similarity index 50% rename from packages/plugin-kit/json-schema.ts rename to packages/kit/json-schema.ts index 333f9dcb..20d791c6 100644 --- a/packages/plugin-kit/json-schema.ts +++ b/packages/kit/json-schema.ts @@ -1,6 +1,6 @@ -import { zodToJsonSchema } from "zod-to-json-schema"; +import * as z from "zod/v4"; import { manifestSchema } from "./schema.ts"; -const jsonSchema = zodToJsonSchema(manifestSchema); +const jsonSchema = z.toJSONSchema(manifestSchema); console.write(`${JSON.stringify(jsonSchema, null, 4)}\n`); diff --git a/packages/plugin-kit/manifest.schema.json b/packages/kit/manifest.schema.json similarity index 100% rename from packages/plugin-kit/manifest.schema.json rename to packages/kit/manifest.schema.json diff --git a/packages/kit/markdown.ts b/packages/kit/markdown.ts new file mode 100644 index 00000000..7ad88a17 --- /dev/null +++ b/packages/kit/markdown.ts @@ -0,0 +1,35 @@ +import markdownItTaskLists from "@hackmd/markdown-it-task-lists"; +import MarkdownIt from "markdown-it"; +import markdownItContainer from "markdown-it-container"; +import markdownItTocDoneRight from "markdown-it-toc-done-right"; + +const createMarkdownIt = (): MarkdownIt => { + const renderer = MarkdownIt({ + html: true, + linkify: true, + }); + + renderer.use(markdownItTocDoneRight, { + containerClass: "toc", + level: [1, 2, 3, 4], + listType: "ul", + listClass: "toc-list", + itemClass: "toc-item", + linkClass: "toc-link", + }); + + renderer.use(markdownItTaskLists); + + renderer.use(markdownItContainer); + + return renderer; +}; + +/** + * Converts markdown text to HTML using MarkdownIt. + * @param content + * @returns + */ +export const markdownToHtml = async (content: string): Promise => { + return (await createMarkdownIt()).render(content); +}; diff --git a/packages/kit/package.json b/packages/kit/package.json new file mode 100644 index 00000000..0e15c802 --- /dev/null +++ b/packages/kit/package.json @@ -0,0 +1,134 @@ +{ + "name": "@versia-server/kit", + "module": "index.ts", + "type": "module", + "version": "0.0.0", + "description": "Framework for building Versia Server plugins", + "author": { + "email": "contact@cpluspatch.com", + "name": "CPlusPatch", + "url": "https://cpluspatch.com" + }, + "scripts": { + "build": "bun run build.ts" + }, + "bugs": { + "url": "https://github.com/versia-pub/server/issues" + }, + "icon": "https://github.com/versia-pub/server", + "license": "AGPL-3.0-or-later", + "keywords": [ + "federated", + "activitypub", + "bun" + ], + "maintainers": [ + { + "email": "contact@cpluspatch.com", + "name": "CPlusPatch", + "url": "https://cpluspatch.com" + } + ], + "repository": { + "type": "git", + "url": "git+https://github.com/versia-pub/server.git" + }, + "private": true, + "dependencies": { + "drizzle-orm": "catalog:", + "hono": "catalog:", + "mitt": "catalog:", + "zod": "catalog:", + "zod-validation-error": "catalog:", + "chalk": "catalog:", + "@versia/client": "workspace:*", + "@versia-server/config": "workspace:*", + "@versia-server/logging": "workspace:*", + "@versia/sdk": "workspace:*", + "html-to-text": "catalog:", + "sharp": "catalog:", + "magic-regexp": "catalog:", + "altcha-lib": "catalog:", + "hono-openapi": "catalog:", + "qs": "catalog:", + "@hono/standard-validator": "catalog:", + "ioredis": "catalog:", + "linkify-html": "catalog:", + "markdown-it": "catalog:", + "markdown-it-toc-done-right": "catalog:", + "markdown-it-container": "catalog:", + "@hackmd/markdown-it-task-lists": "catalog:", + "bullmq": "catalog:", + "web-push": "catalog:", + "ip-matching": "catalog:", + "sonic-channel": "catalog:" + }, + "files": [ + "tables/migrations" + ], + "exports": { + ".": { + "import": "./index.ts" + }, + "./db": { + "import": "./db/index.ts" + }, + "./tables": { + "import": "./tables/schema.ts" + }, + "./api": { + "import": "./api.ts" + }, + "./redis": { + "import": "./redis.ts" + }, + "./regex": { + "import": "./regex.ts" + }, + "./queues/delivery": { + "import": "./queues/delivery/queue.ts" + }, + "./queues/delivery/worker": { + "import": "./queues/delivery/worker.ts" + }, + "./queues/fetch": { + "import": "./queues/fetch/queue.ts" + }, + "./queues/fetch/worker": { + "import": "./queues/fetch/worker.ts" + }, + "./queues/inbox": { + "import": "./queues/inbox/queue.ts" + }, + "./queues/inbox/worker": { + "import": "./queues/inbox/worker.ts" + }, + "./queues/media": { + "import": "./queues/media/queue.ts" + }, + "./queues/media/worker": { + "import": "./queues/media/worker.ts" + }, + "./queues/push": { + "import": "./queues/push/queue.ts" + }, + "./queues/push/worker": { + "import": "./queues/push/worker.ts" + }, + "./queues/relationships": { + "import": "./queues/relationships/queue.ts" + }, + "./queues/relationships/worker": { + "import": "./queues/relationships/worker.ts" + }, + "./markdown": { + "import": "./markdown.ts" + }, + "./parsers": { + "import": "./parsers.ts" + }, + "./search": { + "import": "./search-manager.ts" + } + } +} diff --git a/packages/kit/parsers.ts b/packages/kit/parsers.ts new file mode 100644 index 00000000..5d0e66ac --- /dev/null +++ b/packages/kit/parsers.ts @@ -0,0 +1,169 @@ +import type * as VersiaEntities from "@versia/sdk/entities"; +import { FederationRequester } from "@versia/sdk/http"; +import { config } from "@versia-server/config"; +import { and, eq, inArray, isNull, or } from "drizzle-orm"; +import linkifyHtml from "linkify-html"; +import { + anyOf, + charIn, + createRegExp, + digit, + exactly, + global, + letter, +} from "magic-regexp"; +import { sanitizeHtml, sanitizeHtmlInline } from "@/sanitization"; +import { User } from "./db/user.ts"; +import { markdownToHtml } from "./markdown.ts"; +import { mention } from "./regex.ts"; +import { db } from "./tables/db.ts"; +import { Instances, Users } from "./tables/schema.ts"; + +/** + * Get people mentioned in the content (match @username or @username@domain.com mentions) + * @param text The text to parse mentions from. + * @returns An array of users mentioned in the text. + */ +export const parseMentionsFromText = async (text: string): Promise => { + const mentionedPeople = [...text.matchAll(mention)]; + if (mentionedPeople.length === 0) { + return []; + } + + const baseUrlHost = config.http.base_url.host; + const isLocal = (host?: string): boolean => host === baseUrlHost || !host; + + // Find local and matching users + const foundUsers = await db + .select({ + id: Users.id, + username: Users.username, + baseUrl: Instances.baseUrl, + }) + .from(Users) + .leftJoin(Instances, eq(Users.instanceId, Instances.id)) + .where( + or( + ...mentionedPeople.map((person) => + and( + eq(Users.username, person[1] ?? ""), + isLocal(person[2]) + ? isNull(Users.instanceId) + : eq(Instances.baseUrl, person[2] ?? ""), + ), + ), + ), + ); + + // Separate found and unresolved users + const finalList = await User.manyFromSql( + inArray( + Users.id, + foundUsers.map((u) => u.id), + ), + ); + + // Every remote user that isn't in database + const notFoundRemoteUsers = mentionedPeople.filter( + (p) => + !( + foundUsers.some( + (user) => user.username === p[1] && user.baseUrl === p[2], + ) || isLocal(p[2]) + ), + ); + + // Resolve remote mentions not in database + for (const person of notFoundRemoteUsers) { + const url = await FederationRequester.resolveWebFinger( + person[1] ?? "", + person[2] ?? "", + ); + + if (url) { + const user = await User.resolve(url); + + if (user) { + finalList.push(user); + } + } + } + + return finalList; +}; + +export const linkifyUserMentions = (text: string, mentions: User[]): string => { + return mentions.reduce((finalText, mention) => { + const { username, instance } = mention.data; + const { uri } = mention; + const baseHost = config.http.base_url.host; + const linkTemplate = (displayText: string): string => + `${displayText}`; + + if (mention.remote) { + return finalText.replaceAll( + `@${username}@${instance?.baseUrl}`, + linkTemplate(`@${username}@${instance?.baseUrl}`), + ); + } + + return finalText.replace( + createRegExp( + exactly( + exactly(`@${username}`) + .notBefore(anyOf(letter, digit, charIn("@"))) + .notAfter(anyOf(letter, digit, charIn("@"))), + ).or(exactly(`@${username}@${baseHost}`)), + [global], + ), + linkTemplate(`@${username}@${baseHost}`), + ); + }, text); +}; + +export const versiaTextToHtml = async ( + content: VersiaEntities.TextContentFormat, + mentions: User[] = [], + inline = false, +): Promise => { + const sanitizer = inline ? sanitizeHtmlInline : sanitizeHtml; + let htmlContent = ""; + + if (content.data["text/html"]) { + htmlContent = await sanitizer(content.data["text/html"].content); + } else if (content.data["text/markdown"]) { + htmlContent = await sanitizer( + await markdownToHtml(content.data["text/markdown"].content), + ); + } else if (content.data["text/plain"]?.content) { + htmlContent = (await sanitizer(content.data["text/plain"].content)) + .split("\n") + .map((line) => `

${line}

`) + .join("\n"); + } + + htmlContent = linkifyUserMentions(htmlContent, mentions); + + return linkifyHtml(htmlContent, { + defaultProtocol: "https", + validate: { email: (): false => false }, + target: "_blank", + rel: "nofollow noopener noreferrer", + }); +}; + +export const parseUserAddress = ( + address: string, +): { + username: string; + domain?: string; +} => { + let output = address; + // Remove leading @ if it exists + if (output.startsWith("@")) { + output = output.slice(1); + } + + const [username, domain] = output.split("@"); + return { username, domain }; +}; diff --git a/packages/plugin-kit/plugin.ts b/packages/kit/plugin.ts similarity index 90% rename from packages/plugin-kit/plugin.ts rename to packages/kit/plugin.ts index bd408022..43eee5be 100644 --- a/packages/plugin-kit/plugin.ts +++ b/packages/kit/plugin.ts @@ -1,6 +1,6 @@ import type { Hono, MiddlewareHandler } from "hono"; import { createMiddleware } from "hono/factory"; -import type { z } from "zod"; +import type { z } from "zod/v4"; import { fromZodError, type ZodError } from "zod-validation-error"; import type { HonoEnv } from "~/types/api"; import type { ServerHooks } from "./hooks.ts"; @@ -12,14 +12,15 @@ export type HonoPluginEnv = HonoEnv & { }; export class Plugin { - private handlers: Partial = {}; + private readonly handlers: Partial = {}; + // biome-ignore lint/nursery/useReadonlyClassProperties: biome is wrong lol private store: z.infer | null = null; - private routes: { + private readonly routes: { path: string; fn: (app: Hono>) => void; }[] = []; - public constructor(private configSchema: ConfigSchema) {} + public constructor(private readonly configSchema: ConfigSchema) {} public get middleware(): MiddlewareHandler> { // Middleware that adds the plugin's configuration to the request object diff --git a/packages/kit/queues/delivery/queue.ts b/packages/kit/queues/delivery/queue.ts new file mode 100644 index 00000000..0709fa70 --- /dev/null +++ b/packages/kit/queues/delivery/queue.ts @@ -0,0 +1,20 @@ +import type { JSONObject } from "@versia/sdk"; +import { Queue } from "bullmq"; +import { connection } from "../../redis.ts"; + +export enum DeliveryJobType { + FederateEntity = "federateEntity", +} + +export type DeliveryJobData = { + entity: JSONObject; + recipientId: string; + senderId: string; +}; + +export const deliveryQueue = new Queue( + "delivery", + { + connection, + }, +); diff --git a/classes/queues/delivery.ts b/packages/kit/queues/delivery/worker.ts similarity index 80% rename from classes/queues/delivery.ts rename to packages/kit/queues/delivery/worker.ts index 448c8da3..b56d475d 100644 --- a/classes/queues/delivery.ts +++ b/packages/kit/queues/delivery/worker.ts @@ -1,27 +1,14 @@ -import { User } from "@versia/kit/db"; -import { Queue, Worker } from "bullmq"; +import * as VersiaEntities from "@versia/sdk/entities"; +import { config } from "@versia-server/config"; +import { Worker } from "bullmq"; import chalk from "chalk"; -import { config } from "~/config.ts"; -import * as VersiaEntities from "~/packages/sdk/entities"; -import type { JSONObject } from "~/packages/sdk/types"; -import { connection } from "~/utils/redis.ts"; - -export enum DeliveryJobType { - FederateEntity = "federateEntity", -} - -export type DeliveryJobData = { - entity: JSONObject; - recipientId: string; - senderId: string; -}; - -export const deliveryQueue = new Queue( - "delivery", - { - connection, - }, -); +import { User } from "../../db/user.ts"; +import { connection } from "../../redis.ts"; +import { + type DeliveryJobData, + DeliveryJobType, + deliveryQueue, +} from "./queue.ts"; export const getDeliveryWorker = (): Worker< DeliveryJobData, diff --git a/packages/kit/queues/fetch/queue.ts b/packages/kit/queues/fetch/queue.ts new file mode 100644 index 00000000..a28c1698 --- /dev/null +++ b/packages/kit/queues/fetch/queue.ts @@ -0,0 +1,17 @@ +import { Queue } from "bullmq"; +import { connection } from "../../redis.ts"; + +export enum FetchJobType { + Instance = "instance", + User = "user", + Note = "user", +} + +export type FetchJobData = { + uri: string; + refetcher?: string; +}; + +export const fetchQueue = new Queue("fetch", { + connection, +}); diff --git a/classes/queues/fetch.ts b/packages/kit/queues/fetch/worker.ts similarity index 77% rename from classes/queues/fetch.ts rename to packages/kit/queues/fetch/worker.ts index f76a910f..90ddfbff 100644 --- a/classes/queues/fetch.ts +++ b/packages/kit/queues/fetch/worker.ts @@ -1,24 +1,10 @@ -import { Instance } from "@versia/kit/db"; -import { Instances } from "@versia/kit/tables"; -import { Queue, Worker } from "bullmq"; +import { config } from "@versia-server/config"; +import { Worker } from "bullmq"; import { eq } from "drizzle-orm"; -import { config } from "~/config.ts"; -import { connection } from "~/utils/redis.ts"; - -export enum FetchJobType { - Instance = "instance", - User = "user", - Note = "user", -} - -export type FetchJobData = { - uri: string; - refetcher?: string; -}; - -export const fetchQueue = new Queue("fetch", { - connection, -}); +import { Instance } from "../../db/instance.ts"; +import { connection } from "../../redis.ts"; +import { Instances } from "../../tables/schema.ts"; +import { type FetchJobData, FetchJobType, fetchQueue } from "./queue.ts"; export const getFetchWorker = (): Worker => new Worker( diff --git a/packages/kit/queues/inbox/queue.ts b/packages/kit/queues/inbox/queue.ts new file mode 100644 index 00000000..741b1ec9 --- /dev/null +++ b/packages/kit/queues/inbox/queue.ts @@ -0,0 +1,31 @@ +import type { JSONObject } from "@versia/sdk"; +import { Queue } from "bullmq"; +import type { SocketAddress } from "bun"; +import { connection } from "../../redis.ts"; + +export enum InboxJobType { + ProcessEntity = "processEntity", +} + +export type InboxJobData = { + data: JSONObject; + headers: { + "versia-signature"?: string; + "versia-signed-at"?: number; + "versia-signed-by"?: string; + authorization?: string; + }; + request: { + url: string; + method: string; + body: string; + }; + ip: SocketAddress | null; +}; + +export const inboxQueue = new Queue( + "inbox", + { + connection, + }, +); diff --git a/classes/queues/inbox.ts b/packages/kit/queues/inbox/worker.ts similarity index 85% rename from classes/queues/inbox.ts rename to packages/kit/queues/inbox/worker.ts index a55f9136..95220d92 100644 --- a/classes/queues/inbox.ts +++ b/packages/kit/queues/inbox/worker.ts @@ -1,39 +1,11 @@ -import { getLogger } from "@logtape/logtape"; -import { Instance, User } from "@versia/kit/db"; -import { Queue, Worker } from "bullmq"; -import type { SocketAddress } from "bun"; -import { config } from "~/config.ts"; -import type { JSONObject } from "~/packages/sdk/types.ts"; -import { connection } from "~/utils/redis.ts"; -import { ApiError } from "../errors/api-error.ts"; -import { InboxProcessor } from "../inbox/processor.ts"; - -export enum InboxJobType { - ProcessEntity = "processEntity", -} - -export type InboxJobData = { - data: JSONObject; - headers: { - "versia-signature"?: string; - "versia-signed-at"?: number; - "versia-signed-by"?: string; - authorization?: string; - }; - request: { - url: string; - method: string; - body: string; - }; - ip: SocketAddress | null; -}; - -export const inboxQueue = new Queue( - "inbox", - { - connection, - }, -); +import { config } from "@versia-server/config"; +import { Worker } from "bullmq"; +import { ApiError } from "../../api-error.ts"; +import { Instance } from "../../db/instance.ts"; +import { User } from "../../db/user.ts"; +import { InboxProcessor } from "../../inbox-processor.ts"; +import { connection } from "../../redis.ts"; +import { type InboxJobData, InboxJobType, inboxQueue } from "./queue.ts"; export const getInboxWorker = (): Worker => new Worker( @@ -64,7 +36,6 @@ export const getInboxWorker = (): Worker => data, null, headers.authorization, - getLogger(["federation", "inbox"]), ip, ); @@ -160,7 +131,6 @@ export const getInboxWorker = (): Worker => key, }, undefined, - getLogger(["federation", "inbox"]), ip, ); diff --git a/packages/kit/queues/media/queue.ts b/packages/kit/queues/media/queue.ts new file mode 100644 index 00000000..7501db04 --- /dev/null +++ b/packages/kit/queues/media/queue.ts @@ -0,0 +1,16 @@ +import { Queue } from "bullmq"; +import { connection } from "../../redis.ts"; + +export enum MediaJobType { + ConvertMedia = "convertMedia", + CalculateMetadata = "calculateMetadata", +} + +export type MediaJobData = { + attachmentId: string; + filename: string; +}; + +export const mediaQueue = new Queue("media", { + connection, +}); diff --git a/classes/queues/media.ts b/packages/kit/queues/media/worker.ts similarity index 85% rename from classes/queues/media.ts rename to packages/kit/queues/media/worker.ts index a10a0e38..38a40d84 100644 --- a/classes/queues/media.ts +++ b/packages/kit/queues/media/worker.ts @@ -1,23 +1,10 @@ -import { Media } from "@versia/kit/db"; -import { Queue, Worker } from "bullmq"; -import { config } from "~/config.ts"; -import { connection } from "~/utils/redis.ts"; -import { calculateBlurhash } from "../media/preprocessors/blurhash.ts"; -import { convertImage } from "../media/preprocessors/image-conversion.ts"; - -export enum MediaJobType { - ConvertMedia = "convertMedia", - CalculateMetadata = "calculateMetadata", -} - -export type MediaJobData = { - attachmentId: string; - filename: string; -}; - -export const mediaQueue = new Queue("media", { - connection, -}); +import { config } from "@versia-server/config"; +import { Worker } from "bullmq"; +import { calculateBlurhash } from "../../../../classes/media/preprocessors/blurhash.ts"; +import { convertImage } from "../../../../classes/media/preprocessors/image-conversion.ts"; +import { Media } from "../../db/media.ts"; +import { connection } from "../../redis.ts"; +import { type MediaJobData, MediaJobType, mediaQueue } from "./queue.ts"; export const getMediaWorker = (): Worker => new Worker( diff --git a/packages/kit/queues/push/queue.ts b/packages/kit/queues/push/queue.ts new file mode 100644 index 00000000..0e71d0ca --- /dev/null +++ b/packages/kit/queues/push/queue.ts @@ -0,0 +1,18 @@ +import { Queue } from "bullmq"; +import { connection } from "../../redis.ts"; + +export enum PushJobType { + Notify = "notify", +} + +export type PushJobData = { + psId: string; + type: string; + relatedUserId: string; + noteId?: string; + notificationId: string; +}; + +export const pushQueue = new Queue("push", { + connection, +}); diff --git a/classes/queues/push.ts b/packages/kit/queues/push/worker.ts similarity index 90% rename from classes/queues/push.ts rename to packages/kit/queues/push/worker.ts index 1378cb4e..d3e5b871 100644 --- a/classes/queues/push.ts +++ b/packages/kit/queues/push/worker.ts @@ -1,25 +1,13 @@ -import { Note, PushSubscription, Token, User } from "@versia/kit/db"; -import { Queue, Worker } from "bullmq"; +import { config } from "@versia-server/config"; +import { Worker } from "bullmq"; import { sendNotification } from "web-push"; import { htmlToText } from "@/content_types.ts"; -import { config } from "~/config.ts"; -import { connection } from "~/utils/redis.ts"; - -export enum PushJobType { - Notify = "notify", -} - -export type PushJobData = { - psId: string; - type: string; - relatedUserId: string; - noteId?: string; - notificationId: string; -}; - -export const pushQueue = new Queue("push", { - connection, -}); +import { Note } from "../../db/note.ts"; +import { PushSubscription } from "../../db/pushsubscription.ts"; +import { Token } from "../../db/token.ts"; +import { User } from "../../db/user.ts"; +import { connection } from "../../redis.ts"; +import { type PushJobData, type PushJobType, pushQueue } from "./queue.ts"; export const getPushWorker = (): Worker => new Worker( diff --git a/packages/kit/queues/relationships/queue.ts b/packages/kit/queues/relationships/queue.ts new file mode 100644 index 00000000..91aaf007 --- /dev/null +++ b/packages/kit/queues/relationships/queue.ts @@ -0,0 +1,19 @@ +import { Queue } from "bullmq"; +import { connection } from "../../redis.ts"; + +export enum RelationshipJobType { + Unmute = "unmute", +} + +export type RelationshipJobData = { + ownerId: string; + subjectId: string; +}; + +export const relationshipQueue = new Queue< + RelationshipJobData, + void, + RelationshipJobType +>("relationships", { + connection, +}); diff --git a/classes/queues/relationships.ts b/packages/kit/queues/relationships/worker.ts similarity index 75% rename from classes/queues/relationships.ts rename to packages/kit/queues/relationships/worker.ts index 0a7960b8..04b49abf 100644 --- a/classes/queues/relationships.ts +++ b/packages/kit/queues/relationships/worker.ts @@ -1,24 +1,13 @@ -import { Relationship, User } from "@versia/kit/db"; -import { Queue, Worker } from "bullmq"; -import { config } from "~/config.ts"; -import { connection } from "~/utils/redis.ts"; - -export enum RelationshipJobType { - Unmute = "unmute", -} - -export type RelationshipJobData = { - ownerId: string; - subjectId: string; -}; - -export const relationshipQueue = new Queue< - RelationshipJobData, - void, - RelationshipJobType ->("relationships", { - connection, -}); +import { config } from "@versia-server/config"; +import { Worker } from "bullmq"; +import { Relationship } from "../../db/relationship.ts"; +import { User } from "../../db/user.ts"; +import { connection } from "../../redis.ts"; +import { + type RelationshipJobData, + RelationshipJobType, + relationshipQueue, +} from "./queue.ts"; export const getRelationshipWorker = (): Worker< RelationshipJobData, diff --git a/utils/redis.ts b/packages/kit/redis.ts similarity index 84% rename from utils/redis.ts rename to packages/kit/redis.ts index 09ab1bac..fdda66d5 100644 --- a/utils/redis.ts +++ b/packages/kit/redis.ts @@ -1,5 +1,5 @@ +import { config } from "@versia-server/config"; import IORedis from "ioredis"; -import { config } from "~/config.ts"; export const connection = new IORedis({ host: config.redis.queue.host, diff --git a/packages/kit/regex.ts b/packages/kit/regex.ts new file mode 100644 index 00000000..11cb505c --- /dev/null +++ b/packages/kit/regex.ts @@ -0,0 +1,49 @@ +import { + anyOf, + caseInsensitive, + charIn, + createRegExp, + digit, + exactly, + global, + letter, + maybe, + oneOrMore, +} from "magic-regexp"; + +export const uuid = createRegExp( + anyOf(digit, charIn("ABCDEF")).times(8), + exactly("-"), + anyOf(digit, charIn("ABCDEF")).times(4), + exactly("-"), + exactly("7"), + anyOf(digit, charIn("ABCDEF")).times(3), + exactly("-"), + anyOf("8", "9", "A", "B").times(1), + anyOf(digit, charIn("ABCDEF")).times(3), + exactly("-"), + anyOf(digit, charIn("ABCDEF")).times(12), + [caseInsensitive], +); + +export const mention = createRegExp( + exactly("@"), + oneOrMore(anyOf(letter.lowercase, digit, charIn("-_"))).groupedAs( + "username", + ), + maybe( + exactly("@"), + oneOrMore(anyOf(letter, digit, charIn("_-.:"))).groupedAs("domain"), + ), + [global], +); + +export const webfingerMention = createRegExp( + exactly("acct:"), + oneOrMore(anyOf(letter, digit, charIn("-_"))).groupedAs("username"), + maybe( + exactly("@"), + oneOrMore(anyOf(letter, digit, charIn("_-.:"))).groupedAs("domain"), + ), + [], +); diff --git a/packages/plugin-kit/schema.ts b/packages/kit/schema.ts similarity index 94% rename from packages/plugin-kit/schema.ts rename to packages/kit/schema.ts index fb8eac32..473211a8 100644 --- a/packages/plugin-kit/schema.ts +++ b/packages/kit/schema.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; export const manifestSchema = z.object({ // biome-ignore lint/style/useNamingConvention: JSON schema requires this to be $schema @@ -15,8 +15,8 @@ export const manifestSchema = z.object({ .array( z.object({ name: z.string().min(1).max(100), - email: z.string().email().optional(), - url: z.string().url().optional(), + email: z.email().optional(), + url: z.url().optional(), }), ) .optional(), @@ -47,7 +47,7 @@ export const manifestSchema = z.object({ "other", ]) .optional(), - url: z.string().url().optional(), + url: z.url().optional(), }) .optional(), }); diff --git a/packages/kit/search-manager.ts b/packages/kit/search-manager.ts new file mode 100644 index 00000000..d6567ea5 --- /dev/null +++ b/packages/kit/search-manager.ts @@ -0,0 +1,312 @@ +/** + * @file search-manager.ts + * @description Sonic search integration for indexing and searching accounts and statuses + */ + +import { config } from "@versia-server/config"; +import { sonicLogger } from "@versia-server/logging"; +import type { SQL, ValueOrArray } from "drizzle-orm"; +import { + Ingest as SonicChannelIngest, + Search as SonicChannelSearch, +} from "sonic-channel"; +import { Note } from "./db/note.ts"; +import { User } from "./db/user.ts"; +import { db } from "./tables/db.ts"; + +/** + * Enum for Sonic index types + */ +export enum SonicIndexType { + Accounts = "accounts", + Statuses = "statuses", +} + +/** + * Class for managing Sonic search operations + */ +export class SonicSearchManager { + private readonly searchChannel: SonicChannelSearch; + private readonly ingestChannel: SonicChannelIngest; + // biome-ignore lint/nursery/useReadonlyClassProperties: biome is wrong lol + private connected = false; + + /** + * @param config Configuration for Sonic + */ + public constructor() { + if (!config.search.sonic) { + throw new Error("Sonic configuration is missing"); + } + + this.searchChannel = new SonicChannelSearch({ + host: config.search.sonic.host, + port: config.search.sonic.port, + auth: config.search.sonic.password, + }); + + this.ingestChannel = new SonicChannelIngest({ + host: config.search.sonic.host, + port: config.search.sonic.port, + auth: config.search.sonic.password, + }); + } + + /** + * Connect to Sonic + */ + public async connect(silent = false): Promise { + if (!config.search.enabled) { + !silent && sonicLogger.info`Sonic search is disabled`; + return; + } + + if (this.connected) { + return; + } + + !silent && sonicLogger.info`Connecting to Sonic...`; + + // Connect to Sonic + await new Promise((resolve, reject) => { + this.searchChannel.connect({ + connected: (): void => { + !silent && + sonicLogger.info`Connected to Sonic Search Channel`; + resolve(true); + }, + disconnected: (): void => + sonicLogger.error`Disconnected from Sonic Search Channel. You might be using an incorrect password.`, + timeout: (): void => + sonicLogger.error`Sonic Search Channel connection timed out`, + retrying: (): void => + sonicLogger.warn`Retrying connection to Sonic Search Channel`, + error: (error): void => { + sonicLogger.error`Failed to connect to Sonic Search Channel: ${error}`; + reject(error); + }, + }); + }); + + await new Promise((resolve, reject) => { + this.ingestChannel.connect({ + connected: (): void => { + !silent && + sonicLogger.info`Connected to Sonic Ingest Channel`; + resolve(true); + }, + disconnected: (): void => + sonicLogger.error`Disconnected from Sonic Ingest Channel`, + timeout: (): void => + sonicLogger.error`Sonic Ingest Channel connection timed out`, + retrying: (): void => + sonicLogger.warn`Retrying connection to Sonic Ingest Channel`, + error: (error): void => { + sonicLogger.error`Failed to connect to Sonic Ingest Channel: ${error}`; + reject(error); + }, + }); + }); + + try { + await Promise.all([ + this.searchChannel.ping(), + this.ingestChannel.ping(), + ]); + this.connected = true; + !silent && sonicLogger.info`Connected to Sonic`; + } catch (error) { + sonicLogger.fatal`Error while connecting to Sonic: ${error}`; + throw error; + } + } + + /** + * Add a user to Sonic + * @param user User to add + */ + public async addUser(user: User): Promise { + if (!config.search.enabled) { + return; + } + + try { + await this.ingestChannel.push( + SonicIndexType.Accounts, + "users", + user.id, + `${user.data.username} ${user.data.displayName} ${user.data.note}`, + ); + } catch (error) { + sonicLogger.error`Failed to add user to Sonic: ${error}`; + } + } + + /** + * Get a batch of accounts from the database + * @param n Batch number + * @param batchSize Size of the batch + */ + private static getNthDatabaseAccountBatch( + n: number, + batchSize = 1000, + ): Promise[]> { + return db.query.Users.findMany({ + offset: n * batchSize, + limit: batchSize, + columns: { + id: true, + username: true, + displayName: true, + note: true, + createdAt: true, + }, + orderBy: (user, { asc }): ValueOrArray => asc(user.createdAt), + }); + } + + /** + * Get a batch of statuses from the database + * @param n Batch number + * @param batchSize Size of the batch + */ + private static getNthDatabaseStatusBatch( + n: number, + batchSize = 1000, + ): Promise[]> { + return db.query.Notes.findMany({ + offset: n * batchSize, + limit: batchSize, + columns: { + id: true, + content: true, + createdAt: true, + }, + orderBy: (status, { asc }): ValueOrArray => + asc(status.createdAt), + }); + } + + /** + * Rebuild search indexes + * @param indexes Indexes to rebuild + * @param batchSize Size of each batch + * @param progressCallback Callback for progress updates + */ + public async rebuildSearchIndexes( + indexes: SonicIndexType[], + batchSize = 100, + progressCallback?: (progress: number) => void, + ): Promise { + for (const index of indexes) { + if (index === SonicIndexType.Accounts) { + await this.rebuildAccountsIndex(batchSize, progressCallback); + } else if (index === SonicIndexType.Statuses) { + await this.rebuildStatusesIndex(batchSize, progressCallback); + } + } + } + + /** + * Rebuild accounts index + * @param batchSize Size of each batch + * @param progressCallback Callback for progress updates + */ + private async rebuildAccountsIndex( + batchSize: number, + progressCallback?: (progress: number) => void, + ): Promise { + const accountCount = await User.getCount(); + const batchCount = Math.ceil(accountCount / batchSize); + + for (let i = 0; i < batchCount; i++) { + const accounts = + await SonicSearchManager.getNthDatabaseAccountBatch( + i, + batchSize, + ); + await Promise.all( + accounts.map((account) => + this.ingestChannel.push( + SonicIndexType.Accounts, + "users", + account.id as string, + `${account.username} ${account.displayName} ${account.note}`, + ), + ), + ); + progressCallback?.((i + 1) / batchCount); + } + } + + /** + * Rebuild statuses index + * @param batchSize Size of each batch + * @param progressCallback Callback for progress updates + */ + private async rebuildStatusesIndex( + batchSize: number, + progressCallback?: (progress: number) => void, + ): Promise { + const statusCount = await Note.getCount(); + const batchCount = Math.ceil(statusCount / batchSize); + + for (let i = 0; i < batchCount; i++) { + const statuses = await SonicSearchManager.getNthDatabaseStatusBatch( + i, + batchSize, + ); + await Promise.all( + statuses.map((status) => + this.ingestChannel.push( + SonicIndexType.Statuses, + "notes", + status.id as string, + status.content as string, + ), + ), + ); + progressCallback?.((i + 1) / batchCount); + } + } + + /** + * Search for accounts + * @param query Search query + * @param limit Maximum number of results + * @param offset Offset for pagination + */ + public searchAccounts( + query: string, + limit = 10, + offset = 0, + ): Promise { + return this.searchChannel.query( + SonicIndexType.Accounts, + "users", + query, + { limit, offset }, + ); + } + + /** + * Search for statuses + * @param query Search query + * @param limit Maximum number of results + * @param offset Offset for pagination + */ + public searchStatuses( + query: string, + limit = 10, + offset = 0, + ): Promise { + return this.searchChannel.query( + SonicIndexType.Statuses, + "notes", + query, + { limit, offset }, + ); + } +} + +export const searchManager = new SonicSearchManager(); diff --git a/drizzle/db.ts b/packages/kit/tables/db.ts similarity index 81% rename from drizzle/db.ts rename to packages/kit/tables/db.ts index d0d4e7fc..90a23c8d 100644 --- a/drizzle/db.ts +++ b/packages/kit/tables/db.ts @@ -1,10 +1,11 @@ -import { getLogger } from "@logtape/logtape"; +import { join } from "node:path"; +import { config } from "@versia-server/config"; +import { databaseLogger } from "@versia-server/logging"; import { SQL } from "bun"; import chalk from "chalk"; import { type BunSQLDatabase, drizzle } from "drizzle-orm/bun-sql"; import { withReplicas } from "drizzle-orm/pg-core"; import { migrate } from "drizzle-orm/postgres-js/migrator"; -import { config } from "~/config.ts"; import * as schema from "./schema.ts"; const primaryDb = new SQL({ @@ -40,8 +41,6 @@ export const db = : drizzle(primaryDb, { schema }); export const setupDatabase = async (info = true): Promise => { - const logger = getLogger("database"); - for (const dbPool of [primaryDb, ...replicas]) { try { await dbPool.connect(); @@ -53,7 +52,7 @@ export const setupDatabase = async (info = true): Promise => { return; } - logger.fatal`Failed to connect to database ${chalk.bold( + databaseLogger.fatal`Failed to connect to database ${chalk.bold( // Index of the database in the array replicas.indexOf(dbPool) === -1 ? "primary" @@ -65,17 +64,17 @@ export const setupDatabase = async (info = true): Promise => { } // Migrate the database - info && logger.info`Migrating database...`; + info && databaseLogger.info`Migrating database...`; try { await migrate(db, { - migrationsFolder: "./drizzle/migrations", + migrationsFolder: join(import.meta.dir, "migrations"), }); } catch (e) { - logger.fatal`Failed to migrate database. Please check your configuration.`; + databaseLogger.fatal`Failed to migrate database. Please check your configuration.`; throw e; } - info && logger.info`Database migrated`; + info && databaseLogger.info`Database migrated`; }; diff --git a/drizzle/migrations/0000_illegal_living_lightning.sql b/packages/kit/tables/migrations/0000_illegal_living_lightning.sql similarity index 100% rename from drizzle/migrations/0000_illegal_living_lightning.sql rename to packages/kit/tables/migrations/0000_illegal_living_lightning.sql diff --git a/drizzle/migrations/0001_salty_night_thrasher.sql b/packages/kit/tables/migrations/0001_salty_night_thrasher.sql similarity index 100% rename from drizzle/migrations/0001_salty_night_thrasher.sql rename to packages/kit/tables/migrations/0001_salty_night_thrasher.sql diff --git a/drizzle/migrations/0002_stiff_ares.sql b/packages/kit/tables/migrations/0002_stiff_ares.sql similarity index 100% rename from drizzle/migrations/0002_stiff_ares.sql rename to packages/kit/tables/migrations/0002_stiff_ares.sql diff --git a/drizzle/migrations/0003_spicy_arachne.sql b/packages/kit/tables/migrations/0003_spicy_arachne.sql similarity index 100% rename from drizzle/migrations/0003_spicy_arachne.sql rename to packages/kit/tables/migrations/0003_spicy_arachne.sql diff --git a/drizzle/migrations/0004_burly_lockjaw.sql b/packages/kit/tables/migrations/0004_burly_lockjaw.sql similarity index 100% rename from drizzle/migrations/0004_burly_lockjaw.sql rename to packages/kit/tables/migrations/0004_burly_lockjaw.sql diff --git a/drizzle/migrations/0005_sleepy_puma.sql b/packages/kit/tables/migrations/0005_sleepy_puma.sql similarity index 100% rename from drizzle/migrations/0005_sleepy_puma.sql rename to packages/kit/tables/migrations/0005_sleepy_puma.sql diff --git a/drizzle/migrations/0006_messy_network.sql b/packages/kit/tables/migrations/0006_messy_network.sql similarity index 100% rename from drizzle/migrations/0006_messy_network.sql rename to packages/kit/tables/migrations/0006_messy_network.sql diff --git a/drizzle/migrations/0007_naive_sleeper.sql b/packages/kit/tables/migrations/0007_naive_sleeper.sql similarity index 100% rename from drizzle/migrations/0007_naive_sleeper.sql rename to packages/kit/tables/migrations/0007_naive_sleeper.sql diff --git a/drizzle/migrations/0008_flawless_brother_voodoo.sql b/packages/kit/tables/migrations/0008_flawless_brother_voodoo.sql similarity index 100% rename from drizzle/migrations/0008_flawless_brother_voodoo.sql rename to packages/kit/tables/migrations/0008_flawless_brother_voodoo.sql diff --git a/drizzle/migrations/0009_easy_slyde.sql b/packages/kit/tables/migrations/0009_easy_slyde.sql similarity index 100% rename from drizzle/migrations/0009_easy_slyde.sql rename to packages/kit/tables/migrations/0009_easy_slyde.sql diff --git a/drizzle/migrations/0010_daffy_frightful_four.sql b/packages/kit/tables/migrations/0010_daffy_frightful_four.sql similarity index 100% rename from drizzle/migrations/0010_daffy_frightful_four.sql rename to packages/kit/tables/migrations/0010_daffy_frightful_four.sql diff --git a/drizzle/migrations/0011_special_the_fury.sql b/packages/kit/tables/migrations/0011_special_the_fury.sql similarity index 100% rename from drizzle/migrations/0011_special_the_fury.sql rename to packages/kit/tables/migrations/0011_special_the_fury.sql diff --git a/drizzle/migrations/0012_certain_thor_girl.sql b/packages/kit/tables/migrations/0012_certain_thor_girl.sql similarity index 100% rename from drizzle/migrations/0012_certain_thor_girl.sql rename to packages/kit/tables/migrations/0012_certain_thor_girl.sql diff --git a/drizzle/migrations/0013_wandering_celestials.sql b/packages/kit/tables/migrations/0013_wandering_celestials.sql similarity index 100% rename from drizzle/migrations/0013_wandering_celestials.sql rename to packages/kit/tables/migrations/0013_wandering_celestials.sql diff --git a/drizzle/migrations/0014_wonderful_sandman.sql b/packages/kit/tables/migrations/0014_wonderful_sandman.sql similarity index 100% rename from drizzle/migrations/0014_wonderful_sandman.sql rename to packages/kit/tables/migrations/0014_wonderful_sandman.sql diff --git a/drizzle/migrations/0015_easy_mojo.sql b/packages/kit/tables/migrations/0015_easy_mojo.sql similarity index 100% rename from drizzle/migrations/0015_easy_mojo.sql rename to packages/kit/tables/migrations/0015_easy_mojo.sql diff --git a/drizzle/migrations/0016_keen_mindworm.sql b/packages/kit/tables/migrations/0016_keen_mindworm.sql similarity index 100% rename from drizzle/migrations/0016_keen_mindworm.sql rename to packages/kit/tables/migrations/0016_keen_mindworm.sql diff --git a/drizzle/migrations/0017_dusty_black_knight.sql b/packages/kit/tables/migrations/0017_dusty_black_knight.sql similarity index 100% rename from drizzle/migrations/0017_dusty_black_knight.sql rename to packages/kit/tables/migrations/0017_dusty_black_knight.sql diff --git a/drizzle/migrations/0018_rapid_hairball.sql b/packages/kit/tables/migrations/0018_rapid_hairball.sql similarity index 100% rename from drizzle/migrations/0018_rapid_hairball.sql rename to packages/kit/tables/migrations/0018_rapid_hairball.sql diff --git a/drizzle/migrations/0019_mushy_lorna_dane.sql b/packages/kit/tables/migrations/0019_mushy_lorna_dane.sql similarity index 100% rename from drizzle/migrations/0019_mushy_lorna_dane.sql rename to packages/kit/tables/migrations/0019_mushy_lorna_dane.sql diff --git a/drizzle/migrations/0020_giant_the_stranger.sql b/packages/kit/tables/migrations/0020_giant_the_stranger.sql similarity index 100% rename from drizzle/migrations/0020_giant_the_stranger.sql rename to packages/kit/tables/migrations/0020_giant_the_stranger.sql diff --git a/drizzle/migrations/0021_wise_stephen_strange.sql b/packages/kit/tables/migrations/0021_wise_stephen_strange.sql similarity index 100% rename from drizzle/migrations/0021_wise_stephen_strange.sql rename to packages/kit/tables/migrations/0021_wise_stephen_strange.sql diff --git a/drizzle/migrations/0022_curly_the_call.sql b/packages/kit/tables/migrations/0022_curly_the_call.sql similarity index 100% rename from drizzle/migrations/0022_curly_the_call.sql rename to packages/kit/tables/migrations/0022_curly_the_call.sql diff --git a/drizzle/migrations/0023_lazy_wolfsbane.sql b/packages/kit/tables/migrations/0023_lazy_wolfsbane.sql similarity index 100% rename from drizzle/migrations/0023_lazy_wolfsbane.sql rename to packages/kit/tables/migrations/0023_lazy_wolfsbane.sql diff --git a/drizzle/migrations/0024_lush_aaron_stack.sql b/packages/kit/tables/migrations/0024_lush_aaron_stack.sql similarity index 100% rename from drizzle/migrations/0024_lush_aaron_stack.sql rename to packages/kit/tables/migrations/0024_lush_aaron_stack.sql diff --git a/drizzle/migrations/0025_violet_susan_delgado.sql b/packages/kit/tables/migrations/0025_violet_susan_delgado.sql similarity index 100% rename from drizzle/migrations/0025_violet_susan_delgado.sql rename to packages/kit/tables/migrations/0025_violet_susan_delgado.sql diff --git a/drizzle/migrations/0026_neat_stranger.sql b/packages/kit/tables/migrations/0026_neat_stranger.sql similarity index 100% rename from drizzle/migrations/0026_neat_stranger.sql rename to packages/kit/tables/migrations/0026_neat_stranger.sql diff --git a/drizzle/migrations/0027_peaceful_whistler.sql b/packages/kit/tables/migrations/0027_peaceful_whistler.sql similarity index 100% rename from drizzle/migrations/0027_peaceful_whistler.sql rename to packages/kit/tables/migrations/0027_peaceful_whistler.sql diff --git a/drizzle/migrations/0028_unique_fat_cobra.sql b/packages/kit/tables/migrations/0028_unique_fat_cobra.sql similarity index 100% rename from drizzle/migrations/0028_unique_fat_cobra.sql rename to packages/kit/tables/migrations/0028_unique_fat_cobra.sql diff --git a/drizzle/migrations/0029_shiny_korvac.sql b/packages/kit/tables/migrations/0029_shiny_korvac.sql similarity index 100% rename from drizzle/migrations/0029_shiny_korvac.sql rename to packages/kit/tables/migrations/0029_shiny_korvac.sql diff --git a/drizzle/migrations/0030_curvy_vulture.sql b/packages/kit/tables/migrations/0030_curvy_vulture.sql similarity index 100% rename from drizzle/migrations/0030_curvy_vulture.sql rename to packages/kit/tables/migrations/0030_curvy_vulture.sql diff --git a/drizzle/migrations/0031_mature_demogoblin.sql b/packages/kit/tables/migrations/0031_mature_demogoblin.sql similarity index 100% rename from drizzle/migrations/0031_mature_demogoblin.sql rename to packages/kit/tables/migrations/0031_mature_demogoblin.sql diff --git a/drizzle/migrations/0032_ambiguous_sue_storm.sql b/packages/kit/tables/migrations/0032_ambiguous_sue_storm.sql similarity index 100% rename from drizzle/migrations/0032_ambiguous_sue_storm.sql rename to packages/kit/tables/migrations/0032_ambiguous_sue_storm.sql diff --git a/drizzle/migrations/0033_panoramic_sister_grimm.sql b/packages/kit/tables/migrations/0033_panoramic_sister_grimm.sql similarity index 100% rename from drizzle/migrations/0033_panoramic_sister_grimm.sql rename to packages/kit/tables/migrations/0033_panoramic_sister_grimm.sql diff --git a/drizzle/migrations/0034_jittery_proemial_gods.sql b/packages/kit/tables/migrations/0034_jittery_proemial_gods.sql similarity index 100% rename from drizzle/migrations/0034_jittery_proemial_gods.sql rename to packages/kit/tables/migrations/0034_jittery_proemial_gods.sql diff --git a/drizzle/migrations/0035_pretty_whiplash.sql b/packages/kit/tables/migrations/0035_pretty_whiplash.sql similarity index 100% rename from drizzle/migrations/0035_pretty_whiplash.sql rename to packages/kit/tables/migrations/0035_pretty_whiplash.sql diff --git a/drizzle/migrations/0036_cuddly_ironclad.sql b/packages/kit/tables/migrations/0036_cuddly_ironclad.sql similarity index 100% rename from drizzle/migrations/0036_cuddly_ironclad.sql rename to packages/kit/tables/migrations/0036_cuddly_ironclad.sql diff --git a/drizzle/migrations/0037_condemned_talisman.sql b/packages/kit/tables/migrations/0037_condemned_talisman.sql similarity index 100% rename from drizzle/migrations/0037_condemned_talisman.sql rename to packages/kit/tables/migrations/0037_condemned_talisman.sql diff --git a/drizzle/migrations/0038_friendly_supernaut.sql b/packages/kit/tables/migrations/0038_friendly_supernaut.sql similarity index 100% rename from drizzle/migrations/0038_friendly_supernaut.sql rename to packages/kit/tables/migrations/0038_friendly_supernaut.sql diff --git a/drizzle/migrations/0039_special_serpent_society.sql b/packages/kit/tables/migrations/0039_special_serpent_society.sql similarity index 100% rename from drizzle/migrations/0039_special_serpent_society.sql rename to packages/kit/tables/migrations/0039_special_serpent_society.sql diff --git a/drizzle/migrations/0040_good_nocturne.sql b/packages/kit/tables/migrations/0040_good_nocturne.sql similarity index 100% rename from drizzle/migrations/0040_good_nocturne.sql rename to packages/kit/tables/migrations/0040_good_nocturne.sql diff --git a/drizzle/migrations/0041_bright_doctor_spectrum.sql b/packages/kit/tables/migrations/0041_bright_doctor_spectrum.sql similarity index 100% rename from drizzle/migrations/0041_bright_doctor_spectrum.sql rename to packages/kit/tables/migrations/0041_bright_doctor_spectrum.sql diff --git a/drizzle/migrations/0042_swift_the_phantom.sql b/packages/kit/tables/migrations/0042_swift_the_phantom.sql similarity index 100% rename from drizzle/migrations/0042_swift_the_phantom.sql rename to packages/kit/tables/migrations/0042_swift_the_phantom.sql diff --git a/drizzle/migrations/0043_mute_jigsaw.sql b/packages/kit/tables/migrations/0043_mute_jigsaw.sql similarity index 100% rename from drizzle/migrations/0043_mute_jigsaw.sql rename to packages/kit/tables/migrations/0043_mute_jigsaw.sql diff --git a/drizzle/migrations/0044_quiet_jasper_sitwell.sql b/packages/kit/tables/migrations/0044_quiet_jasper_sitwell.sql similarity index 100% rename from drizzle/migrations/0044_quiet_jasper_sitwell.sql rename to packages/kit/tables/migrations/0044_quiet_jasper_sitwell.sql diff --git a/drizzle/migrations/0045_polite_mikhail_rasputin.sql b/packages/kit/tables/migrations/0045_polite_mikhail_rasputin.sql similarity index 100% rename from drizzle/migrations/0045_polite_mikhail_rasputin.sql rename to packages/kit/tables/migrations/0045_polite_mikhail_rasputin.sql diff --git a/drizzle/migrations/0046_wooden_electro.sql b/packages/kit/tables/migrations/0046_wooden_electro.sql similarity index 100% rename from drizzle/migrations/0046_wooden_electro.sql rename to packages/kit/tables/migrations/0046_wooden_electro.sql diff --git a/drizzle/migrations/0047_black_vermin.sql b/packages/kit/tables/migrations/0047_black_vermin.sql similarity index 100% rename from drizzle/migrations/0047_black_vermin.sql rename to packages/kit/tables/migrations/0047_black_vermin.sql diff --git a/drizzle/migrations/0048_chilly_vector.sql b/packages/kit/tables/migrations/0048_chilly_vector.sql similarity index 100% rename from drizzle/migrations/0048_chilly_vector.sql rename to packages/kit/tables/migrations/0048_chilly_vector.sql diff --git a/drizzle/migrations/0049_graceful_iron_man.sql b/packages/kit/tables/migrations/0049_graceful_iron_man.sql similarity index 100% rename from drizzle/migrations/0049_graceful_iron_man.sql rename to packages/kit/tables/migrations/0049_graceful_iron_man.sql diff --git a/drizzle/migrations/0050_thick_lester.sql b/packages/kit/tables/migrations/0050_thick_lester.sql similarity index 100% rename from drizzle/migrations/0050_thick_lester.sql rename to packages/kit/tables/migrations/0050_thick_lester.sql diff --git a/drizzle/migrations/meta/0000_snapshot.json b/packages/kit/tables/migrations/meta/0000_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0000_snapshot.json rename to packages/kit/tables/migrations/meta/0000_snapshot.json diff --git a/drizzle/migrations/meta/0001_snapshot.json b/packages/kit/tables/migrations/meta/0001_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0001_snapshot.json rename to packages/kit/tables/migrations/meta/0001_snapshot.json diff --git a/drizzle/migrations/meta/0002_snapshot.json b/packages/kit/tables/migrations/meta/0002_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0002_snapshot.json rename to packages/kit/tables/migrations/meta/0002_snapshot.json diff --git a/drizzle/migrations/meta/0003_snapshot.json b/packages/kit/tables/migrations/meta/0003_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0003_snapshot.json rename to packages/kit/tables/migrations/meta/0003_snapshot.json diff --git a/drizzle/migrations/meta/0004_snapshot.json b/packages/kit/tables/migrations/meta/0004_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0004_snapshot.json rename to packages/kit/tables/migrations/meta/0004_snapshot.json diff --git a/drizzle/migrations/meta/0005_snapshot.json b/packages/kit/tables/migrations/meta/0005_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0005_snapshot.json rename to packages/kit/tables/migrations/meta/0005_snapshot.json diff --git a/drizzle/migrations/meta/0006_snapshot.json b/packages/kit/tables/migrations/meta/0006_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0006_snapshot.json rename to packages/kit/tables/migrations/meta/0006_snapshot.json diff --git a/drizzle/migrations/meta/0007_snapshot.json b/packages/kit/tables/migrations/meta/0007_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0007_snapshot.json rename to packages/kit/tables/migrations/meta/0007_snapshot.json diff --git a/drizzle/migrations/meta/0008_snapshot.json b/packages/kit/tables/migrations/meta/0008_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0008_snapshot.json rename to packages/kit/tables/migrations/meta/0008_snapshot.json diff --git a/drizzle/migrations/meta/0009_snapshot.json b/packages/kit/tables/migrations/meta/0009_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0009_snapshot.json rename to packages/kit/tables/migrations/meta/0009_snapshot.json diff --git a/drizzle/migrations/meta/0010_snapshot.json b/packages/kit/tables/migrations/meta/0010_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0010_snapshot.json rename to packages/kit/tables/migrations/meta/0010_snapshot.json diff --git a/drizzle/migrations/meta/0011_snapshot.json b/packages/kit/tables/migrations/meta/0011_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0011_snapshot.json rename to packages/kit/tables/migrations/meta/0011_snapshot.json diff --git a/drizzle/migrations/meta/0012_snapshot.json b/packages/kit/tables/migrations/meta/0012_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0012_snapshot.json rename to packages/kit/tables/migrations/meta/0012_snapshot.json diff --git a/drizzle/migrations/meta/0013_snapshot.json b/packages/kit/tables/migrations/meta/0013_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0013_snapshot.json rename to packages/kit/tables/migrations/meta/0013_snapshot.json diff --git a/drizzle/migrations/meta/0014_snapshot.json b/packages/kit/tables/migrations/meta/0014_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0014_snapshot.json rename to packages/kit/tables/migrations/meta/0014_snapshot.json diff --git a/drizzle/migrations/meta/0015_snapshot.json b/packages/kit/tables/migrations/meta/0015_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0015_snapshot.json rename to packages/kit/tables/migrations/meta/0015_snapshot.json diff --git a/drizzle/migrations/meta/0016_snapshot.json b/packages/kit/tables/migrations/meta/0016_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0016_snapshot.json rename to packages/kit/tables/migrations/meta/0016_snapshot.json diff --git a/drizzle/migrations/meta/0017_snapshot.json b/packages/kit/tables/migrations/meta/0017_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0017_snapshot.json rename to packages/kit/tables/migrations/meta/0017_snapshot.json diff --git a/drizzle/migrations/meta/0018_snapshot.json b/packages/kit/tables/migrations/meta/0018_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0018_snapshot.json rename to packages/kit/tables/migrations/meta/0018_snapshot.json diff --git a/drizzle/migrations/meta/0019_snapshot.json b/packages/kit/tables/migrations/meta/0019_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0019_snapshot.json rename to packages/kit/tables/migrations/meta/0019_snapshot.json diff --git a/drizzle/migrations/meta/0020_snapshot.json b/packages/kit/tables/migrations/meta/0020_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0020_snapshot.json rename to packages/kit/tables/migrations/meta/0020_snapshot.json diff --git a/drizzle/migrations/meta/0021_snapshot.json b/packages/kit/tables/migrations/meta/0021_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0021_snapshot.json rename to packages/kit/tables/migrations/meta/0021_snapshot.json diff --git a/drizzle/migrations/meta/0022_snapshot.json b/packages/kit/tables/migrations/meta/0022_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0022_snapshot.json rename to packages/kit/tables/migrations/meta/0022_snapshot.json diff --git a/drizzle/migrations/meta/0023_snapshot.json b/packages/kit/tables/migrations/meta/0023_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0023_snapshot.json rename to packages/kit/tables/migrations/meta/0023_snapshot.json diff --git a/drizzle/migrations/meta/0024_snapshot.json b/packages/kit/tables/migrations/meta/0024_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0024_snapshot.json rename to packages/kit/tables/migrations/meta/0024_snapshot.json diff --git a/drizzle/migrations/meta/0025_snapshot.json b/packages/kit/tables/migrations/meta/0025_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0025_snapshot.json rename to packages/kit/tables/migrations/meta/0025_snapshot.json diff --git a/drizzle/migrations/meta/0026_snapshot.json b/packages/kit/tables/migrations/meta/0026_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0026_snapshot.json rename to packages/kit/tables/migrations/meta/0026_snapshot.json diff --git a/drizzle/migrations/meta/0027_snapshot.json b/packages/kit/tables/migrations/meta/0027_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0027_snapshot.json rename to packages/kit/tables/migrations/meta/0027_snapshot.json diff --git a/drizzle/migrations/meta/0028_snapshot.json b/packages/kit/tables/migrations/meta/0028_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0028_snapshot.json rename to packages/kit/tables/migrations/meta/0028_snapshot.json diff --git a/drizzle/migrations/meta/0029_snapshot.json b/packages/kit/tables/migrations/meta/0029_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0029_snapshot.json rename to packages/kit/tables/migrations/meta/0029_snapshot.json diff --git a/drizzle/migrations/meta/0030_snapshot.json b/packages/kit/tables/migrations/meta/0030_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0030_snapshot.json rename to packages/kit/tables/migrations/meta/0030_snapshot.json diff --git a/drizzle/migrations/meta/0031_snapshot.json b/packages/kit/tables/migrations/meta/0031_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0031_snapshot.json rename to packages/kit/tables/migrations/meta/0031_snapshot.json diff --git a/drizzle/migrations/meta/0032_snapshot.json b/packages/kit/tables/migrations/meta/0032_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0032_snapshot.json rename to packages/kit/tables/migrations/meta/0032_snapshot.json diff --git a/drizzle/migrations/meta/0033_snapshot.json b/packages/kit/tables/migrations/meta/0033_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0033_snapshot.json rename to packages/kit/tables/migrations/meta/0033_snapshot.json diff --git a/drizzle/migrations/meta/0034_snapshot.json b/packages/kit/tables/migrations/meta/0034_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0034_snapshot.json rename to packages/kit/tables/migrations/meta/0034_snapshot.json diff --git a/drizzle/migrations/meta/0035_snapshot.json b/packages/kit/tables/migrations/meta/0035_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0035_snapshot.json rename to packages/kit/tables/migrations/meta/0035_snapshot.json diff --git a/drizzle/migrations/meta/0036_snapshot.json b/packages/kit/tables/migrations/meta/0036_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0036_snapshot.json rename to packages/kit/tables/migrations/meta/0036_snapshot.json diff --git a/drizzle/migrations/meta/0037_snapshot.json b/packages/kit/tables/migrations/meta/0037_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0037_snapshot.json rename to packages/kit/tables/migrations/meta/0037_snapshot.json diff --git a/drizzle/migrations/meta/0038_snapshot.json b/packages/kit/tables/migrations/meta/0038_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0038_snapshot.json rename to packages/kit/tables/migrations/meta/0038_snapshot.json diff --git a/drizzle/migrations/meta/0039_snapshot.json b/packages/kit/tables/migrations/meta/0039_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0039_snapshot.json rename to packages/kit/tables/migrations/meta/0039_snapshot.json diff --git a/drizzle/migrations/meta/0040_snapshot.json b/packages/kit/tables/migrations/meta/0040_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0040_snapshot.json rename to packages/kit/tables/migrations/meta/0040_snapshot.json diff --git a/drizzle/migrations/meta/0041_snapshot.json b/packages/kit/tables/migrations/meta/0041_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0041_snapshot.json rename to packages/kit/tables/migrations/meta/0041_snapshot.json diff --git a/drizzle/migrations/meta/0042_snapshot.json b/packages/kit/tables/migrations/meta/0042_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0042_snapshot.json rename to packages/kit/tables/migrations/meta/0042_snapshot.json diff --git a/drizzle/migrations/meta/0043_snapshot.json b/packages/kit/tables/migrations/meta/0043_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0043_snapshot.json rename to packages/kit/tables/migrations/meta/0043_snapshot.json diff --git a/drizzle/migrations/meta/0044_snapshot.json b/packages/kit/tables/migrations/meta/0044_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0044_snapshot.json rename to packages/kit/tables/migrations/meta/0044_snapshot.json diff --git a/drizzle/migrations/meta/0045_snapshot.json b/packages/kit/tables/migrations/meta/0045_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0045_snapshot.json rename to packages/kit/tables/migrations/meta/0045_snapshot.json diff --git a/drizzle/migrations/meta/0046_snapshot.json b/packages/kit/tables/migrations/meta/0046_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0046_snapshot.json rename to packages/kit/tables/migrations/meta/0046_snapshot.json diff --git a/drizzle/migrations/meta/0047_snapshot.json b/packages/kit/tables/migrations/meta/0047_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0047_snapshot.json rename to packages/kit/tables/migrations/meta/0047_snapshot.json diff --git a/drizzle/migrations/meta/0048_snapshot.json b/packages/kit/tables/migrations/meta/0048_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0048_snapshot.json rename to packages/kit/tables/migrations/meta/0048_snapshot.json diff --git a/drizzle/migrations/meta/0049_snapshot.json b/packages/kit/tables/migrations/meta/0049_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0049_snapshot.json rename to packages/kit/tables/migrations/meta/0049_snapshot.json diff --git a/drizzle/migrations/meta/0050_snapshot.json b/packages/kit/tables/migrations/meta/0050_snapshot.json similarity index 100% rename from drizzle/migrations/meta/0050_snapshot.json rename to packages/kit/tables/migrations/meta/0050_snapshot.json diff --git a/drizzle/migrations/meta/_journal.json b/packages/kit/tables/migrations/meta/_journal.json similarity index 100% rename from drizzle/migrations/meta/_journal.json rename to packages/kit/tables/migrations/meta/_journal.json diff --git a/drizzle/schema.ts b/packages/kit/tables/schema.ts similarity index 99% rename from drizzle/schema.ts rename to packages/kit/tables/schema.ts index b174f359..7f0c8e17 100644 --- a/drizzle/schema.ts +++ b/packages/kit/tables/schema.ts @@ -4,6 +4,13 @@ import type { Source, Status as StatusSchema, } from "@versia/client/schemas"; +import type { + ContentFormatSchema, + ImageContentFormatSchema, + InstanceMetadataSchema, + NonTextContentFormatSchema, + TextContentFormatSchema, +} from "@versia/sdk/schemas"; import type { Challenge } from "altcha-lib/types"; import { relations, sql } from "drizzle-orm"; import { @@ -18,14 +25,7 @@ import { uniqueIndex, uuid, } from "drizzle-orm/pg-core"; -import type { z } from "zod"; -import type { - ContentFormatSchema, - ImageContentFormatSchema, - InstanceMetadataSchema, - NonTextContentFormatSchema, - TextContentFormatSchema, -} from "~/packages/sdk/schemas"; +import type { z } from "zod/v4"; const createdAt = () => timestamp("created_at", { precision: 3, mode: "string" }) diff --git a/packages/kit/timelines/streaming.test.ts b/packages/kit/timelines/streaming.test.ts new file mode 100644 index 00000000..1e56ff67 --- /dev/null +++ b/packages/kit/timelines/streaming.test.ts @@ -0,0 +1,465 @@ +import { afterAll, beforeAll, describe, expect, test } from "bun:test"; +import { randomUUIDv7 } from "bun"; +import { User } from "../db/user.ts"; +import { connection } from "../redis.ts"; +import { setupDatabase } from "../tables/db.ts"; +import { + type EventTypes, + StreamingTimeline, + type TimelineTypes, +} from "./streaming.ts"; + +// Set up database and create test users +await setupDatabase(); + +const testUsers: User[] = []; + +beforeAll(async (): Promise => { + // Ensure Redis is connected + await connection.ping(); + + // Create test users* + // Can't use stuff from @versia-server/tests because it depends on this package + for (let i = 0; i < 2; i++) { + const user = await User.register( + `test-streaming-${randomUUIDv7().slice(0, 8)}`, + { + email: `test-streaming-${i}@example.com`, + }, + ); + testUsers.push(user); + } +}); + +afterAll(async (): Promise => { + // Delete test users + for (const user of testUsers) { + await user.delete(); + } +}); + +describe("StreamingTimeline", (): void => { + test("should create timeline with user", (): void => { + const timeline = new StreamingTimeline("user", testUsers[0]); + + expect(timeline.timeline).toBe("user"); + expect(timeline.user).toBe(testUsers[0]); + expect(timeline.emitter).toBeDefined(); + + timeline.close(); + }); + + test("should create timeline without user", (): void => { + const timeline = new StreamingTimeline("public"); + + expect(timeline.timeline).toBe("public"); + expect(timeline.user).toBe(null); + expect(timeline.emitter).toBeDefined(); + + timeline.close(); + }); + + test("should generate correct channel name for user timeline", (): void => { + const timeline = new StreamingTimeline("user", testUsers[0]); + + // Access private property for testing + const channelName = (timeline as unknown as { channelName: string }) + .channelName; + expect(channelName).toBe(`timeline:user:${testUsers[0].id}`); + + timeline.close(); + }); + + test("should generate correct channel name for public timeline", (): void => { + const timeline = new StreamingTimeline("public"); + + // Access private property for testing + const channelName = (timeline as unknown as { channelName: string }) + .channelName; + expect(channelName).toBe("timeline:public"); + + timeline.close(); + }); + + test("should emit event when publishing to same channel", async (): Promise => { + const emitterTimeline = new StreamingTimeline("public"); + const receiverTimeline = new StreamingTimeline("public"); + let receivedType = ""; + let receivedPayload: unknown = null; + + // Listen for update events + receiverTimeline.emitter.on("update", (payload): void => { + receivedType = "update"; + receivedPayload = payload; + }); + + // Wait a bit for subscription to be established + await new Promise((resolve): void => { + setTimeout(resolve, 10); + }); + + // Emit an event + emitterTimeline.emitEvent("update", { id: "test-status-123" }); + + // Wait for event to be processed + await new Promise((resolve): void => { + setTimeout(resolve, 50); + }); + + expect(receivedType).toBe("update"); + expect(receivedPayload).toEqual({ id: "test-status-123" }); + + emitterTimeline.close(); + receiverTimeline.close(); + }); + + test("should not emit event for different channel", async (): Promise => { + const timeline1 = new StreamingTimeline("public"); + const timeline2 = new StreamingTimeline("user", testUsers[0]); + + let receivedEvent = false; + + // Listen for events on timeline1 + timeline1.emitter.on("update", (): void => { + receivedEvent = true; + }); + + // Wait a bit for subscription to be established + await new Promise((resolve): void => { + setTimeout(resolve, 10); + }); + + // Emit event on timeline2 (different channel) + timeline2.emitEvent("update", { id: "test-status-123" }); + + // Wait for potential event processing + await new Promise((resolve): void => { + setTimeout(resolve, 50); + }); + + expect(receivedEvent).toBe(false); + + timeline1.close(); + timeline2.close(); + }); + + test("should handle different event types", async (): Promise => { + const timeline = new StreamingTimeline("public"); + const receivedEvents: Array<{ type: string; payload: unknown }> = []; + + // Listen for different event types + timeline.emitter.on("update", (payload): void => { + receivedEvents.push({ type: "update", payload }); + }); + timeline.emitter.on("delete", (payload): void => { + receivedEvents.push({ type: "delete", payload }); + }); + timeline.emitter.on("notification", (payload): void => { + receivedEvents.push({ type: "notification", payload }); + }); + + // Wait for subscription + await new Promise((resolve): void => { + setTimeout(resolve, 10); + }); + + // Emit different events + timeline.emitEvent("update", { id: "status-1" }); + timeline.emitEvent("delete", { id: "status-2" }); + timeline.emitEvent("notification", { id: "notif-1" }); + + // Wait for events + await new Promise((resolve): void => { + setTimeout(resolve, 100); + }); + + expect(receivedEvents).toHaveLength(3); + expect(receivedEvents[0]).toEqual({ + type: "update", + payload: { id: "status-1" }, + }); + expect(receivedEvents[1]).toEqual({ + type: "delete", + payload: { id: "status-2" }, + }); + expect(receivedEvents[2]).toEqual({ + type: "notification", + payload: { id: "notif-1" }, + }); + + timeline.close(); + }); + + test("should handle malformed JSON messages gracefully", async (): Promise => { + const timeline = new StreamingTimeline("public"); + + let receivedEvent = false; + let warningLogged = false; + + // Mock console.warn to capture warnings + const originalWarn = console.warn; + console.warn = (): void => { + warningLogged = true; + }; + + timeline.emitter.on("update", (): void => { + receivedEvent = true; + }); + + // Wait for subscription + await new Promise((resolve): void => { + setTimeout(resolve, 10); + }); + + // Publish malformed JSON directly to Redis + const channelName = (timeline as unknown as { channelName: string }) + .channelName; + await connection.publish(channelName, "invalid json"); + + // Wait for message processing + await new Promise((resolve): void => { + setTimeout(resolve, 50); + }); + + expect(receivedEvent).toBe(false); + expect(warningLogged).toBe(true); + + // Restore console.warn + console.warn = originalWarn; + timeline.close(); + }); + + test("should handle messages with missing type or payload", async (): Promise => { + const timeline = new StreamingTimeline("public"); + + let receivedEvent = false; + + timeline.emitter.on("update", (): void => { + receivedEvent = true; + }); + + // Wait for subscription + await new Promise((resolve): void => { + setTimeout(resolve, 10); + }); + + // Publish message with missing type + const channelName = (timeline as unknown as { channelName: string }) + .channelName; + await connection.publish( + channelName, + JSON.stringify({ payload: { id: "test" } }), + ); + + // Publish message with missing payload + await connection.publish( + channelName, + JSON.stringify({ type: "update" }), + ); + + // Wait for message processing + await new Promise((resolve): void => { + setTimeout(resolve, 50); + }); + + expect(receivedEvent).toBe(false); + + timeline.close(); + }); + + test("should properly clean up resources on close", async (): Promise => { + const timeline = new StreamingTimeline("public"); + + // Wait for subscription + await new Promise((resolve): void => { + setTimeout(resolve, 10); + }); + + // Verify subscription is active + const channelName = (timeline as unknown as { channelName: string }) + .channelName; + expect( + timeline.redisConnection.listenerCount("message"), + ).toBeGreaterThan(0); + + // Close timeline + timeline.close(); + + // Wait a bit + await new Promise((resolve): void => { + setTimeout(resolve, 10); + }); + + // Try to emit event - should not be received + let receivedEvent = false; + timeline.emitter.on("update", (): void => { + receivedEvent = true; + }); + + await connection.publish( + channelName, + JSON.stringify({ type: "update", payload: { id: "test" } }), + ); + + await new Promise((resolve): void => { + setTimeout(resolve, 50); + }); + + expect(receivedEvent).toBe(false); + }); + + test("should handle multiple timelines with different channels", async (): Promise => { + const timeline1 = new StreamingTimeline("public"); + const timeline2 = new StreamingTimeline("user", testUsers[0]); + const timeline3 = new StreamingTimeline("user", testUsers[1]); + + const events1: unknown[] = []; + const events2: unknown[] = []; + const events3: unknown[] = []; + + timeline1.emitter.on("update", (payload): void => { + events1.push(payload); + }); + timeline2.emitter.on("update", (payload): void => { + events2.push(payload); + }); + timeline3.emitter.on("update", (payload): void => { + events3.push(payload); + }); + + // Wait for subscriptions + await new Promise((resolve): void => { + setTimeout(resolve, 20); + }); + + // Emit events to different timelines + timeline1.emitEvent("update", { id: "public-1" }); + timeline2.emitEvent("update", { id: "user1-1" }); + timeline3.emitEvent("update", { id: "user2-1" }); + + // Wait for events + await new Promise((resolve): void => { + setTimeout(resolve, 100); + }); + + expect(events1).toEqual([{ id: "public-1" }]); + expect(events2).toEqual([{ id: "user1-1" }]); + expect(events3).toEqual([{ id: "user2-1" }]); + + timeline1.close(); + timeline2.close(); + timeline3.close(); + }); + + test("should handle filters_changed event with null payload", async (): Promise => { + const timeline = new StreamingTimeline("public"); + let receivedType = ""; + let receivedPayload: unknown; + + timeline.emitter.on("filters_changed", (payload): void => { + receivedType = "filters_changed"; + receivedPayload = payload; + }); + + await new Promise((resolve): void => { + setTimeout(resolve, 10); + }); + + timeline.emitEvent("filters_changed", null); + + await new Promise((resolve): void => { + setTimeout(resolve, 50); + }); + + expect(receivedType).toBe("filters_changed"); + expect(receivedPayload).toBeNull(); + + timeline.close(); + }); + + test("should handle all supported timeline types", (): void => { + const timelineTypes: TimelineTypes[] = [ + "public", + "public:media", + "public:local", + "public:local:media", + "public:remote", + "public:remote:media", + "hashtag", + "hashtag:local", + "user", + "user:notification", + "list", + ]; + + const timelines = timelineTypes.map( + (type) => new StreamingTimeline(type), + ); + + for (const [index, timeline] of timelines.entries()) { + expect(timeline.timeline).toBe(timelineTypes[index]); + const channelName = (timeline as unknown as { channelName: string }) + .channelName; + expect(channelName).toBe(`timeline:${timelineTypes[index]}`); + } + + // Clean up + for (const timeline of timelines) { + timeline.close(); + } + }); + + test("should handle all supported event types", async (): Promise => { + const timeline = new StreamingTimeline("public"); + const eventTypes: EventTypes[] = [ + "update", + "delete", + "notification", + "filters_changed", + "announcement", + "announcement.reaction", + "announcement.delete", + "status.update", + ]; + + const receivedEvents: Array<{ type: string; payload: unknown }> = []; + + // Set up listeners for all event types + for (const eventType of eventTypes) { + timeline.emitter.on(eventType, (payload): void => { + receivedEvents.push({ type: eventType, payload }); + }); + } + + await new Promise((resolve): void => { + setTimeout(resolve, 10); + }); + + // Emit all event types + for (const [index, eventType] of eventTypes.entries()) { + const payload = + eventType === "filters_changed" + ? null + : { id: `${eventType}-${index}` }; + timeline.emitEvent(eventType, payload as never); + } + + await new Promise((resolve): void => { + setTimeout(resolve, 100); + }); + + expect(receivedEvents).toHaveLength(eventTypes.length); + for (const [index, eventType] of eventTypes.entries()) { + const expectedPayload = + eventType === "filters_changed" + ? null + : { id: `${eventType}-${index}` }; + expect(receivedEvents[index]).toEqual({ + type: eventType, + payload: expectedPayload, + }); + } + + timeline.close(); + }); +}); diff --git a/packages/kit/timelines/streaming.ts b/packages/kit/timelines/streaming.ts new file mode 100644 index 00000000..4f184f37 --- /dev/null +++ b/packages/kit/timelines/streaming.ts @@ -0,0 +1,122 @@ +/** + * Handles live updates to timelines using Redis to communicate between instances. + * + * Used in the Streaming API to push updates to clients in real-time. + * @see https://docs.joinmastodon.org/methods/streaming/#websocket + */ + +import { config } from "@versia-server/config"; +import IORedis from "ioredis"; +import mitt from "mitt"; +import type { User } from "../db/user.ts"; +import { connection } from "../redis.ts"; + +export type TimelineTypes = + | "public" + | "public:media" + | "public:local" + | "public:local:media" + | "public:remote" + | "public:remote:media" + | "hashtag" + | "hashtag:local" + | "user" + | "user:notification" + | "list"; + +export type EventTypes = + | "update" + | "delete" + | "notification" + | "filters_changed" + | "announcement" + | "announcement.reaction" + | "announcement.delete" + | "status.update"; + +export type EventPayloads = { + update: { id: string }; + delete: { id: string }; + notification: { id: string }; + filters_changed: null; + announcement: { id: string }; + "announcement.reaction": { id: string }; + "announcement.delete": { id: string }; + "status.update": { id: string }; +}; + +export class StreamingTimeline { + public readonly emitter = + mitt<{ + [K in EventTypes]: EventPayloads[K]; + }>(); + public readonly redisConnection: IORedis; + + public constructor( + public readonly timeline: TimelineTypes, + public readonly user: User | null = null, + ) { + this.redisConnection = new IORedis({ + host: config.redis.queue.host, + port: config.redis.queue.port, + password: config.redis.queue.password, + db: config.redis.queue.database, + maxRetriesPerRequest: null, + }); + this.initializeRedisWatcher(); + } + + private get channelName(): string { + if (this.user) { + return `timeline:${this.timeline}:${this.user.id}`; + } + + return `timeline:${this.timeline}`; + } + + private readonly messageHandler = ( + channel: string, + message: string, + ): void => { + if (channel === this.channelName) { + try { + const parsed = JSON.parse(message); + if ( + typeof parsed === "object" && + parsed !== null && + "type" in parsed && + "payload" in parsed + ) { + const { type, payload } = parsed as { + type: EventTypes; + payload: unknown; + }; + this.emitter.emit( + type, + payload as EventPayloads[typeof type], + ); + } + } catch (error) { + // Silently ignore malformed messages + console.warn("Failed to parse streaming message:", error); + } + } + }; + + private initializeRedisWatcher(): void { + this.redisConnection.subscribe(this.channelName); + this.redisConnection.on("message", this.messageHandler); + } + + public close(): void { + this.redisConnection.unsubscribe(this.channelName); + this.redisConnection.off("message", this.messageHandler); + } + + public emitEvent( + type: K, + payload: EventPayloads[K], + ): void { + connection.publish(this.channelName, JSON.stringify({ type, payload })); + } +} diff --git a/packages/logging/build.ts b/packages/logging/build.ts new file mode 100644 index 00000000..908e5d50 --- /dev/null +++ b/packages/logging/build.ts @@ -0,0 +1,19 @@ +import { $, build } from "bun"; +import manifest from "./package.json" with { type: "json" }; + +console.log("Building..."); + +await $`rm -rf dist && mkdir dist`; + +await build({ + entrypoints: Object.values(manifest.exports).map((entry) => entry.import), + outdir: "dist", + target: "bun", + splitting: true, + minify: true, + external: [ + ...Object.keys(manifest.dependencies).filter((dep) => + dep.startsWith("@versia"), + ), + ], +}); diff --git a/packages/logging/formatter.ts b/packages/logging/formatter.ts new file mode 100644 index 00000000..075d3cca --- /dev/null +++ b/packages/logging/formatter.ts @@ -0,0 +1,56 @@ +import type { LogLevel, LogRecord } from "@logtape/logtape"; +import chalk, { type ChalkInstance } from "chalk"; + +const levelAbbreviations: Record = { + debug: "DBG", + info: "INF", + warning: "WRN", + error: "ERR", + fatal: "FTL", + trace: "TRC", +}; + +/** + * The styles for the log level in the console. + */ +const logLevelStyles: Record = { + debug: chalk.white.bgGray, + info: chalk.black.bgWhite, + warning: chalk.black.bgYellow, + error: chalk.white.bgRed, + fatal: chalk.white.bgRedBright, + trace: chalk.white.bgBlue, +}; + +/** + * Pretty colored console formatter. + * + * @param record The log record to format. + * @returns The formatted log record, as an array of arguments for + * {@link console.log}. + */ +export function consoleFormatter(record: LogRecord): string[] { + const msg = record.message.join(""); + const date = new Date(record.timestamp); + const time = `${date.getUTCHours().toString().padStart(2, "0")}:${date + .getUTCMinutes() + .toString() + .padStart( + 2, + "0", + )}:${date.getUTCSeconds().toString().padStart(2, "0")}.${date + .getUTCMilliseconds() + .toString() + .padStart(3, "0")}`; + + const formattedTime = chalk.gray(time); + const formattedLevel = logLevelStyles[record.level]( + levelAbbreviations[record.level], + ); + const formattedCategory = chalk.gray(record.category.join("\xb7")); + const formattedMsg = chalk.reset(msg); + + return [ + `${formattedTime} ${formattedLevel} ${formattedCategory} ${formattedMsg}`, + ]; +} diff --git a/packages/logging/index.ts b/packages/logging/index.ts new file mode 100644 index 00000000..0ed43128 --- /dev/null +++ b/packages/logging/index.ts @@ -0,0 +1,154 @@ +import { mkdir } from "node:fs/promises"; +import { dirname } from "node:path"; +import { getFileSink, getRotatingFileSink } from "@logtape/file"; +import { + configure, + getConsoleSink, + getLevelFilter, + getLogger, + type Sink, + withFilter, +} from "@logtape/logtape"; +import { getSentrySink } from "@logtape/sentry"; +import * as Sentry from "@sentry/bun"; +import { config } from "@versia-server/config"; +import { env } from "bun"; +import pkg from "../../package.json" with { type: "json" }; +import { consoleFormatter } from "./formatter.ts"; + +if (config.logging.file?.path) { + // config.logging.file.path is a path to a file, create the directory if it doesn't exist + await mkdir(dirname(config.logging.file.path), { recursive: true }); +} + +/** + * Returns all configured sinks depending on the configuration. + */ +const getSinks = (): Record<"file" | "console" | "sentry", Sink> => { + const sinks: Record = {}; + + if (config.logging.file) { + if (config.logging.file.rotation) { + sinks.file = getRotatingFileSink(config.logging.file.path, { + maxFiles: config.logging.file.rotation.max_files, + maxSize: config.logging.file.rotation.max_size, + }); + } else { + sinks.file = getFileSink(config.logging.file.path); + } + + sinks.file = withFilter( + sinks.file, + getLevelFilter(config.logging.file.log_level), + ); + } + + if (config.logging.sentry) { + sinks.sentry = withFilter( + getSentrySink( + Sentry.init({ + dsn: config.logging.sentry.dsn.origin, + debug: config.logging.sentry.debug, + sampleRate: config.logging.sentry.sample_rate, + maxBreadcrumbs: config.logging.sentry.max_breadcrumbs, + tracesSampleRate: config.logging.sentry.traces_sample_rate, + environment: config.logging.sentry.environment, + tracePropagationTargets: + config.logging.sentry.trace_propagation_targets, + release: env.GIT_COMMIT + ? `${pkg.version}-${env.GIT_COMMIT}` + : pkg.version, + integrations: [Sentry.extraErrorDataIntegration()], + }), + ), + getLevelFilter(config.logging.sentry.log_level), + ); + } + + sinks.console = withFilter( + getConsoleSink({ + formatter: consoleFormatter, + }), + getLevelFilter(config.logging.log_level), + ); + + return sinks; +}; + +const getSinkNames = (): ("file" | "console" | "sentry")[] => { + const names = [] as ("file" | "console" | "sentry")[]; + + if (config.logging.file) { + names.push("file"); + } + + if (config.logging.sentry) { + names.push("sentry"); + } + + names.push("console"); + + return names; +}; + +await configure({ + reset: true, + sinks: getSinks(), + loggers: [ + { + category: "server", + sinks: getSinkNames(), + }, + { + category: ["federation", "inbox"], + sinks: getSinkNames(), + }, + { + category: ["federation", "delivery"], + sinks: getSinkNames(), + }, + { + category: ["federation", "bridge"], + sinks: getSinkNames(), + }, + { + category: ["federation", "resolvers"], + sinks: getSinkNames(), + }, + { + category: ["federation", "messaging"], + sinks: getSinkNames(), + }, + { + category: "database", + sinks: getSinkNames(), + }, + { + category: "webfinger", + sinks: getSinkNames(), + }, + { + category: "sonic", + sinks: getSinkNames(), + }, + { + category: ["logtape", "meta"], + lowestLevel: "error", + }, + { + category: "plugin", + sinks: getSinkNames(), + }, + ], +}); + +export const serverLogger = getLogger("server"); +export const federationInboxLogger = getLogger(["federation", "inbox"]); +export const federationDeliveryLogger = getLogger(["federation", "delivery"]); +export const federationBridgeLogger = getLogger(["federation", "bridge"]); +export const federationResolversLogger = getLogger(["federation", "resolvers"]); +export const federationMessagingLogger = getLogger(["federation", "messaging"]); +export const databaseLogger = getLogger("database"); +export const webfingerLogger = getLogger("webfinger"); +export const sonicLogger = getLogger("sonic"); +export const pluginLogger = getLogger("plugin"); diff --git a/packages/logging/package.json b/packages/logging/package.json new file mode 100644 index 00000000..4409422b --- /dev/null +++ b/packages/logging/package.json @@ -0,0 +1,24 @@ +{ + "name": "@versia-server/logging", + "module": "index.ts", + "type": "module", + "version": "0.0.1", + "private": true, + "scripts": { + "build": "bun run build.ts" + }, + "exports": { + ".": { + "import": "./index.ts" + } + }, + "dependencies": { + "@versia-server/config": "workspace:*", + "@logtape/logtape": "catalog:", + "@logtape/file": "catalog:", + "@logtape/sentry": "catalog:", + "@logtape/otel": "catalog:", + "@sentry/bun": "catalog:", + "chalk": "catalog:" + } +} diff --git a/packages/plugin-kit/exports/db.ts b/packages/plugin-kit/exports/db.ts deleted file mode 100644 index 7754d60a..00000000 --- a/packages/plugin-kit/exports/db.ts +++ /dev/null @@ -1,15 +0,0 @@ -export { Application } from "~/classes/database/application.ts"; -export { Emoji } from "~/classes/database/emoji.ts"; -export { Instance } from "~/classes/database/instance.ts"; -export { Like } from "~/classes/database/like.ts"; -export { Media } from "~/classes/database/media"; -export { Note } from "~/classes/database/note.ts"; -export { Notification } from "~/classes/database/notification.ts"; -export { PushSubscription } from "~/classes/database/pushsubscription.ts"; -export { Reaction } from "~/classes/database/reaction.ts"; -export { Relationship } from "~/classes/database/relationship.ts"; -export { Role } from "~/classes/database/role.ts"; -export { Timeline } from "~/classes/database/timeline.ts"; -export { Token } from "~/classes/database/token.ts"; -export { User } from "~/classes/database/user.ts"; -export { db } from "~/drizzle/db.ts"; diff --git a/packages/plugin-kit/exports/drizzle.ts b/packages/plugin-kit/exports/drizzle.ts deleted file mode 100644 index 5ac1e878..00000000 --- a/packages/plugin-kit/exports/drizzle.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "drizzle-orm"; diff --git a/packages/plugin-kit/exports/tables.ts b/packages/plugin-kit/exports/tables.ts deleted file mode 100644 index 8d1685fe..00000000 --- a/packages/plugin-kit/exports/tables.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "~/drizzle/schema"; diff --git a/packages/plugin-kit/index.ts b/packages/plugin-kit/index.ts deleted file mode 100644 index b992d990..00000000 --- a/packages/plugin-kit/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { Hooks } from "./hooks.ts"; -export { Plugin } from "./plugin.ts"; -export type { Manifest } from "./schema.ts"; diff --git a/packages/plugin-kit/package.json b/packages/plugin-kit/package.json deleted file mode 100644 index 07a6c60f..00000000 --- a/packages/plugin-kit/package.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "name": "@versia/kit", - "module": "index.ts", - "type": "module", - "version": "0.0.0", - "description": "Framework for building Versia Server plugins", - "author": { - "email": "contact@cpluspatch.com", - "name": "CPlusPatch", - "url": "https://cpluspatch.com" - }, - "bugs": { - "url": "https://github.com/versia-pub/server/issues" - }, - "icon": "https://github.com/versia-pub/server", - "license": "AGPL-3.0-or-later", - "keywords": [ - "federated", - "activitypub", - "bun" - ], - "maintainers": [ - { - "email": "contact@cpluspatch.com", - "name": "CPlusPatch", - "url": "https://cpluspatch.com" - } - ], - "repository": { - "type": "git", - "url": "git+https://github.com/versia-pub/server.git" - }, - "private": true, - "dependencies": { - "drizzle-orm": "^0.44.2", - "hono": "^4.7.7", - "mitt": "^3.0.1", - "zod": "^3.24.2", - "zod-to-json-schema": "^3.24.5", - "zod-validation-error": "^3.4.0" - }, - "exports": { - ".": { - "import": "./index.ts", - "default": "./index.ts" - }, - "./db": { - "import": "./exports/db.ts", - "default": "./exports/db.ts" - }, - "./drizzle": { - "import": "./exports/drizzle.ts", - "default": "./exports/drizzle.ts" - }, - "./tables": { - "import": "./exports/tables.ts", - "default": "./exports/tables.ts" - } - } -} diff --git a/packages/sdk/build.ts b/packages/sdk/build.ts new file mode 100644 index 00000000..cdff0968 --- /dev/null +++ b/packages/sdk/build.ts @@ -0,0 +1,29 @@ +import { $, build } from "bun"; +import manifest from "./package.json" with { type: "json" }; + +console.log("Building..."); + +await $`rm -rf dist && mkdir dist`; + +await build({ + entrypoints: Object.values(manifest.exports).map((entry) => entry.import), + outdir: "dist", + target: "bun", + splitting: true, + minify: true, + external: [ + ...Object.keys(manifest.dependencies).filter((dep) => + dep.startsWith("@versia"), + ), + "acorn", + ], +}); + +console.log("Copying files..."); + +await $`mkdir -p dist/node_modules`; + +// Copy acorn to dist +await $`cp -rL ../../node_modules/acorn dist/node_modules/acorn`; + +console.log("Build complete!"); diff --git a/packages/sdk/entities/collection.ts b/packages/sdk/entities/collection.ts index a1093b29..acf9a191 100644 --- a/packages/sdk/entities/collection.ts +++ b/packages/sdk/entities/collection.ts @@ -1,4 +1,4 @@ -import type { z } from "zod"; +import type { z } from "zod/v4"; import { CollectionSchema, URICollectionSchema, diff --git a/packages/sdk/entities/contentformat.ts b/packages/sdk/entities/contentformat.ts index 93999f1c..69e4760e 100644 --- a/packages/sdk/entities/contentformat.ts +++ b/packages/sdk/entities/contentformat.ts @@ -1,4 +1,4 @@ -import type { z } from "zod"; +import type { z } from "zod/v4"; import { AudioContentFormatSchema, ContentFormatSchema, diff --git a/packages/sdk/entities/delete.ts b/packages/sdk/entities/delete.ts index 6df3d896..717ba2bc 100644 --- a/packages/sdk/entities/delete.ts +++ b/packages/sdk/entities/delete.ts @@ -1,4 +1,4 @@ -import type { z } from "zod"; +import type { z } from "zod/v4"; import { DeleteSchema } from "../schemas/delete.ts"; import type { JSONObject } from "../types.ts"; import { Entity } from "./entity.ts"; diff --git a/packages/sdk/entities/extensions/likes.ts b/packages/sdk/entities/extensions/likes.ts index f8db6885..d9c93dae 100644 --- a/packages/sdk/entities/extensions/likes.ts +++ b/packages/sdk/entities/extensions/likes.ts @@ -1,4 +1,4 @@ -import type { z } from "zod"; +import type { z } from "zod/v4"; import { DislikeSchema, LikeSchema } from "../../schemas/extensions/likes.ts"; import type { JSONObject } from "../../types.ts"; import { Entity } from "../entity.ts"; diff --git a/packages/sdk/entities/extensions/polls.ts b/packages/sdk/entities/extensions/polls.ts index 995e324d..6b32b26c 100644 --- a/packages/sdk/entities/extensions/polls.ts +++ b/packages/sdk/entities/extensions/polls.ts @@ -1,4 +1,4 @@ -import type { z } from "zod"; +import type { z } from "zod/v4"; import { VoteSchema } from "../../schemas/extensions/polls.ts"; import type { JSONObject } from "../../types.ts"; import { Entity } from "../entity.ts"; diff --git a/packages/sdk/entities/extensions/reactions.ts b/packages/sdk/entities/extensions/reactions.ts index 5f3f785e..8ce921cf 100644 --- a/packages/sdk/entities/extensions/reactions.ts +++ b/packages/sdk/entities/extensions/reactions.ts @@ -1,4 +1,4 @@ -import type { z } from "zod"; +import type { z } from "zod/v4"; import { ReactionSchema } from "../../schemas/extensions/reactions.ts"; import type { JSONObject } from "../../types.ts"; import { Entity } from "../entity.ts"; diff --git a/packages/sdk/entities/extensions/reports.ts b/packages/sdk/entities/extensions/reports.ts index d15cfe48..faa79532 100644 --- a/packages/sdk/entities/extensions/reports.ts +++ b/packages/sdk/entities/extensions/reports.ts @@ -1,4 +1,4 @@ -import type { z } from "zod"; +import type { z } from "zod/v4"; import { ReportSchema } from "../../schemas/extensions/reports.ts"; import type { JSONObject } from "../../types.ts"; import { Entity } from "../entity.ts"; diff --git a/packages/sdk/entities/extensions/share.ts b/packages/sdk/entities/extensions/share.ts index f3817744..617a4308 100644 --- a/packages/sdk/entities/extensions/share.ts +++ b/packages/sdk/entities/extensions/share.ts @@ -1,4 +1,4 @@ -import type { z } from "zod"; +import type { z } from "zod/v4"; import { ShareSchema } from "../../schemas/extensions/share.ts"; import type { JSONObject } from "../../types.ts"; import { Entity } from "../entity.ts"; diff --git a/packages/sdk/entities/follow.ts b/packages/sdk/entities/follow.ts index d38d9728..149b10bc 100644 --- a/packages/sdk/entities/follow.ts +++ b/packages/sdk/entities/follow.ts @@ -1,4 +1,4 @@ -import type { z } from "zod"; +import type { z } from "zod/v4"; import { FollowAcceptSchema, FollowRejectSchema, diff --git a/packages/sdk/entities/instancemetadata.ts b/packages/sdk/entities/instancemetadata.ts index d95f44ab..b17700c2 100644 --- a/packages/sdk/entities/instancemetadata.ts +++ b/packages/sdk/entities/instancemetadata.ts @@ -1,4 +1,4 @@ -import type { z } from "zod"; +import type { z } from "zod/v4"; import { InstanceMetadataSchema } from "../schemas/instance.ts"; import type { JSONObject } from "../types.ts"; import { ImageContentFormat } from "./contentformat.ts"; diff --git a/packages/sdk/entities/note.ts b/packages/sdk/entities/note.ts index 7314e763..e995e94d 100644 --- a/packages/sdk/entities/note.ts +++ b/packages/sdk/entities/note.ts @@ -1,4 +1,4 @@ -import type { z } from "zod"; +import type { z } from "zod/v4"; import { NoteSchema } from "../schemas/note.ts"; import type { JSONObject } from "../types.ts"; import { NonTextContentFormat, TextContentFormat } from "./contentformat.ts"; diff --git a/packages/sdk/entities/user.ts b/packages/sdk/entities/user.ts index 0d744632..b809aba1 100644 --- a/packages/sdk/entities/user.ts +++ b/packages/sdk/entities/user.ts @@ -1,4 +1,4 @@ -import type { z } from "zod"; +import type { z } from "zod/v4"; import { UserSchema } from "../schemas/user.ts"; import type { JSONObject } from "../types.ts"; import { ImageContentFormat, TextContentFormat } from "./contentformat.ts"; diff --git a/packages/sdk/inbox-processor.ts b/packages/sdk/inbox-processor.ts index 6c6e000a..1fee5da1 100644 --- a/packages/sdk/inbox-processor.ts +++ b/packages/sdk/inbox-processor.ts @@ -17,9 +17,9 @@ type MaybePromise = T | Promise; * .sort(); */ export class EntitySorter { - private handlers: EntitySorterHandlers = new Map(); + private readonly handlers: EntitySorterHandlers = new Map(); - public constructor(private jsonData: JSONObject) {} + public constructor(private readonly jsonData: JSONObject) {} public on( entity: T, @@ -52,3 +52,5 @@ export class EntitySorter { } } } + +export type { JSONObject } from "./types.ts"; diff --git a/packages/sdk/jsr.jsonc b/packages/sdk/jsr.jsonc index 427be9be..2f401862 100644 --- a/packages/sdk/jsr.jsonc +++ b/packages/sdk/jsr.jsonc @@ -2,6 +2,7 @@ "$schema": "https://jsr.io/schema/config-file.v1.json", "name": "@versia/sdk", "version": "0.0.1", + "license": "MIT", "exports": { ".": "./inbox-processor.ts", "./http": "./http.ts", diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 2ef017cb..beb44f86 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -7,6 +7,9 @@ "name": "Jesse Wierzbinski (CPlusPatch)", "url": "https://cpluspatch.com" }, + "scripts": { + "build": "bun run build.ts" + }, "readme": "README.md", "repository": { "type": "git", @@ -41,24 +44,19 @@ }, "exports": { ".": { - "import": "./inbox-processor.ts", - "default": "./inbox-processor.ts" + "import": "./inbox-processor.ts" }, "./http": { - "import": "./http.ts", - "default": "./http.ts" + "import": "./http.ts" }, "./crypto": { - "import": "./crypto.ts", - "default": "./crypto.ts" + "import": "./crypto.ts" }, "./entities": { - "import": "./entities/index.ts", - "default": "./entities/index.ts" + "import": "./entities/index.ts" }, "./schemas": { - "import": "./schemas/index.ts", - "default": "./schemas/index.ts" + "import": "./schemas/index.ts" } }, "funding": { @@ -71,10 +69,9 @@ "typescript", "sdk" ], - "packageManager": "bun@1.2.5", "dependencies": { - "magic-regexp": "^0.10.0", - "mime-types": "^3.0.1", - "zod": "^3.24.2" + "magic-regexp": "catalog:", + "mime-types": "catalog:", + "zod": "catalog:" } } diff --git a/packages/sdk/schemas/collection.ts b/packages/sdk/schemas/collection.ts index 92accefb..80758c63 100644 --- a/packages/sdk/schemas/collection.ts +++ b/packages/sdk/schemas/collection.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { u64, url } from "./common.ts"; export const CollectionSchema = z.strictObject({ diff --git a/packages/sdk/schemas/common.ts b/packages/sdk/schemas/common.ts index 63cc13ba..995ddc0e 100644 --- a/packages/sdk/schemas/common.ts +++ b/packages/sdk/schemas/common.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; export const f64 = z .number() @@ -11,4 +11,4 @@ export const u64 = z .nonnegative() .max(2 ** 64 - 1); -export const url = z.string().url(); +export const url = z.url(); diff --git a/packages/sdk/schemas/contentformat.ts b/packages/sdk/schemas/contentformat.ts index 5605a9fc..c24f85b4 100644 --- a/packages/sdk/schemas/contentformat.ts +++ b/packages/sdk/schemas/contentformat.ts @@ -1,5 +1,5 @@ import { types } from "mime-types"; -import { z } from "zod"; +import { z } from "zod/v4"; import { f64, u64 } from "./common.ts"; const hashSizes = { @@ -39,10 +39,10 @@ const audioMimeTypes = Object.values(types).filter((v) => v.startsWith("audio/"), ) as [string, ...string[]]; -export const ContentFormatSchema = z.record( +export const ContentFormatSchema = z.partialRecord( z.enum(allMimeTypes), z.strictObject({ - content: z.string().or(z.string().url()), + content: z.string().or(z.url()), remote: z.boolean(), description: z.string().nullish(), size: u64.nullish(), @@ -64,9 +64,9 @@ export const ContentFormatSchema = z.record( }), ); -export const TextContentFormatSchema = z.record( +export const TextContentFormatSchema = z.partialRecord( z.enum(textMimeTypes), - ContentFormatSchema.valueSchema + ContentFormatSchema.valueType .pick({ content: true, remote: true, @@ -77,9 +77,9 @@ export const TextContentFormatSchema = z.record( }), ); -export const NonTextContentFormatSchema = z.record( +export const NonTextContentFormatSchema = z.partialRecord( z.enum(nonTextMimeTypes), - ContentFormatSchema.valueSchema + ContentFormatSchema.valueType .pick({ content: true, remote: true, @@ -91,27 +91,27 @@ export const NonTextContentFormatSchema = z.record( height: true, }) .extend({ - content: z.string().url(), + content: z.url(), remote: z.literal(true), }), ); -export const ImageContentFormatSchema = z.record( +export const ImageContentFormatSchema = z.partialRecord( z.enum(imageMimeTypes), - NonTextContentFormatSchema.valueSchema, + NonTextContentFormatSchema.valueType, ); -export const VideoContentFormatSchema = z.record( +export const VideoContentFormatSchema = z.partialRecord( z.enum(videoMimeTypes), - NonTextContentFormatSchema.valueSchema.extend({ - duration: ContentFormatSchema.valueSchema.shape.duration, - fps: ContentFormatSchema.valueSchema.shape.fps, + NonTextContentFormatSchema.valueType.extend({ + duration: ContentFormatSchema.valueType.shape.duration, + fps: ContentFormatSchema.valueType.shape.fps, }), ); -export const AudioContentFormatSchema = z.record( +export const AudioContentFormatSchema = z.partialRecord( z.enum(audioMimeTypes), - NonTextContentFormatSchema.valueSchema.extend({ - duration: ContentFormatSchema.valueSchema.shape.duration, + NonTextContentFormatSchema.valueType.extend({ + duration: ContentFormatSchema.valueType.shape.duration, }), ); diff --git a/packages/sdk/schemas/delete.ts b/packages/sdk/schemas/delete.ts index 9cd7d9a8..1cbbd9c2 100644 --- a/packages/sdk/schemas/delete.ts +++ b/packages/sdk/schemas/delete.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { url } from "./common.ts"; import { EntitySchema } from "./entity.ts"; diff --git a/packages/sdk/schemas/entity.ts b/packages/sdk/schemas/entity.ts index 9a7ba8c9..3bd5738d 100644 --- a/packages/sdk/schemas/entity.ts +++ b/packages/sdk/schemas/entity.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { isISOString } from "../regex.ts"; import { url } from "./common.ts"; import { CustomEmojiExtensionSchema } from "./extensions/emojis.ts"; @@ -12,7 +12,7 @@ export const ExtensionPropertySchema = z export const EntitySchema = z.strictObject({ // biome-ignore lint/style/useNamingConvention: required for JSON schema - $schema: z.string().url().nullish(), + $schema: z.url().nullish(), id: z.string().max(512), created_at: z .string() diff --git a/packages/sdk/schemas/extensions/emojis.ts b/packages/sdk/schemas/extensions/emojis.ts index d075f9b4..cba32ee8 100644 --- a/packages/sdk/schemas/extensions/emojis.ts +++ b/packages/sdk/schemas/extensions/emojis.ts @@ -4,7 +4,7 @@ * @see module:federation/schemas/base * @see https://versia.pub/extensions/custom-emojis */ -import { z } from "zod"; +import { z } from "zod/v4"; import { emojiRegex } from "../../regex.ts"; import { ImageContentFormatSchema } from "../contentformat.ts"; diff --git a/packages/sdk/schemas/extensions/groups.ts b/packages/sdk/schemas/extensions/groups.ts index 8c246056..1fef7a79 100644 --- a/packages/sdk/schemas/extensions/groups.ts +++ b/packages/sdk/schemas/extensions/groups.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { url } from "../common.ts"; import { TextContentFormatSchema } from "../contentformat.ts"; import { EntitySchema } from "../entity.ts"; diff --git a/packages/sdk/schemas/extensions/likes.ts b/packages/sdk/schemas/extensions/likes.ts index 75208fb7..33c6d0c8 100644 --- a/packages/sdk/schemas/extensions/likes.ts +++ b/packages/sdk/schemas/extensions/likes.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { url } from "../common.ts"; import { EntitySchema } from "../entity.ts"; diff --git a/packages/sdk/schemas/extensions/migration.ts b/packages/sdk/schemas/extensions/migration.ts index da215ce3..8d276542 100644 --- a/packages/sdk/schemas/extensions/migration.ts +++ b/packages/sdk/schemas/extensions/migration.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { url } from "../common.ts"; import { EntitySchema } from "../entity.ts"; diff --git a/packages/sdk/schemas/extensions/polls.ts b/packages/sdk/schemas/extensions/polls.ts index 09741b8c..1fbbc800 100644 --- a/packages/sdk/schemas/extensions/polls.ts +++ b/packages/sdk/schemas/extensions/polls.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { isISOString } from "../../regex.ts"; import { u64, url } from "../common.ts"; import { TextContentFormatSchema } from "../contentformat.ts"; diff --git a/packages/sdk/schemas/extensions/reactions.ts b/packages/sdk/schemas/extensions/reactions.ts index 99b7162d..c2645588 100644 --- a/packages/sdk/schemas/extensions/reactions.ts +++ b/packages/sdk/schemas/extensions/reactions.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { url } from "../common.ts"; import { EntitySchema } from "../entity.ts"; diff --git a/packages/sdk/schemas/extensions/reports.ts b/packages/sdk/schemas/extensions/reports.ts index c49bc8db..8c6886aa 100644 --- a/packages/sdk/schemas/extensions/reports.ts +++ b/packages/sdk/schemas/extensions/reports.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { url } from "../common.ts"; import { EntitySchema } from "../entity.ts"; diff --git a/packages/sdk/schemas/extensions/share.ts b/packages/sdk/schemas/extensions/share.ts index 20968a08..a9acd249 100644 --- a/packages/sdk/schemas/extensions/share.ts +++ b/packages/sdk/schemas/extensions/share.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { url } from "../common.ts"; import { EntitySchema } from "../entity.ts"; diff --git a/packages/sdk/schemas/extensions/vanity.ts b/packages/sdk/schemas/extensions/vanity.ts index 6d47136a..e9e49c0a 100644 --- a/packages/sdk/schemas/extensions/vanity.ts +++ b/packages/sdk/schemas/extensions/vanity.ts @@ -5,7 +5,7 @@ * @see https://versia.pub/extensions/vanity */ -import { z } from "zod"; +import { z } from "zod/v4"; import { ianaTimezoneRegex, isISOString } from "../../regex.ts"; import { url } from "../common.ts"; import { diff --git a/packages/sdk/schemas/follow.ts b/packages/sdk/schemas/follow.ts index 62c89c32..1b9b8a0e 100644 --- a/packages/sdk/schemas/follow.ts +++ b/packages/sdk/schemas/follow.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { url } from "./common.ts"; import { EntitySchema } from "./entity.ts"; diff --git a/packages/sdk/schemas/instance.ts b/packages/sdk/schemas/instance.ts index e62648b5..0ed5b8f4 100644 --- a/packages/sdk/schemas/instance.ts +++ b/packages/sdk/schemas/instance.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { extensionRegex, semverRegex } from "../regex.ts"; import { url } from "./common.ts"; import { ImageContentFormatSchema } from "./contentformat.ts"; diff --git a/packages/sdk/schemas/note.ts b/packages/sdk/schemas/note.ts index 9d58beca..2afeacd6 100644 --- a/packages/sdk/schemas/note.ts +++ b/packages/sdk/schemas/note.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { url } from "./common.ts"; import { NonTextContentFormatSchema, diff --git a/packages/sdk/schemas/user.ts b/packages/sdk/schemas/user.ts index aeaf1256..763ca7b6 100644 --- a/packages/sdk/schemas/user.ts +++ b/packages/sdk/schemas/user.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { url } from "./common.ts"; import { ImageContentFormatSchema, diff --git a/packages/sdk/schemas/webfinger.ts b/packages/sdk/schemas/webfinger.ts index 524d2f00..3b97e2e2 100644 --- a/packages/sdk/schemas/webfinger.ts +++ b/packages/sdk/schemas/webfinger.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod/v4"; import { url } from "./common.ts"; export const WebFingerSchema = z.object({ diff --git a/tests/utils.ts b/packages/tests/index.ts similarity index 95% rename from tests/utils.ts rename to packages/tests/index.ts index 410906c3..96468b82 100644 --- a/tests/utils.ts +++ b/packages/tests/index.ts @@ -1,16 +1,15 @@ import { mock } from "bun:test"; import { Client as VersiaClient } from "@versia/client"; -import { db, Note, Token, User } from "@versia/kit/db"; -import { Notes, Users } from "@versia/kit/tables"; +import { config } from "@versia-server/config"; +import { db, Note, setupDatabase, Token, User } from "@versia-server/kit/db"; +import { searchManager } from "@versia-server/kit/search"; +import { Notes, Users } from "@versia-server/kit/tables"; import { solveChallenge } from "altcha-lib"; import { env, randomUUIDv7 } from "bun"; import { asc, type InferSelectModel, inArray, like } from "drizzle-orm"; import { generateChallenge } from "@/challenges"; import { randomString } from "@/math"; -import { appFactory } from "~/app"; -import { searchManager } from "~/classes/search/search-manager"; -import { config } from "~/config.ts"; -import { setupDatabase } from "~/drizzle/db"; +import { appFactory } from "~/packages/api/app"; env.DISABLE_RATE_LIMIT = "true"; await setupDatabase(); diff --git a/packages/tests/package.json b/packages/tests/package.json new file mode 100644 index 00000000..f149b8b2 --- /dev/null +++ b/packages/tests/package.json @@ -0,0 +1,19 @@ +{ + "name": "@versia-server/tests", + "module": "index.ts", + "type": "module", + "version": "0.0.1", + "private": true, + "exports": { + ".": { + "import": "./index.ts" + } + }, + "dependencies": { + "@versia-server/config": "workspace:*", + "@versia-server/kit": "workspace:*", + "@versia/client": "workspace:*", + "altcha-lib": "catalog:", + "drizzle-orm": "catalog:" + } +} diff --git a/packages/tests/setup.ts b/packages/tests/setup.ts new file mode 100644 index 00000000..a578afb8 --- /dev/null +++ b/packages/tests/setup.ts @@ -0,0 +1,8 @@ +import { setupDatabase } from "@versia-server/kit/db"; +import { deleteOldTestUsers } from "./index.ts"; + +await setupDatabase(); +await deleteOldTestUsers(); + +// Start workers +await import("../../worker.ts"); diff --git a/packages/worker/build.ts b/packages/worker/build.ts new file mode 100644 index 00000000..908e5d50 --- /dev/null +++ b/packages/worker/build.ts @@ -0,0 +1,19 @@ +import { $, build } from "bun"; +import manifest from "./package.json" with { type: "json" }; + +console.log("Building..."); + +await $`rm -rf dist && mkdir dist`; + +await build({ + entrypoints: Object.values(manifest.exports).map((entry) => entry.import), + outdir: "dist", + target: "bun", + splitting: true, + minify: true, + external: [ + ...Object.keys(manifest.dependencies).filter((dep) => + dep.startsWith("@versia"), + ), + ], +}); diff --git a/packages/worker/package.json b/packages/worker/package.json new file mode 100644 index 00000000..26af6f52 --- /dev/null +++ b/packages/worker/package.json @@ -0,0 +1,54 @@ +{ + "name": "@versia-server/worker", + "module": "index.ts", + "type": "module", + "version": "0.9.0-alpha.0", + "description": "Powerful, configurable and modular federated server using the Versia Protocol.", + "homepage": "https://versia.pub", + "author": { + "email": "contact@cpluspatch.com", + "name": "Jesse Wierzbinski", + "url": "https://cpluspatch.com" + }, + "bugs": { + "url": "https://github.com/versia-pub/server/issues" + }, + "icon": "https://cdn.versia.pub/branding/icon.svg", + "license": "AGPL-3.0-or-later", + "keywords": [ + "federated", + "activitypub", + "bun" + ], + "maintainers": [ + { + "email": "contact@cpluspatch.com", + "name": "Jesse Wierzbinski", + "url": "https://cpluspatch.com" + } + ], + "repository": { + "type": "git", + "url": "git+https://github.com/versia-pub/server.git", + "directory": "packages/worker" + }, + "private": true, + "scripts": { + "dev": "bun run --hot index.ts", + "build": "bun run build.ts" + }, + "exports": { + ".": { + "import": "./workers.ts" + }, + "./setup": { + "import": "./setup.ts" + } + }, + "dependencies": { + "@versia-server/config": "workspace:*", + "@versia-server/kit": "workspace:*", + "@versia-server/logging": "workspace:*", + "chalk": "catalog:" + } +} diff --git a/entrypoints/worker/setup.ts b/packages/worker/setup.ts similarity index 78% rename from entrypoints/worker/setup.ts rename to packages/worker/setup.ts index f51c2e70..3d684288 100644 --- a/entrypoints/worker/setup.ts +++ b/packages/worker/setup.ts @@ -1,18 +1,12 @@ -import { getLogger } from "@logtape/logtape"; -import { Note } from "@versia/kit/db"; +import { config } from "@versia-server/config"; +import { Note, setupDatabase } from "@versia-server/kit/db"; +import { connection } from "@versia-server/kit/redis"; +import { searchManager } from "@versia-server/kit/search"; +import { serverLogger } from "@versia-server/logging"; import chalk from "chalk"; -import { configureLoggers } from "@/loggers"; -import { connection } from "@/redis.ts"; -import { config } from "~/config.ts"; -import { setupDatabase } from "~/drizzle/db"; -import { searchManager } from "../../classes/search/search-manager.ts"; const timeAtStart = performance.now(); -await configureLoggers(); - -const serverLogger = getLogger("server"); - console.info(` ██╗ ██╗███████╗██████╗ ███████╗██╗ █████╗ ██║ ██║██╔════╝██╔══██╗██╔════╝██║██╔══██╗ diff --git a/packages/worker/workers.ts b/packages/worker/workers.ts new file mode 100644 index 00000000..d7691877 --- /dev/null +++ b/packages/worker/workers.ts @@ -0,0 +1,15 @@ +import { getDeliveryWorker } from "@versia-server/kit/queues/delivery/worker"; +import { getFetchWorker } from "@versia-server/kit/queues/fetch/worker"; +import { getInboxWorker } from "@versia-server/kit/queues/inbox/worker"; +import { getMediaWorker } from "@versia-server/kit/queues/media/worker"; +import { getPushWorker } from "@versia-server/kit/queues/push/worker"; +import { getRelationshipWorker } from "@versia-server/kit/queues/relationships/worker"; + +export const workers = { + fetch: getFetchWorker, + delivery: getDeliveryWorker, + inbox: getInboxWorker, + push: getPushWorker, + media: getMediaWorker, + relationship: getRelationshipWorker, +} as const; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml deleted file mode 100644 index ae3ffd53..00000000 --- a/pnpm-lock.yaml +++ /dev/null @@ -1,6355 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -importers: - - .: - dependencies: - '@bull-board/api': - specifier: ^6.10.1 - version: 6.10.1(@bull-board/ui@6.10.1) - '@bull-board/hono': - specifier: ^6.10.1 - version: 6.10.1(hono@4.7.11) - '@clerc/plugin-completions': - specifier: ^0.44.0 - version: 0.44.0(@clerc/core@0.44.0) - '@clerc/plugin-friendly-error': - specifier: ^0.44.0 - version: 0.44.0(@clerc/core@0.44.0) - '@clerc/plugin-help': - specifier: ^0.44.0 - version: 0.44.0(@clerc/core@0.44.0) - '@clerc/plugin-not-found': - specifier: ^0.44.0 - version: 0.44.0(@clerc/core@0.44.0) - '@clerc/plugin-version': - specifier: ^0.44.0 - version: 0.44.0(@clerc/core@0.44.0) - '@hackmd/markdown-it-task-lists': - specifier: ^2.1.4 - version: 2.1.4 - '@hono/zod-validator': - specifier: ^0.7.0 - version: 0.7.0(hono@4.7.11)(zod@3.25.64) - '@inquirer/confirm': - specifier: ^5.1.12 - version: 5.1.12(@types/node@22.15.3) - '@logtape/file': - specifier: ^0.12.0 - version: 0.12.0(@logtape/logtape@0.12.0) - '@logtape/logtape': - specifier: ^0.12.0 - version: 0.12.0 - '@scalar/hono-api-reference': - specifier: ^0.9.4 - version: 0.9.4(hono@4.7.11) - '@sentry/bun': - specifier: ^9.29.0 - version: 9.29.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.34.0) - '@versia/client': - specifier: workspace:* - version: link:packages/client - '@versia/kit': - specifier: workspace:* - version: link:packages/plugin-kit - '@versia/sdk': - specifier: workspace:* - version: link:packages/sdk - altcha-lib: - specifier: ^1.3.0 - version: 1.3.0 - blurhash: - specifier: ^2.0.5 - version: 2.0.5 - bullmq: - specifier: ^5.53.3 - version: 5.53.3 - chalk: - specifier: ^5.4.1 - version: 5.4.1 - clerc: - specifier: ^0.44.0 - version: 0.44.0 - confbox: - specifier: ^0.2.2 - version: 0.2.2 - drizzle-orm: - specifier: ^0.44.2 - version: 0.44.2(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(bun-types@1.2.16) - feed: - specifier: ^5.1.0 - version: 5.1.0 - hono: - specifier: ^4.7.11 - version: 4.7.11 - hono-openapi: - specifier: ^0.4.8 - version: 0.4.8(@hono/zod-validator@0.7.0(hono@4.7.11)(zod@3.25.64))(hono@4.7.11)(openapi-types@12.1.3)(zod-openapi@4.2.4(zod@3.25.64))(zod@3.25.64) - hono-rate-limiter: - specifier: ^0.4.2 - version: 0.4.2(hono@4.7.11) - html-to-text: - specifier: ^9.0.5 - version: 9.0.5 - ioredis: - specifier: ^5.6.1 - version: 5.6.1 - ip-matching: - specifier: ^2.1.2 - version: 2.1.2 - iso-639-1: - specifier: ^3.1.5 - version: 3.1.5 - jose: - specifier: ^6.0.11 - version: 6.0.11 - linkify-html: - specifier: ^4.3.1 - version: 4.3.1(linkifyjs@4.3.1) - linkify-string: - specifier: ^4.3.1 - version: 4.3.1(linkifyjs@4.3.1) - linkifyjs: - specifier: ^4.3.1 - version: 4.3.1 - magic-regexp: - specifier: ^0.10.0 - version: 0.10.0 - markdown-it: - specifier: ^14.1.0 - version: 14.1.0 - markdown-it-anchor: - specifier: ^9.2.0 - version: 9.2.0(@types/markdown-it@14.1.2)(markdown-it@14.1.0) - markdown-it-container: - specifier: ^4.0.0 - version: 4.0.0 - markdown-it-mathjax3: - specifier: ^4.3.2 - version: 4.3.2 - markdown-it-toc-done-right: - specifier: ^4.2.0 - version: 4.2.0 - mime-types: - specifier: ^3.0.1 - version: 3.0.1 - mitata: - specifier: ^1.0.34 - version: 1.0.34 - oauth4webapi: - specifier: ^3.5.2 - version: 3.5.2 - ora: - specifier: ^8.2.0 - version: 8.2.0 - qs: - specifier: ^6.14.0 - version: 6.14.0 - sharp: - specifier: ^0.34.2 - version: 0.34.2 - sonic-channel: - specifier: ^1.3.1 - version: 1.3.1 - string-comparison: - specifier: ^1.3.0 - version: 1.3.0 - stringify-entities: - specifier: ^4.0.4 - version: 4.0.4 - unicode-emoji-json: - specifier: ^0.8.0 - version: 0.8.0 - uqr: - specifier: ^0.1.2 - version: 0.1.2 - web-push: - specifier: ^3.6.7 - version: 3.6.7 - xss: - specifier: ^1.0.15 - version: 1.0.15 - youch: - specifier: ^4.1.0-beta.7 - version: 4.1.0-beta.7 - zod: - specifier: ^3.25.64 - version: 3.25.64 - zod-openapi: - specifier: ^4.2.4 - version: 4.2.4(zod@3.25.64) - zod-validation-error: - specifier: ^3.5.0 - version: 3.5.0(zod@3.25.64) - devDependencies: - '@biomejs/biome': - specifier: 2.0.0-beta.5 - version: 2.0.0-beta.5 - '@types/bun': - specifier: ^1.2.16 - version: 1.2.16 - '@types/html-to-text': - specifier: ^9.0.4 - version: 9.0.4 - '@types/markdown-it-container': - specifier: ^2.0.10 - version: 2.0.10 - '@types/mime-types': - specifier: ^3.0.1 - version: 3.0.1 - '@types/qs': - specifier: ^6.14.0 - version: 6.14.0 - '@types/web-push': - specifier: ^3.6.4 - version: 3.6.4 - bun-bagel: - specifier: ^1.2.0 - version: 1.2.0(typescript@5.8.3) - drizzle-kit: - specifier: ^0.31.1 - version: 0.31.1 - markdown-it-image-figures: - specifier: ^2.1.1 - version: 2.1.1(markdown-it@14.1.0) - ts-prune: - specifier: ^0.10.3 - version: 0.10.3 - typescript: - specifier: ^5.8.3 - version: 5.8.3 - vitepress: - specifier: ^1.6.3 - version: 1.6.3(@algolia/client-search@5.24.0)(@types/node@22.15.3)(markdown-it-mathjax3@4.3.2)(postcss@8.5.3)(search-insights@2.17.3)(typescript@5.8.3) - vitepress-plugin-tabs: - specifier: ^0.7.1 - version: 0.7.1(vitepress@1.6.3(@algolia/client-search@5.24.0)(@types/node@22.15.3)(markdown-it-mathjax3@4.3.2)(postcss@8.5.3)(search-insights@2.17.3)(typescript@5.8.3))(vue@3.5.16(typescript@5.8.3)) - vitepress-sidebar: - specifier: ^1.31.1 - version: 1.31.1 - vue: - specifier: ^3.5.16 - version: 3.5.16(typescript@5.8.3) - zod-to-json-schema: - specifier: ^3.24.5 - version: 3.24.5(zod@3.25.64) - - packages/client: - dependencies: - '@badgateway/oauth2-client': - specifier: ^3.0.0 - version: 3.2.0 - iso-639-1: - specifier: ^3.1.5 - version: 3.1.5 - magic-regexp: - specifier: ^0.10.0 - version: 0.10.0 - zod: - specifier: ^3.24.2 - version: 3.24.3 - zod-openapi: - specifier: ^4.2.4 - version: 4.2.4(zod@3.24.3) - - packages/plugin-kit: - dependencies: - drizzle-orm: - specifier: ^0.44.2 - version: 0.44.2(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(bun-types@1.2.16) - hono: - specifier: ^4.7.7 - version: 4.7.8 - mitt: - specifier: ^3.0.1 - version: 3.0.1 - zod: - specifier: ^3.24.2 - version: 3.24.3 - zod-to-json-schema: - specifier: ^3.24.5 - version: 3.24.5(zod@3.24.3) - zod-validation-error: - specifier: ^3.4.0 - version: 3.4.0(zod@3.24.3) - - packages/sdk: - dependencies: - magic-regexp: - specifier: ^0.10.0 - version: 0.10.0 - mime-types: - specifier: ^3.0.1 - version: 3.0.1 - zod: - specifier: ^3.24.2 - version: 3.24.3 - -packages: - - '@algolia/autocomplete-core@1.17.7': - resolution: {integrity: sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==} - - '@algolia/autocomplete-plugin-algolia-insights@1.17.7': - resolution: {integrity: sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==} - peerDependencies: - search-insights: '>= 1 < 3' - - '@algolia/autocomplete-preset-algolia@1.17.7': - resolution: {integrity: sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==} - peerDependencies: - '@algolia/client-search': '>= 4.9.1 < 6' - algoliasearch: '>= 4.9.1 < 6' - - '@algolia/autocomplete-shared@1.17.7': - resolution: {integrity: sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==} - peerDependencies: - '@algolia/client-search': '>= 4.9.1 < 6' - algoliasearch: '>= 4.9.1 < 6' - - '@algolia/client-abtesting@5.24.0': - resolution: {integrity: sha512-pNTIB5YqVVwu6UogvdX8TqsRZENaflqMMjdY7/XIPMNGrBoNH9tewINLI7+qc9tIaOLcAp3ZldqoEwAihZZ3ig==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-analytics@5.24.0': - resolution: {integrity: sha512-IF+r9RRQsIf0ylIBNFxo7c6hDxxuhIfIbffhBXEF1HD13rjhP5AVfiaea9RzbsAZoySkm318plDpH/nlGIjbRA==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-common@5.24.0': - resolution: {integrity: sha512-p8K6tiXQTebRBxbrzWIfGCvfkT+Umml+2lzI92acZjHsvl6KYH6igOfVstKqXJRei9pvRzEEvVDNDLXDVleGTA==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-insights@5.24.0': - resolution: {integrity: sha512-jOHF0+tixR3IZJMhZPquFNdCVPzwzzXoiqVsbTvfKojeaY6ZXybgUiTSB8JNX+YpsUT8Ebhu3UvRy4mw2PbEzw==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-personalization@5.24.0': - resolution: {integrity: sha512-Fx/Fp6d8UmDBHecTt0XYF8C9TAaA3qeCQortfGSZzWp4gVmtrUCFNZ1SUwb8ULREnO9DanVrM5hGE8R8C4zZTQ==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-query-suggestions@5.24.0': - resolution: {integrity: sha512-F8ypOedSMhz6W7zuT5O1SXXsdXSOVhY2U6GkRbYk/mzrhs3jWFR3uQIfeQVWmsJjUwIGZmPoAr9E+T/Zm2M4wA==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-search@5.24.0': - resolution: {integrity: sha512-k+nuciQuq7WERNNE+hsx3DX636zIy+9R4xdtvW3PANT2a2BDGOv3fv2mta8+QUMcVTVcGe/Mo3QCb4pc1HNoxA==} - engines: {node: '>= 14.0.0'} - - '@algolia/ingestion@1.24.0': - resolution: {integrity: sha512-/lqVxmrvwoA+OyVK4XLMdz/PJaCTW4qYchX1AZ+98fdnH3K6XM/kMydQLfP0bUNGBQbmVrF88MqhqZRnZEn/MA==} - engines: {node: '>= 14.0.0'} - - '@algolia/monitoring@1.24.0': - resolution: {integrity: sha512-cRisDXQJhvfZCXL4hD22qca2CmW52TniOx6L7pvkaBDx0oQk1k9o+3w11fgfcCG+47OndMeNx5CMpu+K+COMzg==} - engines: {node: '>= 14.0.0'} - - '@algolia/recommend@5.24.0': - resolution: {integrity: sha512-JTMz0JqN2gidvKa2QCF/rMe8LNtdHaght03px2cluZaZfBRYy8TgHgkCeBspKKvV/abWJwl7J0FzWThCshqT3w==} - engines: {node: '>= 14.0.0'} - - '@algolia/requester-browser-xhr@5.24.0': - resolution: {integrity: sha512-B2Gc+iSxct1WSza5CF6AgfNgmLvVb61d5bqmIWUZixtJIhyAC6lSQZuF+nvt+lmKhQwuY2gYjGGClil8onQvKQ==} - engines: {node: '>= 14.0.0'} - - '@algolia/requester-fetch@5.24.0': - resolution: {integrity: sha512-6E5+hliqGc5w8ZbyTAQ+C3IGLZ/GiX623Jl2bgHA974RPyFWzVSj4rKqkboUAxQmrFY7Z02ybJWVZS5OhPQocA==} - engines: {node: '>= 14.0.0'} - - '@algolia/requester-node-http@5.24.0': - resolution: {integrity: sha512-zM+nnqZpiQj20PyAh6uvgdSz+hD7Rj7UfAZwizqNP+bLvcbGXZwABERobuilkCQqyDBBH4uv0yqIcPRl8dSBEg==} - engines: {node: '>= 14.0.0'} - - '@apidevtools/json-schema-ref-parser@11.9.3': - resolution: {integrity: sha512-60vepv88RwcJtSHrD6MjIL6Ta3SOYbgfnkHb+ppAVK+o9mXprRtulx7VlRl3lN3bbvysAfCS7WMVfhUYemB0IQ==} - engines: {node: '>= 16'} - - '@babel/code-frame@7.27.1': - resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} - engines: {node: '>=6.9.0'} - - '@babel/helper-string-parser@7.27.1': - resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-identifier@7.27.1': - resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} - engines: {node: '>=6.9.0'} - - '@babel/parser@7.27.2': - resolution: {integrity: sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==} - engines: {node: '>=6.0.0'} - hasBin: true - - '@babel/runtime@7.27.1': - resolution: {integrity: sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==} - engines: {node: '>=6.9.0'} - - '@babel/types@7.27.1': - resolution: {integrity: sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==} - engines: {node: '>=6.9.0'} - - '@badgateway/oauth2-client@3.2.0': - resolution: {integrity: sha512-EHsoV6oLHot7HeYkIoSxCZApNgBjwNo1OTV9kXIDnmijGAshlVkJreVAAtexFn+sfDKPE0JW5SCPYJV1y4IoMg==} - engines: {node: '>= 18'} - - '@biomejs/biome@2.0.0-beta.5': - resolution: {integrity: sha512-1ldO4AepieVvg4aLi1ubZkA7NsefQT2UTNssbJbDiQTGem8kCHx/PZCwLxIR6UzFpGIjh0xsDzivyVvhnmqmuA==} - engines: {node: '>=14.21.3'} - hasBin: true - - '@biomejs/cli-darwin-arm64@2.0.0-beta.5': - resolution: {integrity: sha512-pnJiaoDpwGo+ctGkMu4POcO8jgOgCErBdYbhutr+K9rxxJS+TlHLr0LR91GCEWbGV2O1oyZRFQcW21rYFoak4w==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [darwin] - - '@biomejs/cli-darwin-x64@2.0.0-beta.5': - resolution: {integrity: sha512-WwEZpqcmsNoFpZkUFNQcbZo52WK4hLGQ0vZk3PQ8JlZ55gJsHiyhtv6aem6fVlyVCvZgpsC0sYPLE3VvFVKNAQ==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [darwin] - - '@biomejs/cli-linux-arm64-musl@2.0.0-beta.5': - resolution: {integrity: sha512-4vxNkYx1uEt211W8hLdXddc7icRHQgYENb72g6uTd/tLVPSBvIwqUAxAOkU+9Ai1E/8R4sWy7HIxREgpuFgbNA==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [linux] - - '@biomejs/cli-linux-arm64@2.0.0-beta.5': - resolution: {integrity: sha512-lAF1de+Ki0vnq14NwDXouKkAR/iviyMNrUngSHjTGFC4z8XGVEfIw0ZMSm7fAdJZ5fAWodt9HiYmEAVs5EtHQg==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [linux] - - '@biomejs/cli-linux-x64-musl@2.0.0-beta.5': - resolution: {integrity: sha512-nUeKGO517GtRCxziVD9les1HiCs2s2/WIVITMN9+9RRuLOko8r+T77E8ZXEmlfLOfOIOeE6z62WITqei3oNccA==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [linux] - - '@biomejs/cli-linux-x64@2.0.0-beta.5': - resolution: {integrity: sha512-I0Pt1VHeL1mN8G7ZwV2u9AfzBd5ZKfbvHUI4x2wETUZbwcQlAu/nEzEa2LUe5HqSmnctTR36ig7RkkM9qbmIrA==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [linux] - - '@biomejs/cli-win32-arm64@2.0.0-beta.5': - resolution: {integrity: sha512-YXW6hgbrgBcWQ1SLO69ypWlluPchgQV5C1lTG4xOcBUWdCsfYuQirM64S6Dov7SFPqsMIoFC6LlQRW+n8qAyiA==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [win32] - - '@biomejs/cli-win32-x64@2.0.0-beta.5': - resolution: {integrity: sha512-N7Yby52BJmvEdst1iMbclE5hxxefboaXKRJLm1tLfBYr4FeuoCe6j8HdiQSwhCRdIUGFFqBLaDXh//LLF6EReA==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [win32] - - '@bull-board/api@6.10.1': - resolution: {integrity: sha512-VPkZa2XZI2Wk2MqK1XyiiS+tOhNan54mnm2fpv2KA0fdZ92mQqNjhKkOpsykhQv9XUEc8cCRlZqGxf67YCMJbQ==} - peerDependencies: - '@bull-board/ui': 6.10.1 - - '@bull-board/hono@6.10.1': - resolution: {integrity: sha512-2I9BUS7jbtQ4tCKOJdVKQn6uW8MXJKUGIhHLK2r4X8kiXZvk2I7jjt0KZ4VNyF9nfoBblgX5WemxE4sU61kGGg==} - peerDependencies: - hono: ^4 - - '@bull-board/ui@6.10.1': - resolution: {integrity: sha512-b6z6MBid/0DEShAMFPjPVZoPSoWqRBHCvTknyaxr/m8gL2/C+QP7jlCXut+L7uTFbCj9qs+CreAP0x/VdLI/Ig==} - - '@clerc/core@0.44.0': - resolution: {integrity: sha512-o8RgXNcMRoHRujSw9OPDMxqrmoNk7HG0XAZkjZgOrSyIfRXCf85VLyHGBT3XmaOrPEGY964h02ZxMVFdp8RnNQ==} - - '@clerc/plugin-completions@0.44.0': - resolution: {integrity: sha512-r69KpaB+EcWccqe31OwK5iyJQZmgmhxJjEBL4RAGlRr2tu6MRX42AOmD3GDW+ZPHkc4D9NJdkqukLboTJlbycA==} - peerDependencies: - '@clerc/core': '*' - - '@clerc/plugin-friendly-error@0.44.0': - resolution: {integrity: sha512-hbGp+T+fl9Dz+Ln1LGoijbNL7N+RfBqhSRWtT55QdFPs86O7Mi5oqiqIrUS9wDpaHzJGn+ETFPIOUyDRdrD8ag==} - peerDependencies: - '@clerc/core': '*' - - '@clerc/plugin-help@0.44.0': - resolution: {integrity: sha512-QIH+Lrk6WZtXKNxEAA4gOk7dwseS7U0jTZ0TbJfcyOoNA3fF2p48UV8c7hmKk7OhfPS5009eJRW5CVQEgBB8Ng==} - peerDependencies: - '@clerc/core': '*' - - '@clerc/plugin-not-found@0.44.0': - resolution: {integrity: sha512-8GIxDv/V2qaKRJKy+jjHAmlCEbTabLYPIeklqY231skiNOYCDQ3wJS3isP7EQgARfhm0otMDRXPPC2aNfSJjRw==} - peerDependencies: - '@clerc/core': '*' - - '@clerc/plugin-strict-flags@0.44.0': - resolution: {integrity: sha512-8ztQrn4pfCj6b/sDN1mU0MVlfSqp3qHohW59v6k6nS3UUnYytJDU5wZQZmQ+7t3DZqjw+Xle6ZzEIWQatAdZ9A==} - peerDependencies: - '@clerc/core': '*' - - '@clerc/plugin-version@0.44.0': - resolution: {integrity: sha512-YETH54A0sO32oJcLABpb4P5FyhEkhIhe5oe3IXyeUj9/LMcInvKCm6x/gDMIUjTQuh0a5l4iton0A1RscAANhw==} - peerDependencies: - '@clerc/core': '*' - - '@clerc/utils@0.44.0': - resolution: {integrity: sha512-//1zl8UgVhv1NbqsRoCWWci0Y9uBxzAVn8TqoKZchDywGQNZWK6vQI/Ms9uGe3+PZTDXedoXbVjklOINcVC2aA==} - peerDependencies: - '@clerc/core': '*' - - '@docsearch/css@3.8.2': - resolution: {integrity: sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==} - - '@docsearch/js@3.8.2': - resolution: {integrity: sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ==} - - '@docsearch/react@3.8.2': - resolution: {integrity: sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg==} - peerDependencies: - '@types/react': '>= 16.8.0 < 19.0.0' - react: '>= 16.8.0 < 19.0.0' - react-dom: '>= 16.8.0 < 19.0.0' - search-insights: '>= 1 < 3' - peerDependenciesMeta: - '@types/react': - optional: true - react: - optional: true - react-dom: - optional: true - search-insights: - optional: true - - '@drizzle-team/brocli@0.10.2': - resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==} - - '@emnapi/runtime@1.4.3': - resolution: {integrity: sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==} - - '@esbuild-kit/core-utils@3.3.2': - resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==} - deprecated: 'Merged into tsx: https://tsx.is' - - '@esbuild-kit/esm-loader@2.6.5': - resolution: {integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==} - deprecated: 'Merged into tsx: https://tsx.is' - - '@esbuild/aix-ppc64@0.21.5': - resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [aix] - - '@esbuild/aix-ppc64@0.25.3': - resolution: {integrity: sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - - '@esbuild/android-arm64@0.18.20': - resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm64@0.21.5': - resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm64@0.25.3': - resolution: {integrity: sha512-XelR6MzjlZuBM4f5z2IQHK6LkK34Cvv6Rj2EntER3lwCBFdg6h2lKbtRjpTTsdEjD/WSe1q8UyPBXP1x3i/wYQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm@0.18.20': - resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} - engines: {node: '>=12'} - cpu: [arm] - os: [android] - - '@esbuild/android-arm@0.21.5': - resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} - engines: {node: '>=12'} - cpu: [arm] - os: [android] - - '@esbuild/android-arm@0.25.3': - resolution: {integrity: sha512-PuwVXbnP87Tcff5I9ngV0lmiSu40xw1At6i3GsU77U7cjDDB4s0X2cyFuBiDa1SBk9DnvWwnGvVaGBqoFWPb7A==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - - '@esbuild/android-x64@0.18.20': - resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - - '@esbuild/android-x64@0.21.5': - resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - - '@esbuild/android-x64@0.25.3': - resolution: {integrity: sha512-ogtTpYHT/g1GWS/zKM0cc/tIebFjm1F9Aw1boQ2Y0eUQ+J89d0jFY//s9ei9jVIlkYi8AfOjiixcLJSGNSOAdQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - - '@esbuild/darwin-arm64@0.18.20': - resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-arm64@0.21.5': - resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-arm64@0.25.3': - resolution: {integrity: sha512-eESK5yfPNTqpAmDfFWNsOhmIOaQA59tAcF/EfYvo5/QWQCzXn5iUSOnqt3ra3UdzBv073ykTtmeLJZGt3HhA+w==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-x64@0.18.20': - resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - - '@esbuild/darwin-x64@0.21.5': - resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - - '@esbuild/darwin-x64@0.25.3': - resolution: {integrity: sha512-Kd8glo7sIZtwOLcPbW0yLpKmBNWMANZhrC1r6K++uDR2zyzb6AeOYtI6udbtabmQpFaxJ8uduXMAo1gs5ozz8A==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - - '@esbuild/freebsd-arm64@0.18.20': - resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-arm64@0.21.5': - resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-arm64@0.25.3': - resolution: {integrity: sha512-EJiyS70BYybOBpJth3M0KLOus0n+RRMKTYzhYhFeMwp7e/RaajXvP+BWlmEXNk6uk+KAu46j/kaQzr6au+JcIw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.18.20': - resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.21.5': - resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.25.3': - resolution: {integrity: sha512-Q+wSjaLpGxYf7zC0kL0nDlhsfuFkoN+EXrx2KSB33RhinWzejOd6AvgmP5JbkgXKmjhmpfgKZq24pneodYqE8Q==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - - '@esbuild/linux-arm64@0.18.20': - resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm64@0.21.5': - resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm64@0.25.3': - resolution: {integrity: sha512-xCUgnNYhRD5bb1C1nqrDV1PfkwgbswTTBRbAd8aH5PhYzikdf/ddtsYyMXFfGSsb/6t6QaPSzxtbfAZr9uox4A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm@0.18.20': - resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-arm@0.21.5': - resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-arm@0.25.3': - resolution: {integrity: sha512-dUOVmAUzuHy2ZOKIHIKHCm58HKzFqd+puLaS424h6I85GlSDRZIA5ycBixb3mFgM0Jdh+ZOSB6KptX30DD8YOQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-ia32@0.18.20': - resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-ia32@0.21.5': - resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-ia32@0.25.3': - resolution: {integrity: sha512-yplPOpczHOO4jTYKmuYuANI3WhvIPSVANGcNUeMlxH4twz/TeXuzEP41tGKNGWJjuMhotpGabeFYGAOU2ummBw==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-loong64@0.18.20': - resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-loong64@0.21.5': - resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-loong64@0.25.3': - resolution: {integrity: sha512-P4BLP5/fjyihmXCELRGrLd793q/lBtKMQl8ARGpDxgzgIKJDRJ/u4r1A/HgpBpKpKZelGct2PGI4T+axcedf6g==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-mips64el@0.18.20': - resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-mips64el@0.21.5': - resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-mips64el@0.25.3': - resolution: {integrity: sha512-eRAOV2ODpu6P5divMEMa26RRqb2yUoYsuQQOuFUexUoQndm4MdpXXDBbUoKIc0iPa4aCO7gIhtnYomkn2x+bag==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-ppc64@0.18.20': - resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-ppc64@0.21.5': - resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-ppc64@0.25.3': - resolution: {integrity: sha512-ZC4jV2p7VbzTlnl8nZKLcBkfzIf4Yad1SJM4ZMKYnJqZFD4rTI+pBG65u8ev4jk3/MPwY9DvGn50wi3uhdaghg==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-riscv64@0.18.20': - resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-riscv64@0.21.5': - resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-riscv64@0.25.3': - resolution: {integrity: sha512-LDDODcFzNtECTrUUbVCs6j9/bDVqy7DDRsuIXJg6so+mFksgwG7ZVnTruYi5V+z3eE5y+BJZw7VvUadkbfg7QA==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-s390x@0.18.20': - resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-s390x@0.21.5': - resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-s390x@0.25.3': - resolution: {integrity: sha512-s+w/NOY2k0yC2p9SLen+ymflgcpRkvwwa02fqmAwhBRI3SC12uiS10edHHXlVWwfAagYSY5UpmT/zISXPMW3tQ==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-x64@0.18.20': - resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - - '@esbuild/linux-x64@0.21.5': - resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - - '@esbuild/linux-x64@0.25.3': - resolution: {integrity: sha512-nQHDz4pXjSDC6UfOE1Fw9Q8d6GCAd9KdvMZpfVGWSJztYCarRgSDfOVBY5xwhQXseiyxapkiSJi/5/ja8mRFFA==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - - '@esbuild/netbsd-arm64@0.25.3': - resolution: {integrity: sha512-1QaLtOWq0mzK6tzzp0jRN3eccmN3hezey7mhLnzC6oNlJoUJz4nym5ZD7mDnS/LZQgkrhEbEiTn515lPeLpgWA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.18.20': - resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.21.5': - resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.25.3': - resolution: {integrity: sha512-i5Hm68HXHdgv8wkrt+10Bc50zM0/eonPb/a/OFVfB6Qvpiirco5gBA5bz7S2SHuU+Y4LWn/zehzNX14Sp4r27g==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - - '@esbuild/openbsd-arm64@0.25.3': - resolution: {integrity: sha512-zGAVApJEYTbOC6H/3QBr2mq3upG/LBEXr85/pTtKiv2IXcgKV0RT0QA/hSXZqSvLEpXeIxah7LczB4lkiYhTAQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.18.20': - resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.21.5': - resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.25.3': - resolution: {integrity: sha512-fpqctI45NnCIDKBH5AXQBsD0NDPbEFczK98hk/aa6HJxbl+UtLkJV2+Bvy5hLSLk3LHmqt0NTkKNso1A9y1a4w==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - - '@esbuild/sunos-x64@0.18.20': - resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - - '@esbuild/sunos-x64@0.21.5': - resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - - '@esbuild/sunos-x64@0.25.3': - resolution: {integrity: sha512-ROJhm7d8bk9dMCUZjkS8fgzsPAZEjtRJqCAmVgB0gMrvG7hfmPmz9k1rwO4jSiblFjYmNvbECL9uhaPzONMfgA==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - - '@esbuild/win32-arm64@0.18.20': - resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-arm64@0.21.5': - resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-arm64@0.25.3': - resolution: {integrity: sha512-YWcow8peiHpNBiIXHwaswPnAXLsLVygFwCB3A7Bh5jRkIBFWHGmNQ48AlX4xDvQNoMZlPYzjVOQDYEzWCqufMQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-ia32@0.18.20': - resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-ia32@0.21.5': - resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-ia32@0.25.3': - resolution: {integrity: sha512-qspTZOIGoXVS4DpNqUYUs9UxVb04khS1Degaw/MnfMe7goQ3lTfQ13Vw4qY/Nj0979BGvMRpAYbs/BAxEvU8ew==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-x64@0.18.20': - resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - - '@esbuild/win32-x64@0.21.5': - resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - - '@esbuild/win32-x64@0.25.3': - resolution: {integrity: sha512-ICgUR+kPimx0vvRzf+N/7L7tVSQeE3BYY+NhHRHXS1kBuPO7z2+7ea2HbhDyZdTephgvNvKrlDDKUexuCVBVvg==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - - '@hackmd/markdown-it-task-lists@2.1.4': - resolution: {integrity: sha512-njMloWVihC7a7N4zxczv547bgNxPVG3GBzh6Z6f2xnO8/92JaxTmQuMV7YvaKKkOyhh2RW4RT84uSgax8u4qfQ==} - - '@hono/zod-validator@0.7.0': - resolution: {integrity: sha512-qe2ZE6sHFE98dcUrbYMtS3bAV8hqcCOflykvZga2S7XhmNSZzT+dIz4OuMILsjLHkJw9JMn912/dB7dQOmuPvg==} - peerDependencies: - hono: '>=3.9.0' - zod: ^3.25.0 - - '@iconify-json/simple-icons@1.2.33': - resolution: {integrity: sha512-nL5/UmI9x5PQ/AHv6bOaL2pH6twEdEz4pI89efB/K7HFn5etQnxMtGx9DFlOg/sRA2/yFpX8KXvc95CSDv5bJA==} - - '@iconify/types@2.0.0': - resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} - - '@img/sharp-darwin-arm64@0.34.2': - resolution: {integrity: sha512-OfXHZPppddivUJnqyKoi5YVeHRkkNE2zUFT2gbpKxp/JZCFYEYubnMg+gOp6lWfasPrTS+KPosKqdI+ELYVDtg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [darwin] - - '@img/sharp-darwin-x64@0.34.2': - resolution: {integrity: sha512-dYvWqmjU9VxqXmjEtjmvHnGqF8GrVjM2Epj9rJ6BUIXvk8slvNDJbhGFvIoXzkDhrJC2jUxNLz/GUjjvSzfw+g==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [darwin] - - '@img/sharp-libvips-darwin-arm64@1.1.0': - resolution: {integrity: sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==} - cpu: [arm64] - os: [darwin] - - '@img/sharp-libvips-darwin-x64@1.1.0': - resolution: {integrity: sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==} - cpu: [x64] - os: [darwin] - - '@img/sharp-libvips-linux-arm64@1.1.0': - resolution: {integrity: sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==} - cpu: [arm64] - os: [linux] - - '@img/sharp-libvips-linux-arm@1.1.0': - resolution: {integrity: sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==} - cpu: [arm] - os: [linux] - - '@img/sharp-libvips-linux-ppc64@1.1.0': - resolution: {integrity: sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==} - cpu: [ppc64] - os: [linux] - - '@img/sharp-libvips-linux-s390x@1.1.0': - resolution: {integrity: sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==} - cpu: [s390x] - os: [linux] - - '@img/sharp-libvips-linux-x64@1.1.0': - resolution: {integrity: sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==} - cpu: [x64] - os: [linux] - - '@img/sharp-libvips-linuxmusl-arm64@1.1.0': - resolution: {integrity: sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==} - cpu: [arm64] - os: [linux] - - '@img/sharp-libvips-linuxmusl-x64@1.1.0': - resolution: {integrity: sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==} - cpu: [x64] - os: [linux] - - '@img/sharp-linux-arm64@0.34.2': - resolution: {integrity: sha512-D8n8wgWmPDakc83LORcfJepdOSN6MvWNzzz2ux0MnIbOqdieRZwVYY32zxVx+IFUT8er5KPcyU3XXsn+GzG/0Q==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [linux] - - '@img/sharp-linux-arm@0.34.2': - resolution: {integrity: sha512-0DZzkvuEOqQUP9mo2kjjKNok5AmnOr1jB2XYjkaoNRwpAYMDzRmAqUIa1nRi58S2WswqSfPOWLNOr0FDT3H5RQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm] - os: [linux] - - '@img/sharp-linux-s390x@0.34.2': - resolution: {integrity: sha512-EGZ1xwhBI7dNISwxjChqBGELCWMGDvmxZXKjQRuqMrakhO8QoMgqCrdjnAqJq/CScxfRn+Bb7suXBElKQpPDiw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [s390x] - os: [linux] - - '@img/sharp-linux-x64@0.34.2': - resolution: {integrity: sha512-sD7J+h5nFLMMmOXYH4DD9UtSNBD05tWSSdWAcEyzqW8Cn5UxXvsHAxmxSesYUsTOBmUnjtxghKDl15EvfqLFbQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [linux] - - '@img/sharp-linuxmusl-arm64@0.34.2': - resolution: {integrity: sha512-NEE2vQ6wcxYav1/A22OOxoSOGiKnNmDzCYFOZ949xFmrWZOVII1Bp3NqVVpvj+3UeHMFyN5eP/V5hzViQ5CZNA==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [linux] - - '@img/sharp-linuxmusl-x64@0.34.2': - resolution: {integrity: sha512-DOYMrDm5E6/8bm/yQLCWyuDJwUnlevR8xtF8bs+gjZ7cyUNYXiSf/E8Kp0Ss5xasIaXSHzb888V1BE4i1hFhAA==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [linux] - - '@img/sharp-wasm32@0.34.2': - resolution: {integrity: sha512-/VI4mdlJ9zkaq53MbIG6rZY+QRN3MLbR6usYlgITEzi4Rpx5S6LFKsycOQjkOGmqTNmkIdLjEvooFKwww6OpdQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [wasm32] - - '@img/sharp-win32-arm64@0.34.2': - resolution: {integrity: sha512-cfP/r9FdS63VA5k0xiqaNaEoGxBg9k7uE+RQGzuK9fHt7jib4zAVVseR9LsE4gJcNWgT6APKMNnCcnyOtmSEUQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [win32] - - '@img/sharp-win32-ia32@0.34.2': - resolution: {integrity: sha512-QLjGGvAbj0X/FXl8n1WbtQ6iVBpWU7JO94u/P2M4a8CFYsvQi4GW2mRy/JqkRx0qpBzaOdKJKw8uc930EX2AHw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [ia32] - os: [win32] - - '@img/sharp-win32-x64@0.34.2': - resolution: {integrity: sha512-aUdT6zEYtDKCaxkofmmJDJYGCf0+pJg3eU9/oBuqvEeoB9dKI6ZLc/1iLJCTuJQDO4ptntAlkUmHgGjyuobZbw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [win32] - - '@inquirer/confirm@5.1.12': - resolution: {integrity: sha512-dpq+ielV9/bqgXRUbNH//KsY6WEw9DrGPmipkpmgC1Y46cwuBTNx7PXFWTjc3MQ+urcc0QxoVHcMI0FW4Ok0hg==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@inquirer/core@10.1.13': - resolution: {integrity: sha512-1viSxebkYN2nJULlzCxES6G9/stgHSepZ9LqqfdIGPHj5OHhiBUXVS0a6R0bEC2A+VL4D9w6QB66ebCr6HGllA==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@inquirer/figures@1.0.12': - resolution: {integrity: sha512-MJttijd8rMFcKJC8NYmprWr6hD3r9Gd9qUC0XwPNwoEPWSMVJwA2MlXxF+nhZZNMY+HXsWa+o7KY2emWYIn0jQ==} - engines: {node: '>=18'} - - '@inquirer/type@3.0.7': - resolution: {integrity: sha512-PfunHQcjwnju84L+ycmcMKB/pTPIngjUJvfnRhKY6FKPuYXlM4aQCb/nIdTFR6BEhMjFvngzvng/vBAJMZpLSA==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@ioredis/commands@1.2.0': - resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==} - - '@isaacs/cliui@8.0.2': - resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} - engines: {node: '>=12'} - - '@jridgewell/sourcemap-codec@1.5.0': - resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} - - '@jsdevtools/ono@7.1.3': - resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} - - '@logtape/file@0.12.0': - resolution: {integrity: sha512-C3VLzKF8RQJUjozj8xstmfw3LRVMHScR9275MaIb/krLooNdy8z14IaQ2XGESfQr2NM946wRPBdAcNKm0DV6Ww==} - peerDependencies: - '@logtape/logtape': 0.12.0 - - '@logtape/logtape@0.12.0': - resolution: {integrity: sha512-FPAI0VHDLaU/+sFC53k50RhJyMvj8tsuENF7Bzf+jKdMac2HavV2/eB4YyFltjt4PwSbvKimXc0cE9iwfHbPpw==} - - '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': - resolution: {integrity: sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==} - cpu: [arm64] - os: [darwin] - - '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': - resolution: {integrity: sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==} - cpu: [x64] - os: [darwin] - - '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': - resolution: {integrity: sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==} - cpu: [arm64] - os: [linux] - - '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': - resolution: {integrity: sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==} - cpu: [arm] - os: [linux] - - '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': - resolution: {integrity: sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==} - cpu: [x64] - os: [linux] - - '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': - resolution: {integrity: sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==} - cpu: [x64] - os: [win32] - - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} - - '@opentelemetry/api-logs@0.57.2': - resolution: {integrity: sha512-uIX52NnTM0iBh84MShlpouI7UKqkZ7MrUszTmaypHBu4r7NofznSnQRfJ+uUeDtQDj6w8eFGg5KBLDAwAPz1+A==} - engines: {node: '>=14'} - - '@opentelemetry/api@1.9.0': - resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} - engines: {node: '>=8.0.0'} - - '@opentelemetry/context-async-hooks@1.30.1': - resolution: {integrity: sha512-s5vvxXPVdjqS3kTLKMeBMvop9hbWkwzBpu+mUO2M7sZtlkyDJGwFe33wRKnbaYDo8ExRVBIIdwIGrqpxHuKttA==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' - - '@opentelemetry/core@1.30.1': - resolution: {integrity: sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' - - '@opentelemetry/instrumentation-amqplib@0.46.1': - resolution: {integrity: sha512-AyXVnlCf/xV3K/rNumzKxZqsULyITJH6OVLiW6730JPRqWA7Zc9bvYoVNpN6iOpTU8CasH34SU/ksVJmObFibQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-connect@0.43.1': - resolution: {integrity: sha512-ht7YGWQuV5BopMcw5Q2hXn3I8eG8TH0J/kc/GMcW4CuNTgiP6wCu44BOnucJWL3CmFWaRHI//vWyAhaC8BwePw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-dataloader@0.16.1': - resolution: {integrity: sha512-K/qU4CjnzOpNkkKO4DfCLSQshejRNAJtd4esgigo/50nxCB6XCyi1dhAblUHM9jG5dRm8eu0FB+t87nIo99LYQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-express@0.47.1': - resolution: {integrity: sha512-QNXPTWteDclR2B4pDFpz0TNghgB33UMjUt14B+BZPmtH1MwUFAfLHBaP5If0Z5NZC+jaH8oF2glgYjrmhZWmSw==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-fs@0.19.1': - resolution: {integrity: sha512-6g0FhB3B9UobAR60BGTcXg4IHZ6aaYJzp0Ki5FhnxyAPt8Ns+9SSvgcrnsN2eGmk3RWG5vYycUGOEApycQL24A==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-generic-pool@0.43.1': - resolution: {integrity: sha512-M6qGYsp1cURtvVLGDrPPZemMFEbuMmCXgQYTReC/IbimV5sGrLBjB+/hANUpRZjX67nGLdKSVLZuQQAiNz+sww==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-graphql@0.47.1': - resolution: {integrity: sha512-EGQRWMGqwiuVma8ZLAZnExQ7sBvbOx0N/AE/nlafISPs8S+QtXX+Viy6dcQwVWwYHQPAcuY3bFt3xgoAwb4ZNQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-hapi@0.45.2': - resolution: {integrity: sha512-7Ehow/7Wp3aoyCrZwQpU7a2CnoMq0XhIcioFuKjBb0PLYfBfmTsFTUyatlHu0fRxhwcRsSQRTvEhmZu8CppBpQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-http@0.57.2': - resolution: {integrity: sha512-1Uz5iJ9ZAlFOiPuwYg29Bf7bJJc/GeoeJIFKJYQf67nTVKFe8RHbEtxgkOmK4UGZNHKXcpW4P8cWBYzBn1USpg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-ioredis@0.47.1': - resolution: {integrity: sha512-OtFGSN+kgk/aoKgdkKQnBsQFDiG8WdCxu+UrHr0bXScdAmtSzLSraLo7wFIb25RVHfRWvzI5kZomqJYEg/l1iA==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-kafkajs@0.7.1': - resolution: {integrity: sha512-OtjaKs8H7oysfErajdYr1yuWSjMAectT7Dwr+axIoZqT9lmEOkD/H/3rgAs8h/NIuEi2imSXD+vL4MZtOuJfqQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-knex@0.44.1': - resolution: {integrity: sha512-U4dQxkNhvPexffjEmGwCq68FuftFK15JgUF05y/HlK3M6W/G2iEaACIfXdSnwVNe9Qh0sPfw8LbOPxrWzGWGMQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-koa@0.47.1': - resolution: {integrity: sha512-l/c+Z9F86cOiPJUllUCt09v+kICKvT+Vg1vOAJHtHPsJIzurGayucfCMq2acd/A/yxeNWunl9d9eqZ0G+XiI6A==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-lru-memoizer@0.44.1': - resolution: {integrity: sha512-5MPkYCvG2yw7WONEjYj5lr5JFehTobW7wX+ZUFy81oF2lr9IPfZk9qO+FTaM0bGEiymwfLwKe6jE15nHn1nmHg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-mongodb@0.52.0': - resolution: {integrity: sha512-1xmAqOtRUQGR7QfJFfGV/M2kC7wmI2WgZdpru8hJl3S0r4hW0n3OQpEHlSGXJAaNFyvT+ilnwkT+g5L4ljHR6g==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-mongoose@0.46.1': - resolution: {integrity: sha512-3kINtW1LUTPkiXFRSSBmva1SXzS/72we/jL22N+BnF3DFcoewkdkHPYOIdAAk9gSicJ4d5Ojtt1/HeibEc5OQg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-mysql2@0.45.2': - resolution: {integrity: sha512-h6Ad60FjCYdJZ5DTz1Lk2VmQsShiViKe0G7sYikb0GHI0NVvApp2XQNRHNjEMz87roFttGPLHOYVPlfy+yVIhQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-mysql@0.45.1': - resolution: {integrity: sha512-TKp4hQ8iKQsY7vnp/j0yJJ4ZsP109Ht6l4RHTj0lNEG1TfgTrIH5vJMbgmoYXWzNHAqBH2e7fncN12p3BP8LFg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-pg@0.51.1': - resolution: {integrity: sha512-QxgjSrxyWZc7Vk+qGSfsejPVFL1AgAJdSBMYZdDUbwg730D09ub3PXScB9d04vIqPriZ+0dqzjmQx0yWKiCi2Q==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-redis-4@0.46.1': - resolution: {integrity: sha512-UMqleEoabYMsWoTkqyt9WAzXwZ4BlFZHO40wr3d5ZvtjKCHlD4YXLm+6OLCeIi/HkX7EXvQaz8gtAwkwwSEvcQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-tedious@0.18.1': - resolution: {integrity: sha512-5Cuy/nj0HBaH+ZJ4leuD7RjgvA844aY2WW+B5uLcWtxGjRZl3MNLuxnNg5DYWZNPO+NafSSnra0q49KWAHsKBg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/instrumentation-undici@0.10.1': - resolution: {integrity: sha512-rkOGikPEyRpMCmNu9AQuV5dtRlDmJp2dK5sw8roVshAGoB6hH/3QjDtRhdwd75SsJwgynWUNRUYe0wAkTo16tQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.7.0 - - '@opentelemetry/instrumentation@0.57.2': - resolution: {integrity: sha512-BdBGhQBh8IjZ2oIIX6F2/Q3LKm/FDDKi6ccYKcBTeilh6SNdNKveDOLk73BkSJjQLJk6qe4Yh+hHw1UPhCDdrg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - - '@opentelemetry/redis-common@0.36.2': - resolution: {integrity: sha512-faYX1N0gpLhej/6nyp6bgRjzAKXn5GOEMYY7YhciSfCoITAktLUtQ36d24QEWNA1/WA1y6qQunCe0OhHRkVl9g==} - engines: {node: '>=14'} - - '@opentelemetry/resources@1.30.1': - resolution: {integrity: sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' - - '@opentelemetry/sdk-trace-base@1.30.1': - resolution: {integrity: sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' - - '@opentelemetry/semantic-conventions@1.28.0': - resolution: {integrity: sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==} - engines: {node: '>=14'} - - '@opentelemetry/semantic-conventions@1.34.0': - resolution: {integrity: sha512-aKcOkyrorBGlajjRdVoJWHTxfxO1vCNHLJVlSDaRHDIdjU+pX8IYQPvPDkYiujKLbRnWU+1TBwEt0QRgSm4SGA==} - engines: {node: '>=14'} - - '@opentelemetry/sql-common@0.40.1': - resolution: {integrity: sha512-nSDlnHSqzC3pXn/wZEZVLuAuJ1MYMXPBwtv2qAbCa3847SaHItdE7SzUq/Jtb0KZmh1zfAbNi3AAMjztTT4Ugg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.1.0 - - '@pkgjs/parseargs@0.11.0': - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} - - '@poppinss/colors@4.1.4': - resolution: {integrity: sha512-FA+nTU8p6OcSH4tLDY5JilGYr1bVWHpNmcLr7xmMEdbWmKHa+3QZ+DqefrXKmdjO/brHTnQZo20lLSjaO7ydog==} - engines: {node: '>=18.16.0'} - - '@poppinss/dumper@0.6.3': - resolution: {integrity: sha512-iombbn8ckOixMtuV1p3f8jN6vqhXefNjJttoPaJDMeIk/yIGhkkL3OrHkEjE9SRsgoAx1vBUU2GtgggjvA5hCA==} - - '@poppinss/exception@1.2.1': - resolution: {integrity: sha512-aQypoot0HPSJa6gDPEPTntc1GT6QINrSbgRlRhadGW2WaYqUK3tK4Bw9SBMZXhmxd3GeAlZjVcODHgiu+THY7A==} - engines: {node: '>=18'} - - '@prisma/instrumentation@6.8.2': - resolution: {integrity: sha512-5NCTbZjw7a+WIZ/ey6G8SY+YKcyM2zBF0hOT1muvqC9TbVtTCr5Qv3RL/2iNDOzLUHEvo4I1uEfioyfuNOGK8Q==} - peerDependencies: - '@opentelemetry/api': ^1.8 - - '@rollup/rollup-android-arm-eabi@4.40.1': - resolution: {integrity: sha512-kxz0YeeCrRUHz3zyqvd7n+TVRlNyTifBsmnmNPtk3hQURUyG9eAB+usz6DAwagMusjx/zb3AjvDUvhFGDAexGw==} - cpu: [arm] - os: [android] - - '@rollup/rollup-android-arm64@4.40.1': - resolution: {integrity: sha512-PPkxTOisoNC6TpnDKatjKkjRMsdaWIhyuMkA4UsBXT9WEZY4uHezBTjs6Vl4PbqQQeu6oION1w2voYZv9yquCw==} - cpu: [arm64] - os: [android] - - '@rollup/rollup-darwin-arm64@4.40.1': - resolution: {integrity: sha512-VWXGISWFY18v/0JyNUy4A46KCFCb9NVsH+1100XP31lud+TzlezBbz24CYzbnA4x6w4hx+NYCXDfnvDVO6lcAA==} - cpu: [arm64] - os: [darwin] - - '@rollup/rollup-darwin-x64@4.40.1': - resolution: {integrity: sha512-nIwkXafAI1/QCS7pxSpv/ZtFW6TXcNUEHAIA9EIyw5OzxJZQ1YDrX+CL6JAIQgZ33CInl1R6mHet9Y/UZTg2Bw==} - cpu: [x64] - os: [darwin] - - '@rollup/rollup-freebsd-arm64@4.40.1': - resolution: {integrity: sha512-BdrLJ2mHTrIYdaS2I99mriyJfGGenSaP+UwGi1kB9BLOCu9SR8ZpbkmmalKIALnRw24kM7qCN0IOm6L0S44iWw==} - cpu: [arm64] - os: [freebsd] - - '@rollup/rollup-freebsd-x64@4.40.1': - resolution: {integrity: sha512-VXeo/puqvCG8JBPNZXZf5Dqq7BzElNJzHRRw3vjBE27WujdzuOPecDPc/+1DcdcTptNBep3861jNq0mYkT8Z6Q==} - cpu: [x64] - os: [freebsd] - - '@rollup/rollup-linux-arm-gnueabihf@4.40.1': - resolution: {integrity: sha512-ehSKrewwsESPt1TgSE/na9nIhWCosfGSFqv7vwEtjyAqZcvbGIg4JAcV7ZEh2tfj/IlfBeZjgOXm35iOOjadcg==} - cpu: [arm] - os: [linux] - - '@rollup/rollup-linux-arm-musleabihf@4.40.1': - resolution: {integrity: sha512-m39iO/aaurh5FVIu/F4/Zsl8xppd76S4qoID8E+dSRQvTyZTOI2gVk3T4oqzfq1PtcvOfAVlwLMK3KRQMaR8lg==} - cpu: [arm] - os: [linux] - - '@rollup/rollup-linux-arm64-gnu@4.40.1': - resolution: {integrity: sha512-Y+GHnGaku4aVLSgrT0uWe2o2Rq8te9hi+MwqGF9r9ORgXhmHK5Q71N757u0F8yU1OIwUIFy6YiJtKjtyktk5hg==} - cpu: [arm64] - os: [linux] - - '@rollup/rollup-linux-arm64-musl@4.40.1': - resolution: {integrity: sha512-jEwjn3jCA+tQGswK3aEWcD09/7M5wGwc6+flhva7dsQNRZZTe30vkalgIzV4tjkopsTS9Jd7Y1Bsj6a4lzz8gQ==} - cpu: [arm64] - os: [linux] - - '@rollup/rollup-linux-loongarch64-gnu@4.40.1': - resolution: {integrity: sha512-ySyWikVhNzv+BV/IDCsrraOAZ3UaC8SZB67FZlqVwXwnFhPihOso9rPOxzZbjp81suB1O2Topw+6Ug3JNegejQ==} - cpu: [loong64] - os: [linux] - - '@rollup/rollup-linux-powerpc64le-gnu@4.40.1': - resolution: {integrity: sha512-BvvA64QxZlh7WZWqDPPdt0GH4bznuL6uOO1pmgPnnv86rpUpc8ZxgZwcEgXvo02GRIZX1hQ0j0pAnhwkhwPqWg==} - cpu: [ppc64] - os: [linux] - - '@rollup/rollup-linux-riscv64-gnu@4.40.1': - resolution: {integrity: sha512-EQSP+8+1VuSulm9RKSMKitTav89fKbHymTf25n5+Yr6gAPZxYWpj3DzAsQqoaHAk9YX2lwEyAf9S4W8F4l3VBQ==} - cpu: [riscv64] - os: [linux] - - '@rollup/rollup-linux-riscv64-musl@4.40.1': - resolution: {integrity: sha512-n/vQ4xRZXKuIpqukkMXZt9RWdl+2zgGNx7Uda8NtmLJ06NL8jiHxUawbwC+hdSq1rrw/9CghCpEONor+l1e2gA==} - cpu: [riscv64] - os: [linux] - - '@rollup/rollup-linux-s390x-gnu@4.40.1': - resolution: {integrity: sha512-h8d28xzYb98fMQKUz0w2fMc1XuGzLLjdyxVIbhbil4ELfk5/orZlSTpF/xdI9C8K0I8lCkq+1En2RJsawZekkg==} - cpu: [s390x] - os: [linux] - - '@rollup/rollup-linux-x64-gnu@4.40.1': - resolution: {integrity: sha512-XiK5z70PEFEFqcNj3/zRSz/qX4bp4QIraTy9QjwJAb/Z8GM7kVUsD0Uk8maIPeTyPCP03ChdI+VVmJriKYbRHQ==} - cpu: [x64] - os: [linux] - - '@rollup/rollup-linux-x64-musl@4.40.1': - resolution: {integrity: sha512-2BRORitq5rQ4Da9blVovzNCMaUlyKrzMSvkVR0D4qPuOy/+pMCrh1d7o01RATwVy+6Fa1WBw+da7QPeLWU/1mQ==} - cpu: [x64] - os: [linux] - - '@rollup/rollup-win32-arm64-msvc@4.40.1': - resolution: {integrity: sha512-b2bcNm9Kbde03H+q+Jjw9tSfhYkzrDUf2d5MAd1bOJuVplXvFhWz7tRtWvD8/ORZi7qSCy0idW6tf2HgxSXQSg==} - cpu: [arm64] - os: [win32] - - '@rollup/rollup-win32-ia32-msvc@4.40.1': - resolution: {integrity: sha512-DfcogW8N7Zg7llVEfpqWMZcaErKfsj9VvmfSyRjCyo4BI3wPEfrzTtJkZG6gKP/Z92wFm6rz2aDO7/JfiR/whA==} - cpu: [ia32] - os: [win32] - - '@rollup/rollup-win32-x64-msvc@4.40.1': - resolution: {integrity: sha512-ECyOuDeH3C1I8jH2MK1RtBJW+YPMvSfT0a5NN0nHfQYnDSJ6tUiZH3gzwVP5/Kfh/+Tt7tpWVF9LXNTnhTJ3kA==} - cpu: [x64] - os: [win32] - - '@scalar/core@0.3.3': - resolution: {integrity: sha512-OedH8YQriBsE7JnCN2ly4p3u7iobjzAoDkxD/o1hiF55m6WRicvtklEdByHlceu9UnJ/znikZ50ymo98uG3aqA==} - engines: {node: '>=20'} - - '@scalar/hono-api-reference@0.9.4': - resolution: {integrity: sha512-pAUWS8MRGcS5VjMjAVd6DrhST63NpVXGel2BbYsKPQAaONwwA3hU30fAFxSs3PUGA8M7q+e76F6w4ALBtHxLRA==} - engines: {node: '>=20'} - peerDependencies: - hono: ^4.0.0 - - '@scalar/openapi-types@0.3.3': - resolution: {integrity: sha512-mr3OvbCyvuzSDgv9gR9D3pJAsl82BRNBp1P8ldgRgKF4Wiswfa7nQeKibpX0kHSGXLgiyvxWJULUFwgdWMXNNA==} - engines: {node: '>=20'} - - '@scalar/types@0.2.3': - resolution: {integrity: sha512-K1/Vp5xaQ8TEGDYwHg88OBuQWJRWrE3ToZ0Z2LJwskcY1Eg+uDiRQXKF4QPngZzu5rrWgRErAq4yte4Y7omqgA==} - engines: {node: '>=20'} - - '@selderee/plugin-htmlparser2@0.11.0': - resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==} - - '@sentry/bun@9.29.0': - resolution: {integrity: sha512-85wFqI0oEOZ/73KcTTmd4wMkKCyaxMLMWCiDJVrXxGbuC37pevQqZmIEgNABfs6NZLc2bHSxOzKlWzgJs30Pig==} - engines: {node: '>=18'} - - '@sentry/core@9.29.0': - resolution: {integrity: sha512-wDyNe45PM+RCGtUn1tK7LzJ08ksv8i8KRUHrst7lsinEfRm83YH+wbWrPmwkVNEngUZvYkHwGLbNXM7xgFUuDQ==} - engines: {node: '>=18'} - - '@sentry/node@9.29.0': - resolution: {integrity: sha512-oABipgC/fClRuvyMeK43rigv9F+OAaoR84UaMKB7aPXN6iz634wBRVsaoZAwiR3xLL+R7MafEPPA/s9XqlG7ag==} - engines: {node: '>=18'} - - '@sentry/opentelemetry@9.29.0': - resolution: {integrity: sha512-QTUmre8i5+832RjzQW+g8IQ3UmBe5fbQXGbCF5hQ0UNuHle9r3Z8UZcIff5W8tm5AXMxPqvptTnDEZUUXHgBiA==} - engines: {node: '>=18'} - peerDependencies: - '@opentelemetry/api': ^1.9.0 - '@opentelemetry/context-async-hooks': ^1.30.1 || ^2.0.0 - '@opentelemetry/core': ^1.30.1 || ^2.0.0 - '@opentelemetry/instrumentation': ^0.57.1 || ^0.200.0 - '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.0.0 - '@opentelemetry/semantic-conventions': ^1.34.0 - - '@shikijs/core@2.5.0': - resolution: {integrity: sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg==} - - '@shikijs/engine-javascript@2.5.0': - resolution: {integrity: sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w==} - - '@shikijs/engine-oniguruma@2.5.0': - resolution: {integrity: sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw==} - - '@shikijs/langs@2.5.0': - resolution: {integrity: sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w==} - - '@shikijs/themes@2.5.0': - resolution: {integrity: sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw==} - - '@shikijs/transformers@2.5.0': - resolution: {integrity: sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg==} - - '@shikijs/types@2.5.0': - resolution: {integrity: sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw==} - - '@shikijs/vscode-textmate@10.0.2': - resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} - - '@sindresorhus/is@7.0.1': - resolution: {integrity: sha512-QWLl2P+rsCJeofkDNIT3WFmb6NrRud1SUYW8dIhXK/46XFV8Q/g7Bsvib0Askb0reRLe+WYPeeE+l5cH7SlkuQ==} - engines: {node: '>=18'} - - '@speed-highlight/core@1.2.7': - resolution: {integrity: sha512-0dxmVj4gxg3Jg879kvFS/msl4s9F3T9UXC1InxgOf7t5NvcPD97u/WTA5vL/IxWHMn7qSxBozqrnnE2wvl1m8g==} - - '@ts-morph/common@0.12.3': - resolution: {integrity: sha512-4tUmeLyXJnJWvTFOKtcNJ1yh0a3SsTLi2MUoyj8iUNznFRN1ZquaNe7Oukqrnki2FzZkm0J9adCNLDZxUzvj+w==} - - '@types/bun@1.2.16': - resolution: {integrity: sha512-1aCZJ/6nSiViw339RsaNhkNoEloLaPzZhxMOYEa7OzRzO41IGg5n/7I43/ZIAW/c+Q6cT12Vf7fOZOoVIzb5BQ==} - - '@types/connect@3.4.38': - resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} - - '@types/estree@1.0.7': - resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} - - '@types/hast@3.0.4': - resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} - - '@types/html-to-text@9.0.4': - resolution: {integrity: sha512-pUY3cKH/Nm2yYrEmDlPR1mR7yszjGx4DrwPjQ702C4/D5CwHuZTgZdIdwPkRbcuhs7BAh2L5rg3CL5cbRiGTCQ==} - - '@types/json-schema@7.0.15': - resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - - '@types/linkify-it@5.0.0': - resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} - - '@types/markdown-it-container@2.0.10': - resolution: {integrity: sha512-zv+YxrlSYRq51e9kzm3orv4OvF4U79Ll1OyplNXr00o4ZC/8PukJk/jEWH7CnsMtrSWZlyv0czhz42jm9J4uLw==} - - '@types/markdown-it@14.1.2': - resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==} - - '@types/mdast@4.0.4': - resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} - - '@types/mdurl@2.0.0': - resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} - - '@types/mime-types@3.0.1': - resolution: {integrity: sha512-xRMsfuQbnRq1Ef+C+RKaENOxXX87Ygl38W1vDfPHRku02TgQr+Qd8iivLtAMcR0KF5/29xlnFihkTlbqFrGOVQ==} - - '@types/mysql@2.15.26': - resolution: {integrity: sha512-DSLCOXhkvfS5WNNPbfn2KdICAmk8lLc+/PNvnPnF7gOdMZCxopXduqv0OQ13y/yA/zXTSikZZqVgybUxOEg6YQ==} - - '@types/node@22.15.3': - resolution: {integrity: sha512-lX7HFZeHf4QG/J7tBZqrCAXwz9J5RD56Y6MpP0eJkka8p+K0RY/yBTW7CYFJ4VGCclxqOLKmiGP5juQc6MKgcw==} - - '@types/parse-json@4.0.2': - resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} - - '@types/pg-pool@2.0.6': - resolution: {integrity: sha512-TaAUE5rq2VQYxab5Ts7WZhKNmuN78Q6PiFonTDdpbx8a1H0M1vhy3rhiMjl+e2iHmogyMw7jZF4FrE6eJUy5HQ==} - - '@types/pg@8.6.1': - resolution: {integrity: sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==} - - '@types/qs@6.14.0': - resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} - - '@types/shimmer@1.2.0': - resolution: {integrity: sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==} - - '@types/tedious@4.0.14': - resolution: {integrity: sha512-KHPsfX/FoVbUGbyYvk1q9MMQHLPeRZhRJZdO45Q4YjvFkv4hMNghCWTvy7rdKessBsmtz4euWCWAB6/tVpI1Iw==} - - '@types/text-table@0.2.5': - resolution: {integrity: sha512-hcZhlNvMkQG/k1vcZ6yHOl6WAYftQ2MLfTHcYRZ2xYZFD8tGVnE3qFV0lj1smQeDSR7/yY0PyuUalauf33bJeA==} - - '@types/unist@3.0.3': - resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} - - '@types/web-bluetooth@0.0.21': - resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==} - - '@types/web-push@3.6.4': - resolution: {integrity: sha512-GnJmSr40H3RAnj0s34FNTcJi1hmWFV5KXugE0mYWnYhgTAHLJ/dJKAwDmvPJYMke0RplY2XE9LnM4hqSqKIjhQ==} - - '@ungap/structured-clone@1.3.0': - resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} - - '@vitejs/plugin-vue@5.2.3': - resolution: {integrity: sha512-IYSLEQj4LgZZuoVpdSUCw3dIynTWQgPlaRP6iAvMle4My0HdYwr5g5wQAfwOeHQBmYwEkqF70nRpSilr6PoUDg==} - engines: {node: ^18.0.0 || >=20.0.0} - peerDependencies: - vite: ^5.0.0 || ^6.0.0 - vue: ^3.2.25 - - '@vue/compiler-core@3.5.16': - resolution: {integrity: sha512-AOQS2eaQOaaZQoL1u+2rCJIKDruNXVBZSiUD3chnUrsoX5ZTQMaCvXlWNIfxBJuU15r1o7+mpo5223KVtIhAgQ==} - - '@vue/compiler-dom@3.5.16': - resolution: {integrity: sha512-SSJIhBr/teipXiXjmWOVWLnxjNGo65Oj/8wTEQz0nqwQeP75jWZ0n4sF24Zxoht1cuJoWopwj0J0exYwCJ0dCQ==} - - '@vue/compiler-sfc@3.5.16': - resolution: {integrity: sha512-rQR6VSFNpiinDy/DVUE0vHoIDUF++6p910cgcZoaAUm3POxgNOOdS/xgoll3rNdKYTYPnnbARDCZOyZ+QSe6Pw==} - - '@vue/compiler-ssr@3.5.16': - resolution: {integrity: sha512-d2V7kfxbdsjrDSGlJE7my1ZzCXViEcqN6w14DOsDrUCHEA6vbnVCpRFfrc4ryCP/lCKzX2eS1YtnLE/BuC9f/A==} - - '@vue/devtools-api@7.7.6': - resolution: {integrity: sha512-b2Xx0KvXZObePpXPYHvBRRJLDQn5nhKjXh7vUhMEtWxz1AYNFOVIsh5+HLP8xDGL7sy+Q7hXeUxPHB/KgbtsPw==} - - '@vue/devtools-kit@7.7.6': - resolution: {integrity: sha512-geu7ds7tem2Y7Wz+WgbnbZ6T5eadOvozHZ23Atk/8tksHMFOFylKi1xgGlQlVn0wlkEf4hu+vd5ctj1G4kFtwA==} - - '@vue/devtools-shared@7.7.6': - resolution: {integrity: sha512-yFEgJZ/WblEsojQQceuyK6FzpFDx4kqrz2ohInxNj5/DnhoX023upTv4OD6lNPLAA5LLkbwPVb10o/7b+Y4FVA==} - - '@vue/reactivity@3.5.16': - resolution: {integrity: sha512-FG5Q5ee/kxhIm1p2bykPpPwqiUBV3kFySsHEQha5BJvjXdZTUfmya7wP7zC39dFuZAcf/PD5S4Lni55vGLMhvA==} - - '@vue/runtime-core@3.5.16': - resolution: {integrity: sha512-bw5Ykq6+JFHYxrQa7Tjr+VSzw7Dj4ldR/udyBZbq73fCdJmyy5MPIFR9IX/M5Qs+TtTjuyUTCnmK3lWWwpAcFQ==} - - '@vue/runtime-dom@3.5.16': - resolution: {integrity: sha512-T1qqYJsG2xMGhImRUV9y/RseB9d0eCYZQ4CWca9ztCuiPj/XWNNN+lkNBuzVbia5z4/cgxdL28NoQCvC0Xcfww==} - - '@vue/server-renderer@3.5.16': - resolution: {integrity: sha512-BrX0qLiv/WugguGsnQUJiYOE0Fe5mZTwi6b7X/ybGB0vfrPH9z0gD/Y6WOR1sGCgX4gc25L1RYS5eYQKDMoNIg==} - peerDependencies: - vue: 3.5.16 - - '@vue/shared@3.5.13': - resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==} - - '@vue/shared@3.5.16': - resolution: {integrity: sha512-c/0fWy3Jw6Z8L9FmTyYfkpM5zklnqqa9+a6dz3DvONRKW2NEbh46BP0FHuLFSWi2TnQEtp91Z6zOWNrU6QiyPg==} - - '@vueuse/core@12.8.2': - resolution: {integrity: sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==} - - '@vueuse/integrations@12.8.2': - resolution: {integrity: sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g==} - 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 - 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@12.8.2': - resolution: {integrity: sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==} - - '@vueuse/shared@12.8.2': - resolution: {integrity: sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==} - - '@xmldom/xmldom@0.9.8': - resolution: {integrity: sha512-p96FSY54r+WJ50FIOsCOjyj/wavs8921hG5+kVMmZgKcvIKxMXHTrjNJvRgWa/zuX3B6t2lijLNFaOyuxUH+2A==} - engines: {node: '>=14.6'} - - acorn-import-attributes@1.9.5: - resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} - peerDependencies: - acorn: ^8 - - acorn@8.14.1: - resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} - engines: {node: '>=0.4.0'} - hasBin: true - - agent-base@7.1.3: - resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} - engines: {node: '>= 14'} - - algoliasearch@5.24.0: - resolution: {integrity: sha512-CkaUygzZ91Xbw11s0CsHMawrK3tl+Ue57725HGRgRzKgt2Z4wvXVXRCtQfvzh8K7Tp4Zp7f1pyHAtMROtTJHxg==} - engines: {node: '>= 14.0.0'} - - altcha-lib@1.3.0: - resolution: {integrity: sha512-PpFg/JPuR+Jiud7Vs54XSDqDxvylcp+0oDa/i1ARxBA/iKDqLeNlO8PorQbfuDTMVLYRypAa/2VDK3nbBTAu5A==} - - ansi-colors@4.1.3: - resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} - engines: {node: '>=6'} - - ansi-escapes@4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} - engines: {node: '>=8'} - - ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - - ansi-regex@6.1.0: - resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} - engines: {node: '>=12'} - - ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - - ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} - engines: {node: '>=12'} - - argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} - - argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - - asn1.js@5.4.1: - resolution: {integrity: sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==} - - async@3.2.6: - resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} - - balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - - birpc@2.3.0: - resolution: {integrity: sha512-ijbtkn/F3Pvzb6jHypHRyve2QApOCZDR25D/VnkY2G/lBNcXCTsnsCxgY4k4PkVB7zfwzYbY3O9Lcqe3xufS5g==} - - blurhash@2.0.5: - resolution: {integrity: sha512-cRygWd7kGBQO3VEhPiTgq4Wc43ctsM+o46urrmPOiuAe+07fzlSB9OJVdpgDL0jPqXUVQ9ht7aq7kxOeJHRK+w==} - - bn.js@4.12.2: - resolution: {integrity: sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==} - - boolbase@1.0.0: - resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} - - brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} - - brace-expansion@2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} - - braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} - - buffer-equal-constant-time@1.0.1: - resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} - - buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - - bullmq@5.53.3: - resolution: {integrity: sha512-8E/5fbVmJ3W/5FoDnRf3TW7vXgpg+A2RqOoQqpHxlvs3ws6SALw6f2NjHA1q5ePF0T4zsDZ/LAZwdMknk3b0MQ==} - - bun-bagel@1.2.0: - resolution: {integrity: sha512-c4S68dNddpnog9nxXp9PAhcep0alOy49jpRlC1yACoxplUvgX22NZxeQUIIov5TCJJDH/snT5R9bMyix7AG0KQ==} - peerDependencies: - typescript: ^5.0.0 - - bun-types@1.2.16: - resolution: {integrity: sha512-ciXLrHV4PXax9vHvUrkvun9VPVGOVwbbbBF/Ev1cXz12lyEZMoJpIJABOfPcN9gDJRaiKF9MVbSygLg4NXu3/A==} - - call-bind-apply-helpers@1.0.2: - resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} - engines: {node: '>= 0.4'} - - call-bound@1.0.4: - resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} - engines: {node: '>= 0.4'} - - callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} - - ccount@2.0.1: - resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} - - chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} - - chalk@5.4.1: - resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - - 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==} - - cheerio-select@1.6.0: - resolution: {integrity: sha512-eq0GdBvxVFbqWgmCm7M3XGs1I8oLy/nExUnh6oLqmBditPO9AqQJrkslDpMun/hZ0yyTs8L0m85OHp4ho6Qm9g==} - - cheerio@1.0.0-rc.10: - resolution: {integrity: sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw==} - engines: {node: '>= 6'} - - cjs-module-lexer@1.4.3: - resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} - - clerc@0.44.0: - resolution: {integrity: sha512-fy7JcE7qW3hi5hvPeaEXKdgzeOz3WwE8Nd1SCfOpOSyWxoFJsLuHwUhwFggvJqEJIQ6kLTLZ7yWguwoTcMXO5w==} - - cli-cursor@5.0.0: - resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} - engines: {node: '>=18'} - - cli-spinners@2.9.2: - resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} - engines: {node: '>=6'} - - cli-width@4.1.0: - resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} - engines: {node: '>= 12'} - - clone@2.1.2: - resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} - engines: {node: '>=0.8'} - - cluster-key-slot@1.1.2: - resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} - engines: {node: '>=0.10.0'} - - code-block-writer@11.0.3: - resolution: {integrity: sha512-NiujjUFB4SwScJq2bwbYUtXbZhBSlY6vYzm++3Q6oC+U+injTqfPYFK8wS9COOmb2lueqp0ZRB4nK1VYeHgNyw==} - - color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} - - color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - - color-string@1.9.1: - resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} - - color@4.2.3: - resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} - engines: {node: '>=12.5.0'} - - comma-separated-tokens@2.0.3: - resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} - - commander@13.1.0: - resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} - engines: {node: '>=18'} - - commander@2.20.3: - resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - - commander@6.2.1: - resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==} - engines: {node: '>= 6'} - - concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - - confbox@0.1.8: - resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} - - confbox@0.2.2: - resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} - - cookie@1.0.2: - resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} - engines: {node: '>=18'} - - copy-anything@3.0.5: - resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==} - engines: {node: '>=12.13'} - - cosmiconfig@7.1.0: - resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} - engines: {node: '>=10'} - - cron-parser@4.9.0: - resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==} - engines: {node: '>=12.0.0'} - - cross-spawn@7.0.6: - resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} - engines: {node: '>= 8'} - - css-select@4.3.0: - resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} - - css-what@6.1.0: - resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} - engines: {node: '>= 6'} - - cssfilter@0.0.10: - resolution: {integrity: sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw==} - - csstype@3.1.3: - resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - - debug@4.4.0: - resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - - deepmerge@4.3.1: - resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} - engines: {node: '>=0.10.0'} - - defu@6.1.4: - resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} - - denque@2.1.0: - resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} - engines: {node: '>=0.10'} - - dequal@2.0.3: - resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} - engines: {node: '>=6'} - - detect-libc@2.0.4: - resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} - engines: {node: '>=8'} - - devlop@1.1.0: - resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} - - didyoumean2@6.0.1: - resolution: {integrity: sha512-PSy0zQwMg5O+LjT5Mz7vnKC8I7DfWLPF6M7oepqW7WP5mn2CY3hz46xZOa1GJY+KVfyXhdmz6+tdgXwrHlZc5g==} - engines: {node: ^16.14.0 || >=18.12.0} - - dom-serializer@1.4.1: - resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} - - dom-serializer@2.0.0: - resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} - - domelementtype@2.3.0: - resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} - - domhandler@3.3.0: - resolution: {integrity: sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==} - engines: {node: '>= 4'} - - domhandler@4.3.1: - resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} - engines: {node: '>= 4'} - - domhandler@5.0.3: - resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} - engines: {node: '>= 4'} - - domutils@2.8.0: - resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} - - domutils@3.2.2: - resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} - - drizzle-kit@0.31.1: - resolution: {integrity: sha512-PUjYKWtzOzPtdtQlTHQG3qfv4Y0XT8+Eas6UbxCmxTj7qgMf+39dDujf1BP1I+qqZtw9uzwTh8jYtkMuCq+B0Q==} - hasBin: true - - drizzle-orm@0.44.2: - resolution: {integrity: sha512-zGAqBzWWkVSFjZpwPOrmCrgO++1kZ5H/rZ4qTGeGOe18iXGVJWf3WPfHOVwFIbmi8kHjfJstC6rJomzGx8g/dQ==} - peerDependencies: - '@aws-sdk/client-rds-data': '>=3' - '@cloudflare/workers-types': '>=4' - '@electric-sql/pglite': '>=0.2.0' - '@libsql/client': '>=0.10.0' - '@libsql/client-wasm': '>=0.10.0' - '@neondatabase/serverless': '>=0.10.0' - '@op-engineering/op-sqlite': '>=2' - '@opentelemetry/api': ^1.4.1 - '@planetscale/database': '>=1.13' - '@prisma/client': '*' - '@tidbcloud/serverless': '*' - '@types/better-sqlite3': '*' - '@types/pg': '*' - '@types/sql.js': '*' - '@upstash/redis': '>=1.34.7' - '@vercel/postgres': '>=0.8.0' - '@xata.io/client': '*' - better-sqlite3: '>=7' - bun-types: '*' - expo-sqlite: '>=14.0.0' - gel: '>=2' - knex: '*' - kysely: '*' - mysql2: '>=2' - pg: '>=8' - postgres: '>=3' - prisma: '*' - sql.js: '>=1' - sqlite3: '>=5' - peerDependenciesMeta: - '@aws-sdk/client-rds-data': - optional: true - '@cloudflare/workers-types': - optional: true - '@electric-sql/pglite': - optional: true - '@libsql/client': - optional: true - '@libsql/client-wasm': - optional: true - '@neondatabase/serverless': - optional: true - '@op-engineering/op-sqlite': - optional: true - '@opentelemetry/api': - optional: true - '@planetscale/database': - optional: true - '@prisma/client': - optional: true - '@tidbcloud/serverless': - optional: true - '@types/better-sqlite3': - optional: true - '@types/pg': - optional: true - '@types/sql.js': - optional: true - '@upstash/redis': - optional: true - '@vercel/postgres': - optional: true - '@xata.io/client': - optional: true - better-sqlite3: - optional: true - bun-types: - optional: true - expo-sqlite: - optional: true - gel: - optional: true - knex: - optional: true - kysely: - optional: true - mysql2: - optional: true - pg: - optional: true - postgres: - optional: true - prisma: - optional: true - sql.js: - optional: true - sqlite3: - optional: true - - dunder-proto@1.0.1: - resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} - engines: {node: '>= 0.4'} - - eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - - ecdsa-sig-formatter@1.0.11: - resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} - - ejs@3.1.10: - resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} - engines: {node: '>=0.10.0'} - hasBin: true - - emoji-regex-xs@1.0.0: - resolution: {integrity: sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==} - - emoji-regex@10.4.0: - resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} - - emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - - emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - - entities@2.2.0: - resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} - - entities@4.5.0: - resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} - engines: {node: '>=0.12'} - - error-ex@1.3.2: - resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - - error-stack-parser-es@1.0.5: - resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==} - - es-define-property@1.0.1: - resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} - engines: {node: '>= 0.4'} - - es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} - - es-object-atoms@1.1.1: - resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} - engines: {node: '>= 0.4'} - - esbuild-register@3.6.0: - resolution: {integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==} - peerDependencies: - esbuild: '>=0.12 <1' - - esbuild@0.18.20: - resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} - engines: {node: '>=12'} - hasBin: true - - esbuild@0.21.5: - resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} - engines: {node: '>=12'} - hasBin: true - - esbuild@0.25.3: - resolution: {integrity: sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q==} - engines: {node: '>=18'} - hasBin: true - - escape-goat@3.0.0: - resolution: {integrity: sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==} - engines: {node: '>=10'} - - escape-string-regexp@5.0.0: - resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} - engines: {node: '>=12'} - - esm@3.2.25: - resolution: {integrity: sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==} - engines: {node: '>=6'} - - esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - - estree-walker@2.0.2: - resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} - - estree-walker@3.0.3: - resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} - - extend-shallow@2.0.1: - resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} - engines: {node: '>=0.10.0'} - - fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} - - fastest-levenshtein@1.0.16: - resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} - engines: {node: '>= 4.9.1'} - - fastq@1.19.1: - resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} - - feed@5.1.0: - resolution: {integrity: sha512-qGNhgYygnefSkAHHrNHqC7p3R8J0/xQDS/cYUud8er/qD9EFGWyCdUDfULHTJQN1d3H3WprzVwMc9MfB4J50Wg==} - engines: {node: '>=20', pnpm: '>=10'} - - figures@5.0.0: - resolution: {integrity: sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==} - engines: {node: '>=14'} - - filelist@1.0.4: - resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} - - fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} - - focus-trap@7.6.4: - resolution: {integrity: sha512-xx560wGBk7seZ6y933idtjJQc1l+ck+pI3sKvhKozdBV1dRZoKhkW5xoCaFv9tQiX5RH1xfSxjuNu6g+lmN/gw==} - - foreground-child@3.3.1: - resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} - engines: {node: '>=14'} - - forwarded-parse@2.1.2: - resolution: {integrity: sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==} - - fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - - function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - - get-east-asian-width@1.3.0: - resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} - engines: {node: '>=18'} - - get-intrinsic@1.3.0: - resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} - engines: {node: '>= 0.4'} - - get-proto@1.0.1: - resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} - engines: {node: '>= 0.4'} - - get-tsconfig@4.10.0: - resolution: {integrity: sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==} - - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - - glob@10.4.5: - resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} - hasBin: true - - gopd@1.2.0: - resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} - engines: {node: '>= 0.4'} - - gray-matter@4.0.3: - resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==} - engines: {node: '>=6.0'} - - has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - - has-symbols@1.1.0: - resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} - engines: {node: '>= 0.4'} - - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} - - hast-util-to-html@9.0.5: - resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} - - hast-util-whitespace@3.0.0: - resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} - - hono-openapi@0.4.8: - resolution: {integrity: sha512-LYr5xdtD49M7hEAduV1PftOMzuT8ZNvkyWfh1DThkLsIr4RkvDb12UxgIiFbwrJB6FLtFXLoOZL9x4IeDk2+VA==} - peerDependencies: - '@hono/arktype-validator': ^2.0.0 - '@hono/effect-validator': ^1.2.0 - '@hono/typebox-validator': ^0.2.0 || ^0.3.0 - '@hono/valibot-validator': ^0.5.1 - '@hono/zod-validator': ^0.4.1 - '@sinclair/typebox': ^0.34.9 - '@valibot/to-json-schema': ^1.0.0-beta.3 - arktype: ^2.0.0 - effect: ^3.11.3 - hono: ^4.6.13 - openapi-types: ^12.1.3 - valibot: ^1.0.0-beta.9 - zod: ^3.23.8 - zod-openapi: ^4.0.0 - peerDependenciesMeta: - '@hono/arktype-validator': - optional: true - '@hono/effect-validator': - optional: true - '@hono/typebox-validator': - optional: true - '@hono/valibot-validator': - optional: true - '@hono/zod-validator': - optional: true - '@sinclair/typebox': - optional: true - '@valibot/to-json-schema': - optional: true - arktype: - optional: true - effect: - optional: true - hono: - optional: true - valibot: - optional: true - zod: - optional: true - zod-openapi: - optional: true - - hono-rate-limiter@0.4.2: - resolution: {integrity: sha512-AAtFqgADyrmbDijcRTT/HJfwqfvhalya2Zo+MgfdrMPas3zSMD8SU03cv+ZsYwRU1swv7zgVt0shwN059yzhjw==} - peerDependencies: - hono: ^4.1.1 - - hono@4.7.11: - resolution: {integrity: sha512-rv0JMwC0KALbbmwJDEnxvQCeJh+xbS3KEWW5PC9cMJ08Ur9xgatI0HmtgYZfOdOSOeYsp5LO2cOhdI8cLEbDEQ==} - engines: {node: '>=16.9.0'} - - hono@4.7.8: - resolution: {integrity: sha512-PCibtFdxa7/Ldud9yddl1G81GjYaeMYYTq4ywSaNsYbB1Lug4mwtOMJf2WXykL0pntYwmpRJeOI3NmoDgD+Jxw==} - engines: {node: '>=16.9.0'} - - hookable@5.5.3: - resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} - - html-to-text@9.0.5: - resolution: {integrity: sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==} - engines: {node: '>=14'} - - html-void-elements@3.0.0: - resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} - - htmlparser2@5.0.1: - resolution: {integrity: sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==} - - htmlparser2@6.1.0: - resolution: {integrity: sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==} - - htmlparser2@8.0.2: - resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} - - http_ece@1.2.0: - resolution: {integrity: sha512-JrF8SSLVmcvc5NducxgyOrKXe3EsyHMgBFgSaIUGmArKe+rwr0uphRkRXvwiom3I+fpIfoItveHrfudL8/rxuA==} - engines: {node: '>=16'} - - https-proxy-agent@7.0.6: - resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} - engines: {node: '>= 14'} - - import-fresh@3.3.1: - resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} - engines: {node: '>=6'} - - import-in-the-middle@1.13.1: - resolution: {integrity: sha512-k2V9wNm9B+ysuelDTHjI9d5KPc4l8zAZTGqj+pcynvWkypZd857ryzN8jNC7Pg2YZXNMJcHRPpaDyCBbNyVRpA==} - - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - - ioredis@5.6.1: - resolution: {integrity: sha512-UxC0Yv1Y4WRJiGQxQkP0hfdL0/5/6YvdfOOClRgJ0qppSarkhneSa6UvkMkms0AkdGimSH3Ikqm+6mkMmX7vGA==} - engines: {node: '>=12.22.0'} - - ip-matching@2.1.2: - resolution: {integrity: sha512-/ok+VhKMasgR5gvTRViwRFQfc0qYt9Vdowg6TO4/pFlDCob5ZjGPkwuOoQVCd5OrMm20zqh+1vA8KLJZTeWudg==} - - is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - - is-arrayish@0.3.2: - resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} - - is-core-module@2.16.1: - resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} - engines: {node: '>= 0.4'} - - is-extendable@0.1.1: - resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} - engines: {node: '>=0.10.0'} - - is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - - is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - - is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} - - is-interactive@2.0.0: - resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} - engines: {node: '>=12'} - - is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - - is-platform@1.0.0: - resolution: {integrity: sha512-AKxe6+dvzAQsDXhhhxGRL9G67q5rKiyTL0BUl5mCyQz2NdvmqWNmMsjoCOIVdyXOYpP6MhkmZ1DPYGkfgv0MpA==} - - is-unicode-supported@1.3.0: - resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} - engines: {node: '>=12'} - - is-unicode-supported@2.1.0: - resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} - engines: {node: '>=18'} - - is-what@4.1.16: - resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} - engines: {node: '>=12.13'} - - isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - - iso-639-1@3.1.5: - resolution: {integrity: sha512-gXkz5+KN7HrG0Q5UGqSMO2qB9AsbEeyLP54kF1YrMsIxmu+g4BdB7rflReZTSTZGpfj8wywu6pfPBCylPIzGQA==} - engines: {node: '>=6.0'} - - jackspeak@3.4.3: - resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - - jake@10.9.2: - resolution: {integrity: sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==} - engines: {node: '>=10'} - hasBin: true - - jose@6.0.11: - resolution: {integrity: sha512-QxG7EaliDARm1O1S8BGakqncGT9s25bKL1WSf6/oa17Tkqwi8D2ZNglqCF+DsYF88/rV66Q/Q2mFAy697E1DUg==} - - js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - - js-yaml@3.14.1: - resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} - hasBin: true - - js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} - hasBin: true - - json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - - json-schema-walker@2.0.0: - resolution: {integrity: sha512-nXN2cMky0Iw7Af28w061hmxaPDaML5/bQD9nwm1lOoIKEGjHcRGxqWe4MfrkYThYAPjSUhmsp4bJNoLAyVn9Xw==} - engines: {node: '>=10'} - - json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - - juice@8.1.0: - resolution: {integrity: sha512-FLzurJrx5Iv1e7CfBSZH68dC04EEvXvvVvPYB7Vx1WAuhCp1ZPIMtqxc+WTWxVkpTIC2Ach/GAv0rQbtGf6YMA==} - engines: {node: '>=10.0.0'} - hasBin: true - - jwa@2.0.0: - resolution: {integrity: sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==} - - jws@4.0.0: - resolution: {integrity: sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==} - - kind-of@6.0.3: - resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} - engines: {node: '>=0.10.0'} - - kleur@4.1.5: - resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} - engines: {node: '>=6'} - - kons@0.7.1: - resolution: {integrity: sha512-mW1CkTgrLeIQjiBYd1n0U73T/2W7Vdzxx8rpta5Q4cSDAlr8hXw+ZctxGZlGgdUAmlcDlpkh0vUX8AOW+y1dog==} - - leac@0.6.0: - resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==} - - lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - - linkify-html@4.3.1: - resolution: {integrity: sha512-6ZNyucw7fH9Ncu17s+hvHFB2sU6fLWowqH6MqkXxtVL2kKkhnrho/DMCE3fWovmzVPgWSFGvg6zLkW+VWrVr4w==} - peerDependencies: - linkifyjs: ^4.0.0 - - linkify-it@5.0.0: - resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} - - linkify-string@4.3.1: - resolution: {integrity: sha512-1AnH52wZwuJi+skG/9dUphhQEUblVGSf0ntkM8z21RS9bF7xR0qPpqnNTyCo2Obqs5MR5wi8y5wOLPoBbzxm2w==} - peerDependencies: - linkifyjs: ^4.0.0 - - linkifyjs@4.3.1: - resolution: {integrity: sha512-DRSlB9DKVW04c4SUdGvKK5FR6be45lTU9M76JnngqPeeGDqPwYc0zdUErtsNVMtxPXgUWV4HbXbnC4sNyBxkYg==} - - lite-emit@2.3.0: - resolution: {integrity: sha512-QMPrnwPho7lfkzZUN3a0RJ/oiwpt464eXf6aVh1HGOYh+s7Utu78q3FcFbW59c8TNWWQaz9flKN1cEb8dmxD+g==} - - lodash.deburr@4.1.0: - resolution: {integrity: sha512-m/M1U1f3ddMCs6Hq2tAsYThTBDaAKFDX3dwDo97GEYzamXi9SqUpjWi/Rrj/gf3X2n8ktwgZrlP1z6E3v/IExQ==} - - lodash.defaults@4.2.0: - resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} - - lodash.isarguments@3.1.0: - resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==} - - lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - - log-symbols@6.0.0: - resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} - engines: {node: '>=18'} - - lru-cache@10.4.3: - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - - luxon@3.6.1: - resolution: {integrity: sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ==} - engines: {node: '>=12'} - - magic-regexp@0.10.0: - resolution: {integrity: sha512-Uly1Bu4lO1hwHUW0CQeSWuRtzCMNO00CmXtS8N6fyvB3B979GOEEeAkiTUDsmbYLAbvpUS/Kt5c4ibosAzVyVg==} - - magic-string@0.30.17: - resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} - - mark.js@8.11.1: - resolution: {integrity: sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==} - - markdown-it-anchor@9.2.0: - resolution: {integrity: sha512-sa2ErMQ6kKOA4l31gLGYliFQrMKkqSO0ZJgGhDHKijPf0pNFM9vghjAh3gn26pS4JDRs7Iwa9S36gxm3vgZTzg==} - peerDependencies: - '@types/markdown-it': '*' - markdown-it: '*' - - markdown-it-container@4.0.0: - resolution: {integrity: sha512-HaNccxUH0l7BNGYbFbjmGpf5aLHAMTinqRZQAEQbMr2cdD3z91Q6kIo1oUn1CQndkT03jat6ckrdRYuwwqLlQw==} - - markdown-it-image-figures@2.1.1: - resolution: {integrity: sha512-mwXSQ2nPeVUzCMIE3HlLvjRioopiqyJLNph0pyx38yf9mpqFDhNGnMpAXF9/A2Xv0oiF2cVyg9xwfF0HNAz05g==} - engines: {node: '>=12.0.0'} - peerDependencies: - markdown-it: '*' - - markdown-it-mathjax3@4.3.2: - resolution: {integrity: sha512-TX3GW5NjmupgFtMJGRauioMbbkGsOXAAt1DZ/rzzYmTHqzkO1rNAdiMD4NiruurToPApn2kYy76x02QN26qr2w==} - - markdown-it-toc-done-right@4.2.0: - resolution: {integrity: sha512-UB/IbzjWazwTlNAX0pvWNlJS8NKsOQ4syrXZQ/C72j+jirrsjVRT627lCaylrKJFBQWfRsPmIVQie8x38DEhAQ==} - - markdown-it@14.1.0: - resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} - hasBin: true - - math-intrinsics@1.1.0: - resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} - engines: {node: '>= 0.4'} - - mathjax-full@3.2.2: - resolution: {integrity: sha512-+LfG9Fik+OuI8SLwsiR02IVdjcnRCy5MufYLi0C3TdMT56L/pjB0alMVGgoWJF8pN9Rc7FESycZB9BMNWIid5w==} - - mdast-util-to-hast@13.2.0: - resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==} - - mdurl@2.0.0: - resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} - - mensch@0.3.4: - resolution: {integrity: sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g==} - - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - - mhchemparser@4.2.1: - resolution: {integrity: sha512-kYmyrCirqJf3zZ9t/0wGgRZ4/ZJw//VwaRVGA75C4nhE60vtnIzhl9J9ndkX/h6hxSN7pjg/cE0VxbnNM+bnDQ==} - - micromark-util-character@2.1.1: - resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} - - micromark-util-encode@2.0.1: - resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} - - micromark-util-sanitize-uri@2.0.1: - resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} - - micromark-util-symbol@2.0.1: - resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} - - micromark-util-types@2.0.2: - resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} - - micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} - engines: {node: '>=8.6'} - - mime-db@1.54.0: - resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} - engines: {node: '>= 0.6'} - - mime-types@3.0.1: - resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} - engines: {node: '>= 0.6'} - - mime@2.6.0: - resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} - engines: {node: '>=4.0.0'} - hasBin: true - - mimic-function@5.0.1: - resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} - engines: {node: '>=18'} - - minimalistic-assert@1.0.1: - resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} - - minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - - minimatch@5.1.6: - resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} - engines: {node: '>=10'} - - minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} - engines: {node: '>=16 || 14 >=14.17'} - - minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - - minipass@7.1.2: - resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} - engines: {node: '>=16 || 14 >=14.17'} - - minisearch@7.1.2: - resolution: {integrity: sha512-R1Pd9eF+MD5JYDDSPAp/q1ougKglm14uEkPMvQ/05RGmx6G9wvmLTrTI/Q5iPNJLYqNdsDQ7qTGIcNWR+FrHmA==} - - mitata@1.0.34: - resolution: {integrity: sha512-Mc3zrtNBKIMeHSCQ0XqRLo1vbdIx1wvFV9c8NJAiyho6AjNfMY8bVhbS12bwciUdd1t4rj8099CH3N3NFahaUA==} - - mitt@3.0.1: - resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} - - mj-context-menu@0.6.1: - resolution: {integrity: sha512-7NO5s6n10TIV96d4g2uDpG7ZDpIhMh0QNfGdJw/W47JswFcosz457wqz/b5sAKvl12sxINGFCn80NZHKwxQEXA==} - - mkdirp@1.0.4: - resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} - engines: {node: '>=10'} - hasBin: true - - mlly@1.7.4: - resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} - - module-details-from-path@1.0.4: - resolution: {integrity: sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==} - - ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - - msgpackr-extract@3.0.3: - resolution: {integrity: sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==} - hasBin: true - - msgpackr@1.11.2: - resolution: {integrity: sha512-F9UngXRlPyWCDEASDpTf6c9uNhGPTqnTeLVt7bN+bU1eajoR/8V9ys2BRaV5C/e5ihE6sJ9uPIKaYt6bFuO32g==} - - mute-stream@2.0.0: - resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} - engines: {node: ^18.17.0 || >=20.5.0} - - nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - - nanoid@5.1.5: - resolution: {integrity: sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==} - engines: {node: ^18 || >=20} - hasBin: true - - node-abort-controller@3.1.1: - resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==} - - node-fetch@2.7.0: - resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} - engines: {node: 4.x || >=6.0.0} - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true - - node-gyp-build-optional-packages@5.2.2: - resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==} - hasBin: true - - nth-check@2.1.1: - resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} - - oauth4webapi@3.5.2: - resolution: {integrity: sha512-VYz5BaP3izIrUc1GAVzIoz4JnljiW0YAUFObMBwsqDnfHxz2sjLu3W7/8vE8Ms9IbMewN9+1kcvhY3tMscAeGQ==} - - object-inspect@1.13.4: - resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} - engines: {node: '>= 0.4'} - - onetime@7.0.0: - resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} - engines: {node: '>=18'} - - oniguruma-to-es@3.1.1: - resolution: {integrity: sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ==} - - openapi-types@12.1.3: - resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} - - ora@8.2.0: - resolution: {integrity: sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==} - engines: {node: '>=18'} - - package-json-from-dist@1.0.1: - resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - - parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} - - parse-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} - - parse5-htmlparser2-tree-adapter@6.0.1: - resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==} - - parse5@6.0.1: - resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} - - parseley@0.12.1: - resolution: {integrity: sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==} - - path-browserify@1.0.1: - resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} - - path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - - path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - - path-scurry@1.11.1: - resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} - engines: {node: '>=16 || 14 >=14.18'} - - path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - - pathe@2.0.3: - resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - - peberminta@0.9.0: - resolution: {integrity: sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==} - - perfect-debounce@1.0.0: - resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} - - pg-int8@1.0.1: - resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} - engines: {node: '>=4.0.0'} - - pg-protocol@1.9.5: - resolution: {integrity: sha512-DYTWtWpfd5FOro3UnAfwvhD8jh59r2ig8bPtc9H8Ds7MscE/9NYruUQWFAOuraRl29jwcT2kyMFQ3MxeaVjUhg==} - - pg-types@2.2.0: - resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} - engines: {node: '>=4'} - - picocolors@1.1.1: - resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - - picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - - picomatch@4.0.2: - resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} - engines: {node: '>=12'} - - pkg-types@1.3.1: - resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} - - postcss@8.5.3: - resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} - engines: {node: ^10 || ^12 || >=14} - - postgres-array@2.0.0: - resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} - engines: {node: '>=4'} - - postgres-bytea@1.0.0: - resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} - engines: {node: '>=0.10.0'} - - postgres-date@1.0.7: - resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} - engines: {node: '>=0.10.0'} - - postgres-interval@1.2.0: - resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} - engines: {node: '>=0.10.0'} - - preact@10.26.5: - resolution: {integrity: sha512-fmpDkgfGU6JYux9teDWLhj9mKN55tyepwYbxHgQuIxbWQzgFg5vk7Mrrtfx7xRxq798ynkY4DDDxZr235Kk+4w==} - - property-information@7.0.0: - resolution: {integrity: sha512-7D/qOz/+Y4X/rzSB6jKxKUsQnphO046ei8qxG59mtM3RG3DHgTK81HrxrmoDVINJb8NKT5ZsRbwHvQ6B68Iyhg==} - - punycode.js@2.3.1: - resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} - engines: {node: '>=6'} - - qs@6.14.0: - resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} - engines: {node: '>=0.6'} - - qsu@1.10.0: - resolution: {integrity: sha512-60UGE7IEYXX/xy/n1w7vDm+is43pmePajAdXAnFOczvVDJKbVqZ5/RvRy+yobjA4iQitr4H/4zojVFtAkNWW9g==} - engines: {node: '>=18.0.0'} - - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - - redis-errors@1.2.0: - resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==} - engines: {node: '>=4'} - - redis-info@3.1.0: - resolution: {integrity: sha512-ER4L9Sh/vm63DkIE0bkSjxluQlioBiBgf5w1UuldaW/3vPcecdljVDisZhmnCMvsxHNiARTTDDHGg9cGwTfrKg==} - - redis-parser@3.0.0: - resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} - engines: {node: '>=4'} - - regex-recursion@6.0.2: - resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} - - regex-utilities@2.3.0: - resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} - - regex@6.0.1: - resolution: {integrity: sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==} - - regexp-tree@0.1.27: - resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==} - hasBin: true - - require-in-the-middle@7.5.2: - resolution: {integrity: sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ==} - engines: {node: '>=8.6.0'} - - resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} - - resolve-pkg-maps@1.0.0: - resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - - resolve@1.22.10: - resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} - engines: {node: '>= 0.4'} - hasBin: true - - restore-cursor@5.1.0: - resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} - engines: {node: '>=18'} - - reusify@1.1.0: - resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - - rfdc@1.4.1: - resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - - rollup@4.40.1: - resolution: {integrity: sha512-C5VvvgCCyfyotVITIAv+4efVytl5F7wt+/I2i9q9GZcEXW9BP52YYOXC58igUi+LFZVHukErIIqQSWwv/M3WRw==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - - safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - - safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - - sax@1.4.1: - resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} - - search-insights@2.17.3: - resolution: {integrity: sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==} - - section-matter@1.0.0: - resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==} - engines: {node: '>=4'} - - selderee@0.11.0: - resolution: {integrity: sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==} - - semver@7.7.2: - resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} - engines: {node: '>=10'} - hasBin: true - - sharp@0.34.2: - resolution: {integrity: sha512-lszvBmB9QURERtyKT2bNmsgxXK0ShJrL/fvqlonCo7e6xBF8nT8xU6pW+PMIbLsz0RxQk3rgH9kd8UmvOzlMJg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - - shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} - - shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - - shiki@2.5.0: - resolution: {integrity: sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ==} - - shimmer@1.2.1: - resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==} - - side-channel-list@1.0.0: - resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} - engines: {node: '>= 0.4'} - - side-channel-map@1.0.1: - resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} - engines: {node: '>= 0.4'} - - side-channel-weakmap@1.0.2: - resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} - engines: {node: '>= 0.4'} - - side-channel@1.1.0: - resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} - engines: {node: '>= 0.4'} - - signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} - - simple-swizzle@0.2.2: - resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} - - slick@1.12.2: - resolution: {integrity: sha512-4qdtOGcBjral6YIBCWJ0ljFSKNLz9KkhbWtuGvUyRowl1kxfuE1x/Z/aJcaiilpb3do9bl5K7/1h9XC5wWpY/A==} - - sonic-channel@1.3.1: - resolution: {integrity: sha512-+K4IZVFE7Tf2DB4EFZ23xo7a/+gJaiOHhFzXVZpzkX6Rs/rvf4YbSxnEGdYw8mrTcjtpG+jLVQEhP8sNTtN5VA==} - engines: {node: '>= 6.0.0'} - - source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} - engines: {node: '>=0.10.0'} - - source-map-support@0.5.21: - resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} - - source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - - space-separated-tokens@2.0.2: - resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} - - speakingurl@14.0.1: - resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} - engines: {node: '>=0.10.0'} - - speech-rule-engine@4.1.2: - resolution: {integrity: sha512-S6ji+flMEga+1QU79NDbwZ8Ivf0S/MpupQQiIC0rTpU/ZTKgcajijJJb1OcByBQDjrXCN1/DJtGz4ZJeBMPGJw==} - hasBin: true - - sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - - standard-as-callback@2.1.0: - resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} - - stdin-discarder@0.2.2: - resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} - engines: {node: '>=18'} - - string-comparison@1.3.0: - resolution: {integrity: sha512-46aD+slEwybxAMPRII83ATbgMgTiz5P8mVd7Z6VJsCzSHFjdt1hkAVLeFxPIyEb11tc6ihpJTlIqoO0MCF6NPw==} - engines: {node: ^16.0.0 || >=18.0.0} - - string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - - string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} - - string-width@6.1.0: - resolution: {integrity: sha512-k01swCJAgQmuADB0YIc+7TuatfNvTBVOoaUWJjTB9R4VJzR5vNWzf5t42ESVZFPS8xTySF7CAdV4t/aaIm3UnQ==} - engines: {node: '>=16'} - - string-width@7.2.0: - resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} - engines: {node: '>=18'} - - stringify-entities@4.0.4: - resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} - - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - - strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} - engines: {node: '>=12'} - - strip-bom-string@1.0.0: - resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==} - engines: {node: '>=0.10.0'} - - superjson@2.2.2: - resolution: {integrity: sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==} - engines: {node: '>=16'} - - supports-color@10.0.0: - resolution: {integrity: sha512-HRVVSbCCMbj7/kdWF9Q+bbckjBHLtHMEoJWlkmYzzdwhYMkjkOwubLM6t7NbWKjgKamGDrWL1++KrjUO1t9oAQ==} - engines: {node: '>=18'} - - supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - - supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} - - tabbable@6.2.0: - resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} - - text-table@0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - - to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - - tr46@0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - - trim-lines@3.0.1: - resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} - - true-myth@4.1.1: - resolution: {integrity: sha512-rqy30BSpxPznbbTcAcci90oZ1YR4DqvKcNXNerG5gQBU2v4jk0cygheiul5J6ExIMrgDVuanv/MkGfqZbKrNNg==} - engines: {node: 10.* || >= 12.*} - - ts-morph@13.0.3: - resolution: {integrity: sha512-pSOfUMx8Ld/WUreoSzvMFQG5i9uEiWIsBYjpU9+TTASOeUa89j5HykomeqVULm1oqWtBdleI3KEFRLrlA3zGIw==} - - ts-prune@0.10.3: - resolution: {integrity: sha512-iS47YTbdIcvN8Nh/1BFyziyUqmjXz7GVzWu02RaZXqb+e/3Qe1B7IQ4860krOeCGUeJmterAlaM2FRH0Ue0hjw==} - hasBin: true - - tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - - type-fest@0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} - engines: {node: '>=10'} - - type-fest@4.40.1: - resolution: {integrity: sha512-9YvLNnORDpI+vghLU/Nf+zSv0kL47KbVJ1o3sKgoTefl6i+zebxbiDQWoe/oWWqPhIgQdRZRT1KA9sCPL810SA==} - engines: {node: '>=16'} - - type-flag@3.0.0: - resolution: {integrity: sha512-3YaYwMseXCAhBB14RXW5cRQfJQlEknS6i4C8fCfeUdS3ihG9EdccdR9kt3vP73ZdeTGmPb4bZtkDn5XMIn1DLA==} - - type-level-regexp@0.1.17: - resolution: {integrity: sha512-wTk4DH3cxwk196uGLK/E9pE45aLfeKJacKmcEgEOA/q5dnPGNxXt0cfYdFxb57L+sEpf1oJH4Dnx/pnRcku9jg==} - - typescript@5.8.3: - resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} - engines: {node: '>=14.17'} - hasBin: true - - uc.micro@2.1.0: - resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} - - ufo@1.6.1: - resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} - - undici-types@6.21.0: - resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - - unicode-emoji-json@0.8.0: - resolution: {integrity: sha512-3wDXXvp6YGoKGhS2O2H7+V+bYduOBydN1lnI0uVfr1cIdY02uFFiEH1i3kE5CCE4l6UqbLKVmEFW9USxTAMD1g==} - - unist-util-is@6.0.0: - resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} - - unist-util-position@5.0.0: - resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} - - unist-util-stringify-position@4.0.0: - resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} - - unist-util-visit-parents@6.0.1: - resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} - - unist-util-visit@5.0.0: - resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} - - unplugin@2.3.2: - resolution: {integrity: sha512-3n7YA46rROb3zSj8fFxtxC/PqoyvYQ0llwz9wtUPUutr9ig09C8gGo5CWCwHrUzlqC1LLR43kxp5vEIyH1ac1w==} - engines: {node: '>=18.12.0'} - - uqr@0.1.2: - resolution: {integrity: sha512-MJu7ypHq6QasgF5YRTjqscSzQp/W11zoUk6kvmlH+fmWEs63Y0Eib13hYFwAzagRJcVY8WVnlV+eBDUGMJ5IbA==} - - uuid@9.0.1: - resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} - hasBin: true - - valid-data-url@3.0.1: - resolution: {integrity: sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA==} - engines: {node: '>=10'} - - vfile-message@4.0.2: - resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} - - vfile@6.0.3: - resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - - vite@5.4.19: - resolution: {integrity: sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' - lightningcss: ^1.21.0 - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - - vitepress-plugin-tabs@0.7.1: - resolution: {integrity: sha512-jxJvsicxnMSIYX9b8mAFLD2nwyKUcMO10dEt4nDSbinZhM8cGvAmMFOHPdf6TBX6gYZRl+/++/iYHtoM14fERQ==} - peerDependencies: - vitepress: ^1.0.0 - vue: ^3.5.0 - - vitepress-sidebar@1.31.1: - resolution: {integrity: sha512-Hx10z5le87jIIXVfKq4AtRrVqVJJ/1cQsZhmwT+ghVR/j4Yor9FjNMszyigJ54ktrEtoxSLO6C9tvuLauT4lZA==} - engines: {node: '>=18.0.0'} - - vitepress@1.6.3: - resolution: {integrity: sha512-fCkfdOk8yRZT8GD9BFqusW3+GggWYZ/rYncOfmgcDtP3ualNHCAg+Robxp2/6xfH1WwPHtGpPwv7mbA3qomtBw==} - hasBin: true - peerDependencies: - markdown-it-mathjax3: ^4 - postcss: ^8 - peerDependenciesMeta: - markdown-it-mathjax3: - optional: true - postcss: - optional: true - - vue@3.5.16: - resolution: {integrity: sha512-rjOV2ecxMd5SiAmof2xzh2WxntRcigkX/He4YFJ6WdRvVUrbt6DxC1Iujh10XLl8xCDRDtGKMeO3D+pRQ1PP9w==} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - web-push@3.6.7: - resolution: {integrity: sha512-OpiIUe8cuGjrj3mMBFWY+e4MMIkW3SVT+7vEIjvD9kejGUypv8GPDf84JdPWskK8zMRIJ6xYGm+Kxr8YkPyA0A==} - engines: {node: '>= 16'} - hasBin: true - - web-resource-inliner@6.0.1: - resolution: {integrity: sha512-kfqDxt5dTB1JhqsCUQVFDj0rmY+4HLwGQIsLPbyrsN9y9WV/1oFDSx3BQ4GfCv9X+jVeQ7rouTqwK53rA/7t8A==} - engines: {node: '>=10.0.0'} - - webidl-conversions@3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - - webpack-virtual-modules@0.6.2: - resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} - - whatwg-url@5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - - which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true - - wicked-good-xpath@1.3.0: - resolution: {integrity: sha512-Gd9+TUn5nXdwj/hFsPVx5cuHHiF5Bwuc30jZ4+ronF1qHK5O7HD0sgmXWSEgwKquT3ClLoKPVbO6qGwVwLzvAw==} - - wrap-ansi@6.2.0: - resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} - engines: {node: '>=8'} - - wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} - - wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} - - xml-js@1.6.11: - resolution: {integrity: sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==} - hasBin: true - - xss@1.0.15: - resolution: {integrity: sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg==} - engines: {node: '>= 0.10.0'} - hasBin: true - - xtend@4.0.2: - resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} - engines: {node: '>=0.4'} - - yaml@1.10.2: - resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} - engines: {node: '>= 6'} - - yoctocolors-cjs@2.1.2: - resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} - engines: {node: '>=18'} - - yoctocolors@1.0.0: - resolution: {integrity: sha512-qJNAmSF77lWjfRVwCZK3PcKYWrr+55RUQTiXDxXHGbxzf8WuuRgftIB3hqZ5fykjOF/MC62cazsG/2ZDBedOnQ==} - engines: {node: '>=14.16'} - - youch-core@0.3.2: - resolution: {integrity: sha512-fusrlIMLeRvTFYLUjJ9KzlGC3N+6MOPJ68HNj/yJv2nz7zq8t4HEviLms2gkdRPUS7F5rZ5n+pYx9r88m6IE1g==} - engines: {node: '>=18'} - - youch@4.1.0-beta.7: - resolution: {integrity: sha512-HUn0M24AUTMvjdkoMtH8fJz2FEd+k1xvtR9EoTrDUoVUi6o7xl5X+pST/vjk4T3GEQo2mJ9FlAvhWBm8dIdD4g==} - engines: {node: '>=18'} - - zod-openapi@4.2.4: - resolution: {integrity: sha512-tsrQpbpqFCXqVXUzi3TPwFhuMtLN3oNZobOtYnK6/5VkXsNdnIgyNr4r8no4wmYluaxzN3F7iS+8xCW8BmMQ8g==} - engines: {node: '>=18'} - peerDependencies: - zod: ^3.21.4 - - zod-to-json-schema@3.24.5: - resolution: {integrity: sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==} - peerDependencies: - zod: ^3.24.1 - - zod-validation-error@3.4.0: - resolution: {integrity: sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ==} - engines: {node: '>=18.0.0'} - peerDependencies: - zod: ^3.18.0 - - zod-validation-error@3.5.0: - resolution: {integrity: sha512-IWK6O51sRkq0YsnYD2oLDuK2BNsIjYUlR0+1YSd4JyBzm6/892IWroUnLc7oW4FU+b0f6948BHi6H8MDcqpOGw==} - engines: {node: '>=18.0.0'} - peerDependencies: - zod: ^3.25.0 - - zod@3.24.1: - resolution: {integrity: sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==} - - zod@3.24.3: - resolution: {integrity: sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==} - - zod@3.25.64: - resolution: {integrity: sha512-hbP9FpSZf7pkS7hRVUrOjhwKJNyampPgtXKc3AN6DsWtoHsg2Sb4SQaS4Tcay380zSwd2VPo9G9180emBACp5g==} - - zwitch@2.0.4: - resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} - -snapshots: - - '@algolia/autocomplete-core@1.17.7(@algolia/client-search@5.24.0)(algoliasearch@5.24.0)(search-insights@2.17.3)': - dependencies: - '@algolia/autocomplete-plugin-algolia-insights': 1.17.7(@algolia/client-search@5.24.0)(algoliasearch@5.24.0)(search-insights@2.17.3) - '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.24.0)(algoliasearch@5.24.0) - transitivePeerDependencies: - - '@algolia/client-search' - - algoliasearch - - search-insights - - '@algolia/autocomplete-plugin-algolia-insights@1.17.7(@algolia/client-search@5.24.0)(algoliasearch@5.24.0)(search-insights@2.17.3)': - dependencies: - '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.24.0)(algoliasearch@5.24.0) - search-insights: 2.17.3 - transitivePeerDependencies: - - '@algolia/client-search' - - algoliasearch - - '@algolia/autocomplete-preset-algolia@1.17.7(@algolia/client-search@5.24.0)(algoliasearch@5.24.0)': - dependencies: - '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.24.0)(algoliasearch@5.24.0) - '@algolia/client-search': 5.24.0 - algoliasearch: 5.24.0 - - '@algolia/autocomplete-shared@1.17.7(@algolia/client-search@5.24.0)(algoliasearch@5.24.0)': - dependencies: - '@algolia/client-search': 5.24.0 - algoliasearch: 5.24.0 - - '@algolia/client-abtesting@5.24.0': - dependencies: - '@algolia/client-common': 5.24.0 - '@algolia/requester-browser-xhr': 5.24.0 - '@algolia/requester-fetch': 5.24.0 - '@algolia/requester-node-http': 5.24.0 - - '@algolia/client-analytics@5.24.0': - dependencies: - '@algolia/client-common': 5.24.0 - '@algolia/requester-browser-xhr': 5.24.0 - '@algolia/requester-fetch': 5.24.0 - '@algolia/requester-node-http': 5.24.0 - - '@algolia/client-common@5.24.0': {} - - '@algolia/client-insights@5.24.0': - dependencies: - '@algolia/client-common': 5.24.0 - '@algolia/requester-browser-xhr': 5.24.0 - '@algolia/requester-fetch': 5.24.0 - '@algolia/requester-node-http': 5.24.0 - - '@algolia/client-personalization@5.24.0': - dependencies: - '@algolia/client-common': 5.24.0 - '@algolia/requester-browser-xhr': 5.24.0 - '@algolia/requester-fetch': 5.24.0 - '@algolia/requester-node-http': 5.24.0 - - '@algolia/client-query-suggestions@5.24.0': - dependencies: - '@algolia/client-common': 5.24.0 - '@algolia/requester-browser-xhr': 5.24.0 - '@algolia/requester-fetch': 5.24.0 - '@algolia/requester-node-http': 5.24.0 - - '@algolia/client-search@5.24.0': - dependencies: - '@algolia/client-common': 5.24.0 - '@algolia/requester-browser-xhr': 5.24.0 - '@algolia/requester-fetch': 5.24.0 - '@algolia/requester-node-http': 5.24.0 - - '@algolia/ingestion@1.24.0': - dependencies: - '@algolia/client-common': 5.24.0 - '@algolia/requester-browser-xhr': 5.24.0 - '@algolia/requester-fetch': 5.24.0 - '@algolia/requester-node-http': 5.24.0 - - '@algolia/monitoring@1.24.0': - dependencies: - '@algolia/client-common': 5.24.0 - '@algolia/requester-browser-xhr': 5.24.0 - '@algolia/requester-fetch': 5.24.0 - '@algolia/requester-node-http': 5.24.0 - - '@algolia/recommend@5.24.0': - dependencies: - '@algolia/client-common': 5.24.0 - '@algolia/requester-browser-xhr': 5.24.0 - '@algolia/requester-fetch': 5.24.0 - '@algolia/requester-node-http': 5.24.0 - - '@algolia/requester-browser-xhr@5.24.0': - dependencies: - '@algolia/client-common': 5.24.0 - - '@algolia/requester-fetch@5.24.0': - dependencies: - '@algolia/client-common': 5.24.0 - - '@algolia/requester-node-http@5.24.0': - dependencies: - '@algolia/client-common': 5.24.0 - - '@apidevtools/json-schema-ref-parser@11.9.3': - dependencies: - '@jsdevtools/ono': 7.1.3 - '@types/json-schema': 7.0.15 - js-yaml: 4.1.0 - - '@babel/code-frame@7.27.1': - dependencies: - '@babel/helper-validator-identifier': 7.27.1 - js-tokens: 4.0.0 - picocolors: 1.1.1 - - '@babel/helper-string-parser@7.27.1': {} - - '@babel/helper-validator-identifier@7.27.1': {} - - '@babel/parser@7.27.2': - dependencies: - '@babel/types': 7.27.1 - - '@babel/runtime@7.27.1': {} - - '@babel/types@7.27.1': - dependencies: - '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.27.1 - - '@badgateway/oauth2-client@3.2.0': {} - - '@biomejs/biome@2.0.0-beta.5': - optionalDependencies: - '@biomejs/cli-darwin-arm64': 2.0.0-beta.5 - '@biomejs/cli-darwin-x64': 2.0.0-beta.5 - '@biomejs/cli-linux-arm64': 2.0.0-beta.5 - '@biomejs/cli-linux-arm64-musl': 2.0.0-beta.5 - '@biomejs/cli-linux-x64': 2.0.0-beta.5 - '@biomejs/cli-linux-x64-musl': 2.0.0-beta.5 - '@biomejs/cli-win32-arm64': 2.0.0-beta.5 - '@biomejs/cli-win32-x64': 2.0.0-beta.5 - - '@biomejs/cli-darwin-arm64@2.0.0-beta.5': - optional: true - - '@biomejs/cli-darwin-x64@2.0.0-beta.5': - optional: true - - '@biomejs/cli-linux-arm64-musl@2.0.0-beta.5': - optional: true - - '@biomejs/cli-linux-arm64@2.0.0-beta.5': - optional: true - - '@biomejs/cli-linux-x64-musl@2.0.0-beta.5': - optional: true - - '@biomejs/cli-linux-x64@2.0.0-beta.5': - optional: true - - '@biomejs/cli-win32-arm64@2.0.0-beta.5': - optional: true - - '@biomejs/cli-win32-x64@2.0.0-beta.5': - optional: true - - '@bull-board/api@6.10.1(@bull-board/ui@6.10.1)': - dependencies: - '@bull-board/ui': 6.10.1 - redis-info: 3.1.0 - - '@bull-board/hono@6.10.1(hono@4.7.11)': - dependencies: - '@bull-board/api': 6.10.1(@bull-board/ui@6.10.1) - '@bull-board/ui': 6.10.1 - ejs: 3.1.10 - hono: 4.7.11 - - '@bull-board/ui@6.10.1': - dependencies: - '@bull-board/api': 6.10.1(@bull-board/ui@6.10.1) - - '@clerc/core@0.44.0': - dependencies: - '@clerc/utils': 0.44.0(@clerc/core@0.44.0) - defu: 6.1.4 - is-platform: 1.0.0 - lite-emit: 2.3.0 - type-fest: 4.40.1 - type-flag: 3.0.0 - - '@clerc/plugin-completions@0.44.0(@clerc/core@0.44.0)': - dependencies: - '@clerc/core': 0.44.0 - '@clerc/utils': 0.44.0(@clerc/core@0.44.0) - - '@clerc/plugin-friendly-error@0.44.0(@clerc/core@0.44.0)': - dependencies: - '@clerc/core': 0.44.0 - kons: 0.7.1 - - '@clerc/plugin-help@0.44.0(@clerc/core@0.44.0)': - dependencies: - '@clerc/core': 0.44.0 - '@clerc/utils': 0.44.0(@clerc/core@0.44.0) - '@types/text-table': 0.2.5 - string-width: 6.1.0 - text-table: 0.2.0 - yoctocolors: 1.0.0 - - '@clerc/plugin-not-found@0.44.0(@clerc/core@0.44.0)': - dependencies: - '@clerc/core': 0.44.0 - '@clerc/utils': 0.44.0(@clerc/core@0.44.0) - didyoumean2: 6.0.1 - yoctocolors: 1.0.0 - - '@clerc/plugin-strict-flags@0.44.0(@clerc/core@0.44.0)': - dependencies: - '@clerc/core': 0.44.0 - - '@clerc/plugin-version@0.44.0(@clerc/core@0.44.0)': - dependencies: - '@clerc/core': 0.44.0 - '@clerc/utils': 0.44.0(@clerc/core@0.44.0) - - '@clerc/utils@0.44.0(@clerc/core@0.44.0)': - dependencies: - '@clerc/core': 0.44.0 - - '@docsearch/css@3.8.2': {} - - '@docsearch/js@3.8.2(@algolia/client-search@5.24.0)(search-insights@2.17.3)': - dependencies: - '@docsearch/react': 3.8.2(@algolia/client-search@5.24.0)(search-insights@2.17.3) - preact: 10.26.5 - transitivePeerDependencies: - - '@algolia/client-search' - - '@types/react' - - react - - react-dom - - search-insights - - '@docsearch/react@3.8.2(@algolia/client-search@5.24.0)(search-insights@2.17.3)': - dependencies: - '@algolia/autocomplete-core': 1.17.7(@algolia/client-search@5.24.0)(algoliasearch@5.24.0)(search-insights@2.17.3) - '@algolia/autocomplete-preset-algolia': 1.17.7(@algolia/client-search@5.24.0)(algoliasearch@5.24.0) - '@docsearch/css': 3.8.2 - algoliasearch: 5.24.0 - optionalDependencies: - search-insights: 2.17.3 - transitivePeerDependencies: - - '@algolia/client-search' - - '@drizzle-team/brocli@0.10.2': {} - - '@emnapi/runtime@1.4.3': - dependencies: - tslib: 2.8.1 - optional: true - - '@esbuild-kit/core-utils@3.3.2': - dependencies: - esbuild: 0.18.20 - source-map-support: 0.5.21 - - '@esbuild-kit/esm-loader@2.6.5': - dependencies: - '@esbuild-kit/core-utils': 3.3.2 - get-tsconfig: 4.10.0 - - '@esbuild/aix-ppc64@0.21.5': - optional: true - - '@esbuild/aix-ppc64@0.25.3': - optional: true - - '@esbuild/android-arm64@0.18.20': - optional: true - - '@esbuild/android-arm64@0.21.5': - optional: true - - '@esbuild/android-arm64@0.25.3': - optional: true - - '@esbuild/android-arm@0.18.20': - optional: true - - '@esbuild/android-arm@0.21.5': - optional: true - - '@esbuild/android-arm@0.25.3': - optional: true - - '@esbuild/android-x64@0.18.20': - optional: true - - '@esbuild/android-x64@0.21.5': - optional: true - - '@esbuild/android-x64@0.25.3': - optional: true - - '@esbuild/darwin-arm64@0.18.20': - optional: true - - '@esbuild/darwin-arm64@0.21.5': - optional: true - - '@esbuild/darwin-arm64@0.25.3': - optional: true - - '@esbuild/darwin-x64@0.18.20': - optional: true - - '@esbuild/darwin-x64@0.21.5': - optional: true - - '@esbuild/darwin-x64@0.25.3': - optional: true - - '@esbuild/freebsd-arm64@0.18.20': - optional: true - - '@esbuild/freebsd-arm64@0.21.5': - optional: true - - '@esbuild/freebsd-arm64@0.25.3': - optional: true - - '@esbuild/freebsd-x64@0.18.20': - optional: true - - '@esbuild/freebsd-x64@0.21.5': - optional: true - - '@esbuild/freebsd-x64@0.25.3': - optional: true - - '@esbuild/linux-arm64@0.18.20': - optional: true - - '@esbuild/linux-arm64@0.21.5': - optional: true - - '@esbuild/linux-arm64@0.25.3': - optional: true - - '@esbuild/linux-arm@0.18.20': - optional: true - - '@esbuild/linux-arm@0.21.5': - optional: true - - '@esbuild/linux-arm@0.25.3': - optional: true - - '@esbuild/linux-ia32@0.18.20': - optional: true - - '@esbuild/linux-ia32@0.21.5': - optional: true - - '@esbuild/linux-ia32@0.25.3': - optional: true - - '@esbuild/linux-loong64@0.18.20': - optional: true - - '@esbuild/linux-loong64@0.21.5': - optional: true - - '@esbuild/linux-loong64@0.25.3': - optional: true - - '@esbuild/linux-mips64el@0.18.20': - optional: true - - '@esbuild/linux-mips64el@0.21.5': - optional: true - - '@esbuild/linux-mips64el@0.25.3': - optional: true - - '@esbuild/linux-ppc64@0.18.20': - optional: true - - '@esbuild/linux-ppc64@0.21.5': - optional: true - - '@esbuild/linux-ppc64@0.25.3': - optional: true - - '@esbuild/linux-riscv64@0.18.20': - optional: true - - '@esbuild/linux-riscv64@0.21.5': - optional: true - - '@esbuild/linux-riscv64@0.25.3': - optional: true - - '@esbuild/linux-s390x@0.18.20': - optional: true - - '@esbuild/linux-s390x@0.21.5': - optional: true - - '@esbuild/linux-s390x@0.25.3': - optional: true - - '@esbuild/linux-x64@0.18.20': - optional: true - - '@esbuild/linux-x64@0.21.5': - optional: true - - '@esbuild/linux-x64@0.25.3': - optional: true - - '@esbuild/netbsd-arm64@0.25.3': - optional: true - - '@esbuild/netbsd-x64@0.18.20': - optional: true - - '@esbuild/netbsd-x64@0.21.5': - optional: true - - '@esbuild/netbsd-x64@0.25.3': - optional: true - - '@esbuild/openbsd-arm64@0.25.3': - optional: true - - '@esbuild/openbsd-x64@0.18.20': - optional: true - - '@esbuild/openbsd-x64@0.21.5': - optional: true - - '@esbuild/openbsd-x64@0.25.3': - optional: true - - '@esbuild/sunos-x64@0.18.20': - optional: true - - '@esbuild/sunos-x64@0.21.5': - optional: true - - '@esbuild/sunos-x64@0.25.3': - optional: true - - '@esbuild/win32-arm64@0.18.20': - optional: true - - '@esbuild/win32-arm64@0.21.5': - optional: true - - '@esbuild/win32-arm64@0.25.3': - optional: true - - '@esbuild/win32-ia32@0.18.20': - optional: true - - '@esbuild/win32-ia32@0.21.5': - optional: true - - '@esbuild/win32-ia32@0.25.3': - optional: true - - '@esbuild/win32-x64@0.18.20': - optional: true - - '@esbuild/win32-x64@0.21.5': - optional: true - - '@esbuild/win32-x64@0.25.3': - optional: true - - '@hackmd/markdown-it-task-lists@2.1.4': {} - - '@hono/zod-validator@0.7.0(hono@4.7.11)(zod@3.25.64)': - dependencies: - hono: 4.7.11 - zod: 3.25.64 - - '@iconify-json/simple-icons@1.2.33': - dependencies: - '@iconify/types': 2.0.0 - - '@iconify/types@2.0.0': {} - - '@img/sharp-darwin-arm64@0.34.2': - optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.1.0 - optional: true - - '@img/sharp-darwin-x64@0.34.2': - optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.1.0 - optional: true - - '@img/sharp-libvips-darwin-arm64@1.1.0': - optional: true - - '@img/sharp-libvips-darwin-x64@1.1.0': - optional: true - - '@img/sharp-libvips-linux-arm64@1.1.0': - optional: true - - '@img/sharp-libvips-linux-arm@1.1.0': - optional: true - - '@img/sharp-libvips-linux-ppc64@1.1.0': - optional: true - - '@img/sharp-libvips-linux-s390x@1.1.0': - optional: true - - '@img/sharp-libvips-linux-x64@1.1.0': - optional: true - - '@img/sharp-libvips-linuxmusl-arm64@1.1.0': - optional: true - - '@img/sharp-libvips-linuxmusl-x64@1.1.0': - optional: true - - '@img/sharp-linux-arm64@0.34.2': - optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.1.0 - optional: true - - '@img/sharp-linux-arm@0.34.2': - optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.1.0 - optional: true - - '@img/sharp-linux-s390x@0.34.2': - optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.1.0 - optional: true - - '@img/sharp-linux-x64@0.34.2': - optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.1.0 - optional: true - - '@img/sharp-linuxmusl-arm64@0.34.2': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.1.0 - optional: true - - '@img/sharp-linuxmusl-x64@0.34.2': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.1.0 - optional: true - - '@img/sharp-wasm32@0.34.2': - dependencies: - '@emnapi/runtime': 1.4.3 - optional: true - - '@img/sharp-win32-arm64@0.34.2': - optional: true - - '@img/sharp-win32-ia32@0.34.2': - optional: true - - '@img/sharp-win32-x64@0.34.2': - optional: true - - '@inquirer/confirm@5.1.12(@types/node@22.15.3)': - dependencies: - '@inquirer/core': 10.1.13(@types/node@22.15.3) - '@inquirer/type': 3.0.7(@types/node@22.15.3) - optionalDependencies: - '@types/node': 22.15.3 - - '@inquirer/core@10.1.13(@types/node@22.15.3)': - dependencies: - '@inquirer/figures': 1.0.12 - '@inquirer/type': 3.0.7(@types/node@22.15.3) - ansi-escapes: 4.3.2 - cli-width: 4.1.0 - mute-stream: 2.0.0 - signal-exit: 4.1.0 - wrap-ansi: 6.2.0 - yoctocolors-cjs: 2.1.2 - optionalDependencies: - '@types/node': 22.15.3 - - '@inquirer/figures@1.0.12': {} - - '@inquirer/type@3.0.7(@types/node@22.15.3)': - optionalDependencies: - '@types/node': 22.15.3 - - '@ioredis/commands@1.2.0': {} - - '@isaacs/cliui@8.0.2': - dependencies: - string-width: 5.1.2 - string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.0 - strip-ansi-cjs: strip-ansi@6.0.1 - wrap-ansi: 8.1.0 - wrap-ansi-cjs: wrap-ansi@7.0.0 - - '@jridgewell/sourcemap-codec@1.5.0': {} - - '@jsdevtools/ono@7.1.3': {} - - '@logtape/file@0.12.0(@logtape/logtape@0.12.0)': - dependencies: - '@logtape/logtape': 0.12.0 - - '@logtape/logtape@0.12.0': {} - - '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': - optional: true - - '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': - optional: true - - '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': - optional: true - - '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': - optional: true - - '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': - optional: true - - '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': - optional: true - - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.19.1 - - '@opentelemetry/api-logs@0.57.2': - dependencies: - '@opentelemetry/api': 1.9.0 - - '@opentelemetry/api@1.9.0': {} - - '@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - - '@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/semantic-conventions': 1.28.0 - - '@opentelemetry/instrumentation-amqplib@0.46.1(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.34.0 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-connect@0.43.1(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.34.0 - '@types/connect': 3.4.38 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-dataloader@0.16.1(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-express@0.47.1(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.34.0 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-fs@0.19.1(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-generic-pool@0.43.1(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-graphql@0.47.1(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-hapi@0.45.2(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.34.0 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-http@0.57.2(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.28.0 - forwarded-parse: 2.1.2 - semver: 7.7.2 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-ioredis@0.47.1(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/redis-common': 0.36.2 - '@opentelemetry/semantic-conventions': 1.34.0 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-kafkajs@0.7.1(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.34.0 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-knex@0.44.1(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.34.0 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-koa@0.47.1(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.34.0 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-lru-memoizer@0.44.1(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-mongodb@0.52.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.34.0 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-mongoose@0.46.1(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.34.0 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-mysql2@0.45.2(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.34.0 - '@opentelemetry/sql-common': 0.40.1(@opentelemetry/api@1.9.0) - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-mysql@0.45.1(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.34.0 - '@types/mysql': 2.15.26 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-pg@0.51.1(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.34.0 - '@opentelemetry/sql-common': 0.40.1(@opentelemetry/api@1.9.0) - '@types/pg': 8.6.1 - '@types/pg-pool': 2.0.6 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-redis-4@0.46.1(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/redis-common': 0.36.2 - '@opentelemetry/semantic-conventions': 1.34.0 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-tedious@0.18.1(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.34.0 - '@types/tedious': 4.0.14 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation-undici@0.10.1(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs': 0.57.2 - '@types/shimmer': 1.2.0 - import-in-the-middle: 1.13.1 - require-in-the-middle: 7.5.2 - semver: 7.7.2 - shimmer: 1.2.1 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/redis-common@0.36.2': {} - - '@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.28.0 - - '@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.28.0 - - '@opentelemetry/semantic-conventions@1.28.0': {} - - '@opentelemetry/semantic-conventions@1.34.0': {} - - '@opentelemetry/sql-common@0.40.1(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) - - '@pkgjs/parseargs@0.11.0': - optional: true - - '@poppinss/colors@4.1.4': - dependencies: - kleur: 4.1.5 - - '@poppinss/dumper@0.6.3': - dependencies: - '@poppinss/colors': 4.1.4 - '@sindresorhus/is': 7.0.1 - supports-color: 10.0.0 - - '@poppinss/exception@1.2.1': {} - - '@prisma/instrumentation@6.8.2(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - transitivePeerDependencies: - - supports-color - - '@rollup/rollup-android-arm-eabi@4.40.1': - optional: true - - '@rollup/rollup-android-arm64@4.40.1': - optional: true - - '@rollup/rollup-darwin-arm64@4.40.1': - optional: true - - '@rollup/rollup-darwin-x64@4.40.1': - optional: true - - '@rollup/rollup-freebsd-arm64@4.40.1': - optional: true - - '@rollup/rollup-freebsd-x64@4.40.1': - optional: true - - '@rollup/rollup-linux-arm-gnueabihf@4.40.1': - optional: true - - '@rollup/rollup-linux-arm-musleabihf@4.40.1': - optional: true - - '@rollup/rollup-linux-arm64-gnu@4.40.1': - optional: true - - '@rollup/rollup-linux-arm64-musl@4.40.1': - optional: true - - '@rollup/rollup-linux-loongarch64-gnu@4.40.1': - optional: true - - '@rollup/rollup-linux-powerpc64le-gnu@4.40.1': - optional: true - - '@rollup/rollup-linux-riscv64-gnu@4.40.1': - optional: true - - '@rollup/rollup-linux-riscv64-musl@4.40.1': - optional: true - - '@rollup/rollup-linux-s390x-gnu@4.40.1': - optional: true - - '@rollup/rollup-linux-x64-gnu@4.40.1': - optional: true - - '@rollup/rollup-linux-x64-musl@4.40.1': - optional: true - - '@rollup/rollup-win32-arm64-msvc@4.40.1': - optional: true - - '@rollup/rollup-win32-ia32-msvc@4.40.1': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.40.1': - optional: true - - '@scalar/core@0.3.3': - dependencies: - '@scalar/types': 0.2.3 - - '@scalar/hono-api-reference@0.9.4(hono@4.7.11)': - dependencies: - '@scalar/core': 0.3.3 - hono: 4.7.11 - - '@scalar/openapi-types@0.3.3': - dependencies: - zod: 3.24.1 - - '@scalar/types@0.2.3': - dependencies: - '@scalar/openapi-types': 0.3.3 - nanoid: 5.1.5 - zod: 3.24.1 - - '@selderee/plugin-htmlparser2@0.11.0': - dependencies: - domhandler: 5.0.3 - selderee: 0.11.0 - - '@sentry/bun@9.29.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.34.0)': - dependencies: - '@sentry/core': 9.29.0 - '@sentry/node': 9.29.0 - '@sentry/opentelemetry': 9.29.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.34.0) - transitivePeerDependencies: - - '@opentelemetry/api' - - '@opentelemetry/context-async-hooks' - - '@opentelemetry/core' - - '@opentelemetry/instrumentation' - - '@opentelemetry/sdk-trace-base' - - '@opentelemetry/semantic-conventions' - - supports-color - - '@sentry/core@9.29.0': {} - - '@sentry/node@9.29.0': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/context-async-hooks': 1.30.1(@opentelemetry/api@1.9.0) - '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-amqplib': 0.46.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-connect': 0.43.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-dataloader': 0.16.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-express': 0.47.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-fs': 0.19.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-generic-pool': 0.43.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-graphql': 0.47.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-hapi': 0.45.2(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-http': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-ioredis': 0.47.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-kafkajs': 0.7.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-knex': 0.44.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-koa': 0.47.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-lru-memoizer': 0.44.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mongodb': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mongoose': 0.46.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mysql': 0.45.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mysql2': 0.45.2(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-pg': 0.51.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-redis-4': 0.46.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-tedious': 0.18.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-undici': 0.10.1(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.34.0 - '@prisma/instrumentation': 6.8.2(@opentelemetry/api@1.9.0) - '@sentry/core': 9.29.0 - '@sentry/opentelemetry': 9.29.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.34.0) - import-in-the-middle: 1.13.1 - minimatch: 9.0.5 - transitivePeerDependencies: - - supports-color - - '@sentry/opentelemetry@9.29.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.34.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/context-async-hooks': 1.30.1(@opentelemetry/api@1.9.0) - '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.34.0 - '@sentry/core': 9.29.0 - - '@shikijs/core@2.5.0': - dependencies: - '@shikijs/engine-javascript': 2.5.0 - '@shikijs/engine-oniguruma': 2.5.0 - '@shikijs/types': 2.5.0 - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 - hast-util-to-html: 9.0.5 - - '@shikijs/engine-javascript@2.5.0': - dependencies: - '@shikijs/types': 2.5.0 - '@shikijs/vscode-textmate': 10.0.2 - oniguruma-to-es: 3.1.1 - - '@shikijs/engine-oniguruma@2.5.0': - dependencies: - '@shikijs/types': 2.5.0 - '@shikijs/vscode-textmate': 10.0.2 - - '@shikijs/langs@2.5.0': - dependencies: - '@shikijs/types': 2.5.0 - - '@shikijs/themes@2.5.0': - dependencies: - '@shikijs/types': 2.5.0 - - '@shikijs/transformers@2.5.0': - dependencies: - '@shikijs/core': 2.5.0 - '@shikijs/types': 2.5.0 - - '@shikijs/types@2.5.0': - dependencies: - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 - - '@shikijs/vscode-textmate@10.0.2': {} - - '@sindresorhus/is@7.0.1': {} - - '@speed-highlight/core@1.2.7': {} - - '@ts-morph/common@0.12.3': - dependencies: - fast-glob: 3.3.3 - minimatch: 3.1.2 - mkdirp: 1.0.4 - path-browserify: 1.0.1 - - '@types/bun@1.2.16': - dependencies: - bun-types: 1.2.16 - - '@types/connect@3.4.38': - dependencies: - '@types/node': 22.15.3 - - '@types/estree@1.0.7': {} - - '@types/hast@3.0.4': - dependencies: - '@types/unist': 3.0.3 - - '@types/html-to-text@9.0.4': {} - - '@types/json-schema@7.0.15': {} - - '@types/linkify-it@5.0.0': {} - - '@types/markdown-it-container@2.0.10': - dependencies: - '@types/markdown-it': 14.1.2 - - '@types/markdown-it@14.1.2': - dependencies: - '@types/linkify-it': 5.0.0 - '@types/mdurl': 2.0.0 - - '@types/mdast@4.0.4': - dependencies: - '@types/unist': 3.0.3 - - '@types/mdurl@2.0.0': {} - - '@types/mime-types@3.0.1': {} - - '@types/mysql@2.15.26': - dependencies: - '@types/node': 22.15.3 - - '@types/node@22.15.3': - dependencies: - undici-types: 6.21.0 - - '@types/parse-json@4.0.2': {} - - '@types/pg-pool@2.0.6': - dependencies: - '@types/pg': 8.6.1 - - '@types/pg@8.6.1': - dependencies: - '@types/node': 22.15.3 - pg-protocol: 1.9.5 - pg-types: 2.2.0 - - '@types/qs@6.14.0': {} - - '@types/shimmer@1.2.0': {} - - '@types/tedious@4.0.14': - dependencies: - '@types/node': 22.15.3 - - '@types/text-table@0.2.5': {} - - '@types/unist@3.0.3': {} - - '@types/web-bluetooth@0.0.21': {} - - '@types/web-push@3.6.4': - dependencies: - '@types/node': 22.15.3 - - '@ungap/structured-clone@1.3.0': {} - - '@vitejs/plugin-vue@5.2.3(vite@5.4.19(@types/node@22.15.3))(vue@3.5.16(typescript@5.8.3))': - dependencies: - vite: 5.4.19(@types/node@22.15.3) - vue: 3.5.16(typescript@5.8.3) - - '@vue/compiler-core@3.5.16': - dependencies: - '@babel/parser': 7.27.2 - '@vue/shared': 3.5.16 - entities: 4.5.0 - estree-walker: 2.0.2 - source-map-js: 1.2.1 - - '@vue/compiler-dom@3.5.16': - dependencies: - '@vue/compiler-core': 3.5.16 - '@vue/shared': 3.5.16 - - '@vue/compiler-sfc@3.5.16': - dependencies: - '@babel/parser': 7.27.2 - '@vue/compiler-core': 3.5.16 - '@vue/compiler-dom': 3.5.16 - '@vue/compiler-ssr': 3.5.16 - '@vue/shared': 3.5.16 - estree-walker: 2.0.2 - magic-string: 0.30.17 - postcss: 8.5.3 - source-map-js: 1.2.1 - - '@vue/compiler-ssr@3.5.16': - dependencies: - '@vue/compiler-dom': 3.5.16 - '@vue/shared': 3.5.16 - - '@vue/devtools-api@7.7.6': - dependencies: - '@vue/devtools-kit': 7.7.6 - - '@vue/devtools-kit@7.7.6': - dependencies: - '@vue/devtools-shared': 7.7.6 - birpc: 2.3.0 - hookable: 5.5.3 - mitt: 3.0.1 - perfect-debounce: 1.0.0 - speakingurl: 14.0.1 - superjson: 2.2.2 - - '@vue/devtools-shared@7.7.6': - dependencies: - rfdc: 1.4.1 - - '@vue/reactivity@3.5.16': - dependencies: - '@vue/shared': 3.5.16 - - '@vue/runtime-core@3.5.16': - dependencies: - '@vue/reactivity': 3.5.16 - '@vue/shared': 3.5.16 - - '@vue/runtime-dom@3.5.16': - dependencies: - '@vue/reactivity': 3.5.16 - '@vue/runtime-core': 3.5.16 - '@vue/shared': 3.5.16 - csstype: 3.1.3 - - '@vue/server-renderer@3.5.16(vue@3.5.16(typescript@5.8.3))': - dependencies: - '@vue/compiler-ssr': 3.5.16 - '@vue/shared': 3.5.16 - vue: 3.5.16(typescript@5.8.3) - - '@vue/shared@3.5.13': {} - - '@vue/shared@3.5.16': {} - - '@vueuse/core@12.8.2(typescript@5.8.3)': - dependencies: - '@types/web-bluetooth': 0.0.21 - '@vueuse/metadata': 12.8.2 - '@vueuse/shared': 12.8.2(typescript@5.8.3) - vue: 3.5.16(typescript@5.8.3) - transitivePeerDependencies: - - typescript - - '@vueuse/integrations@12.8.2(focus-trap@7.6.4)(typescript@5.8.3)': - dependencies: - '@vueuse/core': 12.8.2(typescript@5.8.3) - '@vueuse/shared': 12.8.2(typescript@5.8.3) - vue: 3.5.16(typescript@5.8.3) - optionalDependencies: - focus-trap: 7.6.4 - transitivePeerDependencies: - - typescript - - '@vueuse/metadata@12.8.2': {} - - '@vueuse/shared@12.8.2(typescript@5.8.3)': - dependencies: - vue: 3.5.16(typescript@5.8.3) - transitivePeerDependencies: - - typescript - - '@xmldom/xmldom@0.9.8': {} - - acorn-import-attributes@1.9.5(acorn@8.14.1): - dependencies: - acorn: 8.14.1 - - acorn@8.14.1: {} - - agent-base@7.1.3: {} - - algoliasearch@5.24.0: - dependencies: - '@algolia/client-abtesting': 5.24.0 - '@algolia/client-analytics': 5.24.0 - '@algolia/client-common': 5.24.0 - '@algolia/client-insights': 5.24.0 - '@algolia/client-personalization': 5.24.0 - '@algolia/client-query-suggestions': 5.24.0 - '@algolia/client-search': 5.24.0 - '@algolia/ingestion': 1.24.0 - '@algolia/monitoring': 1.24.0 - '@algolia/recommend': 5.24.0 - '@algolia/requester-browser-xhr': 5.24.0 - '@algolia/requester-fetch': 5.24.0 - '@algolia/requester-node-http': 5.24.0 - - altcha-lib@1.3.0: {} - - ansi-colors@4.1.3: {} - - ansi-escapes@4.3.2: - dependencies: - type-fest: 0.21.3 - - ansi-regex@5.0.1: {} - - ansi-regex@6.1.0: {} - - ansi-styles@4.3.0: - dependencies: - color-convert: 2.0.1 - - ansi-styles@6.2.1: {} - - argparse@1.0.10: - dependencies: - sprintf-js: 1.0.3 - - argparse@2.0.1: {} - - asn1.js@5.4.1: - dependencies: - bn.js: 4.12.2 - inherits: 2.0.4 - minimalistic-assert: 1.0.1 - safer-buffer: 2.1.2 - - async@3.2.6: {} - - balanced-match@1.0.2: {} - - birpc@2.3.0: {} - - blurhash@2.0.5: {} - - bn.js@4.12.2: {} - - boolbase@1.0.0: {} - - brace-expansion@1.1.11: - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - - brace-expansion@2.0.1: - dependencies: - balanced-match: 1.0.2 - - braces@3.0.3: - dependencies: - fill-range: 7.1.1 - - buffer-equal-constant-time@1.0.1: {} - - buffer-from@1.1.2: {} - - bullmq@5.53.3: - dependencies: - cron-parser: 4.9.0 - ioredis: 5.6.1 - msgpackr: 1.11.2 - node-abort-controller: 3.1.1 - semver: 7.7.2 - tslib: 2.8.1 - uuid: 9.0.1 - transitivePeerDependencies: - - supports-color - - bun-bagel@1.2.0(typescript@5.8.3): - dependencies: - typescript: 5.8.3 - - bun-types@1.2.16: - dependencies: - '@types/node': 22.15.3 - - call-bind-apply-helpers@1.0.2: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - - call-bound@1.0.4: - dependencies: - call-bind-apply-helpers: 1.0.2 - get-intrinsic: 1.3.0 - - callsites@3.1.0: {} - - ccount@2.0.1: {} - - chalk@4.1.2: - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - - chalk@5.4.1: {} - - character-entities-html4@2.1.0: {} - - character-entities-legacy@3.0.0: {} - - cheerio-select@1.6.0: - dependencies: - css-select: 4.3.0 - css-what: 6.1.0 - domelementtype: 2.3.0 - domhandler: 4.3.1 - domutils: 2.8.0 - - cheerio@1.0.0-rc.10: - dependencies: - cheerio-select: 1.6.0 - dom-serializer: 1.4.1 - domhandler: 4.3.1 - htmlparser2: 6.1.0 - parse5: 6.0.1 - parse5-htmlparser2-tree-adapter: 6.0.1 - tslib: 2.8.1 - - cjs-module-lexer@1.4.3: {} - - clerc@0.44.0: - dependencies: - '@clerc/core': 0.44.0 - '@clerc/plugin-completions': 0.44.0(@clerc/core@0.44.0) - '@clerc/plugin-friendly-error': 0.44.0(@clerc/core@0.44.0) - '@clerc/plugin-help': 0.44.0(@clerc/core@0.44.0) - '@clerc/plugin-not-found': 0.44.0(@clerc/core@0.44.0) - '@clerc/plugin-strict-flags': 0.44.0(@clerc/core@0.44.0) - '@clerc/plugin-version': 0.44.0(@clerc/core@0.44.0) - - cli-cursor@5.0.0: - dependencies: - restore-cursor: 5.1.0 - - cli-spinners@2.9.2: {} - - cli-width@4.1.0: {} - - clone@2.1.2: {} - - cluster-key-slot@1.1.2: {} - - code-block-writer@11.0.3: {} - - color-convert@2.0.1: - dependencies: - color-name: 1.1.4 - - color-name@1.1.4: {} - - color-string@1.9.1: - dependencies: - color-name: 1.1.4 - simple-swizzle: 0.2.2 - - color@4.2.3: - dependencies: - color-convert: 2.0.1 - color-string: 1.9.1 - - comma-separated-tokens@2.0.3: {} - - commander@13.1.0: {} - - commander@2.20.3: {} - - commander@6.2.1: {} - - concat-map@0.0.1: {} - - confbox@0.1.8: {} - - confbox@0.2.2: {} - - cookie@1.0.2: {} - - copy-anything@3.0.5: - dependencies: - is-what: 4.1.16 - - cosmiconfig@7.1.0: - dependencies: - '@types/parse-json': 4.0.2 - import-fresh: 3.3.1 - parse-json: 5.2.0 - path-type: 4.0.0 - yaml: 1.10.2 - - cron-parser@4.9.0: - dependencies: - luxon: 3.6.1 - - cross-spawn@7.0.6: - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - - css-select@4.3.0: - dependencies: - boolbase: 1.0.0 - css-what: 6.1.0 - domhandler: 4.3.1 - domutils: 2.8.0 - nth-check: 2.1.1 - - css-what@6.1.0: {} - - cssfilter@0.0.10: {} - - csstype@3.1.3: {} - - debug@4.4.0: - dependencies: - ms: 2.1.3 - - deepmerge@4.3.1: {} - - defu@6.1.4: {} - - denque@2.1.0: {} - - dequal@2.0.3: {} - - detect-libc@2.0.4: {} - - devlop@1.1.0: - dependencies: - dequal: 2.0.3 - - didyoumean2@6.0.1: - dependencies: - '@babel/runtime': 7.27.1 - fastest-levenshtein: 1.0.16 - lodash.deburr: 4.1.0 - - dom-serializer@1.4.1: - dependencies: - domelementtype: 2.3.0 - domhandler: 4.3.1 - entities: 2.2.0 - - dom-serializer@2.0.0: - dependencies: - domelementtype: 2.3.0 - domhandler: 5.0.3 - entities: 4.5.0 - - domelementtype@2.3.0: {} - - domhandler@3.3.0: - dependencies: - domelementtype: 2.3.0 - - domhandler@4.3.1: - dependencies: - domelementtype: 2.3.0 - - domhandler@5.0.3: - dependencies: - domelementtype: 2.3.0 - - domutils@2.8.0: - dependencies: - dom-serializer: 1.4.1 - domelementtype: 2.3.0 - domhandler: 4.3.1 - - domutils@3.2.2: - dependencies: - dom-serializer: 2.0.0 - domelementtype: 2.3.0 - domhandler: 5.0.3 - - drizzle-kit@0.31.1: - dependencies: - '@drizzle-team/brocli': 0.10.2 - '@esbuild-kit/esm-loader': 2.6.5 - esbuild: 0.25.3 - esbuild-register: 3.6.0(esbuild@0.25.3) - transitivePeerDependencies: - - supports-color - - drizzle-orm@0.44.2(@opentelemetry/api@1.9.0)(@types/pg@8.6.1)(bun-types@1.2.16): - optionalDependencies: - '@opentelemetry/api': 1.9.0 - '@types/pg': 8.6.1 - bun-types: 1.2.16 - - dunder-proto@1.0.1: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-errors: 1.3.0 - gopd: 1.2.0 - - eastasianwidth@0.2.0: {} - - ecdsa-sig-formatter@1.0.11: - dependencies: - safe-buffer: 5.2.1 - - ejs@3.1.10: - dependencies: - jake: 10.9.2 - - emoji-regex-xs@1.0.0: {} - - emoji-regex@10.4.0: {} - - emoji-regex@8.0.0: {} - - emoji-regex@9.2.2: {} - - entities@2.2.0: {} - - entities@4.5.0: {} - - error-ex@1.3.2: - dependencies: - is-arrayish: 0.2.1 - - error-stack-parser-es@1.0.5: {} - - es-define-property@1.0.1: {} - - es-errors@1.3.0: {} - - es-object-atoms@1.1.1: - dependencies: - es-errors: 1.3.0 - - esbuild-register@3.6.0(esbuild@0.25.3): - dependencies: - debug: 4.4.0 - esbuild: 0.25.3 - transitivePeerDependencies: - - supports-color - - esbuild@0.18.20: - optionalDependencies: - '@esbuild/android-arm': 0.18.20 - '@esbuild/android-arm64': 0.18.20 - '@esbuild/android-x64': 0.18.20 - '@esbuild/darwin-arm64': 0.18.20 - '@esbuild/darwin-x64': 0.18.20 - '@esbuild/freebsd-arm64': 0.18.20 - '@esbuild/freebsd-x64': 0.18.20 - '@esbuild/linux-arm': 0.18.20 - '@esbuild/linux-arm64': 0.18.20 - '@esbuild/linux-ia32': 0.18.20 - '@esbuild/linux-loong64': 0.18.20 - '@esbuild/linux-mips64el': 0.18.20 - '@esbuild/linux-ppc64': 0.18.20 - '@esbuild/linux-riscv64': 0.18.20 - '@esbuild/linux-s390x': 0.18.20 - '@esbuild/linux-x64': 0.18.20 - '@esbuild/netbsd-x64': 0.18.20 - '@esbuild/openbsd-x64': 0.18.20 - '@esbuild/sunos-x64': 0.18.20 - '@esbuild/win32-arm64': 0.18.20 - '@esbuild/win32-ia32': 0.18.20 - '@esbuild/win32-x64': 0.18.20 - - esbuild@0.21.5: - optionalDependencies: - '@esbuild/aix-ppc64': 0.21.5 - '@esbuild/android-arm': 0.21.5 - '@esbuild/android-arm64': 0.21.5 - '@esbuild/android-x64': 0.21.5 - '@esbuild/darwin-arm64': 0.21.5 - '@esbuild/darwin-x64': 0.21.5 - '@esbuild/freebsd-arm64': 0.21.5 - '@esbuild/freebsd-x64': 0.21.5 - '@esbuild/linux-arm': 0.21.5 - '@esbuild/linux-arm64': 0.21.5 - '@esbuild/linux-ia32': 0.21.5 - '@esbuild/linux-loong64': 0.21.5 - '@esbuild/linux-mips64el': 0.21.5 - '@esbuild/linux-ppc64': 0.21.5 - '@esbuild/linux-riscv64': 0.21.5 - '@esbuild/linux-s390x': 0.21.5 - '@esbuild/linux-x64': 0.21.5 - '@esbuild/netbsd-x64': 0.21.5 - '@esbuild/openbsd-x64': 0.21.5 - '@esbuild/sunos-x64': 0.21.5 - '@esbuild/win32-arm64': 0.21.5 - '@esbuild/win32-ia32': 0.21.5 - '@esbuild/win32-x64': 0.21.5 - - esbuild@0.25.3: - optionalDependencies: - '@esbuild/aix-ppc64': 0.25.3 - '@esbuild/android-arm': 0.25.3 - '@esbuild/android-arm64': 0.25.3 - '@esbuild/android-x64': 0.25.3 - '@esbuild/darwin-arm64': 0.25.3 - '@esbuild/darwin-x64': 0.25.3 - '@esbuild/freebsd-arm64': 0.25.3 - '@esbuild/freebsd-x64': 0.25.3 - '@esbuild/linux-arm': 0.25.3 - '@esbuild/linux-arm64': 0.25.3 - '@esbuild/linux-ia32': 0.25.3 - '@esbuild/linux-loong64': 0.25.3 - '@esbuild/linux-mips64el': 0.25.3 - '@esbuild/linux-ppc64': 0.25.3 - '@esbuild/linux-riscv64': 0.25.3 - '@esbuild/linux-s390x': 0.25.3 - '@esbuild/linux-x64': 0.25.3 - '@esbuild/netbsd-arm64': 0.25.3 - '@esbuild/netbsd-x64': 0.25.3 - '@esbuild/openbsd-arm64': 0.25.3 - '@esbuild/openbsd-x64': 0.25.3 - '@esbuild/sunos-x64': 0.25.3 - '@esbuild/win32-arm64': 0.25.3 - '@esbuild/win32-ia32': 0.25.3 - '@esbuild/win32-x64': 0.25.3 - - escape-goat@3.0.0: {} - - escape-string-regexp@5.0.0: {} - - esm@3.2.25: {} - - esprima@4.0.1: {} - - estree-walker@2.0.2: {} - - estree-walker@3.0.3: - dependencies: - '@types/estree': 1.0.7 - - extend-shallow@2.0.1: - dependencies: - is-extendable: 0.1.1 - - fast-glob@3.3.3: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - - fastest-levenshtein@1.0.16: {} - - fastq@1.19.1: - dependencies: - reusify: 1.1.0 - - feed@5.1.0: - dependencies: - xml-js: 1.6.11 - - figures@5.0.0: - dependencies: - escape-string-regexp: 5.0.0 - is-unicode-supported: 1.3.0 - - filelist@1.0.4: - dependencies: - minimatch: 5.1.6 - - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 - - focus-trap@7.6.4: - dependencies: - tabbable: 6.2.0 - - foreground-child@3.3.1: - dependencies: - cross-spawn: 7.0.6 - signal-exit: 4.1.0 - - forwarded-parse@2.1.2: {} - - fsevents@2.3.3: - optional: true - - function-bind@1.1.2: {} - - get-east-asian-width@1.3.0: {} - - get-intrinsic@1.3.0: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - function-bind: 1.1.2 - get-proto: 1.0.1 - gopd: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.2 - math-intrinsics: 1.1.0 - - get-proto@1.0.1: - dependencies: - dunder-proto: 1.0.1 - es-object-atoms: 1.1.1 - - get-tsconfig@4.10.0: - dependencies: - resolve-pkg-maps: 1.0.0 - - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 - - glob@10.4.5: - dependencies: - foreground-child: 3.3.1 - jackspeak: 3.4.3 - minimatch: 9.0.5 - minipass: 7.1.2 - package-json-from-dist: 1.0.1 - path-scurry: 1.11.1 - - gopd@1.2.0: {} - - gray-matter@4.0.3: - dependencies: - js-yaml: 3.14.1 - kind-of: 6.0.3 - section-matter: 1.0.0 - strip-bom-string: 1.0.0 - - has-flag@4.0.0: {} - - has-symbols@1.1.0: {} - - hasown@2.0.2: - dependencies: - function-bind: 1.1.2 - - 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.0 - property-information: 7.0.0 - space-separated-tokens: 2.0.2 - stringify-entities: 4.0.4 - zwitch: 2.0.4 - - hast-util-whitespace@3.0.0: - dependencies: - '@types/hast': 3.0.4 - - hono-openapi@0.4.8(@hono/zod-validator@0.7.0(hono@4.7.11)(zod@3.25.64))(hono@4.7.11)(openapi-types@12.1.3)(zod-openapi@4.2.4(zod@3.25.64))(zod@3.25.64): - dependencies: - json-schema-walker: 2.0.0 - openapi-types: 12.1.3 - optionalDependencies: - '@hono/zod-validator': 0.7.0(hono@4.7.11)(zod@3.25.64) - hono: 4.7.11 - zod: 3.25.64 - zod-openapi: 4.2.4(zod@3.25.64) - - hono-rate-limiter@0.4.2(hono@4.7.11): - dependencies: - hono: 4.7.11 - - hono@4.7.11: {} - - hono@4.7.8: {} - - hookable@5.5.3: {} - - html-to-text@9.0.5: - dependencies: - '@selderee/plugin-htmlparser2': 0.11.0 - deepmerge: 4.3.1 - dom-serializer: 2.0.0 - htmlparser2: 8.0.2 - selderee: 0.11.0 - - html-void-elements@3.0.0: {} - - htmlparser2@5.0.1: - dependencies: - domelementtype: 2.3.0 - domhandler: 3.3.0 - domutils: 2.8.0 - entities: 2.2.0 - - htmlparser2@6.1.0: - dependencies: - domelementtype: 2.3.0 - domhandler: 4.3.1 - domutils: 2.8.0 - entities: 2.2.0 - - htmlparser2@8.0.2: - dependencies: - domelementtype: 2.3.0 - domhandler: 5.0.3 - domutils: 3.2.2 - entities: 4.5.0 - - http_ece@1.2.0: {} - - https-proxy-agent@7.0.6: - dependencies: - agent-base: 7.1.3 - debug: 4.4.0 - transitivePeerDependencies: - - supports-color - - import-fresh@3.3.1: - dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - - import-in-the-middle@1.13.1: - dependencies: - acorn: 8.14.1 - acorn-import-attributes: 1.9.5(acorn@8.14.1) - cjs-module-lexer: 1.4.3 - module-details-from-path: 1.0.4 - - inherits@2.0.4: {} - - ioredis@5.6.1: - dependencies: - '@ioredis/commands': 1.2.0 - cluster-key-slot: 1.1.2 - debug: 4.4.0 - denque: 2.1.0 - lodash.defaults: 4.2.0 - lodash.isarguments: 3.1.0 - redis-errors: 1.2.0 - redis-parser: 3.0.0 - standard-as-callback: 2.1.0 - transitivePeerDependencies: - - supports-color - - ip-matching@2.1.2: {} - - is-arrayish@0.2.1: {} - - is-arrayish@0.3.2: {} - - is-core-module@2.16.1: - dependencies: - hasown: 2.0.2 - - is-extendable@0.1.1: {} - - is-extglob@2.1.1: {} - - is-fullwidth-code-point@3.0.0: {} - - is-glob@4.0.3: - dependencies: - is-extglob: 2.1.1 - - is-interactive@2.0.0: {} - - is-number@7.0.0: {} - - is-platform@1.0.0: {} - - is-unicode-supported@1.3.0: {} - - is-unicode-supported@2.1.0: {} - - is-what@4.1.16: {} - - isexe@2.0.0: {} - - iso-639-1@3.1.5: {} - - jackspeak@3.4.3: - dependencies: - '@isaacs/cliui': 8.0.2 - optionalDependencies: - '@pkgjs/parseargs': 0.11.0 - - jake@10.9.2: - dependencies: - async: 3.2.6 - chalk: 4.1.2 - filelist: 1.0.4 - minimatch: 3.1.2 - - jose@6.0.11: {} - - js-tokens@4.0.0: {} - - js-yaml@3.14.1: - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 - - js-yaml@4.1.0: - dependencies: - argparse: 2.0.1 - - json-parse-even-better-errors@2.3.1: {} - - json-schema-walker@2.0.0: - dependencies: - '@apidevtools/json-schema-ref-parser': 11.9.3 - clone: 2.1.2 - - json5@2.2.3: {} - - juice@8.1.0: - dependencies: - cheerio: 1.0.0-rc.10 - commander: 6.2.1 - mensch: 0.3.4 - slick: 1.12.2 - web-resource-inliner: 6.0.1 - transitivePeerDependencies: - - encoding - - jwa@2.0.0: - dependencies: - buffer-equal-constant-time: 1.0.1 - ecdsa-sig-formatter: 1.0.11 - safe-buffer: 5.2.1 - - jws@4.0.0: - dependencies: - jwa: 2.0.0 - safe-buffer: 5.2.1 - - kind-of@6.0.3: {} - - kleur@4.1.5: {} - - kons@0.7.1: - dependencies: - figures: 5.0.0 - picocolors: 1.1.1 - - leac@0.6.0: {} - - lines-and-columns@1.2.4: {} - - linkify-html@4.3.1(linkifyjs@4.3.1): - dependencies: - linkifyjs: 4.3.1 - - linkify-it@5.0.0: - dependencies: - uc.micro: 2.1.0 - - linkify-string@4.3.1(linkifyjs@4.3.1): - dependencies: - linkifyjs: 4.3.1 - - linkifyjs@4.3.1: {} - - lite-emit@2.3.0: {} - - lodash.deburr@4.1.0: {} - - lodash.defaults@4.2.0: {} - - lodash.isarguments@3.1.0: {} - - lodash@4.17.21: {} - - log-symbols@6.0.0: - dependencies: - chalk: 5.4.1 - is-unicode-supported: 1.3.0 - - lru-cache@10.4.3: {} - - luxon@3.6.1: {} - - magic-regexp@0.10.0: - dependencies: - estree-walker: 3.0.3 - magic-string: 0.30.17 - mlly: 1.7.4 - regexp-tree: 0.1.27 - type-level-regexp: 0.1.17 - ufo: 1.6.1 - unplugin: 2.3.2 - - magic-string@0.30.17: - dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 - - mark.js@8.11.1: {} - - markdown-it-anchor@9.2.0(@types/markdown-it@14.1.2)(markdown-it@14.1.0): - dependencies: - '@types/markdown-it': 14.1.2 - markdown-it: 14.1.0 - - markdown-it-container@4.0.0: {} - - markdown-it-image-figures@2.1.1(markdown-it@14.1.0): - dependencies: - markdown-it: 14.1.0 - - markdown-it-mathjax3@4.3.2: - dependencies: - juice: 8.1.0 - mathjax-full: 3.2.2 - transitivePeerDependencies: - - encoding - - markdown-it-toc-done-right@4.2.0: {} - - markdown-it@14.1.0: - dependencies: - argparse: 2.0.1 - entities: 4.5.0 - linkify-it: 5.0.0 - mdurl: 2.0.0 - punycode.js: 2.3.1 - uc.micro: 2.1.0 - - math-intrinsics@1.1.0: {} - - mathjax-full@3.2.2: - dependencies: - esm: 3.2.25 - mhchemparser: 4.2.1 - mj-context-menu: 0.6.1 - speech-rule-engine: 4.1.2 - - mdast-util-to-hast@13.2.0: - dependencies: - '@types/hast': 3.0.4 - '@types/mdast': 4.0.4 - '@ungap/structured-clone': 1.3.0 - devlop: 1.1.0 - micromark-util-sanitize-uri: 2.0.1 - trim-lines: 3.0.1 - unist-util-position: 5.0.0 - unist-util-visit: 5.0.0 - vfile: 6.0.3 - - mdurl@2.0.0: {} - - mensch@0.3.4: {} - - merge2@1.4.1: {} - - mhchemparser@4.2.1: {} - - micromark-util-character@2.1.1: - dependencies: - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-encode@2.0.1: {} - - micromark-util-sanitize-uri@2.0.1: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-encode: 2.0.1 - micromark-util-symbol: 2.0.1 - - micromark-util-symbol@2.0.1: {} - - micromark-util-types@2.0.2: {} - - micromatch@4.0.8: - dependencies: - braces: 3.0.3 - picomatch: 2.3.1 - - mime-db@1.54.0: {} - - mime-types@3.0.1: - dependencies: - mime-db: 1.54.0 - - mime@2.6.0: {} - - mimic-function@5.0.1: {} - - minimalistic-assert@1.0.1: {} - - minimatch@3.1.2: - dependencies: - brace-expansion: 1.1.11 - - minimatch@5.1.6: - dependencies: - brace-expansion: 2.0.1 - - minimatch@9.0.5: - dependencies: - brace-expansion: 2.0.1 - - minimist@1.2.8: {} - - minipass@7.1.2: {} - - minisearch@7.1.2: {} - - mitata@1.0.34: {} - - mitt@3.0.1: {} - - mj-context-menu@0.6.1: {} - - mkdirp@1.0.4: {} - - mlly@1.7.4: - dependencies: - acorn: 8.14.1 - pathe: 2.0.3 - pkg-types: 1.3.1 - ufo: 1.6.1 - - module-details-from-path@1.0.4: {} - - ms@2.1.3: {} - - msgpackr-extract@3.0.3: - dependencies: - node-gyp-build-optional-packages: 5.2.2 - optionalDependencies: - '@msgpackr-extract/msgpackr-extract-darwin-arm64': 3.0.3 - '@msgpackr-extract/msgpackr-extract-darwin-x64': 3.0.3 - '@msgpackr-extract/msgpackr-extract-linux-arm': 3.0.3 - '@msgpackr-extract/msgpackr-extract-linux-arm64': 3.0.3 - '@msgpackr-extract/msgpackr-extract-linux-x64': 3.0.3 - '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.3 - optional: true - - msgpackr@1.11.2: - optionalDependencies: - msgpackr-extract: 3.0.3 - - mute-stream@2.0.0: {} - - nanoid@3.3.11: {} - - nanoid@5.1.5: {} - - node-abort-controller@3.1.1: {} - - node-fetch@2.7.0: - dependencies: - whatwg-url: 5.0.0 - - node-gyp-build-optional-packages@5.2.2: - dependencies: - detect-libc: 2.0.4 - optional: true - - nth-check@2.1.1: - dependencies: - boolbase: 1.0.0 - - oauth4webapi@3.5.2: {} - - object-inspect@1.13.4: {} - - onetime@7.0.0: - dependencies: - mimic-function: 5.0.1 - - oniguruma-to-es@3.1.1: - dependencies: - emoji-regex-xs: 1.0.0 - regex: 6.0.1 - regex-recursion: 6.0.2 - - openapi-types@12.1.3: {} - - ora@8.2.0: - dependencies: - chalk: 5.4.1 - cli-cursor: 5.0.0 - cli-spinners: 2.9.2 - is-interactive: 2.0.0 - is-unicode-supported: 2.1.0 - log-symbols: 6.0.0 - stdin-discarder: 0.2.2 - string-width: 7.2.0 - strip-ansi: 7.1.0 - - package-json-from-dist@1.0.1: {} - - parent-module@1.0.1: - dependencies: - callsites: 3.1.0 - - parse-json@5.2.0: - dependencies: - '@babel/code-frame': 7.27.1 - error-ex: 1.3.2 - json-parse-even-better-errors: 2.3.1 - lines-and-columns: 1.2.4 - - parse5-htmlparser2-tree-adapter@6.0.1: - dependencies: - parse5: 6.0.1 - - parse5@6.0.1: {} - - parseley@0.12.1: - dependencies: - leac: 0.6.0 - peberminta: 0.9.0 - - path-browserify@1.0.1: {} - - path-key@3.1.1: {} - - path-parse@1.0.7: {} - - path-scurry@1.11.1: - dependencies: - lru-cache: 10.4.3 - minipass: 7.1.2 - - path-type@4.0.0: {} - - pathe@2.0.3: {} - - peberminta@0.9.0: {} - - perfect-debounce@1.0.0: {} - - pg-int8@1.0.1: {} - - pg-protocol@1.9.5: {} - - pg-types@2.2.0: - dependencies: - pg-int8: 1.0.1 - postgres-array: 2.0.0 - postgres-bytea: 1.0.0 - postgres-date: 1.0.7 - postgres-interval: 1.2.0 - - picocolors@1.1.1: {} - - picomatch@2.3.1: {} - - picomatch@4.0.2: {} - - pkg-types@1.3.1: - dependencies: - confbox: 0.1.8 - mlly: 1.7.4 - pathe: 2.0.3 - - postcss@8.5.3: - dependencies: - nanoid: 3.3.11 - picocolors: 1.1.1 - source-map-js: 1.2.1 - - postgres-array@2.0.0: {} - - postgres-bytea@1.0.0: {} - - postgres-date@1.0.7: {} - - postgres-interval@1.2.0: - dependencies: - xtend: 4.0.2 - - preact@10.26.5: {} - - property-information@7.0.0: {} - - punycode.js@2.3.1: {} - - qs@6.14.0: - dependencies: - side-channel: 1.1.0 - - qsu@1.10.0: {} - - queue-microtask@1.2.3: {} - - redis-errors@1.2.0: {} - - redis-info@3.1.0: - dependencies: - lodash: 4.17.21 - - redis-parser@3.0.0: - dependencies: - redis-errors: 1.2.0 - - regex-recursion@6.0.2: - dependencies: - regex-utilities: 2.3.0 - - regex-utilities@2.3.0: {} - - regex@6.0.1: - dependencies: - regex-utilities: 2.3.0 - - regexp-tree@0.1.27: {} - - require-in-the-middle@7.5.2: - dependencies: - debug: 4.4.0 - module-details-from-path: 1.0.4 - resolve: 1.22.10 - transitivePeerDependencies: - - supports-color - - resolve-from@4.0.0: {} - - resolve-pkg-maps@1.0.0: {} - - resolve@1.22.10: - dependencies: - is-core-module: 2.16.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - - restore-cursor@5.1.0: - dependencies: - onetime: 7.0.0 - signal-exit: 4.1.0 - - reusify@1.1.0: {} - - rfdc@1.4.1: {} - - rollup@4.40.1: - dependencies: - '@types/estree': 1.0.7 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.40.1 - '@rollup/rollup-android-arm64': 4.40.1 - '@rollup/rollup-darwin-arm64': 4.40.1 - '@rollup/rollup-darwin-x64': 4.40.1 - '@rollup/rollup-freebsd-arm64': 4.40.1 - '@rollup/rollup-freebsd-x64': 4.40.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.40.1 - '@rollup/rollup-linux-arm-musleabihf': 4.40.1 - '@rollup/rollup-linux-arm64-gnu': 4.40.1 - '@rollup/rollup-linux-arm64-musl': 4.40.1 - '@rollup/rollup-linux-loongarch64-gnu': 4.40.1 - '@rollup/rollup-linux-powerpc64le-gnu': 4.40.1 - '@rollup/rollup-linux-riscv64-gnu': 4.40.1 - '@rollup/rollup-linux-riscv64-musl': 4.40.1 - '@rollup/rollup-linux-s390x-gnu': 4.40.1 - '@rollup/rollup-linux-x64-gnu': 4.40.1 - '@rollup/rollup-linux-x64-musl': 4.40.1 - '@rollup/rollup-win32-arm64-msvc': 4.40.1 - '@rollup/rollup-win32-ia32-msvc': 4.40.1 - '@rollup/rollup-win32-x64-msvc': 4.40.1 - fsevents: 2.3.3 - - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 - - safe-buffer@5.2.1: {} - - safer-buffer@2.1.2: {} - - sax@1.4.1: {} - - search-insights@2.17.3: {} - - section-matter@1.0.0: - dependencies: - extend-shallow: 2.0.1 - kind-of: 6.0.3 - - selderee@0.11.0: - dependencies: - parseley: 0.12.1 - - semver@7.7.2: {} - - sharp@0.34.2: - dependencies: - color: 4.2.3 - detect-libc: 2.0.4 - semver: 7.7.2 - optionalDependencies: - '@img/sharp-darwin-arm64': 0.34.2 - '@img/sharp-darwin-x64': 0.34.2 - '@img/sharp-libvips-darwin-arm64': 1.1.0 - '@img/sharp-libvips-darwin-x64': 1.1.0 - '@img/sharp-libvips-linux-arm': 1.1.0 - '@img/sharp-libvips-linux-arm64': 1.1.0 - '@img/sharp-libvips-linux-ppc64': 1.1.0 - '@img/sharp-libvips-linux-s390x': 1.1.0 - '@img/sharp-libvips-linux-x64': 1.1.0 - '@img/sharp-libvips-linuxmusl-arm64': 1.1.0 - '@img/sharp-libvips-linuxmusl-x64': 1.1.0 - '@img/sharp-linux-arm': 0.34.2 - '@img/sharp-linux-arm64': 0.34.2 - '@img/sharp-linux-s390x': 0.34.2 - '@img/sharp-linux-x64': 0.34.2 - '@img/sharp-linuxmusl-arm64': 0.34.2 - '@img/sharp-linuxmusl-x64': 0.34.2 - '@img/sharp-wasm32': 0.34.2 - '@img/sharp-win32-arm64': 0.34.2 - '@img/sharp-win32-ia32': 0.34.2 - '@img/sharp-win32-x64': 0.34.2 - - shebang-command@2.0.0: - dependencies: - shebang-regex: 3.0.0 - - shebang-regex@3.0.0: {} - - shiki@2.5.0: - dependencies: - '@shikijs/core': 2.5.0 - '@shikijs/engine-javascript': 2.5.0 - '@shikijs/engine-oniguruma': 2.5.0 - '@shikijs/langs': 2.5.0 - '@shikijs/themes': 2.5.0 - '@shikijs/types': 2.5.0 - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 - - shimmer@1.2.1: {} - - side-channel-list@1.0.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - - side-channel-map@1.0.1: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - - side-channel-weakmap@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - side-channel-map: 1.0.1 - - side-channel@1.1.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - side-channel-list: 1.0.0 - side-channel-map: 1.0.1 - side-channel-weakmap: 1.0.2 - - signal-exit@4.1.0: {} - - simple-swizzle@0.2.2: - dependencies: - is-arrayish: 0.3.2 - - slick@1.12.2: {} - - sonic-channel@1.3.1: {} - - source-map-js@1.2.1: {} - - source-map-support@0.5.21: - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 - - source-map@0.6.1: {} - - space-separated-tokens@2.0.2: {} - - speakingurl@14.0.1: {} - - speech-rule-engine@4.1.2: - dependencies: - '@xmldom/xmldom': 0.9.8 - commander: 13.1.0 - wicked-good-xpath: 1.3.0 - - sprintf-js@1.0.3: {} - - standard-as-callback@2.1.0: {} - - stdin-discarder@0.2.2: {} - - string-comparison@1.3.0: {} - - string-width@4.2.3: - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - - string-width@5.1.2: - dependencies: - eastasianwidth: 0.2.0 - emoji-regex: 9.2.2 - strip-ansi: 7.1.0 - - string-width@6.1.0: - dependencies: - eastasianwidth: 0.2.0 - emoji-regex: 10.4.0 - strip-ansi: 7.1.0 - - string-width@7.2.0: - dependencies: - emoji-regex: 10.4.0 - get-east-asian-width: 1.3.0 - strip-ansi: 7.1.0 - - stringify-entities@4.0.4: - dependencies: - character-entities-html4: 2.1.0 - character-entities-legacy: 3.0.0 - - strip-ansi@6.0.1: - dependencies: - ansi-regex: 5.0.1 - - strip-ansi@7.1.0: - dependencies: - ansi-regex: 6.1.0 - - strip-bom-string@1.0.0: {} - - superjson@2.2.2: - dependencies: - copy-anything: 3.0.5 - - supports-color@10.0.0: {} - - supports-color@7.2.0: - dependencies: - has-flag: 4.0.0 - - supports-preserve-symlinks-flag@1.0.0: {} - - tabbable@6.2.0: {} - - text-table@0.2.0: {} - - to-regex-range@5.0.1: - dependencies: - is-number: 7.0.0 - - tr46@0.0.3: {} - - trim-lines@3.0.1: {} - - true-myth@4.1.1: {} - - ts-morph@13.0.3: - dependencies: - '@ts-morph/common': 0.12.3 - code-block-writer: 11.0.3 - - ts-prune@0.10.3: - dependencies: - commander: 6.2.1 - cosmiconfig: 7.1.0 - json5: 2.2.3 - lodash: 4.17.21 - true-myth: 4.1.1 - ts-morph: 13.0.3 - - tslib@2.8.1: {} - - type-fest@0.21.3: {} - - type-fest@4.40.1: {} - - type-flag@3.0.0: {} - - type-level-regexp@0.1.17: {} - - typescript@5.8.3: {} - - uc.micro@2.1.0: {} - - ufo@1.6.1: {} - - undici-types@6.21.0: {} - - unicode-emoji-json@0.8.0: {} - - unist-util-is@6.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-position@5.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-stringify-position@4.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-visit-parents@6.0.1: - dependencies: - '@types/unist': 3.0.3 - unist-util-is: 6.0.0 - - unist-util-visit@5.0.0: - dependencies: - '@types/unist': 3.0.3 - unist-util-is: 6.0.0 - unist-util-visit-parents: 6.0.1 - - unplugin@2.3.2: - dependencies: - acorn: 8.14.1 - picomatch: 4.0.2 - webpack-virtual-modules: 0.6.2 - - uqr@0.1.2: {} - - uuid@9.0.1: {} - - valid-data-url@3.0.1: {} - - vfile-message@4.0.2: - dependencies: - '@types/unist': 3.0.3 - unist-util-stringify-position: 4.0.0 - - vfile@6.0.3: - dependencies: - '@types/unist': 3.0.3 - vfile-message: 4.0.2 - - vite@5.4.19(@types/node@22.15.3): - dependencies: - esbuild: 0.21.5 - postcss: 8.5.3 - rollup: 4.40.1 - optionalDependencies: - '@types/node': 22.15.3 - fsevents: 2.3.3 - - vitepress-plugin-tabs@0.7.1(vitepress@1.6.3(@algolia/client-search@5.24.0)(@types/node@22.15.3)(markdown-it-mathjax3@4.3.2)(postcss@8.5.3)(search-insights@2.17.3)(typescript@5.8.3))(vue@3.5.16(typescript@5.8.3)): - dependencies: - vitepress: 1.6.3(@algolia/client-search@5.24.0)(@types/node@22.15.3)(markdown-it-mathjax3@4.3.2)(postcss@8.5.3)(search-insights@2.17.3)(typescript@5.8.3) - vue: 3.5.16(typescript@5.8.3) - - vitepress-sidebar@1.31.1: - dependencies: - glob: 10.4.5 - gray-matter: 4.0.3 - qsu: 1.10.0 - - vitepress@1.6.3(@algolia/client-search@5.24.0)(@types/node@22.15.3)(markdown-it-mathjax3@4.3.2)(postcss@8.5.3)(search-insights@2.17.3)(typescript@5.8.3): - dependencies: - '@docsearch/css': 3.8.2 - '@docsearch/js': 3.8.2(@algolia/client-search@5.24.0)(search-insights@2.17.3) - '@iconify-json/simple-icons': 1.2.33 - '@shikijs/core': 2.5.0 - '@shikijs/transformers': 2.5.0 - '@shikijs/types': 2.5.0 - '@types/markdown-it': 14.1.2 - '@vitejs/plugin-vue': 5.2.3(vite@5.4.19(@types/node@22.15.3))(vue@3.5.16(typescript@5.8.3)) - '@vue/devtools-api': 7.7.6 - '@vue/shared': 3.5.13 - '@vueuse/core': 12.8.2(typescript@5.8.3) - '@vueuse/integrations': 12.8.2(focus-trap@7.6.4)(typescript@5.8.3) - focus-trap: 7.6.4 - mark.js: 8.11.1 - minisearch: 7.1.2 - shiki: 2.5.0 - vite: 5.4.19(@types/node@22.15.3) - vue: 3.5.16(typescript@5.8.3) - optionalDependencies: - markdown-it-mathjax3: 4.3.2 - postcss: 8.5.3 - transitivePeerDependencies: - - '@algolia/client-search' - - '@types/node' - - '@types/react' - - async-validator - - axios - - change-case - - drauu - - fuse.js - - idb-keyval - - jwt-decode - - less - - lightningcss - - nprogress - - qrcode - - react - - react-dom - - sass - - sass-embedded - - search-insights - - sortablejs - - stylus - - sugarss - - terser - - typescript - - universal-cookie - - vue@3.5.16(typescript@5.8.3): - dependencies: - '@vue/compiler-dom': 3.5.16 - '@vue/compiler-sfc': 3.5.16 - '@vue/runtime-dom': 3.5.16 - '@vue/server-renderer': 3.5.16(vue@3.5.16(typescript@5.8.3)) - '@vue/shared': 3.5.16 - optionalDependencies: - typescript: 5.8.3 - - web-push@3.6.7: - dependencies: - asn1.js: 5.4.1 - http_ece: 1.2.0 - https-proxy-agent: 7.0.6 - jws: 4.0.0 - minimist: 1.2.8 - transitivePeerDependencies: - - supports-color - - web-resource-inliner@6.0.1: - dependencies: - ansi-colors: 4.1.3 - escape-goat: 3.0.0 - htmlparser2: 5.0.1 - mime: 2.6.0 - node-fetch: 2.7.0 - valid-data-url: 3.0.1 - transitivePeerDependencies: - - encoding - - webidl-conversions@3.0.1: {} - - webpack-virtual-modules@0.6.2: {} - - whatwg-url@5.0.0: - dependencies: - tr46: 0.0.3 - webidl-conversions: 3.0.1 - - which@2.0.2: - dependencies: - isexe: 2.0.0 - - wicked-good-xpath@1.3.0: {} - - wrap-ansi@6.2.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - wrap-ansi@7.0.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - wrap-ansi@8.1.0: - dependencies: - ansi-styles: 6.2.1 - string-width: 5.1.2 - strip-ansi: 7.1.0 - - xml-js@1.6.11: - dependencies: - sax: 1.4.1 - - xss@1.0.15: - dependencies: - commander: 2.20.3 - cssfilter: 0.0.10 - - xtend@4.0.2: {} - - yaml@1.10.2: {} - - yoctocolors-cjs@2.1.2: {} - - yoctocolors@1.0.0: {} - - youch-core@0.3.2: - dependencies: - '@poppinss/exception': 1.2.1 - error-stack-parser-es: 1.0.5 - - youch@4.1.0-beta.7: - dependencies: - '@poppinss/dumper': 0.6.3 - '@speed-highlight/core': 1.2.7 - cookie: 1.0.2 - youch-core: 0.3.2 - - zod-openapi@4.2.4(zod@3.24.3): - dependencies: - zod: 3.24.3 - - zod-openapi@4.2.4(zod@3.25.64): - dependencies: - zod: 3.25.64 - - zod-to-json-schema@3.24.5(zod@3.24.3): - dependencies: - zod: 3.24.3 - - zod-to-json-schema@3.24.5(zod@3.25.64): - dependencies: - zod: 3.25.64 - - zod-validation-error@3.4.0(zod@3.24.3): - dependencies: - zod: 3.24.3 - - zod-validation-error@3.5.0(zod@3.25.64): - dependencies: - zod: 3.25.64 - - zod@3.24.1: {} - - zod@3.24.3: {} - - zod@3.25.64: {} - - zwitch@2.0.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml deleted file mode 100644 index acc0aa44..00000000 --- a/pnpm-workspace.yaml +++ /dev/null @@ -1,9 +0,0 @@ -packages: - - packages/* - -onlyBuiltDependencies: - - esbuild - - msgpackr-extract - - sharp - -shamefullyHoist: true diff --git a/scripts/update-nix.sh b/scripts/update-nix.sh new file mode 100644 index 00000000..c4a6f010 --- /dev/null +++ b/scripts/update-nix.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Step 2: Blank the hash in package.nix +sed -i 's/outputHash = ".*";/outputHash = lib.fakeHash;/g' nix/package.nix + +echo "Running nix build to get the correct hash..." + +# Step 3: Run nix build and capture stderr +build_output=$(nix build .#versia-server 2>&1 || true) + +# Step 4: Extract the corrected hash from the output +corrected_hash=$(echo "$build_output" | grep 'got:' | awk '{print $2}') + +echo "Corrected hash: $corrected_hash" + +# Step 5: Replace the blank hash with the corrected one +sed -i "s/outputHash = lib.fakeHash;/outputHash = \"$corrected_hash\";/g" nix/package.nix + +echo "Rebuilding with the corrected hash..." + +# Step 6: Build again +nix build .#versia-server diff --git a/tests/setup.ts b/tests/setup.ts deleted file mode 100644 index 8c374279..00000000 --- a/tests/setup.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { setupDatabase } from "~/drizzle/db"; -import { deleteOldTestUsers } from "./utils.ts"; - -await setupDatabase(); -await deleteOldTestUsers(); - -// Start workers -await import("~/worker.ts"); diff --git a/tests/test-image.webp b/tests/test-image.webp deleted file mode 100755 index 661d0eba..00000000 Binary files a/tests/test-image.webp and /dev/null differ diff --git a/types/api.ts b/types/api.ts index 3e976c69..ce9736c2 100644 --- a/types/api.ts +++ b/types/api.ts @@ -1,13 +1,19 @@ +import type * as VersiaEntities from "@versia/sdk/entities"; +import type { ConfigSchema } from "@versia-server/config"; +import type { Application, Token, User } from "@versia-server/kit/db"; import type { SocketAddress } from "bun"; import type { Hono } from "hono"; import type { RouterRoute } from "hono/types"; -import type { z } from "zod"; -import type { ConfigSchema } from "~/classes/config/schema"; -import type { AuthData } from "~/classes/functions/user"; -import type * as VersiaEntities from "~/packages/sdk/entities"; +import type { z } from "zod/v4"; export type HttpVerb = "GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "OPTIONS"; +export interface AuthData { + user: User | null; + token: Token | null; + application: Application | null; +} + export type HonoEnv = { Variables: { config: z.infer; diff --git a/utils/bull-board.ts b/utils/bull-board.ts index 5eff479a..5d35e33d 100644 --- a/utils/bull-board.ts +++ b/utils/bull-board.ts @@ -1,17 +1,17 @@ import { createBullBoard } from "@bull-board/api"; import { BullMQAdapter } from "@bull-board/api/bullMQAdapter"; import { HonoAdapter } from "@bull-board/hono"; +import { config } from "@versia-server/config"; +import { deliveryQueue } from "@versia-server/kit/queues/delivery"; +import { fetchQueue } from "@versia-server/kit/queues/fetch"; +import { inboxQueue } from "@versia-server/kit/queues/inbox"; +import { mediaQueue } from "@versia-server/kit/queues/media"; +import { pushQueue } from "@versia-server/kit/queues/push"; +import { relationshipQueue } from "@versia-server/kit/queues/relationships"; import type { Hono } from "hono"; import { serveStatic } from "hono/bun"; -import { deliveryQueue } from "~/classes/queues/delivery"; -import { fetchQueue } from "~/classes/queues/fetch"; -import { inboxQueue } from "~/classes/queues/inbox"; -import { mediaQueue } from "~/classes/queues/media"; -import { pushQueue } from "~/classes/queues/push"; -import { relationshipQueue } from "~/classes/queues/relationships"; -import { config } from "~/config.ts"; -import pkg from "~/package.json" with { type: "json" }; import type { HonoEnv } from "~/types/api"; +import pkg from "../package.json" with { type: "json" }; export const applyToHono = (app: Hono): void => { const serverAdapter = new HonoAdapter(serveStatic); @@ -43,6 +43,5 @@ export const applyToHono = (app: Hono): void => { }); serverAdapter.setBasePath("/admin/queues"); - // @ts-expect-error idk why it's like this app.route("/admin/queues", serverAdapter.registerPlugin()); }; diff --git a/utils/challenges.ts b/utils/challenges.ts index 919cd033..01422ba3 100644 --- a/utils/challenges.ts +++ b/utils/challenges.ts @@ -1,9 +1,9 @@ -import { db } from "@versia/kit/db"; -import { Challenges } from "@versia/kit/tables"; +import { config } from "@versia-server/config"; +import { db } from "@versia-server/kit/db"; +import { Challenges } from "@versia-server/kit/tables"; import { createChallenge } from "altcha-lib"; import type { Challenge } from "altcha-lib/types"; import { randomUUIDv7 } from "bun"; -import { config } from "~/config.ts"; export const generateChallenge = async ( maxNumber?: number, diff --git a/utils/content_types.ts b/utils/content_types.ts index f1f0daa1..86f2423e 100644 --- a/utils/content_types.ts +++ b/utils/content_types.ts @@ -1,8 +1,8 @@ +import type { ContentFormatSchema } from "@versia/sdk/schemas"; +import { config } from "@versia-server/config"; import { htmlToText as htmlToTextLib } from "html-to-text"; import { lookup } from "mime-types"; -import type { z } from "zod"; -import { config } from "~/config.ts"; -import type { ContentFormatSchema } from "~/packages/sdk/schemas"; +import type { z } from "zod/v4"; export const getBestContentType = ( content?: z.infer | null, diff --git a/utils/loggers.ts b/utils/loggers.ts deleted file mode 100644 index d2ec074f..00000000 --- a/utils/loggers.ts +++ /dev/null @@ -1,144 +0,0 @@ -import { mkdir } from "node:fs/promises"; -import { dirname } from "node:path"; -import { getRotatingFileSink } from "@logtape/file"; -import { - configure, - getConsoleSink, - getLevelFilter, - type LogLevel, - type LogRecord, -} from "@logtape/logtape"; -import chalk from "chalk"; -import { config } from "~/config.ts"; - -// config.logging.log_file_path is a path to a file, create the directory if it doesn't exist -await mkdir(dirname(config.logging.log_file_path), { recursive: true }); - -const levelAbbreviations: Record = { - debug: "DBG", - info: "INF", - warning: "WRN", - error: "ERR", - fatal: "FTL", - trace: "TRC", -}; - -/** - * The styles for the log level in the console. - */ -const logLevelStyles: Record string> = { - debug: chalk.white.bgGray, - info: chalk.black.bgWhite, - warning: chalk.black.bgYellow, - error: chalk.white.bgRed, - fatal: chalk.white.bgRedBright, - trace: chalk.white.bgBlue, -}; - -/** - * The default console formatter. - * - * @param record The log record to format. - * @returns The formatted log record, as an array of arguments for - * {@link console.log}. - */ -export function defaultConsoleFormatter(record: LogRecord): string[] { - const msg = record.message.join(""); - const date = new Date(record.timestamp); - const time = `${date.getUTCHours().toString().padStart(2, "0")}:${date - .getUTCMinutes() - .toString() - .padStart( - 2, - "0", - )}:${date.getUTCSeconds().toString().padStart(2, "0")}.${date - .getUTCMilliseconds() - .toString() - .padStart(3, "0")}`; - - const formattedTime = chalk.gray(time); - const formattedLevel = logLevelStyles[record.level]( - levelAbbreviations[record.level], - ); - const formattedCategory = chalk.gray(record.category.join("\xb7")); - const formattedMsg = chalk.reset(msg); - - return [ - `${formattedTime} ${formattedLevel} ${formattedCategory} ${formattedMsg}`, - ]; -} - -export const configureLoggers = (silent = false): Promise => - configure({ - reset: true, - sinks: { - console: getConsoleSink({ - formatter: defaultConsoleFormatter, - }), - file: getRotatingFileSink(config.logging.log_file_path, { - maxFiles: 10, - maxSize: 10 * 1024 * 1024, - }), - }, - filters: { - configFilter: silent - ? getLevelFilter(null) - : getLevelFilter(config.logging.log_level), - }, - loggers: [ - { - category: "server", - sinks: ["console", "file"], - filters: ["configFilter"], - }, - { - category: ["federation", "inbox"], - sinks: ["console", "file"], - filters: ["configFilter"], - }, - { - category: ["federation", "delivery"], - sinks: ["console", "file"], - filters: ["configFilter"], - }, - { - category: ["federation", "bridge"], - sinks: ["console", "file"], - filters: ["configFilter"], - }, - { - category: ["federation", "resolvers"], - sinks: ["console", "file"], - filters: ["configFilter"], - }, - { - category: ["federation", "messaging"], - sinks: ["console", "file"], - filters: ["configFilter"], - }, - { - category: "database", - sinks: ["console", "file"], - filters: ["configFilter"], - }, - { - category: "webfinger", - sinks: ["console", "file"], - filters: ["configFilter"], - }, - { - category: "sonic", - sinks: ["console", "file"], - filters: ["configFilter"], - }, - { - category: ["logtape", "meta"], - lowestLevel: "error", - }, - { - category: "plugin", - sinks: ["console", "file"], - filters: ["configFilter"], - }, - ], - }); diff --git a/utils/rss.ts b/utils/rss.ts index 3a07ba78..1633dd57 100644 --- a/utils/rss.ts +++ b/utils/rss.ts @@ -1,10 +1,8 @@ +import { config } from "@versia-server/config"; +import { Media, Note, type User } from "@versia-server/kit/db"; +import { Notes } from "@versia-server/kit/tables"; import { and, eq, inArray } from "drizzle-orm"; import { Feed } from "feed"; -import { Media } from "~/classes/database/media"; -import { Note } from "~/classes/database/note"; -import type { User } from "~/classes/database/user"; -import { config } from "~/config"; -import { Notes } from "~/drizzle/schema"; export const getFeed = async (user: User, page = 0): Promise => { const notes = await Note.manyFromSql( @@ -72,11 +70,11 @@ export const getFeed = async (user: User, page = 0): Promise => { url: image.getUrl().href, title: image.data.content[image.getPreferredMimeType()] - .description ?? undefined, + ?.description ?? undefined, type: image.getPreferredMimeType(), length: image.data.content[image.getPreferredMimeType()] - .size ?? undefined, + ?.size ?? undefined, } : undefined, video: video @@ -84,14 +82,14 @@ export const getFeed = async (user: User, page = 0): Promise => { url: video.getUrl().href, title: video.data.content[video.getPreferredMimeType()] - .description ?? undefined, + ?.description ?? undefined, type: video.getPreferredMimeType(), duration: video.data.content[video.getPreferredMimeType()] - .duration ?? undefined, + ?.duration ?? undefined, length: video.data.content[video.getPreferredMimeType()] - .size ?? undefined, + ?.size ?? undefined, } : undefined, audio: audio @@ -99,14 +97,14 @@ export const getFeed = async (user: User, page = 0): Promise => { url: audio.getUrl().href, title: audio.data.content[audio.getPreferredMimeType()] - .description ?? undefined, + ?.description ?? undefined, type: audio.getPreferredMimeType(), duration: audio.data.content[audio.getPreferredMimeType()] - .duration ?? undefined, + ?.duration ?? undefined, length: audio.data.content[audio.getPreferredMimeType()] - .size ?? undefined, + ?.size ?? undefined, } : undefined, }); diff --git a/utils/sanitization.ts b/utils/sanitization.ts index 857ef17f..1ad1a755 100644 --- a/utils/sanitization.ts +++ b/utils/sanitization.ts @@ -1,6 +1,6 @@ +import { ProxiableUrl } from "@versia-server/config"; import { stringifyEntitiesLight } from "stringify-entities"; import xss, { type IFilterXSSOptions } from "xss"; -import { ProxiableUrl } from "~/classes/media/url.ts"; export const sanitizedHtmlStrip = (html: string): Promise => { return sanitizeHtml(html, { diff --git a/utils/sentry.ts b/utils/sentry.ts deleted file mode 100644 index 460bc5a5..00000000 --- a/utils/sentry.ts +++ /dev/null @@ -1,23 +0,0 @@ -import * as Sentry from "@sentry/bun"; -import { env } from "bun"; -import { config } from "~/config.ts"; -import pkg from "~/package.json" with { type: "json" }; - -const sentryInstance = - config.logging.sentry && - Sentry.init({ - dsn: config.logging.sentry.dsn.origin, - debug: config.logging.sentry.debug, - sampleRate: config.logging.sentry.sample_rate, - maxBreadcrumbs: config.logging.sentry.max_breadcrumbs, - tracesSampleRate: config.logging.sentry.traces_sample_rate, - environment: config.logging.sentry.environment, - tracePropagationTargets: - config.logging.sentry.trace_propagation_targets, - release: env.GIT_COMMIT - ? `${pkg.version}-${env.GIT_COMMIT}` - : pkg.version, - integrations: [Sentry.extraErrorDataIntegration()], - }); - -export const sentry = sentryInstance || undefined; diff --git a/utils/server.ts b/utils/server.ts index 0c1b0fac..fb4ba853 100644 --- a/utils/server.ts +++ b/utils/server.ts @@ -1,9 +1,9 @@ +import type { ConfigSchema } from "@versia-server/config"; +import { debugResponse } from "@versia-server/kit/api"; import { type Server, serve } from "bun"; import type { Hono } from "hono"; -import type { z } from "zod"; -import type { ConfigSchema } from "~/classes/config/schema.ts"; +import type { z } from "zod/v4"; import type { HonoEnv } from "~/types/api"; -import { debugResponse } from "./api.ts"; export const createServer = ( config: z.infer, @@ -24,9 +24,7 @@ export const createServer = ( async fetch(req, server): Promise { const output = await app.fetch(req, { ip: server.requestIP(req) }); - if (config.logging.types.responses) { - await debugResponse(output.clone()); - } + await debugResponse(output.clone()); return output; }, diff --git a/worker.ts b/worker.ts index f41845e0..e3cc7169 100644 --- a/worker.ts +++ b/worker.ts @@ -1 +1,18 @@ -await import("~/entrypoints/worker/index.ts"); +import process from "node:process"; +import { serverLogger } from "@versia-server/logging"; +import { workers } from "@versia-server/worker"; +import chalk from "chalk"; + +process.on("SIGINT", () => { + process.exit(); +}); + +await import("@versia-server/worker/setup"); + +for (const [worker, fn] of Object.entries(workers)) { + serverLogger.info`Starting ${worker} Worker...`; + fn(); + serverLogger.info`${chalk.green("✔")} ${worker} Worker started`; +} + +serverLogger.info`${chalk.green("✔✔✔✔✔✔")} All workers started`;