diff --git a/.changeset/secure-v5-release-readiness.md b/.changeset/secure-v5-release-readiness.md new file mode 100644 index 00000000..ad8bff3f --- /dev/null +++ b/.changeset/secure-v5-release-readiness.md @@ -0,0 +1,32 @@ +--- +'@cleverbrush/async': major +'@cleverbrush/auth': major +'@cleverbrush/client': major +'@cleverbrush/deep': major +'@cleverbrush/di': major +'@cleverbrush/env': major +'@cleverbrush/knex-clickhouse': major +'@cleverbrush/knex-schema': major +'@cleverbrush/log': major +'@cleverbrush/mapper': major +'@cleverbrush/orm': major +'@cleverbrush/orm-cli': major +'@cleverbrush/otel': major +'@cleverbrush/react-form': major +'@cleverbrush/scheduler': major +'@cleverbrush/scheduler-postgres': major +'@cleverbrush/schema': major +'@cleverbrush/schema-json': major +'@cleverbrush/server': major +'@cleverbrush/server-openapi': major +'@cleverbrush/storage': major +'@cleverbrush/storage-s3': major +--- + +Require Node.js 24+ consistently across all published packages. Correct the root and all published-package license files to BSD-3-Clause, matching package metadata and documentation, and verify license consistency in source and npm tarballs. See docs/MIGRATION-v5.md for migration guidance. + +Harden JWT key/algorithm and claim validation, cookie parsing/serialization, and request-body lifecycle handling. Require explicit authorization scope for bounded server idempotency; coalesce concurrent retries and capture full response bodies. Bound response caching and bypass private, no-store and cookie-setting responses. + +Preserve DI scope validation through factories and propagate registered optional-service failures. Fix batch response status/header capture, timeout abort-listener cleanup, concurrent deduplication response cloning, CLI database cleanup on validation/production-guard failures, and the missing client idempotency JavaScript export. Update security-sensitive dependencies and ensure OpenTelemetry disable flags override SDK defaults. + +Add regression tests, package-consumer smoke checks, package-level unit coverage floors, migration/security documentation and release validation gates. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 27819052..22a9bb44 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,12 +1,14 @@ name: CI on: - push: - branches: [master] pull_request: + workflow_call: + +permissions: + contents: read concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: framework-validation-${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: @@ -44,6 +46,27 @@ jobs: - name: Test & Typecheck run: npm run test + - name: Coverage and package floors + run: npm run test:coverage + + - name: Dependency security + run: npm audit --audit-level=high + + - name: Packed package smoke tests + run: npm run test:packages + + - name: Build documentation websites + run: npm run build:schema-site && npm run build:docs-site + + - name: Generate every package API reference + run: npx typedoc --out "$RUNNER_TEMP/framework-api-docs" + + - uses: actions/upload-artifact@v4 + if: always() + with: + name: coverage-node-${{ matrix.node }} + path: coverage/ + query-integration: name: PostgreSQL Query Integration runs-on: ubuntu-latest @@ -74,6 +97,9 @@ jobs: - run: npm run build - run: npm run test:queries:integration - run: npm run test:scheduler:integration + - run: npm run test:queries:integration && npm run test:scheduler:integration + env: + TZ: America/Los_Angeles - run: node demos/durable-jobs/demo.ts - run: node demos/durable-jobs/periodic.ts --fast @@ -89,3 +115,33 @@ jobs: - run: npm ci - run: npm run build - run: npm run test:storage:integration + + demo-e2e: + name: Demo API, browser and telemetry E2E + runs-on: ubuntu-latest + timeout-minutes: 30 + env: + CI: 'true' + KEEP_STACK: '0' + COMPOSE_PROJECT_NAME: framework-e2e + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 24 + cache: npm + - run: npm ci + - run: npm run build + - run: npm run playwright:install --workspace @cleverbrush/demo-e2e -- --with-deps + - run: npm run test:e2e + - name: Collect isolated stack logs + if: failure() + run: docker compose -f demos/docker-compose.yml logs --no-color > e2e-stack.log + - uses: actions/upload-artifact@v4 + if: failure() + with: + name: demo-e2e-logs + path: e2e-stack.log + - name: Remove disposable CI stack + if: always() + run: docker compose -f demos/docker-compose.yml down --volumes --remove-orphans diff --git a/.github/workflows/publish-beta.yml b/.github/workflows/publish-beta.yml index a213f01f..6c8fda99 100644 --- a/.github/workflows/publish-beta.yml +++ b/.github/workflows/publish-beta.yml @@ -12,7 +12,11 @@ permissions: contents: read jobs: + validate: + uses: ./.github/workflows/ci.yml + publish-beta: + needs: validate name: Publish Beta to npm runs-on: ubuntu-latest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0d9a9633..d2e727b2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,13 @@ permissions: pull-requests: write jobs: + validate: + permissions: + contents: read + uses: ./.github/workflows/ci.yml + release: + needs: validate name: Version & Publish runs-on: ubuntu-latest diff --git a/.nvmrc b/.nvmrc index 2bd5a0a9..a45fd52c 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -22 +24 diff --git a/AGENTS.md b/AGENTS.md index c9ac2154..8f5cb8b3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -30,12 +30,12 @@ npm run lint:fix | Property | Value | |---|---| -| Package manager | npm (v24+) | +| Package manager | npm 11 (see `packageManager`) | | Build system | Turborepo (`turbo run build`) | | Language | TypeScript — target ES2022, `moduleResolution: bundler` | | Lint / Format | [Biome](https://biomejs.dev) (not ESLint or Prettier) | | Test runner | [Vitest](https://vitest.dev) (with built-in typecheck) | -| Node.js | 20+ (22 recommended — see `.nvmrc`) | +| Node.js | 24+ (see `.nvmrc`) | | Module system | ES Modules (`"type": "module"` in root `package.json`) | ### Workspace layout @@ -73,6 +73,8 @@ scripts/ ← build/release helper scripts | `@cleverbrush/otel` | OpenTelemetry instrumentation | | `@cleverbrush/env` | Environment-variable parsing with schema validation | | `@cleverbrush/schema-json` | JSON Schema generation from schema builders | +| `@cleverbrush/storage` | Provider-neutral object storage contracts | +| `@cleverbrush/storage-s3` | Streaming S3-compatible storage | --- @@ -152,8 +154,8 @@ The `demos/` directory is linted separately (see `demos/todo-backend/biome.json` ## Testing Conventions - Tests are **co-located** with source files: `src/foo.ts` → `src/foo.test.ts` -- Vitest globals are available (`describe`, `it`, `expect`, etc.) — no explicit - import needed (configured via `"types": ["vitest/globals"]` in `tsconfig.json`) +- Import runtime helpers (`describe`, `it`, `expect`, etc.) from `vitest`. + Ambient TypeScript declarations do not enable runtime globals. - Run with `npm run test` which also performs TypeScript typechecking - Benchmarks live in `libs/benchmarks/` and run with `npm run bench` - Server integration tests live in `libs/server-integration-tests/` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6abba706..d8cbc897 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,8 +4,8 @@ Thanks for your interest in contributing! This guide will help you get started. ## Prerequisites -- **Node.js** 20 or later (22 recommended — see `.nvmrc`) -- **npm** (ships with Node) +- **Node.js** 24 or later (see `.nvmrc`) +- **npm** 11 (see the root `packageManager` field) ## Getting Started @@ -24,6 +24,47 @@ npm run build npm run test ``` +The root build also refreshes workspace CLI links. On a fresh checkout npm cannot +link `cb-orm` until its generated `dist/bin.js` exists; the postbuild step makes +demo migration commands work without a second dependency installation. + +## Release verification + +In addition to lint, build and unit/type tests, CI runs: + +```bash +npm run test:coverage +npm run test:packages +npm audit --audit-level=high +npm run typecheck:schema-site +npm run typecheck:docs-site +npm run build:schema-site +npm run build:docs-site +``` + +`coverage-thresholds.json` sets per-package statement, branch, function and line +floors for **unit** coverage. New published packages need explicit floors; do not +lower existing floors to hide regressions. Refresh README badges explicitly with +`npm run coverage:badges` after a successful coverage run. Badges do not include +the dedicated database or S3 integration suites. In particular, +`scheduler-postgres` relies on the real-database suite, not its small unit suite. + +The package smoke test packs every published workspace, installs the tarballs +and peer dependencies in a disposable consumer, checks every export and TypeScript +declaration, and bundles browser entry points without Node polyfills. It requires +registry access and deletes only its own temporary directory on completion. + +CI runs the query and durable scheduler integration suites against disposable +PostgreSQL in UTC and America/Los_Angeles, and the storage suite against Garage. +Locally, provide isolated `QUERY_TEST_DATABASE_URL` and +`SCHEDULER_TEST_DATABASE_URL` values, then run `npm run test:queries:integration` +and `npm run test:scheduler:integration`. `npm run test:storage:integration` +creates and cleans up its own Docker service. Never point tests at production. +The full demo API/browser/telemetry E2E stack and API-reference generation also +run in CI. Both beta and stable publication wait for this reusable validation +workflow. TypeDoc's non-exported internal-type warnings remain visible; they are +not suppressed by the documentation build. + ## Monorepo Structure This project uses **npm workspaces** with **Turborepo** for orchestration. All packages live under `libs/`: @@ -35,9 +76,12 @@ This project uses **npm workspaces** with **Turborepo** for orchestration. All p | `@cleverbrush/async` | Async utilities (Collector, debounce, throttle, retry) | | `@cleverbrush/mapper` | Schema-driven object mapping | | `@cleverbrush/react-form` | React form library powered by schema PropertyDescriptors | -| `@cleverbrush/scheduler` | Cron-like job scheduler with schema-validated config | +| `@cleverbrush/scheduler` | Typed durable jobs, recurring schedules and progress | | `@cleverbrush/knex-clickhouse` | Knex dialect for ClickHouse | +This table highlights foundational packages. The [root package inventory](README.md#packages) +lists all published packages, including HTTP, persistence, storage and telemetry. + ## Development Workflow ### Code Style @@ -79,11 +123,12 @@ npm run clean The extension system is the primary way to add new validators. See `libs/schema/src/extensions/` for examples. 1. Create your extension file (e.g. `libs/schema/src/extensions/myExtension.ts`) -2. Export extension functions that call the builder's `.extend()` method +2. Define methods with `defineExtension()` and return the new immutable builder 3. Add tests in a co-located `*.test.ts` file 4. Re-export from `libs/schema/src/extensions/index.ts` -Look at `libs/schema/src/extensions/string.ts` for a complete example of how extensions add validators like `email()`, `url()`, `uuid()`, etc. +See the schema README's extension-system examples for `defineExtension()` and +`withExtensions()`. Do not discard the builder returned by an extension method. ## Adding a New Builder @@ -97,7 +142,7 @@ Builders live in `libs/schema/src/builders/`. Each builder extends the base `Sch ## Pull Request Process -1. **Fork** the repo and create a feature branch from `master` +1. Create a feature branch from `development` and target `development` in the PR 2. Make your changes with tests 3. **Add a changeset** — every PR that changes package behavior needs one: ```bash diff --git a/LICENSE b/LICENSE index 0b842d4c..053547c1 100644 --- a/LICENSE +++ b/LICENSE @@ -1,52 +1,28 @@ -Cleverbrush Framework is dual-licensed under both the "Unlicense" and the -"Zero-Clause BSD" (0BSD) licenses. The intent of this dual-licensing -structure is to make Cleverbrush Framework as consumable as possible in as many -environments / countries / companies as possible without encumbering -users. - -This license applies to all of the Cleverbrush Framework source code, build code, -and tests. - -The text of the two licenses follows below: - -============================== UNLICENSE ============================== - -This is free and unencumbered software released into the public domain. - -Anyone is free to copy, modify, publish, use, compile, sell, or -distribute this software, either in source code form or as a compiled -binary, for any purpose, commercial or non-commercial, and by any -means. - -In jurisdictions that recognize copyright laws, the author or authors -of this software dedicate any and all copyright interest in the -software to the public domain. We make this dedication for the benefit -of the public at large and to the detriment of our heirs and -successors. We intend this dedication to be an overt act of -relinquishment in perpetuity of all present and future rights to this -software under copyright law. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR -OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. - -For more information, please refer to - -================================ 0BSD ================================= - -Copyright (C) 2024 by Andrew Zolotuhkin - -Permission to use, copy, modify, and/or distribute this software for -any purpose with or without fee is hereby granted. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. \ No newline at end of file +BSD 3-Clause License + +Copyright (C) 2024 by Andrew Zolotuhkin + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md index bc1a71c9..50722066 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![License: BSD-3-Clause](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)](./LICENSE) [![Standard Schema v1](https://img.shields.io/badge/Standard%20Schema-v1-blue)](https://standardschema.dev/) -![Coverage](https://img.shields.io/badge/coverage-86.6%25-green) +![Unit coverage](https://img.shields.io/badge/unit_coverage-82.2%25-green) Cleverbrush is a schema-first TypeScript framework monorepo. It provides the @@ -48,10 +48,10 @@ JSON Schema, API contracts, and Standard Schema integrations. ```ts import { object, string, number, type InferType } from '@cleverbrush/schema'; -import { endpoint } from '@cleverbrush/server/contract'; +import { endpoint, route } from '@cleverbrush/server/contract'; const UserSchema = object({ - id: number().int().min(1), + id: number().isInteger().min(1), email: string().email(), displayName: string().minLength(2) }); @@ -59,8 +59,7 @@ const UserSchema = object({ type User = InferType; const GetUserEndpoint = endpoint - .get('/api/users/:id') - .params(object({ id: number().int().min(1) })) + .get('/api/users', route({ id: number().coerce().isInteger().min(1) })`/${p => p.id}`) .responses({ 200: UserSchema }); ``` @@ -90,7 +89,7 @@ ES modules. ## Development -Use Node.js 20 or newer. Node.js 22 is recommended. +Use Node.js 24 or newer and npm 11. Install the Node version in `.nvmrc`. ```bash npm ci @@ -120,6 +119,9 @@ demo workflow. ## Documentation +- [Migrating from v4.x to v5](./docs/MIGRATION-v5.md) +- [Security guidance](./SECURITY.md) + - Framework docs: https://docs.cleverbrush.com - Schema docs and playground: https://schema.cleverbrush.com - Standard Schema: https://standardschema.dev @@ -157,4 +159,6 @@ npm run publish:beta ## License -BSD-3-Clause. See [LICENSE](./LICENSE). +All Framework libraries are licensed under BSD-3-Clause. See [LICENSE](./LICENSE). +Each published package includes the same license text; `npm run test:packages` +verifies license text and metadata in both source packages and installed tarballs. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..12ac3966 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,32 @@ +# Security + +Report suspected vulnerabilities privately to `andrew_zol@cleverbrush.com`. +Include affected versions, impact and a minimal reproduction using synthetic data. +Do not publish credentials or real user data in issues, logs or pull requests. + +## Consumer responsibilities + +- Use supported Node.js 24+ runtimes and keep dependency lockfiles current. +- Authenticate and authorize before response-cache or idempotency middleware. + Include every tenant, identity and representation boundary in cache keys and + derive idempotency scopes only from verified identity. Keep client cache/dedupe + instances request/session scoped when used on a server. +- Configure JWT issuer, audience and an explicit permitted algorithm family; + store signing keys outside source control. OAuth/OIDC adapters describe schemes + and delegate validation to the configured callback; they do not implement a + complete login flow or validate tokens automatically. +- Set upload/body limits, authenticate upload endpoints and validate file contents + independently of client-supplied MIME types and filenames. +- Use query parameters for untrusted values. Raw SQL and migration/config modules + are trusted-code boundaries, not sandboxes. Review destructive migrations. +- Restrict CORS origins and configure cookie `Secure`, `HttpOnly`, `SameSite` and + CSRF protection to suit the application. Cookie authentication alone does not + provide CSRF protection. +- Do not include secrets in URLs. Review telemetry configuration before exporting + full URLs, SQL statements, headers or custom attributes to third parties. +- Use transactional deduplication for durable exactly-once business effects; + in-memory idempotency and job retries cannot guarantee them by themselves. + +The demo stack uses development credentials. Do not expose it publicly or reuse +those credentials in deployed applications. Dependency advisories must be reviewed +for reachability; passing an automated scan does not establish absence of defects. diff --git a/coverage-thresholds.json b/coverage-thresholds.json new file mode 100644 index 00000000..a4777d0f --- /dev/null +++ b/coverage-thresholds.json @@ -0,0 +1,134 @@ +{ + "async": { + "statements": 96, + "branches": 91, + "functions": 100, + "lines": 98 + }, + "auth": { + "statements": 92, + "branches": 89, + "functions": 100, + "lines": 92 + }, + "client": { + "statements": 83, + "branches": 79, + "functions": 89, + "lines": 84 + }, + "deep": { + "statements": 78, + "branches": 88, + "functions": 89, + "lines": 77 + }, + "di": { + "statements": 94, + "branches": 80, + "functions": 100, + "lines": 94 + }, + "env": { + "statements": 97, + "branches": 82, + "functions": 100, + "lines": 98 + }, + "knex-clickhouse": { + "statements": 100, + "branches": 95, + "functions": 100, + "lines": 100 + }, + "knex-schema": { + "statements": 63, + "branches": 59, + "functions": 66, + "lines": 64 + }, + "log": { + "statements": 98, + "branches": 92, + "functions": 94, + "lines": 98 + }, + "mapper": { + "statements": 93, + "branches": 83, + "functions": 93, + "lines": 97 + }, + "orm": { + "statements": 80, + "branches": 61, + "functions": 86, + "lines": 83 + }, + "orm-cli": { + "statements": 59, + "branches": 60, + "functions": 60, + "lines": 59 + }, + "otel": { + "statements": 88, + "branches": 75, + "functions": 94, + "lines": 90 + }, + "react-form": { + "statements": 95, + "branches": 89, + "functions": 94, + "lines": 96 + }, + "scheduler": { + "statements": 83, + "branches": 82, + "functions": 89, + "lines": 85 + }, + "scheduler-postgres": { + "statements": 11, + "branches": 21, + "functions": 2, + "lines": 11 + }, + "schema": { + "statements": 97, + "branches": 93, + "functions": 97, + "lines": 97 + }, + "schema-json": { + "statements": 96, + "branches": 93, + "functions": 100, + "lines": 97 + }, + "server": { + "statements": 81, + "branches": 78, + "functions": 85, + "lines": 82 + }, + "server-openapi": { + "statements": 96, + "branches": 86, + "functions": 100, + "lines": 97 + }, + "storage": { + "statements": 100, + "branches": 95, + "functions": 100, + "lines": 100 + }, + "storage-s3": { + "statements": 90, + "branches": 88, + "functions": 92, + "lines": 92 + } +} diff --git a/demos/e2e/package.json b/demos/e2e/package.json index c99f4ed5..9c98144b 100644 --- a/demos/e2e/package.json +++ b/demos/e2e/package.json @@ -15,13 +15,13 @@ "dependencies": { "pg": "^8.13.3", "playwright": "^1.49.1", - "ws": "^8.18.0" + "ws": "^8.22.0" }, "devDependencies": { "@types/node": "^22.0.0", "@types/pg": "^8.11.10", "@types/ws": "^8.5.13", "typescript": "^6.0.2", - "vitest": "^4.1.2" + "vitest": "^4.1.11" } } diff --git a/demos/e2e/src/setup/global-setup.ts b/demos/e2e/src/setup/global-setup.ts index 92d9d692..82d4987f 100644 --- a/demos/e2e/src/setup/global-setup.ts +++ b/demos/e2e/src/setup/global-setup.ts @@ -86,10 +86,8 @@ async function isReachable(url: string, timeoutMs = 1_500): Promise { /** Run pending DB migrations against the dockerized Postgres. */ async function runMigrations(): Promise { - // cb-orm currently keeps the knex pool open after migrations complete, - // so the process lingers ~30s. We implement a portable Node-based timeout - // and send SIGTERM after 60s; the migrations themselves are fully applied - // by then. + // The CLI closes its database pool. A timeout or signal must fail setup, + // never be mistaken for successfully applied migrations. await new Promise((resolve, reject) => { const child = spawn('npm', ['run', 'db:run'], { stdio: 'inherit', @@ -112,9 +110,8 @@ async function runMigrations(): Promise { }); child.on('exit', (code, signal) => { clearTimeout(timer); - // 0 = clean exit; SIGTERM = killed by our timer (still success) - if (code === 0 || signal === 'SIGTERM') resolve(); - else reject(new Error(`Migrations failed with exit code ${code}`)); + if (code === 0) resolve(); + else reject(new Error(`Migrations failed (${signal ?? code})`)); }); }); } diff --git a/demos/e2e/vitest.config.ts b/demos/e2e/vitest.config.ts index 2701d17b..6ac3aa10 100644 --- a/demos/e2e/vitest.config.ts +++ b/demos/e2e/vitest.config.ts @@ -35,7 +35,8 @@ export default defineConfig({ testTimeout: 90_000, hookTimeout: 180_000, pool: 'forks', - forks: { singleFork: true } + fileParallelism: false, + maxWorkers: 1 } } ] diff --git a/demos/todo-backend/Dockerfile b/demos/todo-backend/Dockerfile index 848534b4..fccd77ce 100644 --- a/demos/todo-backend/Dockerfile +++ b/demos/todo-backend/Dockerfile @@ -1,15 +1,17 @@ # ── Stage 1: builder ────────────────────────────────────────────────────────── # Build context must be the monorepo root -FROM node:22-alpine AS builder +FROM node:24-alpine AS builder WORKDIR /app # Copy root workspace manifest for npm workspaces resolution -COPY package.json package-lock.json* turbo.json tsconfig.json tsconfig.build.json ./ +COPY package.json package-lock.json turbo.json tsconfig.json tsconfig.build.json ./ -# Copy all workspace package.json files for dependency resolution +# Copy every workspace manifest so npm ci validates the complete lockfile. COPY libs/async/package.json ./libs/async/ COPY libs/auth/package.json ./libs/auth/ +COPY libs/benchmarks/package.json ./libs/benchmarks/ +COPY libs/client/package.json ./libs/client/ COPY libs/deep/package.json ./libs/deep/ COPY libs/di/package.json ./libs/di/ COPY libs/env/package.json ./libs/env/ @@ -17,22 +19,28 @@ COPY libs/knex-clickhouse/package.json ./libs/knex-clickhouse/ COPY libs/knex-schema/package.json ./libs/knex-schema/ COPY libs/log/package.json ./libs/log/ COPY libs/mapper/package.json ./libs/mapper/ +COPY libs/orm/package.json ./libs/orm/ +COPY libs/orm-cli/package.json ./libs/orm-cli/ +COPY libs/otel/package.json ./libs/otel/ COPY libs/react-form/package.json ./libs/react-form/ -COPY libs/schema/package.json ./libs/schema/ -COPY libs/schema-json/package.json ./libs/schema-json/ COPY libs/scheduler/package.json ./libs/scheduler/ COPY libs/scheduler-postgres/package.json ./libs/scheduler-postgres/ +COPY libs/schema/package.json ./libs/schema/ +COPY libs/schema-json/package.json ./libs/schema-json/ COPY libs/server/package.json ./libs/server/ +COPY libs/server-integration-tests/package.json ./libs/server-integration-tests/ COPY libs/server-openapi/package.json ./libs/server-openapi/ -COPY libs/otel/package.json ./libs/otel/ -COPY libs/client/package.json ./libs/client/ -COPY libs/benchmarks/package.json ./libs/benchmarks/ -COPY libs/orm/package.json ./libs/orm/ -COPY libs/orm-cli/package.json ./libs/orm-cli/ +COPY libs/storage/package.json ./libs/storage/ +COPY libs/storage-s3/package.json ./libs/storage-s3/ +COPY demos/e2e/package.json ./demos/e2e/ COPY demos/todo-backend/package.json ./demos/todo-backend/ +COPY demos/todo-frontend/package.json ./demos/todo-frontend/ +COPY websites/docs/package.json ./websites/docs/ +COPY websites/schema/package.json ./websites/schema/ +COPY websites/shared/package.json ./websites/shared/ # Install all workspace dependencies -RUN npm install --ignore-scripts +RUN npm ci --ignore-scripts # Copy all lib sources (needed for workspace build) COPY libs/ ./libs/ @@ -51,7 +59,7 @@ RUN npx turbo run build --filter=@cleverbrush/todo-backend RUN cp -rL /app/node_modules /app/runtime_modules && \ rm -rf /app/runtime_modules/@cleverbrush # ── Stage 2: runtime ────────────────────────────────────────────────────────── -FROM node:22-alpine AS runtime +FROM node:24-alpine AS runtime LABEL org.opencontainers.image.title="ToDo Management API" LABEL org.opencontainers.image.description="Production-ready ToDo REST API built with @cleverbrush/* framework" diff --git a/demos/todo-backend/package.json b/demos/todo-backend/package.json index 13900022..de9b4977 100644 --- a/demos/todo-backend/package.json +++ b/demos/todo-backend/package.json @@ -19,7 +19,7 @@ "@cleverbrush/schema-json": "*", "@cleverbrush/server": "*", "@cleverbrush/server-openapi": "*", - "@opentelemetry/instrumentation-http": "^0.215.0", + "@opentelemetry/instrumentation-http": "^0.222.0", "@opentelemetry/instrumentation-runtime-node": "^0.28.0", "@opentelemetry/instrumentation-undici": "^0.25.0", "google-auth-library": "^9.15.0", @@ -30,7 +30,7 @@ "files": [ "dist" ], - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "main": "./dist/index.js", "exports": { ".": { diff --git a/demos/todo-frontend/Dockerfile b/demos/todo-frontend/Dockerfile index d0dc2f8c..0e67dc2f 100644 --- a/demos/todo-frontend/Dockerfile +++ b/demos/todo-frontend/Dockerfile @@ -1,39 +1,46 @@ # ── Stage 1: builder ────────────────────────────────────────────────────────── # Build context must be the monorepo root -FROM node:22-alpine AS builder +FROM node:24-alpine AS builder WORKDIR /app # Copy root workspace manifest for dependency resolution -COPY package.json package-lock.json* turbo.json tsconfig.build.json ./ +COPY package.json package-lock.json turbo.json tsconfig.json tsconfig.build.json ./ -# Copy all workspace package.json files for dependency resolution +# Copy every workspace manifest so npm ci validates the complete lockfile. COPY libs/async/package.json ./libs/async/ COPY libs/auth/package.json ./libs/auth/ +COPY libs/benchmarks/package.json ./libs/benchmarks/ +COPY libs/client/package.json ./libs/client/ COPY libs/deep/package.json ./libs/deep/ COPY libs/di/package.json ./libs/di/ COPY libs/env/package.json ./libs/env/ COPY libs/knex-clickhouse/package.json ./libs/knex-clickhouse/ COPY libs/knex-schema/package.json ./libs/knex-schema/ +COPY libs/log/package.json ./libs/log/ COPY libs/mapper/package.json ./libs/mapper/ +COPY libs/orm/package.json ./libs/orm/ +COPY libs/orm-cli/package.json ./libs/orm-cli/ +COPY libs/otel/package.json ./libs/otel/ COPY libs/react-form/package.json ./libs/react-form/ -COPY libs/schema/package.json ./libs/schema/ -COPY libs/schema-json/package.json ./libs/schema-json/ COPY libs/scheduler/package.json ./libs/scheduler/ COPY libs/scheduler-postgres/package.json ./libs/scheduler-postgres/ +COPY libs/schema/package.json ./libs/schema/ +COPY libs/schema-json/package.json ./libs/schema-json/ COPY libs/server/package.json ./libs/server/ +COPY libs/server-integration-tests/package.json ./libs/server-integration-tests/ COPY libs/server-openapi/package.json ./libs/server-openapi/ -COPY libs/client/package.json ./libs/client/ -COPY libs/benchmarks/package.json ./libs/benchmarks/ -COPY libs/log/package.json ./libs/log/ -COPY libs/otel/package.json ./libs/otel/ -COPY libs/orm/package.json ./libs/orm/ -COPY libs/orm-cli/package.json ./libs/orm-cli/ +COPY libs/storage/package.json ./libs/storage/ +COPY libs/storage-s3/package.json ./libs/storage-s3/ +COPY demos/e2e/package.json ./demos/e2e/ COPY demos/todo-backend/package.json ./demos/todo-backend/ COPY demos/todo-frontend/package.json ./demos/todo-frontend/ +COPY websites/docs/package.json ./websites/docs/ +COPY websites/schema/package.json ./websites/schema/ +COPY websites/shared/package.json ./websites/shared/ # Install workspace dependencies -RUN npm install --ignore-scripts +RUN npm ci --ignore-scripts # Copy all lib sources (needed for workspace build) COPY libs/ ./libs/ diff --git a/demos/todo-frontend/package.json b/demos/todo-frontend/package.json index 88daf7e4..28e74a9e 100644 --- a/demos/todo-frontend/package.json +++ b/demos/todo-frontend/package.json @@ -21,13 +21,13 @@ "@react-oauth/google": "^0.12.1", "react": "^19.0.0", "react-dom": "^19.0.0", - "react-router": "^7.5.3" + "react-router": "^7.18.2" }, "devDependencies": { "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", "@vitejs/plugin-react": "^4.3.4", "typescript": "^6.0.2", - "vite": "^6.3.3" + "vite": "^6.4.3" } } diff --git a/docs/MIGRATION-v5.md b/docs/MIGRATION-v5.md new file mode 100644 index 00000000..e2c2f55c --- /dev/null +++ b/docs/MIGRATION-v5.md @@ -0,0 +1,86 @@ +# Migrating from Framework v4.x to v5 + +Upgrade the fixed-version `@cleverbrush/*` package group together. Framework is +application-agnostic; authentication scope, authorization policy and deployment +remain application responsibilities. + +## Runtime and packaging + +Node-based consumers and development tooling require Node.js 24+. Browser entry +points remain browser-safe; this does not introduce Node dependencies into them. +Use npm 11 for this repository. Regenerate consumer lockfiles when upgrading so +patched HTTP, multipart and telemetry dependencies are resolved. + +## Existing migration guides + +- [Immutable schemas and query builders, projections and read/write boundaries](../libs/knex-schema/MIGRATION-v5.md) +- [Durable jobs and recurring schedules](../libs/scheduler/MIGRATION-v5.md) +- [Cache key formats, equality and React form lifecycle](cache-form-migration.md) + +Current package READMEs describe the supported APIs, including connection-independent +query definitions and modular contract implementations. + +## Authentication and cookies + +JWT verification now rejects non-object headers/payloads, invalid registered claim +types, unsupported critical JOSE extensions and expiry at the exact expiration +instant. Clock tolerance must be finite and non-negative. Configure one supported +key family per scheme: RSA public/private keys cannot serve as HMAC secrets. +Configure separate schemes for independently keyed algorithm families. + +Cookie parsing returns a null-prototype dictionary. Use `Object.hasOwn()` rather +than inherited methods; the first duplicate wins. Malformed percent escapes remain +literal and do not prevent other cookies from being parsed. Serialization rejects +attribute injection, invalid expiry dates and non-integer `maxAge` values. Cookie +paths remain omitted unless explicitly configured. + +## Server idempotency + +The server middleware now requires an explicit `scope` callback. Install it after +authentication and authorization and derive the scope from verified identity and +tenant context. Returning `undefined` bypasses replay. Use a constant only for an +intentionally public operation: + +```ts +import { idempotency } from '@cleverbrush/server'; + +const replay = idempotency({ + scope: () => 'public-operation', + ttl: 60_000 +}); +``` + +Keys also include the HTTP method and full request URL. Concurrent identical keys +share one execution. Reusing a key asserts the same input; bodies are not compared. +The store is process-local, not durable exactly-once execution. Restart, expiry or +a thrown handler failure can allow a later execution; use database deduplication +for durable business guarantees. + +The default bounds are 1,000 retained/pending keys and 65,536 response-body bytes per +key (`maxEntries`, `maxResponseBytes`). Capacity exhaustion returns 503 without +running the handler. Oversized/incomplete responses keep a reservation: retries +return 409 instead of repeating a potentially successful mutation. Completed HTTP +responses, including errors, are replayed until expiry; thrown failures are not. + +## Response caching + +Replay preserves status, header overloads, `setHeader()` headers and all chunks +written through `write()`/`end()`. Hooks are restored even when handlers throw. +The response cache retains at most 1000 keys (evicting oldest keys) and skips +incomplete responses, bodies over 65,536 bytes, +`Set-Cookie`, and `Cache-Control: private`/`no-store` responses. Cache tags must +still distinguish every response shape and authorization/tenant scope. +Configure `maxEntries` and `maxResponseBytes` to adjust these bounds. TTL values +must be finite and non-negative. Retention uses the longest TTL among an endpoint's +tags; all zero disables retention. + +## Dependency injection and request context + +Optional service resolution returns `undefined` only for unregistered services. +Factory failures, circular dependencies and scope violations now propagate even +through factory proxies. Root factories respect `validateScopes`; resolve scoped +dependencies inside an explicit scope rather than relying on an implicit cache. + +`IRequestContext.url` is typed as the actual `URL` object, not a string. Concurrent +body readers share the full buffered body or the same error. Aborted reads reject +instead of hanging. Query/header dictionaries use own properties without a prototype. diff --git a/docs/framework-feature-candidates.md b/docs/framework-feature-candidates.md deleted file mode 100644 index 7236eeb0..00000000 --- a/docs/framework-feature-candidates.md +++ /dev/null @@ -1,12 +0,0 @@ -# Framework feature candidates - -Assessment date: 2026-10-03. - -This file lists outstanding reusable Framework capabilities and correctness -fixes. Completed candidates are removed; their identifiers are not reused. -There are currently no outstanding candidates recorded here. The next candidate -identifier is F09. - -Supported query APIs are documented in the -[query guide](../libs/knex-schema/README.md#parameterized-compiled-queries) and -[ORM guide](../libs/orm/README.md#parameterized-compiled-reads). diff --git a/libs/async/LICENSE b/libs/async/LICENSE new file mode 100644 index 00000000..053547c1 --- /dev/null +++ b/libs/async/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (C) 2024 by Andrew Zolotuhkin + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libs/async/README.md b/libs/async/README.md index 522117c5..b9f43e86 100644 --- a/libs/async/README.md +++ b/libs/async/README.md @@ -3,7 +3,7 @@ [![CI](https://github.com/cleverbrush/framework/actions/workflows/ci.yml/badge.svg)](https://github.com/cleverbrush/framework/actions/workflows/ci.yml) [![License: BSD-3-Clause](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)](../../LICENSE) -![Coverage](https://img.shields.io/badge/coverage-96.8%25-brightgreen) +![Unit coverage](https://img.shields.io/badge/unit_coverage-96.8%25-brightgreen) A set of simple utilities for working with asynchronous operations. diff --git a/libs/async/package.json b/libs/async/package.json index ccaa8cc6..105a032b 100644 --- a/libs/async/package.json +++ b/libs/async/package.json @@ -13,7 +13,7 @@ "async utils", "cleverbrush" ], - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "main": "./dist/index.js", "exports": { ".": { @@ -23,6 +23,9 @@ }, "sideEffects": false, "name": "@cleverbrush/async", + "engines": { + "node": ">=24" + }, "readme": "https://github.com/cleverbrush/framework/tree/master/libs/async#readme", "repository": { "type": "git", diff --git a/libs/auth/LICENSE b/libs/auth/LICENSE new file mode 100644 index 00000000..053547c1 --- /dev/null +++ b/libs/auth/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (C) 2024 by Andrew Zolotuhkin + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libs/auth/README.md b/libs/auth/README.md index 7c50d2cc..6ea2c6bd 100644 --- a/libs/auth/README.md +++ b/libs/auth/README.md @@ -2,6 +2,9 @@ [![CI](https://github.com/cleverbrush/framework/actions/workflows/ci.yml/badge.svg)](https://github.com/cleverbrush/framework/actions/workflows/ci.yml) [![License: BSD-3-Clause](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)](../../LICENSE) + +![Unit coverage](https://img.shields.io/badge/unit_coverage-92.5%25-brightgreen) + Transport-agnostic authentication and authorization for TypeScript. Ships with JWT and cookie schemes, a fluent policy builder, and a typed `Principal` value object. Designed for [`@cleverbrush/server`](../server) but usable in any Node.js context. @@ -58,7 +61,11 @@ if (result.succeeded) { import { jwtScheme, signJwt } from '@cleverbrush/auth'; // Sign a token (for testing or token issuance) -const token = signJwt({ sub: 'user-1', role: 'admin' }, 'my-secret'); +const token = signJwt({ + sub: 'user-1', role: 'admin', + iss: 'https://my-app.com', aud: 'my-api', + exp: Math.floor(Date.now() / 1000) + 3600 +}, 'my-secret'); // Verify and authenticate const scheme = jwtScheme({ @@ -74,6 +81,16 @@ const scheme = jwtScheme({ Supported algorithms: `HS256`, `HS384`, `HS512`, `RS256`, `RS384`, `RS512`. +Configure a single key family per scheme: HMAC secrets and RSA keys must never +share an algorithm allowlist. RSA requires an RSA PEM key; HMAC rejects empty +secrets and PEM key material. Use a cryptographically random secret from secure +configuration, not the illustrative value above. Unsupported critical JOSE +extensions, malformed claim types and tokens at their expiration instant are +rejected. Clock tolerance must be finite and non-negative. + +See [security guidance](../../SECURITY.md) and the +[v4.x-to-v5 migration guide](../../docs/MIGRATION-v5.md). + ## Cookie Authentication ```ts diff --git a/libs/auth/package.json b/libs/auth/package.json index 674508cb..5f48cf85 100644 --- a/libs/auth/package.json +++ b/libs/auth/package.json @@ -21,7 +21,7 @@ "typescript", "cleverbrush" ], - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "main": "./dist/index.js", "exports": { ".": { @@ -31,6 +31,9 @@ }, "sideEffects": false, "name": "@cleverbrush/auth", + "engines": { + "node": ">=24" + }, "readme": "https://github.com/cleverbrush/framework/tree/master/libs/auth#readme", "repository": { "type": "git", diff --git a/libs/auth/src/CookieScheme.ts b/libs/auth/src/CookieScheme.ts index b8f0701a..f48aca43 100644 --- a/libs/auth/src/CookieScheme.ts +++ b/libs/auth/src/CookieScheme.ts @@ -47,8 +47,12 @@ class CookieAuthenticationScheme implements AuthenticationScheme { context: AuthenticationContext ): Promise> { // Try pre-parsed cookies first, fall back to header parsing - let cookieValue: string | undefined = - context.cookies[this.#options.cookieName]; + let cookieValue: string | undefined = Object.hasOwn( + context.cookies, + this.#options.cookieName + ) + ? context.cookies[this.#options.cookieName] + : undefined; if (cookieValue === undefined) { const cookieHeader = context.headers['cookie']; @@ -58,7 +62,7 @@ class CookieAuthenticationScheme implements AuthenticationScheme { } } - if (cookieValue === undefined || cookieValue === '') { + if (typeof cookieValue !== 'string' || cookieValue === '') { return { succeeded: false, failure: `Cookie "${this.#options.cookieName}" not found` diff --git a/libs/auth/src/JwtScheme.test.ts b/libs/auth/src/JwtScheme.test.ts index c494b2ea..e227d134 100644 --- a/libs/auth/src/JwtScheme.test.ts +++ b/libs/auth/src/JwtScheme.test.ts @@ -1,5 +1,6 @@ // Supplementary tests for JwtScheme — focused on uncovered branches. +import { createHmac, generateKeyPairSync } from 'node:crypto'; import { describe, expect, it } from 'vitest'; import { jwtScheme, signJwt } from './JwtScheme.js'; @@ -15,6 +16,97 @@ const ctx = (authHeader?: string) => ({ }); describe('JwtScheme — additional branch coverage', () => { + const rawToken = (header: unknown, payload: unknown) => { + const data = [header, payload] + .map(value => + Buffer.from(JSON.stringify(value)).toString('base64url') + ) + .join('.'); + return `${data}.${createHmac('sha256', SECRET).update(data).digest('base64url')}`; + }; + + it.each([null, [], 'header', 1])( + 'rejects non-object header %j without throwing', + async header => { + await expect( + scheme().authenticate(ctx(`Bearer ${rawToken(header, {})}`)) + ).resolves.toMatchObject({ succeeded: false }); + } + ); + + it.each([null, [], 'payload', 1])( + 'rejects non-object payload %j without throwing', + async payload => { + await expect( + scheme().authenticate( + ctx(`Bearer ${rawToken({ alg: 'HS256' }, payload)}`) + ) + ).resolves.toMatchObject({ succeeded: false }); + } + ); + + it.each(['exp', 'nbf', 'iat'])( + 'rejects malformed %s claims', + async name => { + for (const value of ['9999999999', null, {}, []]) { + const token = rawToken({ alg: 'HS256' }, { [name]: value }); + expect( + (await scheme().authenticate(ctx(`Bearer ${token}`))) + .succeeded + ).toBe(false); + } + } + ); + + it('expires at the expiration instant, including tolerance', async () => { + const now = Math.floor(Date.now() / 1000); + for (const tolerance of [0, 60]) { + const token = signJwt({ exp: now - tolerance }, SECRET); + expect( + ( + await scheme({ clockTolerance: tolerance }).authenticate( + ctx(`Bearer ${token}`) + ) + ).succeeded + ).toBe(false); + } + }); + + it('does not ignore critical JOSE extensions', async () => { + const token = rawToken( + { alg: 'HS256', crit: ['unknown'], unknown: true }, + {} + ); + expect( + (await scheme().authenticate(ctx(`Bearer ${token}`))).succeeded + ).toBe(false); + }); + + it('rejects asymmetric key material as an HMAC secret', () => { + const { publicKey } = generateKeyPairSync('rsa', { + modulusLength: 2048 + }); + const pem = publicKey.export({ type: 'spki', format: 'pem' }); + expect(() => + scheme({ secret: pem, algorithms: ['HS256', 'RS256'] }) + ).toThrow(); + expect(() => signJwt({}, pem, 'HS256')).toThrow(); + }); + + it.each([-1, NaN, Infinity])( + 'rejects invalid clock tolerance %s', + clockTolerance => { + expect(() => scheme({ clockTolerance })).toThrow(); + } + ); + + it('accepts case-insensitive bearer authentication', async () => { + const token = signJwt({ sub: 'u1' }, SECRET); + expect( + (await scheme().authenticate(ctx(`bearer ${token}`))).succeeded + ).toBe(true); + }); + it('rejects an empty Bearer token', async () => { const result = await scheme().authenticate(ctx('Bearer ')); expect(result.succeeded).toBe(false); diff --git a/libs/auth/src/JwtScheme.ts b/libs/auth/src/JwtScheme.ts index f865d631..5b98b4f9 100644 --- a/libs/auth/src/JwtScheme.ts +++ b/libs/auth/src/JwtScheme.ts @@ -48,7 +48,7 @@ export interface JwtSchemeOptions { secret: string | Buffer; /** Map raw JWT claims to the typed principal value. */ mapClaims: (claims: JwtPayload) => T; - /** Allowed algorithms (default: ['HS256']). */ + /** Allowed algorithms (default: ['HS256']). Do not mix HMAC and RSA. */ algorithms?: string[]; /** Expected issuer — if set, `iss` must match. */ issuer?: string; @@ -89,6 +89,39 @@ const RSA_ALGOS: Record = { RS512: 'sha512' }; +function assertKey(secret: string | Buffer, algorithms: readonly string[]) { + const hmac = algorithms.some(alg => Object.hasOwn(HMAC_ALGOS, alg)); + const rsa = algorithms.some(alg => Object.hasOwn(RSA_ALGOS, alg)); + if ( + algorithms.length === 0 || + algorithms.some( + alg => + !Object.hasOwn(HMAC_ALGOS, alg) && + !Object.hasOwn(RSA_ALGOS, alg) + ) || + (hmac && rsa) + ) + throw new TypeError('Select supported algorithms from one key family'); + if (hmac) { + if ( + !secret.length || + /-----BEGIN [^-]*KEY-----/.test(secret.toString()) + ) { + throw new TypeError( + 'HMAC requires a non-empty symmetric secret, not a PEM key' + ); + } + } else { + const key = crypto.createPublicKey(secret); + if (key.asymmetricKeyType !== 'rsa') + throw new TypeError('RS algorithms require an RSA key'); + } +} + +function isObject(value: unknown): value is Record { + return value !== null && typeof value === 'object' && !Array.isArray(value); +} + // --------------------------------------------------------------------------- // JWT verification // --------------------------------------------------------------------------- @@ -103,7 +136,9 @@ function verifySignature( const data = `${headerB64}.${payloadB64}`; const signatureBytes = base64urlDecode(signatureB64); - const hmacHash = HMAC_ALGOS[alg]; + const hmacHash = Object.hasOwn(HMAC_ALGOS, alg) + ? HMAC_ALGOS[alg] + : undefined; if (hmacHash) { const expected = crypto .createHmac(hmacHash, secret) @@ -115,7 +150,7 @@ function verifySignature( return crypto.timingSafeEqual(expected, signatureBytes); } - const rsaHash = RSA_ALGOS[alg]; + const rsaHash = Object.hasOwn(RSA_ALGOS, alg) ? RSA_ALGOS[alg] : undefined; if (rsaHash) { return crypto .createVerify(rsaHash) @@ -151,19 +186,36 @@ function decodeJwt( return { error: 'Malformed JWT header' }; } + if (!isObject(header) || typeof header.alg !== 'string') { + return { error: 'Malformed JWT header' }; + } + if ( + header.crit !== undefined || + (header.b64 !== undefined && header.b64 !== true) + ) { + return { error: 'Unsupported critical JWT extension' }; + } + if (!allowedAlgorithms.includes(header.alg)) { return { error: `Unsupported algorithm: ${header.alg}` }; } - if ( - !verifySignature( + if (!parts.every(part => /^[A-Za-z0-9_-]+$/.test(part))) { + return { error: 'Malformed JWT encoding' }; + } + let validSignature = false; + try { + validSignature = verifySignature( headerB64, payloadB64, signatureB64, header.alg, secret - ) - ) { + ); + } catch { + // Malformed signatures fail authentication, not the HTTP request. + } + if (!validSignature) { return { error: 'Invalid signature' }; } @@ -174,9 +226,38 @@ function decodeJwt( return { error: 'Malformed JWT payload' }; } + if (!isObject(payload)) return { error: 'Malformed JWT payload' }; + for (const claim of ['exp', 'nbf', 'iat']) { + const value = payload[claim]; + if ( + value !== undefined && + (typeof value !== 'number' || !Number.isFinite(value)) + ) { + return { error: `Invalid ${claim} claim` }; + } + } + for (const claim of ['iss', 'sub', 'jti']) { + if ( + payload[claim] !== undefined && + typeof payload[claim] !== 'string' + ) { + return { error: `Invalid ${claim} claim` }; + } + } + if ( + payload.aud !== undefined && + typeof payload.aud !== 'string' && + !( + Array.isArray(payload.aud) && + payload.aud.every(value => typeof value === 'string') + ) + ) { + return { error: 'Invalid aud claim' }; + } + const now = Math.floor(Date.now() / 1000); - if (payload.exp !== undefined && now > payload.exp + clockTolerance) { + if (payload.exp !== undefined && now >= payload.exp + clockTolerance) { return { error: 'Token expired' }; } @@ -209,9 +290,23 @@ class JwtAuthenticationScheme implements AuthenticationScheme { readonly #algorithms: string[]; constructor(options: JwtSchemeOptions) { + if ( + !Number.isFinite(options.clockTolerance ?? 0) || + (options.clockTolerance ?? 0) < 0 + ) { + throw new TypeError( + 'clockTolerance must be finite and non-negative' + ); + } this.name = options.name ?? 'jwt'; - this.#options = options; - this.#algorithms = options.algorithms ?? ['HS256']; + this.#options = { + ...options, + secret: Buffer.isBuffer(options.secret) + ? Buffer.from(options.secret) + : options.secret + }; + this.#algorithms = [...(options.algorithms ?? ['HS256'])]; + assertKey(this.#options.secret, this.#algorithms); } async authenticate( @@ -225,7 +320,7 @@ class JwtAuthenticationScheme implements AuthenticationScheme { }; } - if (!authHeader.startsWith('Bearer ')) { + if (!/^Bearer /i.test(authHeader)) { return { succeeded: false, failure: 'Authorization header must use Bearer scheme' @@ -278,7 +373,9 @@ class JwtAuthenticationScheme implements AuthenticationScheme { } /** - * Create a JWT authentication scheme. + * Create a JWT authentication scheme. Invalid token structures and claims fail + * authentication. Unsupported algorithms, mixed key families and invalid + * clock tolerance are rejected at configuration time. */ export function jwtScheme( options: JwtSchemeOptions @@ -307,6 +404,13 @@ export function signJwt( secret: string | Buffer, algorithm = 'HS256' ): string { + if ( + !Object.hasOwn(HMAC_ALGOS, algorithm) && + !Object.hasOwn(RSA_ALGOS, algorithm) + ) { + throw new TypeError(`Unsupported algorithm: ${algorithm}`); + } + assertKey(secret, [algorithm]); const header: JwtHeader = { alg: algorithm, typ: 'JWT' }; const headerB64 = base64urlEncode( diff --git a/libs/auth/src/cookies.test.ts b/libs/auth/src/cookies.test.ts new file mode 100644 index 00000000..26d5440e --- /dev/null +++ b/libs/auth/src/cookies.test.ts @@ -0,0 +1,69 @@ +import { describe, expect, it, vi } from 'vitest'; +import { cookieScheme } from './CookieScheme.js'; +import { parseCookies, serializeCookie } from './cookies.js'; + +describe('cookie parsing boundaries', () => { + it('retains malformed escapes without breaking unrelated cookies', () => { + expect(parseCookies('bad=%E0%A4%A; sid=valid')).toEqual({ + bad: '%E0%A4%A', + sid: 'valid' + }); + }); + + it('uses own keys, including prototype names, and the first duplicate', () => { + const cookies = parseCookies( + '__proto__=a; constructor=b; sid=first; sid=second' + ); + expect(Object.getPrototypeOf(cookies)).toBeNull(); + expect( + Object.getOwnPropertyDescriptor(cookies, '__proto__')?.value + ).toBe('a'); + expect(cookies.constructor).toBe('b'); + expect(cookies.sid).toBe('first'); + }); + + it('does not authenticate inherited cookie properties', async () => { + const validate = vi.fn(async () => ({})); + const scheme = cookieScheme({ cookieName: 'constructor', validate }); + expect( + await scheme.authenticate({ + headers: {}, + cookies: {}, + items: new Map() + }) + ).toMatchObject({ succeeded: false }); + expect(validate).not.toHaveBeenCalled(); + }); +}); + +describe('cookie serialization boundaries', () => { + it.each(['path', 'domain'])( + 'rejects header and attribute injection through %s', + key => { + for (const value of [ + '/; Secure', + 'host\r\nX-Injected: yes', + 'host\u0000' + ]) { + expect(() => + serializeCookie('sid', 'v', { [key]: value }) + ).toThrow(TypeError); + } + } + ); + + it.each([NaN, Infinity, 1.5])('rejects invalid maxAge %s', maxAge => { + expect(() => serializeCookie('sid', 'v', { maxAge })).toThrow( + TypeError + ); + }); + + it('rejects invalid dates and retains deliberate cookie deletion', () => { + expect(() => + serializeCookie('sid', 'v', { expires: new Date(NaN) }) + ).toThrow(TypeError); + expect(serializeCookie('sid', '', { maxAge: -1 })).toContain( + 'Max-Age=-1' + ); + }); +}); diff --git a/libs/auth/src/cookies.ts b/libs/auth/src/cookies.ts index 91a056d9..d498ec07 100644 --- a/libs/auth/src/cookies.ts +++ b/libs/auth/src/cookies.ts @@ -3,12 +3,13 @@ // --------------------------------------------------------------------------- /** - * Parse a `Cookie` header string into a Record. + * Parse a `Cookie` header into a null-prototype record. First duplicate wins; + * malformed percent escapes are retained verbatim instead of throwing. * * @example parseCookies('name1=val1; name2=val2') → { name1: 'val1', name2: 'val2' } */ export function parseCookies(header: string): Record { - const cookies: Record = {}; + const cookies: Record = Object.create(null); if (!header) return cookies; const pairs = header.split(';'); @@ -17,8 +18,12 @@ export function parseCookies(header: string): Record { if (idx < 0) continue; const key = pair.slice(0, idx).trim(); const value = pair.slice(idx + 1).trim(); - if (key.length > 0) { - cookies[key] = decodeURIComponent(value); + if (key.length > 0 && !Object.hasOwn(cookies, key)) { + try { + cookies[key] = decodeURIComponent(value); + } catch { + cookies[key] = value; + } } } return cookies; @@ -37,7 +42,7 @@ export interface CookieOptions { maxAge?: number; /** Absolute expiry date. */ expires?: Date; - /** Cookie path (default "/"). */ + /** Cookie path. Omitted by default (the browser uses the request path). */ path?: string; /** Cookie domain. */ domain?: string; @@ -51,12 +56,40 @@ export interface CookieOptions { /** * Serialize a `Set-Cookie` header value. + * @throws {TypeError} For invalid expiry/maxAge or unsafe attribute values. */ export function serializeCookie( name: string, value: string, options?: CookieOptions ): string { + for (const key of ['path', 'domain'] as const) { + const attribute = options?.[key]; + if ( + attribute !== undefined && + !/^[\x20-\x3A\x3C-\x7E]*$/.test(attribute) + ) { + throw new TypeError(`Invalid cookie ${key}`); + } + } + if ( + options?.maxAge !== undefined && + !Number.isSafeInteger(options.maxAge) + ) { + throw new TypeError('Cookie maxAge must be a safe integer'); + } + if ( + options?.expires !== undefined && + !Number.isFinite(options.expires.getTime()) + ) { + throw new TypeError('Invalid cookie expiry'); + } + if ( + options?.sameSite !== undefined && + !['Strict', 'Lax', 'None'].includes(options.sameSite) + ) { + throw new TypeError('Invalid cookie sameSite'); + } let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`; if (options?.maxAge !== undefined) { diff --git a/libs/benchmarks/package.json b/libs/benchmarks/package.json index 48f2205c..7196cc04 100644 --- a/libs/benchmarks/package.json +++ b/libs/benchmarks/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@cleverbrush/schema": "^4.0.0", - "joi": "^17.13.3", + "joi": "^17.13.7", "yup": "^1.6.1", "zod": "^3.24.4" } diff --git a/libs/client/LICENSE b/libs/client/LICENSE new file mode 100644 index 00000000..053547c1 --- /dev/null +++ b/libs/client/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (C) 2024 by Andrew Zolotuhkin + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libs/client/README.md b/libs/client/README.md index 4eb317ae..9591c604 100644 --- a/libs/client/README.md +++ b/libs/client/README.md @@ -1,4 +1,7 @@ # @cleverbrush/client + +![Unit coverage](https://img.shields.io/badge/unit_coverage-84%25-green) + Typed HTTP client for `@cleverbrush/server` API contracts — zero codegen, full type safety. Optional React + TanStack Query integration via `@cleverbrush/client/react`. diff --git a/libs/client/package.json b/libs/client/package.json index 29c0e05e..b2191cd6 100644 --- a/libs/client/package.json +++ b/libs/client/package.json @@ -44,7 +44,7 @@ "hooks", "cleverbrush" ], - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "main": "./dist/index.js", "exports": { ".": { @@ -90,6 +90,9 @@ }, "sideEffects": false, "name": "@cleverbrush/client", + "engines": { + "node": ">=24" + }, "readme": "https://github.com/cleverbrush/framework/tree/master/libs/client#readme", "repository": { "type": "git", diff --git a/libs/client/src/client.ts b/libs/client/src/client.ts index 674aac32..bb0bd4f7 100644 --- a/libs/client/src/client.ts +++ b/libs/client/src/client.ts @@ -125,7 +125,7 @@ async function wrapAndRunBeforeError( * @example * ```ts * import { api } from 'todo-shared'; - * import { createClient } from '@cleverbrush/web'; + * import { createClient } from '@cleverbrush/client'; * * const client = createClient(api, { * baseUrl: 'https://api.example.com', diff --git a/libs/client/src/errors.ts b/libs/client/src/errors.ts index 9f618578..758d2785 100644 --- a/libs/client/src/errors.ts +++ b/libs/client/src/errors.ts @@ -1,5 +1,5 @@ /** - * Base error class for all errors thrown by the `@cleverbrush/web` client. + * Base error class for all errors thrown by the `@cleverbrush/client` client. * * Provides a common prototype chain so consumers can catch *any* client * error with a single `instanceof WebError` check. diff --git a/libs/client/src/middleware.ts b/libs/client/src/middleware.ts index 2fbae2f9..d9b3871a 100644 --- a/libs/client/src/middleware.ts +++ b/libs/client/src/middleware.ts @@ -1,5 +1,5 @@ /** - * Middleware composition system for the `@cleverbrush/web` typed HTTP client. + * Middleware composition system for the `@cleverbrush/client` typed HTTP client. * * A middleware wraps a {@link FetchLike} function, allowing it to inspect, * modify, or short-circuit requests and responses. Middlewares are composed @@ -7,7 +7,7 @@ * * @example * ```ts - * import { composeMiddleware, type Middleware } from '@cleverbrush/web'; + * import { composeMiddleware, type Middleware } from '@cleverbrush/client'; * * const logger: Middleware = (next) => async (url, init) => { * console.log('→', init.method, url); @@ -99,7 +99,7 @@ export function composeMiddleware( * ``` */ export const PER_CALL_OPTIONS: unique symbol = Symbol.for( - '@cleverbrush/web:per-call-options' + '@cleverbrush/client:per-call-options' ); /** @@ -127,7 +127,7 @@ export function getPerCallOptions( * access to the endpoint's structural info plus the actual call arguments * without any URL parsing or regex. * - * Used by {@link throttlingCache} for cache-invalidation callbacks. + * Used by `throttlingCache` for cache-invalidation callbacks. */ export interface EndpointMeta { /** Contract group name, e.g. `"todos"`. */ diff --git a/libs/client/src/middlewares/cache.ts b/libs/client/src/middlewares/cache.ts index 5ab26bc3..94d2c54f 100644 --- a/libs/client/src/middlewares/cache.ts +++ b/libs/client/src/middlewares/cache.ts @@ -1,5 +1,5 @@ /** - * Throttling cache middleware for the `@cleverbrush/web` client. + * Throttling cache middleware for the `@cleverbrush/client` client. * * Caches successful GET responses for a configurable TTL, avoiding * redundant network requests. Mutating requests (POST, PUT, DELETE, PATCH) @@ -7,8 +7,8 @@ * * @example * ```ts - * import { createClient } from '@cleverbrush/web'; - * import { throttlingCache } from '@cleverbrush/web/cache'; + * import { createClient } from '@cleverbrush/client'; + * import { throttlingCache } from '@cleverbrush/client/cache'; * * const client = createClient(api, { * middlewares: [throttlingCache({ throttle: 2000 })], diff --git a/libs/client/src/middlewares/cacheTags.test.ts b/libs/client/src/middlewares/cacheTags.test.ts index 9c905d85..556aabca 100644 --- a/libs/client/src/middlewares/cacheTags.test.ts +++ b/libs/client/src/middlewares/cacheTags.test.ts @@ -506,20 +506,23 @@ describe('cache invalidation races', () => { }); return { promise, resolve }; } - test.each([ - 400, 500 - ])('failed mutation %s preserves cached reads', async status => { - const fetch = vi - .fn() - .mockResolvedValueOnce(new Response('old')) - .mockResolvedValueOnce(new Response('failed', { status })); - const mw = cacheTags({ defaultTtl: 5000 })(fetch); - const meta = makeTagMeta([tag()]); - await mw('/records', makeInit(meta)); - await mw('/records', makeInit(meta, 'PATCH')); - expect(await (await mw('/records', makeInit(meta))).text()).toBe('old'); - expect(fetch).toHaveBeenCalledTimes(2); - }); + test.each([400, 500])( + 'failed mutation %s preserves cached reads', + async status => { + const fetch = vi + .fn() + .mockResolvedValueOnce(new Response('old')) + .mockResolvedValueOnce(new Response('failed', { status })); + const mw = cacheTags({ defaultTtl: 5000 })(fetch); + const meta = makeTagMeta([tag()]); + await mw('/records', makeInit(meta)); + await mw('/records', makeInit(meta, 'PATCH')); + expect(await (await mw('/records', makeInit(meta))).text()).toBe( + 'old' + ); + expect(fetch).toHaveBeenCalledTimes(2); + } + ); test('transport rejection preserves cached reads', async () => { const fetch = vi .fn() @@ -550,52 +553,52 @@ describe('cache invalidation races', () => { await pending; expect(await (await mw('/records', makeInit(meta))).text()).toBe('new'); }); - test.each([ - false, - true - ])('rejects old fills, read during write: %s', async duringWrite => { - const oldRead = deferred(); - const write = deferred(); - const fetch = vi - .fn() - .mockImplementation(async (_url, init) => { - if (init?.method === 'PATCH') return write.promise; - if ( - fetch.mock.calls.filter(([, i]) => i?.method === 'GET') - .length === 1 - ) { - return oldRead.promise; - } - return new Response('new'); - }); - const mw = cacheTags({ defaultTtl: 5000 })(fetch); - const meta = makeTagMeta([tag(), tag('other')]); - let pendingWrite: Promise; - let pendingRead: Promise; - if (duringWrite) { - pendingWrite = mw( - '/records', - makeInit(makeTagMeta([tag()]), 'PATCH') - ); - pendingRead = mw('/records', makeInit(meta)); - } else { - pendingRead = mw('/records', makeInit(meta)); - pendingWrite = mw( - '/records', - makeInit(makeTagMeta([tag()]), 'PATCH') - ); + test.each([false, true])( + 'rejects old fills, read during write: %s', + async duringWrite => { + const oldRead = deferred(); + const write = deferred(); + const fetch = vi + .fn() + .mockImplementation(async (_url, init) => { + if (init?.method === 'PATCH') return write.promise; + if ( + fetch.mock.calls.filter(([, i]) => i?.method === 'GET') + .length === 1 + ) { + return oldRead.promise; + } + return new Response('new'); + }); + const mw = cacheTags({ defaultTtl: 5000 })(fetch); + const meta = makeTagMeta([tag(), tag('other')]); + let pendingWrite: Promise; + let pendingRead: Promise; + if (duringWrite) { + pendingWrite = mw( + '/records', + makeInit(makeTagMeta([tag()]), 'PATCH') + ); + pendingRead = mw('/records', makeInit(meta)); + } else { + pendingRead = mw('/records', makeInit(meta)); + pendingWrite = mw( + '/records', + makeInit(makeTagMeta([tag()]), 'PATCH') + ); + } + write.resolve(new Response(null, { status: 204 })); + await pendingWrite; + oldRead.resolve(new Response('old')); + expect(await (await pendingRead).text()).toBe('old'); + // No stale body may survive under a second tag either. + expect( + await ( + await mw('/records', makeInit(makeTagMeta([tag('other')]))) + ).text() + ).toBe('new'); } - write.resolve(new Response(null, { status: 204 })); - await pendingWrite; - oldRead.resolve(new Response('old')); - expect(await (await pendingRead).text()).toBe('old'); - // No stale body may survive under a second tag either. - expect( - await ( - await mw('/records', makeInit(makeTagMeta([tag('other')]))) - ).text() - ).toBe('new'); - }); + ); test('invalidates every alias of a previously cached response', async () => { const fetch = vi .fn() diff --git a/libs/client/src/middlewares/dedupe.test.ts b/libs/client/src/middlewares/dedupe.test.ts index bd46110d..76ee7c76 100644 --- a/libs/client/src/middlewares/dedupe.test.ts +++ b/libs/client/src/middlewares/dedupe.test.ts @@ -3,6 +3,19 @@ import type { FetchLike } from '../middleware.js'; import { dedupe } from './dedupe.js'; describe('dedupe middleware', () => { + test('allows immediate body consumption by every concurrent caller', async () => { + const fetch = vi + .fn() + .mockResolvedValue(new Response('body')); + const mw = dedupe()(fetch); + const first = mw('/same', {}).then(response => response.text()); + const second = mw('/same', {}).then(response => response.text()); + await expect(Promise.all([first, second])).resolves.toEqual([ + 'body', + 'body' + ]); + expect(fetch).toHaveBeenCalledOnce(); + }); test('deduplicates concurrent GET requests with the same URL', async () => { let callCount = 0; const fetch = vi.fn().mockImplementation(() => { @@ -20,7 +33,7 @@ describe('dedupe middleware', () => { ]); expect(fetch).toHaveBeenCalledTimes(1); - // First caller gets original, others get clones — all readable + // Every caller gets its own clone — all readable expect(await r1.json()).toEqual({ n: 1 }); expect(await r2.json()).toEqual({ n: 1 }); expect(await r3.json()).toEqual({ n: 1 }); diff --git a/libs/client/src/middlewares/dedupe.ts b/libs/client/src/middlewares/dedupe.ts index 541ec5f3..7d71d2c5 100644 --- a/libs/client/src/middlewares/dedupe.ts +++ b/libs/client/src/middlewares/dedupe.ts @@ -1,5 +1,5 @@ /** - * Request deduplication middleware for the `@cleverbrush/web` client. + * Request deduplication middleware for the `@cleverbrush/client` client. * * Prevents duplicate in-flight requests by sharing the response from * an already-pending request with the same key. Non-GET requests are @@ -7,8 +7,8 @@ * * @example * ```ts - * import { createClient } from '@cleverbrush/web'; - * import { dedupe } from '@cleverbrush/web/dedupe'; + * import { createClient } from '@cleverbrush/client'; + * import { dedupe } from '@cleverbrush/client/dedupe'; * * const client = createClient(api, { * middlewares: [dedupe()], @@ -110,6 +110,8 @@ export function dedupe(options: DedupeOptions = {}): Middleware { () => inflight.delete(k) ); - return promise; + // Never hand out the shared original: an earlier caller may consume + // its body before the next caller's clone callback runs. + return promise.then(res => res.clone()); }; } diff --git a/libs/client/src/middlewares/retry.ts b/libs/client/src/middlewares/retry.ts index 89d04879..3f96563a 100644 --- a/libs/client/src/middlewares/retry.ts +++ b/libs/client/src/middlewares/retry.ts @@ -1,5 +1,5 @@ /** - * Retry middleware for the `@cleverbrush/web` client. + * Retry middleware for the `@cleverbrush/client` client. * * Automatically retries failed requests based on HTTP status codes and * methods. Supports exponential backoff, jitter, and `Retry-After` @@ -7,8 +7,8 @@ * * @example * ```ts - * import { createClient } from '@cleverbrush/web'; - * import { retry } from '@cleverbrush/web/retry'; + * import { createClient } from '@cleverbrush/client'; + * import { retry } from '@cleverbrush/client/retry'; * * const client = createClient(api, { * middlewares: [retry({ limit: 3, jitter: true })], @@ -134,7 +134,7 @@ function applyJitter( // --------------------------------------------------------------------------- /** - * Creates a retry middleware for the `@cleverbrush/web` client. + * Creates a retry middleware for the `@cleverbrush/client` client. * * @param options - Retry configuration. * @returns A {@link Middleware} that wraps fetch calls with retry logic. diff --git a/libs/client/src/middlewares/timeout.test.ts b/libs/client/src/middlewares/timeout.test.ts index 5f662e62..869dea02 100644 --- a/libs/client/src/middlewares/timeout.test.ts +++ b/libs/client/src/middlewares/timeout.test.ts @@ -37,6 +37,20 @@ function hangingFetch(): FetchLike { // --------------------------------------------------------------------------- describe('timeout middleware', () => { + test.each([false, true])( + 'releases caller listeners and timers (throws=%s)', + async throws => { + const caller = new AbortController(); + const remove = vi.spyOn(caller.signal, 'removeEventListener'); + const mw = timeout()(() => { + if (throws) throw new Error('sync failure'); + return Promise.resolve(new Response('ok')); + }); + await mw('/test', { signal: caller.signal }).catch(() => undefined); + expect(remove).toHaveBeenCalledWith('abort', expect.any(Function)); + expect(vi.getTimerCount()).toBe(0); + } + ); beforeEach(() => { vi.useFakeTimers(); }); diff --git a/libs/client/src/middlewares/timeout.ts b/libs/client/src/middlewares/timeout.ts index ef5cd63b..5c58528e 100644 --- a/libs/client/src/middlewares/timeout.ts +++ b/libs/client/src/middlewares/timeout.ts @@ -1,13 +1,13 @@ /** - * Timeout middleware for the `@cleverbrush/web` client. + * Timeout middleware for the `@cleverbrush/client` client. * * Aborts requests that exceed a configurable duration, throwing a * {@link TimeoutError}. * * @example * ```ts - * import { createClient } from '@cleverbrush/web'; - * import { timeout } from '@cleverbrush/web/timeout'; + * import { createClient } from '@cleverbrush/client'; + * import { timeout } from '@cleverbrush/client/timeout'; * * const client = createClient(api, { * middlewares: [timeout({ timeout: 10000 })], @@ -41,7 +41,7 @@ export interface TimeoutOptions { // --------------------------------------------------------------------------- /** - * Creates a timeout middleware for the `@cleverbrush/web` client. + * Creates a timeout middleware for the `@cleverbrush/client` client. * * If the request does not complete within the configured duration, * the request is aborted and a {@link TimeoutError} is thrown. @@ -60,23 +60,20 @@ export interface TimeoutOptions { export function timeout(options: TimeoutOptions = {}): Middleware { const { timeout: ms = 10000 } = options; - return next => (url, init) => { + return next => async (url, init) => { // Per-call override (e.g. { timeout: 30000 }) const perCall = getPerCallOptions(init, 'timeout'); const effectiveMs = perCall ?? ms; const controller = new AbortController(); + const onAbort = () => controller.abort(init.signal!.reason); // If the caller already set a signal, listen to it too. if (init.signal) { if (init.signal.aborted) { controller.abort(init.signal.reason); } else { - init.signal.addEventListener( - 'abort', - () => controller.abort(init.signal!.reason), - { once: true } - ); + init.signal.addEventListener('abort', onAbort, { once: true }); } } @@ -84,19 +81,17 @@ export function timeout(options: TimeoutOptions = {}): Middleware { controller.abort(); }, effectiveMs); - return next(url, { ...init, signal: controller.signal }).then( - response => { - clearTimeout(timer); - return response; - }, - error => { - clearTimeout(timer); - // Only wrap in TimeoutError if *we* caused the abort. - if (controller.signal.aborted && !init.signal?.aborted) { - throw new TimeoutError(effectiveMs); - } - throw error; + try { + return await next(url, { ...init, signal: controller.signal }); + } catch (error) { + // Only wrap in TimeoutError if *we* caused the abort. + if (controller.signal.aborted && !init.signal?.aborted) { + throw new TimeoutError(effectiveMs); } - ); + throw error; + } finally { + clearTimeout(timer); + init.signal?.removeEventListener('abort', onAbort); + } }; } diff --git a/libs/client/src/react/createClient.ts b/libs/client/src/react/createClient.ts index e4926d09..1e1c2236 100644 --- a/libs/client/src/react/createClient.ts +++ b/libs/client/src/react/createClient.ts @@ -30,7 +30,7 @@ import type { UnifiedClient } from './types.js'; * Creates a unified typed client from an API contract. * * Each endpoint on the returned object is **callable** (direct HTTP fetch, - * identical to `@cleverbrush/web`'s `createClient`) and also exposes + * identical to `@cleverbrush/client`'s `createClient`) and also exposes * TanStack Query hooks as properties: * * - **Call directly**: `await client.todos.list()` — returns a Promise @@ -49,7 +49,7 @@ import type { UnifiedClient } from './types.js'; * automatically invalidate TanStack Query entries for the affected group. * * @param contract - An API contract created with `defineApi()`. - * @param options - Client options passed to `@cleverbrush/web`'s `createClient()`. + * @param options - Client options passed to `@cleverbrush/client`'s `createClient()`. * @returns A fully typed unified client proxy. * * @example @@ -131,7 +131,7 @@ export function createClient( return unified; } - // Two-level proxy mirroring @cleverbrush/web's client.ts pattern + // Two-level proxy mirroring @cleverbrush/client's client.ts pattern return new Proxy(Object.create(null) as UnifiedClient, { get(_target, groupName: PropertyKey) { if (typeof groupName !== 'string') return undefined; diff --git a/libs/client/src/react/types.ts b/libs/client/src/react/types.ts index e6acdc01..67ac2e7b 100644 --- a/libs/client/src/react/types.ts +++ b/libs/client/src/react/types.ts @@ -268,7 +268,7 @@ export type TypedQueryClient = { /** * A single endpoint on the unified client. * - * The endpoint is **callable** (direct HTTP fetch, same as `@cleverbrush/web`) + * The endpoint is **callable** (direct HTTP fetch, same as `@cleverbrush/client`) * and also exposes TanStack Query hooks (`useQuery`, `useMutation`, etc.) * as properties on the same object. * diff --git a/libs/client/src/types.ts b/libs/client/src/types.ts index 807acf30..d460b7cc 100644 --- a/libs/client/src/types.ts +++ b/libs/client/src/types.ts @@ -1,5 +1,5 @@ /** - * Type utilities for the `@cleverbrush/web` typed HTTP client. + * Type utilities for the `@cleverbrush/client` typed HTTP client. * * These types extract request argument shapes and response types from * `EndpointBuilder` instances defined via `@cleverbrush/server/contract`. @@ -603,8 +603,8 @@ export interface ClientOptions { * * @example * ```ts - * import { retry } from '@cleverbrush/web/retry'; - * import { timeout } from '@cleverbrush/web/timeout'; + * import { retry } from '@cleverbrush/client/retry'; + * import { timeout } from '@cleverbrush/client/timeout'; * * const client = createClient(api, { * middlewares: [retry(), timeout({ timeout: 10000 })], diff --git a/libs/client/src/validationIssues.integration.test.ts b/libs/client/src/validationIssues.integration.test.ts index 17645107..3e718ffe 100644 --- a/libs/client/src/validationIssues.integration.test.ts +++ b/libs/client/src/validationIssues.integration.test.ts @@ -11,54 +11,58 @@ import { createClient } from './client.js'; import { batching } from './middlewares/batching.js'; import { decodeValidationIssues } from './validationIssues.js'; -test.each([ - false, - true -])('real HTTP validation survives serialization (batched=%s)', async batched => { - const schema = object({ - addresses: array(object({ city: string().minLength(2) })) - }); - const api = defineApi({ - profiles: { - save: endpoint - .post('/profiles') - .body(schema) - .responses({ 200: schema }) - } - }); - const handlers = implement(api) - .group('profiles') - .withHandlers({ save: ({ body }) => body }); - const server = await createServer() - .useBatching() - .handleAll(implement(api).use(handlers).complete()) - .listen(0); - try { - const client = createClient(api, { - baseUrl: `http://127.0.0.1:${server.address!.port}`, - middlewares: batched ? [batching({ windowMs: 5 })] : [] +test.each([false, true])( + 'real HTTP validation survives serialization (batched=%s)', + async batched => { + const schema = object({ + addresses: array(object({ city: string().minLength(2) })) + }); + const api = defineApi({ + profiles: { + save: endpoint + .post('/profiles') + .body(schema) + .responses({ 200: schema }) + } }); - let decoded: unknown; - const [valid] = await Promise.all([ - client.profiles.save({ body: { addresses: [{ city: 'Paris' }] } }), - client.profiles - .save({ body: { addresses: [{ city: '' }] } }) - .catch(error => { - decoded = decodeValidationIssues(error, { source: 'body' }); + const handlers = implement(api) + .group('profiles') + .withHandlers({ save: ({ body }) => body }); + const server = await createServer() + .useBatching() + .handleAll(implement(api).use(handlers).complete()) + .listen(0); + try { + const client = createClient(api, { + baseUrl: `http://127.0.0.1:${server.address!.port}`, + middlewares: batched ? [batching({ windowMs: 5 })] : [] + }); + let decoded: unknown; + const [valid] = await Promise.all([ + client.profiles.save({ + body: { addresses: [{ city: 'Paris' }] } + }), + client.profiles + .save({ body: { addresses: [{ city: '' }] } }) + .catch(error => { + decoded = decodeValidationIssues(error, { + source: 'body' + }); + }) + ]); + expect(valid).toEqual({ addresses: [{ city: 'Paris' }] }); + expect(JSON.parse(JSON.stringify(decoded))).toEqual( + expect.arrayContaining([ + { pointer: '/addresses/0/city', detail: expect.any(String) } + ]) + ); + expect( + await client.profiles.save({ + body: { addresses: [{ city: 'Paris' }] } }) - ]); - expect(valid).toEqual({ addresses: [{ city: 'Paris' }] }); - expect(JSON.parse(JSON.stringify(decoded))).toEqual( - expect.arrayContaining([ - { pointer: '/addresses/0/city', detail: expect.any(String) } - ]) - ); - expect( - await client.profiles.save({ - body: { addresses: [{ city: 'Paris' }] } - }) - ).toEqual({ addresses: [{ city: 'Paris' }] }); - } finally { - await server.close(); + ).toEqual({ addresses: [{ city: 'Paris' }] }); + } finally { + await server.close(); + } } -}); +); diff --git a/libs/client/tsup.config.ts b/libs/client/tsup.config.ts index 2e9edd8d..2895f34a 100644 --- a/libs/client/tsup.config.ts +++ b/libs/client/tsup.config.ts @@ -6,6 +6,7 @@ export default defineConfig({ 'src/retry.ts', 'src/timeout.ts', 'src/dedupe.ts', + 'src/idempotency.ts', 'src/cache.ts', 'src/batching.ts', 'src/optimisticUpdate.ts', diff --git a/libs/deep/LICENSE b/libs/deep/LICENSE new file mode 100644 index 00000000..053547c1 --- /dev/null +++ b/libs/deep/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (C) 2024 by Andrew Zolotuhkin + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libs/deep/README.md b/libs/deep/README.md index a3d2205a..e473daa0 100644 --- a/libs/deep/README.md +++ b/libs/deep/README.md @@ -3,7 +3,7 @@ [![CI](https://github.com/cleverbrush/framework/actions/workflows/ci.yml/badge.svg)](https://github.com/cleverbrush/framework/actions/workflows/ci.yml) [![License: BSD-3-Clause](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)](../../LICENSE) -![Coverage](https://img.shields.io/badge/coverage-97.8%25-brightgreen) +![Unit coverage](https://img.shields.io/badge/unit_coverage-78.9%25-yellowgreen) A library for deep operations on JavaScript objects — cloning, equality, merging, and flattening. diff --git a/libs/deep/package.json b/libs/deep/package.json index 8e93ad3d..fcac13a4 100644 --- a/libs/deep/package.json +++ b/libs/deep/package.json @@ -15,7 +15,7 @@ "deep extend", "cleverbrush" ], - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "main": "./dist/index.js", "exports": { ".": { @@ -25,6 +25,9 @@ }, "sideEffects": false, "name": "@cleverbrush/deep", + "engines": { + "node": ">=24" + }, "readme": "https://github.com/cleverbrush/framework/tree/master/libs/deep#readme", "repository": { "type": "git", diff --git a/libs/deep/src/deepEqual.test.ts b/libs/deep/src/deepEqual.test.ts index 6fdc71b5..eed378aa 100644 --- a/libs/deep/src/deepEqual.test.ts +++ b/libs/deep/src/deepEqual.test.ts @@ -125,18 +125,13 @@ test('deepEqual - objects with different key names return false', () => { expect(deepEqual({ a: 1 }, { b: 1 })).toEqual(false); }); -test.each([ - null, - undefined, - true, - 1, - 'a', - Symbol('a'), - () => 1 -])('deepEqual - object versus %s is symmetric and does not throw', value => { - expect(deepEqual({}, value)).toBe(false); - expect(deepEqual(value, {})).toBe(false); -}); +test.each([null, undefined, true, 1, 'a', Symbol('a'), () => 1])( + 'deepEqual - object versus %s is symmetric and does not throw', + value => { + expect(deepEqual({}, value)).toBe(false); + expect(deepEqual(value, {})).toBe(false); + } +); test('deepEqual - primitives follow Object.is', () => { expect(deepEqual(Number.NaN, Number.NaN)).toBe(true); @@ -191,24 +186,32 @@ test('deepEqual - only enumerable own properties participate', () => { ).toBe(false); }); -test.each([ - false, - true -])('deepEqual - preserves array length, holes and extra properties (unordered=%s)', disregardArrayOrder => { - const options = { disregardArrayOrder }; - expect(deepEqual(new Array(3), new Array(3), options)).toBe(true); - expect(deepEqual(new Array(3), new Array(4), options)).toBe(false); - expect(deepEqual(new Array(1), [undefined], options)).toBe(false); - const key = Symbol('metadata'); - const left = Object.assign([1], { label: 'a', [key]: 2 }); - expect(deepEqual(left, [1], options)).toBe(false); - expect( - deepEqual(left, Object.assign([1], { label: 'a', [key]: 2 }), options) - ).toBe(true); - expect( - deepEqual(left, Object.assign([1], { label: 'a', [key]: 3 }), options) - ).toBe(false); -}); +test.each([false, true])( + 'deepEqual - preserves array length, holes and extra properties (unordered=%s)', + disregardArrayOrder => { + const options = { disregardArrayOrder }; + expect(deepEqual(new Array(3), new Array(3), options)).toBe(true); + expect(deepEqual(new Array(3), new Array(4), options)).toBe(false); + expect(deepEqual(new Array(1), [undefined], options)).toBe(false); + const key = Symbol('metadata'); + const left = Object.assign([1], { label: 'a', [key]: 2 }); + expect(deepEqual(left, [1], options)).toBe(false); + expect( + deepEqual( + left, + Object.assign([1], { label: 'a', [key]: 2 }), + options + ) + ).toBe(true); + expect( + deepEqual( + left, + Object.assign([1], { label: 'a', [key]: 3 }), + options + ) + ).toBe(false); + } +); test('deepEqual - ordered holes retain their positions', () => { const left = new Array(2); @@ -219,26 +222,26 @@ test('deepEqual - ordered holes retain their positions', () => { expect(deepEqual(left, right, { disregardArrayOrder: true })).toBe(true); }); -test.each([ - false, - true -])('deepEqual - cycles terminate and still detect mismatches (unordered=%s)', disregardArrayOrder => { - const options = { disregardArrayOrder }; - const left: any = { value: 1 }; - left.self = left; - const right: any = { value: 1 }; - right.self = right; - expect(deepEqual(left, right, options)).toBe(true); - right.value = 2; - expect(deepEqual(left, right, options)).toBe(false); - const leftArray: any[] = []; - leftArray.push(leftArray, { value: 1 }); - const rightArray: any[] = []; - rightArray.push(rightArray, { value: 1 }); - expect(deepEqual(leftArray, rightArray, options)).toBe(true); - rightArray[1].value = 2; - expect(deepEqual(leftArray, rightArray, options)).toBe(false); -}); +test.each([false, true])( + 'deepEqual - cycles terminate and still detect mismatches (unordered=%s)', + disregardArrayOrder => { + const options = { disregardArrayOrder }; + const left: any = { value: 1 }; + left.self = left; + const right: any = { value: 1 }; + right.self = right; + expect(deepEqual(left, right, options)).toBe(true); + right.value = 2; + expect(deepEqual(left, right, options)).toBe(false); + const leftArray: any[] = []; + leftArray.push(leftArray, { value: 1 }); + const rightArray: any[] = []; + rightArray.push(rightArray, { value: 1 }); + expect(deepEqual(leftArray, rightArray, options)).toBe(true); + rightArray[1].value = 2; + expect(deepEqual(leftArray, rightArray, options)).toBe(false); + } +); test('deepEqual - sharing topology is not part of structural equality', () => { const shared = { value: 1 }; diff --git a/libs/di/LICENSE b/libs/di/LICENSE new file mode 100644 index 00000000..053547c1 --- /dev/null +++ b/libs/di/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (C) 2024 by Andrew Zolotuhkin + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libs/di/README.md b/libs/di/README.md index d98b346f..d87933c5 100644 --- a/libs/di/README.md +++ b/libs/di/README.md @@ -2,6 +2,9 @@ [![CI](https://github.com/cleverbrush/framework/actions/workflows/ci.yml/badge.svg)](https://github.com/cleverbrush/framework/actions/workflows/ci.yml) [![License: BSD-3-Clause](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)](../../LICENSE) + +![Unit coverage](https://img.shields.io/badge/unit_coverage-94.7%25-brightgreen) + A .NET-style dependency injection container for TypeScript. Uses [`@cleverbrush/schema`](../schema) instances as service keys for fully-typed, zero-generic registration and resolution. Supports three service lifetimes, schema-driven function injection, and automatic disposal of scoped services. @@ -132,10 +135,14 @@ const provider = services.buildServiceProvider(); // Throws if not registered const config = provider.get(IConfig); -// Returns undefined if not registered (no throw) +// Returns undefined only if not registered const mailer = provider.getOptional(IMailer); ``` +Registered factories still propagate errors, circular dependencies and scope +violations through `getOptional()`. Root factories respect `validateScopes`; +resolve scoped dependencies through an explicitly created scope. + ## Scopes Create a scope for work that has a bounded lifetime (e.g. an HTTP request). Scoped services are cached within the scope and disposed when the scope exits. diff --git a/libs/di/package.json b/libs/di/package.json index 1506a455..a3d1d5da 100644 --- a/libs/di/package.json +++ b/libs/di/package.json @@ -21,7 +21,7 @@ "schema", "cleverbrush" ], - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "main": "./dist/index.js", "exports": { ".": { @@ -31,6 +31,9 @@ }, "sideEffects": false, "name": "@cleverbrush/di", + "engines": { + "node": ">=24" + }, "readme": "https://github.com/cleverbrush/framework/tree/master/libs/di#readme", "repository": { "type": "git", diff --git a/libs/di/src/ServiceCollection.ts b/libs/di/src/ServiceCollection.ts index 4c0cf780..709f10be 100644 --- a/libs/di/src/ServiceCollection.ts +++ b/libs/di/src/ServiceCollection.ts @@ -203,7 +203,8 @@ export class ServiceCollection { * services.addSingletonInstance(IConfig, config); * ``` * - * @example Register a function value (impossible with {@link addSingleton}) + * @example Register a function value + * Unlike {@link addSingleton}, this API accepts functions as values. * ```ts * const IHandler = func(); * const myHandler = (req: Request) => new Response('ok'); diff --git a/libs/di/src/ServiceProvider.test.ts b/libs/di/src/ServiceProvider.test.ts index 3bdd0a7e..2c567346 100644 --- a/libs/di/src/ServiceProvider.test.ts +++ b/libs/di/src/ServiceProvider.test.ts @@ -21,6 +21,51 @@ const ILogger = object({ // --------------------------------------------------------------------------- describe('ServiceProvider', () => { + test.each([false, true])( + 'optional factory resolution preserves errors and cycles (scoped=%s)', + scoped => { + const services = new ServiceCollection(); + services.addTransient( + IConfig, + provider => provider.getOptional(ILogger) as any + ); + services.addTransient( + ILogger, + provider => provider.getOptional(IConfig) as any + ); + const root = services.buildServiceProvider(); + const scope = root.createScope(); + const provider = scoped ? scope.serviceProvider : root; + expect(() => provider.get(IConfig)).toThrow(/circular/i); + scope.dispose(); + } + ); + + test('does not disguise a registered optional factory failure as an absent service', () => { + const services = new ServiceCollection(); + services.addTransient(ILogger, () => { + throw new Error('factory failed'); + }); + services.addTransient( + IConfig, + provider => provider.getOptional(ILogger) as any + ); + expect(() => services.buildServiceProvider().get(IConfig)).toThrow( + 'factory failed' + ); + }); + + test('enforces root scope validation within factories', () => { + const services = new ServiceCollection(); + services.addScoped(IConfig, () => ({ port: 1, host: 'localhost' })); + services.addSingleton( + ILogger, + provider => provider.get(IConfig) as any + ); + expect(() => services.buildServiceProvider().get(ILogger)).toThrow( + /scop/i + ); + }); // ── Singleton ──────────────────────────────────── test('singleton returns the same instance across calls', () => { @@ -294,7 +339,7 @@ describe('ServiceProvider', () => { // ── Singleton factory resolves scoped service (lines 283-298) ──────── - test('singleton factory can resolve scoped service via proxy', () => { + test('factory can resolve a scoped dependency inside an explicit scope', () => { const IScoped = object({ value: string() }); const ISingleton = object({ length: number() }); @@ -305,7 +350,7 @@ describe('ServiceProvider', () => { })); const provider = services.buildServiceProvider(); - const result = provider.get(ISingleton); + const result = provider.createScope().serviceProvider.get(ISingleton); expect(result.length).toBe(5); }); @@ -328,7 +373,7 @@ describe('ServiceProvider', () => { })); const p = services.buildServiceProvider(); - const result = p.get(IOuter); + const result = p.createScope().serviceProvider.get(IOuter); expect(result.result).toBe(1); }); @@ -345,7 +390,7 @@ describe('ServiceProvider', () => { })); const provider = services.buildServiceProvider(); - const result = provider.get(ISingleton); + const result = provider.createScope().serviceProvider.get(ISingleton); expect(result.found).toBe('42'); }); diff --git a/libs/di/src/ServiceProvider.ts b/libs/di/src/ServiceProvider.ts index 63816393..dddaeeab 100644 --- a/libs/di/src/ServiceProvider.ts +++ b/libs/di/src/ServiceProvider.ts @@ -360,9 +360,11 @@ export class ServiceProvider implements IServiceProvider { resolveScoped( schema: SchemaBuilder, resolutionStack: Set>, - scopedCache: Map, any>, - trackDisposable: (instance: any) => void + scopedCache?: Map, any>, + trackDisposable?: (instance: any) => void, + optional = false ): any { + if (optional && !this.#descriptors.has(schema)) return undefined; return this.#resolve( schema, resolutionStack, @@ -403,18 +405,20 @@ class ScopedResolverProxy implements IServiceProvider { return this.#provider.resolveScoped( schema, this.#resolutionStack, - this.#scopedCache ?? new Map(), - this.#trackDisposable ?? (() => {}) + this.#scopedCache, + this.#trackDisposable ); } getOptional>( schema: TSchema ): InferType | undefined { - try { - return this.get(schema); - } catch { - return undefined; - } + return this.#provider.resolveScoped( + schema, + this.#resolutionStack, + this.#scopedCache, + this.#trackDisposable, + true + ); } } diff --git a/libs/di/src/ServiceScope.ts b/libs/di/src/ServiceScope.ts index d55622eb..9241e43b 100644 --- a/libs/di/src/ServiceScope.ts +++ b/libs/di/src/ServiceScope.ts @@ -408,8 +408,11 @@ export class ScopedServiceProvider implements IServiceProvider { */ resolveWithStack( schema: SchemaBuilder, - resolutionStack: Set> + resolutionStack: Set>, + optional = false ): any { + if (optional && !this.#scope.getDescriptors().has(schema)) + return undefined; return this.#resolve(schema, resolutionStack); } @@ -465,6 +468,10 @@ class ScopedResolverProxy implements IServiceProvider { getOptional>( schema: TSchema ): InferType | undefined { - return this.#provider.getOptional(schema); + return this.#provider.resolveWithStack( + schema, + this.#resolutionStack, + true + ); } } diff --git a/libs/di/src/index.ts b/libs/di/src/index.ts index 89de1247..d1ca0e0b 100644 --- a/libs/di/src/index.ts +++ b/libs/di/src/index.ts @@ -3,7 +3,7 @@ * * Uses `@cleverbrush/schema` instances as service keys for type-safe * registration and resolution. Supports singleton, scoped, and transient - * lifetimes, function injection via {@link FunctionSchemaBuilder}, and + * lifetimes, function injection via `FunctionSchemaBuilder`, and * automatic disposal of scoped services. * * @example Quick start diff --git a/libs/env/LICENSE b/libs/env/LICENSE new file mode 100644 index 00000000..053547c1 --- /dev/null +++ b/libs/env/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (C) 2024 by Andrew Zolotuhkin + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libs/env/README.md b/libs/env/README.md index 65d13f6d..2a81614c 100644 --- a/libs/env/README.md +++ b/libs/env/README.md @@ -2,6 +2,9 @@ [![CI](https://github.com/cleverbrush/framework/actions/workflows/ci.yml/badge.svg)](https://github.com/cleverbrush/framework/actions/workflows/ci.yml) [![License: BSD-3-Clause](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)](../../LICENSE) + +![Unit coverage](https://img.shields.io/badge/unit_coverage-97.9%25-brightgreen) + Type-safe environment variable parsing with `@cleverbrush/schema` — validated, coerced, structured configs from `process.env`. diff --git a/libs/env/package.json b/libs/env/package.json index 406e02ca..7f3a7add 100644 --- a/libs/env/package.json +++ b/libs/env/package.json @@ -25,7 +25,7 @@ "typescript", "cleverbrush" ], - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "main": "./dist/index.js", "exports": { ".": { @@ -35,6 +35,9 @@ }, "sideEffects": false, "name": "@cleverbrush/env", + "engines": { + "node": ">=24" + }, "readme": "https://github.com/cleverbrush/framework/tree/master/libs/env#readme", "repository": { "type": "git", @@ -47,6 +50,7 @@ }, "type": "module", "devDependencies": { + "@cleverbrush/schema": "*", "@types/node": "^25.4.0" }, "types": "./dist/index.d.ts", diff --git a/libs/env/tsconfig.build.json b/libs/env/tsconfig.build.json index 29700385..19f18f4f 100644 --- a/libs/env/tsconfig.build.json +++ b/libs/env/tsconfig.build.json @@ -1,6 +1,7 @@ { "extends": "./tsconfig.json", "compilerOptions": { + "types": ["node"], "rootDir": "./src", "outDir": "./dist" }, diff --git a/libs/knex-clickhouse/LICENSE b/libs/knex-clickhouse/LICENSE new file mode 100644 index 00000000..053547c1 --- /dev/null +++ b/libs/knex-clickhouse/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (C) 2024 by Andrew Zolotuhkin + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libs/knex-clickhouse/README.md b/libs/knex-clickhouse/README.md index c26b35e5..b611e6f1 100644 --- a/libs/knex-clickhouse/README.md +++ b/libs/knex-clickhouse/README.md @@ -1,6 +1,6 @@ # @cleverbrush/knex-clickhouse -![Coverage](https://img.shields.io/badge/coverage-100%25-brightgreen) +![Unit coverage](https://img.shields.io/badge/unit_coverage-100%25-brightgreen) A [Knex](https://knexjs.org/) dialect for [ClickHouse](https://clickhouse.com/). Uses the official `@clickhouse/client` library to connect to ClickHouse and lets you build queries with Knex's familiar API. diff --git a/libs/knex-clickhouse/package.json b/libs/knex-clickhouse/package.json index f51de4d5..1254e757 100644 --- a/libs/knex-clickhouse/package.json +++ b/libs/knex-clickhouse/package.json @@ -23,7 +23,7 @@ "clickhouse", "sql" ], - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "main": "./dist/index.js", "exports": { ".": { @@ -33,6 +33,9 @@ }, "sideEffects": false, "name": "@cleverbrush/knex-clickhouse", + "engines": { + "node": ">=24" + }, "readme": "https://github.com/cleverbrush/framework/tree/master/libs/knex-clickhouse#readme", "repository": { "type": "git", diff --git a/libs/knex-clickhouse/src/makeEscape.ts b/libs/knex-clickhouse/src/makeEscape.ts index 37e0ce32..1aeb1d6b 100644 --- a/libs/knex-clickhouse/src/makeEscape.ts +++ b/libs/knex-clickhouse/src/makeEscape.ts @@ -1,3 +1,5 @@ +import { Buffer } from 'node:buffer'; + /* * NOTE: functions below are taken from knex npm package, * unfortunately they are not exported, so we have to copy them here diff --git a/libs/knex-clickhouse/tsconfig.build.json b/libs/knex-clickhouse/tsconfig.build.json index 41213ea6..081f1477 100644 --- a/libs/knex-clickhouse/tsconfig.build.json +++ b/libs/knex-clickhouse/tsconfig.build.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node"], "rootDir": "./src", "outDir": "./dist", "declaration": true, diff --git a/libs/knex-clickhouse/tsconfig.json b/libs/knex-clickhouse/tsconfig.json index d09afdcd..f3bc8d06 100644 --- a/libs/knex-clickhouse/tsconfig.json +++ b/libs/knex-clickhouse/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node"], "composite": true, "rootDir": "./src", "outDir": "./dist", diff --git a/libs/knex-schema/LICENSE b/libs/knex-schema/LICENSE new file mode 100644 index 00000000..053547c1 --- /dev/null +++ b/libs/knex-schema/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (C) 2024 by Andrew Zolotuhkin + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libs/knex-schema/README.md b/libs/knex-schema/README.md index aaf424b3..d6b66977 100644 --- a/libs/knex-schema/README.md +++ b/libs/knex-schema/README.md @@ -1,4 +1,7 @@ # @cleverbrush/knex-schema + +![Unit coverage](https://img.shields.io/badge/unit_coverage-63.5%25-yellow) + Type-safe, schema-driven query builder for [Knex](https://knexjs.org/). Use `@cleverbrush/schema` object builders to describe your PostgreSQL tables — column name mapping, eager loading, and full CRUD are handled automatically with complete TypeScript inference. diff --git a/libs/knex-schema/integration/queries.test.ts b/libs/knex-schema/integration/queries.test.ts index c033de8b..f296e820 100644 --- a/libs/knex-schema/integration/queries.test.ts +++ b/libs/knex-schema/integration/queries.test.ts @@ -527,23 +527,23 @@ describe('eager ordering', () => { } }); - it.each([ - '"taskId" desc', - '1 desc' - ])('retains ordering by a projected alias or position: %s', async order => { - const rows = await query(knex, taskEntity.schema) - .where(t => t.projectId, 1) - .select(t => ({ taskId: t.id })) - .orderByRaw(order) - .joinMany({ - foreignSchema: Note, - localColumn: t => t.id, - foreignColumn: t => t.taskId, - as: 'notes' - }) - .limit(2); - expect(rows.map(row => row.taskId)).toEqual([105, 104]); - }); + it.each(['"taskId" desc', '1 desc'])( + 'retains ordering by a projected alias or position: %s', + async order => { + const rows = await query(knex, taskEntity.schema) + .where(t => t.projectId, 1) + .select(t => ({ taskId: t.id })) + .orderByRaw(order) + .joinMany({ + foreignSchema: Note, + localColumn: t => t.id, + foreignColumn: t => t.taskId, + as: 'notes' + }) + .limit(2); + expect(rows.map(row => row.taskId)).toEqual([105, 104]); + } + ); }); describe('aggregate results', () => { diff --git a/libs/knex-schema/package.json b/libs/knex-schema/package.json index d7bb3a5a..f593183a 100644 --- a/libs/knex-schema/package.json +++ b/libs/knex-schema/package.json @@ -26,7 +26,7 @@ "eager loading", "cleverbrush" ], - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "main": "./dist/index.js", "exports": { ".": { @@ -40,6 +40,9 @@ }, "sideEffects": false, "name": "@cleverbrush/knex-schema", + "engines": { + "node": ">=24" + }, "readme": "https://github.com/cleverbrush/framework/tree/master/libs/knex-schema#readme", "repository": { "type": "git", diff --git a/libs/knex-schema/src/SchemaQueryBuilder.ts b/libs/knex-schema/src/SchemaQueryBuilder.ts index 155b104d..2557cf26 100644 --- a/libs/knex-schema/src/SchemaQueryBuilder.ts +++ b/libs/knex-schema/src/SchemaQueryBuilder.ts @@ -312,7 +312,7 @@ export class SchemaQueryBuilder< > { /** @internal Nominal identity for typed child-query customizers. */ declare readonly [READ_QUERY]: true; - private declare readonly writable: Writable; + declare private readonly writable: Writable; private readonly alias = `__schema_read_${readAliasSequence++}`; private fields: Record; private loaded: Loaded[] = []; diff --git a/libs/knex-schema/src/compiled-query.test.ts b/libs/knex-schema/src/compiled-query.test.ts index b7a126b6..3a1e63f1 100644 --- a/libs/knex-schema/src/compiled-query.test.ts +++ b/libs/knex-schema/src/compiled-query.test.ts @@ -200,50 +200,50 @@ describe('compiled parameterized SELECTs', () => { expect(image.toSQL(3).bindings).toContain(3); }); - it.each([ - false, - true - ])('executes and caches concurrent calls (inspect first: %s)', async inspectFirst => { - const db = Knex({ client: 'pg' }); - vi.spyOn(db.client, 'acquireConnection').mockResolvedValue({}); - const release = vi - .spyOn(db.client, 'releaseConnection') - .mockResolvedValue(undefined); - const event = vi.fn(); - db.on('query', event); - vi.spyOn(db.client, '_query').mockImplementation( - async (_connection: unknown, statement: any) => { - statement.response = { - command: 'SELECT', - rows: [ - { - id: statement.bindings[0], - birthday: '2026-01-01T00:00:00Z' - } - ] - }; - return statement; - } - ); - const read = query(db, User) - .where(t => t.id, parameter('id')) - .select(t => ({ id: t.id, birthday: t.birthday })); - const compiler = vi.spyOn(db.client, 'queryCompiler'); - if (inspectFirst) read.toSQL(1); - const [a, b] = await Promise.all([read(1), read(2)]); - expect(a).toEqual([ - { id: 1, birthday: new Date('2026-01-01T00:00:00Z') } - ]); - expect(b[0].id).toBe(2); - const compiled = compiler.mock.calls.length; - expect(compiled).toBeGreaterThan(0); - await read(3); - expect(compiler).toHaveBeenCalledTimes(compiled); - expect(event).toHaveBeenCalledTimes(3); - expect(release).toHaveBeenCalledTimes(3); - expect(event.mock.calls[0][0].sql).toContain('$1'); - await db.destroy(); - }); + it.each([false, true])( + 'executes and caches concurrent calls (inspect first: %s)', + async inspectFirst => { + const db = Knex({ client: 'pg' }); + vi.spyOn(db.client, 'acquireConnection').mockResolvedValue({}); + const release = vi + .spyOn(db.client, 'releaseConnection') + .mockResolvedValue(undefined); + const event = vi.fn(); + db.on('query', event); + vi.spyOn(db.client, '_query').mockImplementation( + async (_connection: unknown, statement: any) => { + statement.response = { + command: 'SELECT', + rows: [ + { + id: statement.bindings[0], + birthday: '2026-01-01T00:00:00Z' + } + ] + }; + return statement; + } + ); + const read = query(db, User) + .where(t => t.id, parameter('id')) + .select(t => ({ id: t.id, birthday: t.birthday })); + const compiler = vi.spyOn(db.client, 'queryCompiler'); + if (inspectFirst) read.toSQL(1); + const [a, b] = await Promise.all([read(1), read(2)]); + expect(a).toEqual([ + { id: 1, birthday: new Date('2026-01-01T00:00:00Z') } + ]); + expect(b[0].id).toBe(2); + const compiled = compiler.mock.calls.length; + expect(compiled).toBeGreaterThan(0); + await read(3); + expect(compiler).toHaveBeenCalledTimes(compiled); + expect(event).toHaveBeenCalledTimes(3); + expect(release).toHaveBeenCalledTimes(3); + expect(event.mock.calls[0][0].sql).toContain('$1'); + await db.destroy(); + } + ); it('blocks unbound escape hatches and unsupported placeholder positions', async () => { const read = query(knex, User).where(t => t.id, parameter('id')); diff --git a/libs/knex-schema/src/composable-query.test.ts b/libs/knex-schema/src/composable-query.test.ts index 9a9e2f6e..cb3a7724 100644 --- a/libs/knex-schema/src/composable-query.test.ts +++ b/libs/knex-schema/src/composable-query.test.ts @@ -125,27 +125,18 @@ describe('typed aliases and aggregate SQL', () => { describe('aggregate decoders', () => { const count = compileAggregate(db, aggregate.count(), () => '').decode; - it.each([ - '0', - '3', - 5, - 12n, - String(Number.MAX_SAFE_INTEGER) - ])('decodes safe count %s', value => { - expect(count(value)).toBe(Number(value)); - }); - it.each([ - '9007199254740993', - -1, - '1.5', - '', - null, - NaN, - {}, - Infinity - ])('rejects unsafe/malformed count %s', value => { - expect(() => count(value)).toThrow(); - }); + it.each(['0', '3', 5, 12n, String(Number.MAX_SAFE_INTEGER)])( + 'decodes safe count %s', + value => { + expect(count(value)).toBe(Number(value)); + } + ); + it.each(['9007199254740993', -1, '1.5', '', null, NaN, {}, Infinity])( + 'rejects unsafe/malformed count %s', + value => { + expect(() => count(value)).toThrow(); + } + ); it('lets output schemas replace default decoding, including overflow policy', () => { const compiled = compileAggregate( db, diff --git a/libs/knex-schema/src/entity.ts b/libs/knex-schema/src/entity.ts index 3953f457..a9622a0d 100644 --- a/libs/knex-schema/src/entity.ts +++ b/libs/knex-schema/src/entity.ts @@ -215,10 +215,10 @@ export class Entity< readonly schema: EntityReadSchema; /** @internal Phantom slot to retain `TRels` in inferred types. */ - private declare readonly __relations__: TRels; + declare private readonly __relations__: TRels; /** @internal Phantom slot to retain `TVariantUnion` in inferred types. */ - private declare readonly __variantUnion__: TVariantUnion; + declare private readonly __variantUnion__: TVariantUnion; /** * Wrap a schema as an entity definition with typed relation metadata. diff --git a/libs/knex-schema/src/extension.ts b/libs/knex-schema/src/extension.ts index f60295a6..eeef88a1 100644 --- a/libs/knex-schema/src/extension.ts +++ b/libs/knex-schema/src/extension.ts @@ -49,7 +49,7 @@ export { EXTRA_TYPE_BRAND, METHOD_LITERAL_BRAND } from '@cleverbrush/schema'; /** * Phantom-type brand placed on a column schema by `.primaryKey()`. - * Carried on the property schema's type so that {@link PrimaryKeyOf} can + * Carried on the property schema's type so that `PrimaryKeyOf` can * locate primary-key columns at the type level. * * @public @@ -102,7 +102,7 @@ function hasColumnName(this: SchemaBuilder, name: string) { /** * Stores the SQL table name for an `ObjectSchemaBuilder` using the schema - * extension system. Required for {@link query} to build queries — throws at + * extension system. Required for `query` to build queries — throws at * query creation time if not set. */ function hasTableName( @@ -260,7 +260,7 @@ export const dbExtension = defineExtension({ /** * Set the SQL table name for this object schema. * - * Required before creating a {@link query} builder — throws at + * Required before creating a `query` builder — throws at * query creation time when not set. * * @param name - The SQL table name (e.g. `'users'`). @@ -803,7 +803,7 @@ export const ddlExtension = defineExtension({ * // rows: Array> * ``` * - * @see {@link SchemaQueryBuilder.projected} + * @see `SchemaQueryBuilder.select` */ projection< TProperties extends Record< @@ -982,9 +982,9 @@ export interface VariantInputForResolver { /** * @internal Validate + apply a fully-resolved variant config to a base * schema. Stores the `'variants'` and `'polymorphicVariants'` extensions - * read by {@link QuerySource}. + * read by query execution. * - * Called by the {@link Entity} chain (`.discriminator().ctiVariant().stiVariant()`). + * Called by the `Entity` chain (`.discriminator().ctiVariant().stiVariant()`). * Replaces the previous schema-level `.withVariants()` method. */ export function applyVariantsToSchema( @@ -1280,7 +1280,7 @@ export function getProjections( * Retrieve the resolved variant configuration stored by `.withVariants()`. * Returns `null` when the schema is not polymorphic. * - * @internal — used by {@link QuerySource}. + * @internal — used by query execution. */ export function getVariants( schema: ObjectSchemaBuilder diff --git a/libs/knex-schema/src/mappers.ts b/libs/knex-schema/src/mappers.ts index c59d3586..b2baac37 100644 --- a/libs/knex-schema/src/mappers.ts +++ b/libs/knex-schema/src/mappers.ts @@ -98,7 +98,7 @@ export function mapObject>( * the `mappers` defined on each spec to the nested data, and returns the * mutated row. * - * This is an internal helper used by {@link QuerySource}'s result + * This is an internal helper used by query result * mapping pipeline. Exported to allow custom post-processing if needed. * * @param row - The raw result row (mutated in place). diff --git a/libs/knex-schema/src/migration.ts b/libs/knex-schema/src/migration.ts index e006b38f..0a7406cd 100644 --- a/libs/knex-schema/src/migration.ts +++ b/libs/knex-schema/src/migration.ts @@ -827,7 +827,7 @@ export async function applyDiff( * - Orders tables topologically by FK dependencies so parent tables are * created before child tables in `up` (and dropped after in `down`). * - * @param entities - The entities from your {@link EntityMap}. + * @param entities - The entities from your `EntityMap`. * @param prevSnapshot - The last committed {@link SchemaSnapshot} (empty on first run). * @returns `{ up, down, full, isEmpty, nextSnapshot }` where `nextSnapshot` * should be written to disk after the migration file is created. diff --git a/libs/knex-schema/src/property-navigation.test.ts b/libs/knex-schema/src/property-navigation.test.ts index 79604ae0..4cb5b56d 100644 --- a/libs/knex-schema/src/property-navigation.test.ts +++ b/libs/knex-schema/src/property-navigation.test.ts @@ -160,7 +160,9 @@ test('navigation fixtures typecheck against public package declarations', () => `${name} must be tested through its published declarations` ).toBe(true); } -}, 30000); + // This compiles fixtures across six public packages, including editor + // language-service metadata; allow coverage overhead on shared runners. +}, 120_000); describe.each(references)('$language $name ($property)', reference => { test('goes to the original property declaration', () => { diff --git a/libs/knex-schema/src/sql-identifiers.test.ts b/libs/knex-schema/src/sql-identifiers.test.ts index 5d82666f..da4539fc 100644 --- a/libs/knex-schema/src/sql-identifiers.test.ts +++ b/libs/knex-schema/src/sql-identifiers.test.ts @@ -2,14 +2,10 @@ import { describe, expect, it } from 'vitest'; import { alias, isSqlIdentifier, number, object } from './index.js'; describe('isSqlIdentifier', () => { - it.each([ - 'task', - '_task', - 'task_owner2', - 'TaskOwner', - 'select' - ])('accepts the supported single-name format: %s', name => - expect(isSqlIdentifier(name)).toBe(true)); + it.each(['task', '_task', 'task_owner2', 'TaskOwner', 'select'])( + 'accepts the supported single-name format: %s', + name => expect(isSqlIdentifier(name)).toBe(true) + ); it.each([ '', '2tasks', diff --git a/libs/knex-schema/tsconfig.build.json b/libs/knex-schema/tsconfig.build.json index 2082ba2d..f364027a 100644 --- a/libs/knex-schema/tsconfig.build.json +++ b/libs/knex-schema/tsconfig.build.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node"], "rootDir": "./src", "outDir": "./dist", "declaration": true, diff --git a/libs/knex-schema/tsconfig.json b/libs/knex-schema/tsconfig.json index 6b885309..c9933512 100644 --- a/libs/knex-schema/tsconfig.json +++ b/libs/knex-schema/tsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node", "vitest/globals"], "composite": true, "rootDir": "./src", "outDir": "./dist", diff --git a/libs/log/LICENSE b/libs/log/LICENSE new file mode 100644 index 00000000..053547c1 --- /dev/null +++ b/libs/log/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (C) 2024 by Andrew Zolotuhkin + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libs/log/README.md b/libs/log/README.md index 606a4ce5..70feb3bd 100644 --- a/libs/log/README.md +++ b/libs/log/README.md @@ -1,4 +1,7 @@ # @cleverbrush/log + +![Unit coverage](https://img.shields.io/badge/unit_coverage-98.5%25-brightgreen) + Enterprise structured logging for TypeScript — Serilog-style message templates, CLEF format, batching sinks with circuit breaking, ambient correlation IDs. diff --git a/libs/log/package.json b/libs/log/package.json index f6caa281..c99aeb34 100644 --- a/libs/log/package.json +++ b/libs/log/package.json @@ -16,7 +16,7 @@ "clef", "cleverbrush" ], - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "main": "./dist/index.js", "exports": { ".": { @@ -30,6 +30,9 @@ }, "sideEffects": false, "name": "@cleverbrush/log", + "engines": { + "node": ">=24" + }, "readme": "https://github.com/cleverbrush/framework/tree/master/libs/log#readme", "repository": { "type": "git", @@ -61,6 +64,9 @@ } }, "devDependencies": { + "@cleverbrush/di": "*", + "@cleverbrush/server": "*", + "@cleverbrush/knex-clickhouse": "*", "@types/node": "^25.4.0" }, "type": "module", diff --git a/libs/mapper/LICENSE b/libs/mapper/LICENSE new file mode 100644 index 00000000..053547c1 --- /dev/null +++ b/libs/mapper/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (C) 2024 by Andrew Zolotuhkin + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libs/mapper/README.md b/libs/mapper/README.md index f2e54b7a..32bfc361 100644 --- a/libs/mapper/README.md +++ b/libs/mapper/README.md @@ -3,7 +3,7 @@ [![CI](https://github.com/cleverbrush/framework/actions/workflows/ci.yml/badge.svg)](https://github.com/cleverbrush/framework/actions/workflows/ci.yml) [![License: BSD-3-Clause](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)](../../LICENSE) -![Coverage](https://img.shields.io/badge/coverage-96.8%25-brightgreen) +![Unit coverage](https://img.shields.io/badge/unit_coverage-93.9%25-brightgreen) A type-safe, declarative object mapper for converting objects between different `@cleverbrush/schema` representations. Uses PropertyDescriptors as pointers to properties (similar to expressions in C# .NET) and enforces **compile-time completeness** — TypeScript will produce an error if any target property is not mapped, auto-mapped, or explicitly ignored. diff --git a/libs/mapper/package.json b/libs/mapper/package.json index 6c457f6c..b8d75392 100644 --- a/libs/mapper/package.json +++ b/libs/mapper/package.json @@ -20,7 +20,7 @@ "data transformation", "cleverbrush" ], - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "main": "./dist/index.js", "exports": { ".": { @@ -30,6 +30,9 @@ }, "sideEffects": false, "name": "@cleverbrush/mapper", + "engines": { + "node": ">=24" + }, "readme": "https://github.com/cleverbrush/framework/tree/master/libs/mapper#readme", "repository": { "type": "git", diff --git a/libs/orm-cli/LICENSE b/libs/orm-cli/LICENSE new file mode 100644 index 00000000..053547c1 --- /dev/null +++ b/libs/orm-cli/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (C) 2024 by Andrew Zolotuhkin + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libs/orm-cli/README.md b/libs/orm-cli/README.md index 3a82bf41..929da28b 100644 --- a/libs/orm-cli/README.md +++ b/libs/orm-cli/README.md @@ -1,4 +1,7 @@ # `@cleverbrush/orm-cli` + +![Unit coverage](https://img.shields.io/badge/unit_coverage-61.2%25-yellow) + A standalone CLI for managing PostgreSQL schema migrations for projects built with [`@cleverbrush/orm`](https://github.com/cleverbrush/framework). @@ -95,7 +98,7 @@ npx cb-orm migrate run # ✓ 20260423120000_add_role_column.ts ``` -Use `--to ` to migrate up to a specific file: +Use `--to ` to apply that one pending file (not every preceding file): ```sh npx cb-orm migrate run --to 20260423000001_init.ts @@ -157,7 +160,7 @@ npx cb-orm db push --yes # skip confirmation |------|----------|-------------| | `--config ` | all | Path to config file (default: `db.config.ts` in cwd) | | `--dir ` | all | Migrations directory (overrides `config.migrations.directory`) | -| `--to ` | `migrate run` | Apply up to a specific migration filename | +| `--to ` | `migrate run` | Apply one pending migration filename | | `--all` | `migrate rollback` | Roll back all batches | | `--yes` | `db push` | Skip the interactive confirmation prompt | diff --git a/libs/orm-cli/package.json b/libs/orm-cli/package.json index 6bbe901d..e1a53ebc 100644 --- a/libs/orm-cli/package.json +++ b/libs/orm-cli/package.json @@ -27,7 +27,7 @@ "cli", "cleverbrush" ], - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "main": "./dist/index.js", "exports": { ".": { @@ -37,6 +37,9 @@ }, "sideEffects": false, "name": "@cleverbrush/orm-cli", + "engines": { + "node": ">=24" + }, "readme": "https://github.com/cleverbrush/framework/tree/master/libs/orm-cli#readme", "repository": { "type": "git", diff --git a/libs/orm-cli/src/cli.test.ts b/libs/orm-cli/src/cli.test.ts index 768ae8e7..40a47ed3 100644 --- a/libs/orm-cli/src/cli.test.ts +++ b/libs/orm-cli/src/cli.test.ts @@ -189,14 +189,10 @@ describe('generate command', () => { // ═══════════════════════════════════════════════════════════════════════════ describe('push command — production guard', () => { - it('exits 1 in NODE_ENV=production without --yes', async () => { + it('rejects production writes without --yes before opening a transaction', async () => { const originalEnv = process.env.NODE_ENV; process.env.NODE_ENV = 'production'; - const exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => { - throw new Error('process.exit called'); - }) as any); - const { push } = await import('./commands/push.js'); const config = { knex: {} as any, @@ -204,10 +200,9 @@ describe('push command — production guard', () => { migrations: { directory: './migrations' } }; - await expect(push(config, {})).rejects.toThrow('process.exit called'); - expect(exitSpy).toHaveBeenCalledWith(1); - - exitSpy.mockRestore(); + await expect(push(config, {})).rejects.toThrow( + 'without the --yes flag' + ); process.env.NODE_ENV = originalEnv; }); }); @@ -262,15 +257,12 @@ describe('validate command', () => { } }); - it('exits 1 when schema drift is detected', async () => { + it('reports schema drift to the command router', async () => { _mockValidateEntitiesAgainstDatabase.mockResolvedValueOnce({ valid: false, checkedTables: ['users'], issues: [{ type: 'missing-table', tableName: 'users' }] }); - const exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => { - throw new Error('process.exit called'); - }) as any); const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); const { validate } = await import('./commands/validate.js'); @@ -280,13 +272,11 @@ describe('validate command', () => { migrations: { directory: './migrations' } }; - await expect(validate(config)).rejects.toThrow('process.exit called'); - expect(exitSpy).toHaveBeenCalledWith(1); + await expect(validate(config)).rejects.toThrow('Schema drift detected'); expect(errSpy.mock.calls.flat().join('\n')).toMatch( /Missing table: users/ ); - exitSpy.mockRestore(); errSpy.mockRestore(); }); }); @@ -332,49 +322,76 @@ describe('cli.run — knex pool cleanup', () => { } }); - it('calls knex.destroy() even when the command throws', async () => { - const destroy = vi.fn().mockResolvedValue(undefined); - const fakeKnex = { - schema: {}, - destroy, - migrate: { - list: vi.fn().mockRejectedValue(new Error('boom')) - } - }; - - const exitSpy = vi.spyOn(process, 'exit').mockImplementation((() => { - throw new Error('process.exit called'); - }) as any); - const errSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); - - const tmp = path.join(os.tmpdir(), `orm-cli-destroy-err-${Date.now()}`); - mkdirSync(tmp, { recursive: true }); - const cfgPath = path.join(tmp, 'db.config.mjs'); - const stash = (globalThis as any).__cbOrmFakeKnex; - (globalThis as any).__cbOrmFakeKnex = fakeKnex; - try { - const cfgSrc = ` + it.each(['migrate', 'validate', 'db'])( + 'closes the pool before %s command failure exits', + async command => { + const originalEnv = process.env.NODE_ENV; + process.env.NODE_ENV = 'production'; + _mockValidateEntitiesAgainstDatabase.mockResolvedValue({ + valid: false, + checkedTables: [], + issues: [] + }); + const destroy = vi.fn().mockResolvedValue(undefined); + const fakeKnex = { + schema: {}, + destroy, + migrate: { + list: vi.fn().mockRejectedValue(new Error('boom')) + } + }; + + const exitSpy = vi + .spyOn(process, 'exit') + .mockImplementation((() => { + throw new Error('process.exit called'); + }) as any); + const errSpy = vi + .spyOn(console, 'error') + .mockImplementation(() => {}); + + const tmp = path.join( + os.tmpdir(), + `orm-cli-destroy-err-${Date.now()}` + ); + mkdirSync(tmp, { recursive: true }); + const cfgPath = path.join(tmp, 'db.config.mjs'); + const stash = (globalThis as any).__cbOrmFakeKnex; + (globalThis as any).__cbOrmFakeKnex = fakeKnex; + try { + const cfgSrc = ` export default { knex: globalThis.__cbOrmFakeKnex, entities: {}, migrations: { directory: ${JSON.stringify(tmp)} } }; `; - const fs = await import('node:fs'); - fs.writeFileSync(cfgPath, cfgSrc, 'utf-8'); - - const { run } = await import('./cli.js'); - await expect( - run(['migrate', 'status', '--config', cfgPath]) - ).rejects.toThrow('process.exit called'); - - expect(destroy).toHaveBeenCalledTimes(1); - expect(exitSpy).toHaveBeenCalledWith(1); - } finally { - (globalThis as any).__cbOrmFakeKnex = stash; - exitSpy.mockRestore(); - errSpy.mockRestore(); - rmSync(tmp, { recursive: true, force: true }); + const fs = await import('node:fs'); + fs.writeFileSync(cfgPath, cfgSrc, 'utf-8'); + + const { run } = await import('./cli.js'); + await expect( + run([ + command, + ...(command === 'migrate' + ? ['status'] + : command === 'db' + ? ['push'] + : []), + '--config', + cfgPath + ]) + ).rejects.toThrow('process.exit called'); + + expect(destroy).toHaveBeenCalledTimes(1); + expect(exitSpy).toHaveBeenCalledWith(1); + } finally { + process.env.NODE_ENV = originalEnv; + (globalThis as any).__cbOrmFakeKnex = stash; + exitSpy.mockRestore(); + errSpy.mockRestore(); + rmSync(tmp, { recursive: true, force: true }); + } } - }); + ); }); diff --git a/libs/orm-cli/src/cli.ts b/libs/orm-cli/src/cli.ts index 6519947e..2e7ba63f 100644 --- a/libs/orm-cli/src/cli.ts +++ b/libs/orm-cli/src/cli.ts @@ -1,12 +1,13 @@ // @cleverbrush/orm-cli — main command router -import { createRequire } from 'node:module'; +import { readFileSync } from 'node:fs'; import { loadConfig } from './config.js'; import type { OrmCliConfig } from './types.js'; // Read the CLI's own version at startup -const _require = createRequire(import.meta.url); -const _pkg = _require('../package.json') as { version: string }; +const _pkg = JSON.parse( + readFileSync(new URL('../package.json', import.meta.url), 'utf8') +) as { version: string }; export async function run(argv: string[]): Promise { const [cmd, sub, ...rest] = argv; @@ -170,7 +171,7 @@ COMMANDS OPTIONS --config Path to db.config.ts (default: db.config.ts in cwd) --dir Migrations directory (overrides config.migrations.directory) - --to (migrate run) Apply up to a specific migration by filename + --to (migrate run) Apply one pending migration by filename --all (migrate rollback) Roll back all applied migrations --yes (db push) Skip the confirmation prompt --help, -h Show this help diff --git a/libs/orm-cli/src/commands/push.ts b/libs/orm-cli/src/commands/push.ts index 51e167b2..a7785fc4 100644 --- a/libs/orm-cli/src/commands/push.ts +++ b/libs/orm-cli/src/commands/push.ts @@ -36,11 +36,10 @@ export async function push( const confirmed = flags['--yes'] === true; if (isProduction && !confirmed) { - console.error( - '\nError: `db push` is not allowed in NODE_ENV=production ' + - 'without the --yes flag.\n' + // Let the command router close the database pool before exiting. + throw new Error( + '`db push` is not allowed in NODE_ENV=production without the --yes flag' ); - process.exit(1); } if (!confirmed) { diff --git a/libs/orm-cli/src/commands/run.ts b/libs/orm-cli/src/commands/run.ts index d54fa389..86ec8bd8 100644 --- a/libs/orm-cli/src/commands/run.ts +++ b/libs/orm-cli/src/commands/run.ts @@ -4,7 +4,7 @@ import type { OrmCliConfig } from '../types.js'; /** * Apply pending migrations via `knex.migrate.latest`. - * Pass `--to ` to migrate up to a specific file. + * Pass `--to ` to apply that one pending migration file. */ export async function run( config: OrmCliConfig, diff --git a/libs/orm-cli/src/commands/validate.ts b/libs/orm-cli/src/commands/validate.ts index 1d2047af..96135d65 100644 --- a/libs/orm-cli/src/commands/validate.ts +++ b/libs/orm-cli/src/commands/validate.ts @@ -36,7 +36,8 @@ export async function validate(config: OrmCliConfig): Promise { } } - process.exit(1); + // Let the command router close the database pool before exiting. + throw new Error('Schema drift detected'); } function summarizeDiff(diff: MigrationDiff): string { diff --git a/libs/orm-cli/src/config.test.ts b/libs/orm-cli/src/config.test.ts index a9fa85ed..6080c5ac 100644 --- a/libs/orm-cli/src/config.test.ts +++ b/libs/orm-cli/src/config.test.ts @@ -33,6 +33,26 @@ export default { knex, entities, migrations }; `; describe('loadConfig', () => { + it.each(['', ' '])( + 'rejects an empty migrations directory %j', + async directory => { + dir = mkdtempSync(path.join(os.tmpdir(), 'cfg-')); + const file = writeConfig( + 'bad.mjs', + `export default { knex: { schema: {} }, entities: {}, migrations: { directory: ${JSON.stringify(directory)} } };` + ); + await expect(loadConfig(file)).rejects.toThrow(/non-empty/); + } + ); + + it('rejects arrays as the entity registry', async () => { + dir = mkdtempSync(path.join(os.tmpdir(), 'cfg-')); + const file = writeConfig( + 'bad.mjs', + 'export default { knex: { schema: {} }, entities: [], migrations: { directory: "./m" } };' + ); + await expect(loadConfig(file)).rejects.toThrow(/entities/); + }); it('throws when explicit configPath does not exist', async () => { dir = mkdtempSync(path.join(os.tmpdir(), 'cfg-')); await expect(loadConfig(path.join(dir, 'missing.ts'))).rejects.toThrow( diff --git a/libs/orm-cli/src/config.ts b/libs/orm-cli/src/config.ts index 6c4dc6d0..f396bcc1 100644 --- a/libs/orm-cli/src/config.ts +++ b/libs/orm-cli/src/config.ts @@ -66,12 +66,20 @@ function assertConfig(config: unknown): asserts config is OrmCliConfig { if (!c.knex || typeof (c.knex as any).schema !== 'object') { throw new Error('config.knex must be a Knex instance.'); } - if (!c.entities || typeof c.entities !== 'object') { + if ( + !c.entities || + typeof c.entities !== 'object' || + Array.isArray(c.entities) + ) { throw new Error( 'config.entities must be an object mapping names to Entity instances.' ); } - if (!c.migrations || typeof (c.migrations as any).directory !== 'string') { + if ( + !c.migrations || + typeof (c.migrations as any).directory !== 'string' || + !(c.migrations as any).directory.trim() + ) { throw new Error( 'config.migrations.directory must be a non-empty string path.' ); diff --git a/libs/orm-cli/tsconfig.build.json b/libs/orm-cli/tsconfig.build.json index 7954e377..cc528c23 100644 --- a/libs/orm-cli/tsconfig.build.json +++ b/libs/orm-cli/tsconfig.build.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node"], "rootDir": "./src", "outDir": "./dist", "declaration": true, diff --git a/libs/orm/LICENSE b/libs/orm/LICENSE new file mode 100644 index 00000000..053547c1 --- /dev/null +++ b/libs/orm/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (C) 2024 by Andrew Zolotuhkin + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libs/orm/README.md b/libs/orm/README.md index 3bc4e3ee..3be4a538 100644 --- a/libs/orm/README.md +++ b/libs/orm/README.md @@ -1,4 +1,7 @@ # `@cleverbrush/orm` + +![Unit coverage](https://img.shields.io/badge/unit_coverage-81%25-green) + EF-Core-like typed ORM layer on top of [`@cleverbrush/knex-schema`](../knex-schema). diff --git a/libs/orm/package.json b/libs/orm/package.json index 971559d3..57c787cb 100644 --- a/libs/orm/package.json +++ b/libs/orm/package.json @@ -31,7 +31,7 @@ "dbset", "cleverbrush" ], - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "main": "./dist/index.js", "exports": { ".": { @@ -41,6 +41,9 @@ }, "sideEffects": false, "name": "@cleverbrush/orm", + "engines": { + "node": ">=24" + }, "readme": "https://github.com/cleverbrush/framework/tree/master/libs/orm#readme", "repository": { "type": "git", diff --git a/libs/orm/src/errors.ts b/libs/orm/src/errors.ts index 8307a051..a440d60b 100644 --- a/libs/orm/src/errors.ts +++ b/libs/orm/src/errors.ts @@ -1,7 +1,7 @@ // @cleverbrush/orm — typed error classes /** - * Thrown by {@link DbSet.findOrFail} when no row matches the supplied + * Thrown by `DbSet.findOrFail` when no row matches the supplied * primary key value(s). The `entity` field carries the SQL table name (or * the entity's schema name when a table name is unavailable); `pk` is the * exact value (scalar or tuple) that was looked up. @@ -32,7 +32,7 @@ export class EntityNotFoundError extends Error { } /** - * Thrown by {@link DbContext.saveChanges} when an UPDATE or DELETE detects + * Thrown by `DbContext.saveChanges` when an UPDATE or DELETE detects * that the row's row-version column (`.rowVersion()`) has changed since the * entity was last loaded — indicating a concurrent modification. * @@ -63,7 +63,7 @@ export class ConcurrencyError extends Error { } /** - * Thrown by {@link DbContext.saveChanges} when an invariant is violated on + * Thrown by `DbContext.saveChanges` when an invariant is violated on * a tracked entity — e.g. the primary key or discriminator column was mutated. * * @public diff --git a/libs/orm/tsconfig.build.json b/libs/orm/tsconfig.build.json index 2082ba2d..f364027a 100644 --- a/libs/orm/tsconfig.build.json +++ b/libs/orm/tsconfig.build.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "types": ["node"], "rootDir": "./src", "outDir": "./dist", "declaration": true, diff --git a/libs/otel/LICENSE b/libs/otel/LICENSE new file mode 100644 index 00000000..053547c1 --- /dev/null +++ b/libs/otel/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (C) 2024 by Andrew Zolotuhkin + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libs/otel/README.md b/libs/otel/README.md index e9a06869..5a734702 100644 --- a/libs/otel/README.md +++ b/libs/otel/README.md @@ -1,4 +1,7 @@ # @cleverbrush/otel + +![Unit coverage](https://img.shields.io/badge/unit_coverage-89%25-green) + OpenTelemetry instrumentation for the Cleverbrush framework — traces, logs, and metrics over OTLP for `@cleverbrush/server`, `@cleverbrush/orm`, and `@cleverbrush/log`. Designed to ship straight into SigNoz, Grafana Tempo, Jaeger, or any OTLP-compatible backend. diff --git a/libs/otel/package.json b/libs/otel/package.json index 8fe90f79..b98dbab4 100644 --- a/libs/otel/package.json +++ b/libs/otel/package.json @@ -20,7 +20,7 @@ "logs", "metrics" ], - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "main": "./dist/index.js", "exports": { ".": { @@ -38,6 +38,9 @@ }, "sideEffects": false, "name": "@cleverbrush/otel", + "engines": { + "node": ">=24" + }, "readme": "https://github.com/cleverbrush/framework/tree/master/libs/otel#readme", "repository": { "type": "git", @@ -50,24 +53,24 @@ }, "dependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/api-logs": "^0.215.0", - "@opentelemetry/context-async-hooks": "^2.7.0", - "@opentelemetry/core": "^2.7.0", - "@opentelemetry/exporter-logs-otlp-http": "^0.215.0", - "@opentelemetry/exporter-metrics-otlp-http": "^0.215.0", - "@opentelemetry/exporter-trace-otlp-http": "^0.215.0", - "@opentelemetry/resources": "^2.7.0", - "@opentelemetry/sdk-logs": "^0.215.0", - "@opentelemetry/sdk-metrics": "^2.7.0", - "@opentelemetry/sdk-node": "^0.215.0", - "@opentelemetry/sdk-trace-node": "^2.7.0", + "@opentelemetry/api-logs": "^0.222.0", + "@opentelemetry/context-async-hooks": "^2.11.0", + "@opentelemetry/core": "^2.11.0", + "@opentelemetry/exporter-logs-otlp-http": "^0.222.0", + "@opentelemetry/exporter-metrics-otlp-http": "^0.222.0", + "@opentelemetry/exporter-trace-otlp-http": "^0.222.0", + "@opentelemetry/resources": "^2.11.0", + "@opentelemetry/sdk-logs": "^0.222.0", + "@opentelemetry/sdk-metrics": "^2.11.0", + "@opentelemetry/sdk-node": "^0.222.0", + "@opentelemetry/sdk-trace-node": "^2.11.0", "@opentelemetry/semantic-conventions": "^1.40.0" }, "peerDependencies": { "@cleverbrush/di": "^4.0.0", "@cleverbrush/log": "^4.0.0", "@cleverbrush/server": "^4.0.0", - "@opentelemetry/instrumentation-http": "^0.215.0", + "@opentelemetry/instrumentation-http": "^0.222.0", "@opentelemetry/instrumentation-runtime-node": "^0.28.0", "@opentelemetry/instrumentation-undici": "^0.25.0", "knex": "^3.0.0" @@ -99,7 +102,7 @@ "@cleverbrush/di": "*", "@cleverbrush/log": "*", "@cleverbrush/server": "*", - "@opentelemetry/instrumentation-http": "^0.215.0", + "@opentelemetry/instrumentation-http": "^0.222.0", "@opentelemetry/instrumentation-runtime-node": "^0.28.0", "@opentelemetry/instrumentation-undici": "^0.25.0", "@types/node": "^25.4.0", diff --git a/libs/otel/src/di.ts b/libs/otel/src/di.ts index 9a352802..09f671bc 100644 --- a/libs/otel/src/di.ts +++ b/libs/otel/src/di.ts @@ -4,7 +4,7 @@ import { type Meter, metrics, type Tracer, trace } from '@opentelemetry/api'; * DI service key for an OpenTelemetry `Tracer`. * * Resolved from the global `TracerProvider` set by - * {@link import('./setupOtel.js').setupOtel}. Components that prefer + * `setupOtel`. Components that prefer * dependency injection over the global API can inject this token. */ export const ITracer = Symbol.for('ITracer') as unknown as { @@ -15,7 +15,7 @@ export const ITracer = Symbol.for('ITracer') as unknown as { * DI service key for an OpenTelemetry `Meter`. * * Resolved from the global `MeterProvider` set by - * {@link import('./setupOtel.js').setupOtel}. + * `setupOtel`. */ export const IMeter = Symbol.for('IMeter') as unknown as { __brand: 'IMeter'; @@ -47,7 +47,7 @@ export interface ConfigureOtelOptions { * Registers OTel `Tracer` and `Meter` instances in the DI container. * * Both are resolved lazily from the global providers configured by - * {@link import('./setupOtel.js').setupOtel}, so this helper can be + * `setupOtel`, so this helper can be * called at DI setup time even before the SDK has fully started. * * @param services - the `ServiceCollection` to register with diff --git a/libs/otel/src/enrichers/trace.ts b/libs/otel/src/enrichers/trace.ts index fb2f36a5..68a6a2c1 100644 --- a/libs/otel/src/enrichers/trace.ts +++ b/libs/otel/src/enrichers/trace.ts @@ -7,7 +7,7 @@ import { trace } from '@opentelemetry/api'; * * Reads from the OpenTelemetry context via `@opentelemetry/api`, * so it works with any tracer provider — including the one - * configured by {@link import('../setupOtel.js').setupOtel}. + * configured by `setupOtel`. * * No-op when no span is active. * diff --git a/libs/otel/src/instrumentations.ts b/libs/otel/src/instrumentations.ts index ea3391cd..596e1788 100644 --- a/libs/otel/src/instrumentations.ts +++ b/libs/otel/src/instrumentations.ts @@ -26,7 +26,7 @@ const requireFromHere = createRequire(import.meta.url); * npm install @opentelemetry/instrumentation-http @opentelemetry/instrumentation-undici * ``` * - * @returns an array of instrumentation instances ready to pass to {@link import('./setupOtel.js').setupOtel} + * @returns an array of instrumentation instances ready to pass to `setupOtel` * * @example * ```ts diff --git a/libs/otel/src/setupOtel.config.test.ts b/libs/otel/src/setupOtel.config.test.ts new file mode 100644 index 00000000..f71aa1dd --- /dev/null +++ b/libs/otel/src/setupOtel.config.test.ts @@ -0,0 +1,30 @@ +import { expect, it, vi } from 'vitest'; +import { setupOtel } from './setupOtel.js'; + +const { configured } = vi.hoisted(() => ({ configured: vi.fn() })); +vi.mock('@opentelemetry/sdk-node', () => ({ + NodeSDK: class { + constructor(options: unknown) { + configured(options); + } + start() {} + async shutdown() {} + } +})); + +it('explicitly disables every signal without allowing SDK environment defaults', async () => { + const handle = setupOtel({ + serviceName: 'disabled-signals', + disableTraces: true, + disableLogs: true, + disableMetrics: true + }); + expect(configured).toHaveBeenCalledWith( + expect.objectContaining({ + spanProcessors: [], + logRecordProcessors: [], + metricReaders: [] + }) + ); + await handle.shutdown(); +}); diff --git a/libs/otel/src/setupOtel.ts b/libs/otel/src/setupOtel.ts index f74a5ca6..bc1b6882 100644 --- a/libs/otel/src/setupOtel.ts +++ b/libs/otel/src/setupOtel.ts @@ -212,7 +212,9 @@ export function setupOtel(config: OtelConfig): OtelHandle { ...(url ? { url } : {}), ...(headers ? { headers } : {}) }); - logRecordProcessors = [new BatchLogRecordProcessor(logExporter)]; + logRecordProcessors = [ + new BatchLogRecordProcessor({ exporter: logExporter }) + ]; } let metricReader: PeriodicExportingMetricReader | undefined; @@ -234,9 +236,11 @@ export function setupOtel(config: OtelConfig): OtelHandle { const sdk = new NodeSDK({ resource, - ...(spanProcessors ? { spanProcessors } : {}), - ...(logRecordProcessors ? { logRecordProcessors } : {}), - ...(metricReader ? { metricReaders: [metricReader] } : {}), + // Explicit empty lists disable exporters; omitted options let the SDK + // re-enable defaults from the environment. + spanProcessors: spanProcessors ?? [], + logRecordProcessors: logRecordProcessors ?? [], + metricReaders: metricReader ? [metricReader] : [], instrumentations: (config.instrumentations ?? []) as any }); diff --git a/libs/otel/src/sinks/OtelLogSink.test.ts b/libs/otel/src/sinks/OtelLogSink.test.ts index f03dbe11..5cce6715 100644 --- a/libs/otel/src/sinks/OtelLogSink.test.ts +++ b/libs/otel/src/sinks/OtelLogSink.test.ts @@ -16,7 +16,7 @@ import { otelLogSink } from './OtelLogSink.js'; const logExporter = new InMemoryLogRecordExporter(); const loggerProvider = new LoggerProvider({ - processors: [new SimpleLogRecordProcessor(logExporter)] + processors: [new SimpleLogRecordProcessor({ exporter: logExporter })] }); const spanExporter = new InMemorySpanExporter(); diff --git a/libs/react-form/LICENSE b/libs/react-form/LICENSE new file mode 100644 index 00000000..053547c1 --- /dev/null +++ b/libs/react-form/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (C) 2024 by Andrew Zolotuhkin + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libs/react-form/README.md b/libs/react-form/README.md index e0a4c171..8cb45134 100644 --- a/libs/react-form/README.md +++ b/libs/react-form/README.md @@ -3,7 +3,7 @@ [![CI](https://github.com/cleverbrush/framework/actions/workflows/ci.yml/badge.svg)](https://github.com/cleverbrush/framework/actions/workflows/ci.yml) [![License: BSD-3-Clause](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)](../../LICENSE) -![Coverage](https://img.shields.io/badge/coverage-98.7%25-brightgreen) +![Unit coverage](https://img.shields.io/badge/unit_coverage-95.5%25-brightgreen) A headless, schema-driven form system for React based on `@cleverbrush/schema`. Uses PropertyDescriptors for type-safe field binding, supports global UI renderer configuration via a provider, and is completely UI-agnostic — works with plain HTML, MUI, Ant Design, or any component library. diff --git a/libs/react-form/package.json b/libs/react-form/package.json index e107bd94..5d172e99 100644 --- a/libs/react-form/package.json +++ b/libs/react-form/package.json @@ -26,7 +26,7 @@ "form system", "cleverbrush" ], - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "main": "./dist/index.js", "exports": { ".": { @@ -36,6 +36,9 @@ }, "sideEffects": false, "name": "@cleverbrush/react-form", + "engines": { + "node": ">=24" + }, "readme": "https://github.com/cleverbrush/framework/tree/master/libs/react-form#readme", "repository": { "type": "git", diff --git a/libs/react-form/src/declarations.test.ts b/libs/react-form/src/declarations.test.ts index 32f9d9c1..b2e18595 100644 --- a/libs/react-form/src/declarations.test.ts +++ b/libs/react-form/src/declarations.test.ts @@ -34,4 +34,6 @@ test('consumers can emit declarations for exported inferred form systems', () => ) ).toEqual([]); expect(output.join('\n')).toContain('TypedFormSystem'); -}); + // In-process TypeScript declaration emit is substantially slower with V8 + // coverage on shared CI runners than ordinary form behavior tests. +}, 60_000); diff --git a/libs/react-form/src/issues.test.tsx b/libs/react-form/src/issues.test.tsx index 3d3a07f9..356894f1 100644 --- a/libs/react-form/src/issues.test.tsx +++ b/libs/react-form/src/issues.test.tsx @@ -106,34 +106,34 @@ test('clearing issues preserves local errors, and reset clears all issue state', expect(result.current.name.error).toBeUndefined(); expect(result.current.form.error).toBeUndefined(); }); -test.each([ - 'edit', - 'reset', - 'unmount' -] as const)('ignores late issues after %s', async action => { - const { result, unmount } = ready(); - let resolve!: (result: any) => void; - const pending = new Promise(r => { - resolve = r; - }); - let submit!: Promise; - await act(async () => { - submit = result.current.form.handleSubmit(() => pending)(); - }); - if (action === 'edit') - await act(async () => result.current.name.onChange('Grace')); - if (action === 'reset') act(() => result.current.form.reset()); - if (action === 'unmount') unmount(); - await act(async () => { - resolve({ - ok: false, - error: 'Rejected', - issues: [{ pointer: '/name', detail: 'Old issue' }] +test.each(['edit', 'reset', 'unmount'] as const)( + 'ignores late issues after %s', + async action => { + const { result, unmount } = ready(); + let resolve!: (result: any) => void; + const pending = new Promise(r => { + resolve = r; }); - await submit; - }); - if (action !== 'unmount') expect(result.current.name.error).toBeUndefined(); -}); + let submit!: Promise; + await act(async () => { + submit = result.current.form.handleSubmit(() => pending)(); + }); + if (action === 'edit') + await act(async () => result.current.name.onChange('Grace')); + if (action === 'reset') act(() => result.current.form.reset()); + if (action === 'unmount') unmount(); + await act(async () => { + resolve({ + ok: false, + error: 'Rejected', + issues: [{ pointer: '/name', detail: 'Old issue' }] + }); + await submit; + }); + if (action !== 'unmount') + expect(result.current.name.error).toBeUndefined(); + } +); test('local validation reaches indexed fields and programmatic array replacement clears stale issues', async () => { const { result } = ready(); await act(async () => result.current.city.onChange('')); diff --git a/libs/react-form/src/lifecycle.test.tsx b/libs/react-form/src/lifecycle.test.tsx index 38416931..2d2d0017 100644 --- a/libs/react-form/src/lifecycle.test.tsx +++ b/libs/react-form/src/lifecycle.test.tsx @@ -234,45 +234,44 @@ describe('synchronized mounted fields', () => { }); describe('validation generations', () => { - test.each([ - 'reset', - 'setValue', - 'onChange' - ] as const)('ignores old async results after %s', async action => { - vi.useFakeTimers(); - const pending = deferred<{ - valid: boolean; - errors: { message: string }[]; - }>(); - const asyncSchema = object({ - name: string().addValidator(() => pending.promise) - }); - const { result } = renderHook(() => { - const form = useSchemaForm(asyncSchema, { - validationDebounceMs: 100 + test.each(['reset', 'setValue', 'onChange'] as const)( + 'ignores old async results after %s', + async action => { + vi.useFakeTimers(); + const pending = deferred<{ + valid: boolean; + errors: { message: string }[]; + }>(); + const asyncSchema = object({ + name: string().addValidator(() => pending.promise) }); - return { form, field: form.useField(t => t.name) }; - }); - act(() => result.current.form.reset({ name: 'old' })); - let validation!: ReturnType; - act(() => { - validation = result.current.form.validate(); - }); - act(() => { - if (action === 'onChange') result.current.field.onChange('new'); - else result.current.form[action]({ name: 'new' }); - }); - await act(async () => { - pending.resolve({ - valid: false, - errors: [{ message: 'obsolete' }] + const { result } = renderHook(() => { + const form = useSchemaForm(asyncSchema, { + validationDebounceMs: 100 + }); + return { form, field: form.useField(t => t.name) }; }); - await validation; - }); - expect(result.current.field.value).toBe('new'); - expect(result.current.field.error).toBeUndefined(); - expect(result.current.field.validating).toBe(false); - }); + act(() => result.current.form.reset({ name: 'old' })); + let validation!: ReturnType; + act(() => { + validation = result.current.form.validate(); + }); + act(() => { + if (action === 'onChange') result.current.field.onChange('new'); + else result.current.form[action]({ name: 'new' }); + }); + await act(async () => { + pending.resolve({ + valid: false, + errors: [{ message: 'obsolete' }] + }); + await validation; + }); + expect(result.current.field.value).toBe('new'); + expect(result.current.field.error).toBeUndefined(); + expect(result.current.field.validating).toBe(false); + } + ); test('reset and unmount cancel scheduled validation', async () => { vi.useFakeTimers(); const validator = vi.fn(() => ({ valid: true })); @@ -393,30 +392,30 @@ describe('submission lifecycle', () => { }); expect(result.current.submitting).toBe(false); }); - test.each([ - 'reset', - 'unmount' - ] as const)('ignores obsolete success after %s', async action => { - const { result, unmount } = ready(); - const pending = deferred(); - const success = vi.fn(); - let submitted!: Promise; - await act(async () => { - submitted = result.current.handleSubmit(() => pending.promise, { - onSuccess: success - })(); - }); - if (action === 'reset') - act(() => result.current.reset({ name: 'New' })); - else unmount(); - await act(async () => { - pending.resolve(); - await submitted; - }); - expect(success).not.toHaveBeenCalled(); - if (action === 'reset') - expect(result.current.getValue()).toEqual({ name: 'New' }); - }); + test.each(['reset', 'unmount'] as const)( + 'ignores obsolete success after %s', + async action => { + const { result, unmount } = ready(); + const pending = deferred(); + const success = vi.fn(); + let submitted!: Promise; + await act(async () => { + submitted = result.current.handleSubmit(() => pending.promise, { + onSuccess: success + })(); + }); + if (action === 'reset') + act(() => result.current.reset({ name: 'New' })); + else unmount(); + await act(async () => { + pending.resolve(); + await submitted; + }); + expect(success).not.toHaveBeenCalled(); + if (action === 'reset') + expect(result.current.getValue()).toEqual({ name: 'New' }); + } + ); test('does not dispatch values changed during asynchronous validation', async () => { const pending = deferred<{ valid: true }>(); const guarded = object({ diff --git a/libs/scheduler-postgres/LICENSE b/libs/scheduler-postgres/LICENSE new file mode 100644 index 00000000..053547c1 --- /dev/null +++ b/libs/scheduler-postgres/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (C) 2024 by Andrew Zolotuhkin + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libs/scheduler-postgres/README.md b/libs/scheduler-postgres/README.md index 7131e87e..9ef51c5d 100644 --- a/libs/scheduler-postgres/README.md +++ b/libs/scheduler-postgres/README.md @@ -1,4 +1,7 @@ # @cleverbrush/scheduler-postgres + +![Unit coverage](https://img.shields.io/badge/unit_coverage-11.2%25-red) + PostgreSQL persistence for [@cleverbrush/scheduler](../scheduler/README.md). Uses @cleverbrush/orm and @cleverbrush/knex-schema for schemas, migrations and diff --git a/libs/scheduler-postgres/package.json b/libs/scheduler-postgres/package.json index 4ffdac09..eb02a05f 100644 --- a/libs/scheduler-postgres/package.json +++ b/libs/scheduler-postgres/package.json @@ -1,5 +1,8 @@ { "name": "@cleverbrush/scheduler-postgres", + "engines": { + "node": ">=24" + }, "version": "4.4.3", "description": "Transactional PostgreSQL repository for the Cleverbrush durable scheduler", "type": "module", diff --git a/libs/scheduler/LICENSE b/libs/scheduler/LICENSE new file mode 100644 index 00000000..053547c1 --- /dev/null +++ b/libs/scheduler/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (C) 2024 by Andrew Zolotuhkin + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libs/scheduler/README.md b/libs/scheduler/README.md index 9cddfeda..0ce773b4 100644 --- a/libs/scheduler/README.md +++ b/libs/scheduler/README.md @@ -1,4 +1,7 @@ # @cleverbrush/scheduler + +![Unit coverage](https://img.shields.io/badge/unit_coverage-83.4%25-green) + Typed immediate, delayed and recurring jobs with durable progress. Producers, dispatchers and workers share one execution model, with separate lifecycles. diff --git a/libs/scheduler/package.json b/libs/scheduler/package.json index 9408ea48..89326d7a 100644 --- a/libs/scheduler/package.json +++ b/libs/scheduler/package.json @@ -21,7 +21,7 @@ "scheduler", "cleverbrush" ], - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "main": "./dist/index.js", "exports": { ".": { @@ -31,6 +31,9 @@ }, "sideEffects": false, "name": "@cleverbrush/scheduler", + "engines": { + "node": ">=24" + }, "readme": "https://github.com/cleverbrush/framework/tree/master/libs/scheduler#readme", "repository": { "type": "git", diff --git a/libs/scheduler/src/calendar.test.ts b/libs/scheduler/src/calendar.test.ts index 898ecf04..6c1b935e 100644 --- a/libs/scheduler/src/calendar.test.ts +++ b/libs/scheduler/src/calendar.test.ts @@ -63,17 +63,18 @@ describe('native calendar and time zones', () => { executable ); }); - it.each([ - 0, 1, 22, 99, -1 - ])('preserves ISO year %s, including eras', year => { - const wall = calendarDate(year, 1, 1, 0, 0, 0, 123); - expect(wall.getUTCFullYear()).toBe(year); - expect(localDate(wall.getTime(), 'Etc/UTC')).toEqual(wall); - expect(resolveLocal(wall, 'Etc/UTC')).toEqual({ - at: wall.getTime(), - executable: true - }); - }); + it.each([0, 1, 22, 99, -1])( + 'preserves ISO year %s, including eras', + year => { + const wall = calendarDate(year, 1, 1, 0, 0, 0, 123); + expect(wall.getUTCFullYear()).toBe(year); + expect(localDate(wall.getTime(), 'Etc/UTC')).toEqual(wall); + expect(resolveLocal(wall, 'Etc/UTC')).toEqual({ + at: wall.getTime(), + executable: true + }); + } + ); it('uses calendar arithmetic without changing the input date', () => { const leap = calendarDate(2028, 2, 28, 9); expect(addDays(leap, 1).toISOString()).toBe('2028-02-29T09:00:00.000Z'); diff --git a/libs/scheduler/src/recurrence.test.ts b/libs/scheduler/src/recurrence.test.ts index a8bf2670..5eb42131 100644 --- a/libs/scheduler/src/recurrence.test.ts +++ b/libs/scheduler/src/recurrence.test.ts @@ -67,30 +67,29 @@ describe('calendar recurrence', () => { expect(calc.next().date.getTime()).toBe(8640000000000000); expect(calc.hasNext()).toBe(false); }); - it.each([ - 'UTC', - 'America/Los_Angeles', - 'Asia/Tokyo' - ])('is independent of the host TZ=%s', tz => { - const moduleUrl = new URL('../dist/index.js', import.meta.url).href; - const program = `import { ScheduleCalculator } from ${JSON.stringify(moduleUrl)}; + it.each(['UTC', 'America/Los_Angeles', 'Asia/Tokyo'])( + 'is independent of the host TZ=%s', + tz => { + const moduleUrl = new URL('../dist/index.js', import.meta.url).href; + const program = `import { ScheduleCalculator } from ${JSON.stringify(moduleUrl)}; const calc = new ScheduleCalculator({ every: 'day', hour: 9, timeZone: 'Europe/Berlin', startsOn: new Date('2026-03-28T00:00:00Z') }); process.stdout.write(JSON.stringify([calc.next().date, calc.next().date]));`; - const result = execFileSync( - process.execPath, - ['--input-type=module', '--eval', program], - { - env: { ...process.env, TZ: tz }, - encoding: 'utf8', - timeout: 5000 - } - ); - expect(JSON.parse(result)).toEqual([ - '2026-03-28T08:00:00.000Z', - '2026-03-29T07:00:00.000Z' - ]); - }); + const result = execFileSync( + process.execPath, + ['--input-type=module', '--eval', program], + { + env: { ...process.env, TZ: tz }, + encoding: 'utf8', + timeout: 5000 + } + ); + expect(JSON.parse(result)).toEqual([ + '2026-03-28T08:00:00.000Z', + '2026-03-29T07:00:00.000Z' + ]); + } + ); it('skips DST gaps and selects the earlier repeated wall time once', () => { expect( dates( diff --git a/libs/scheduler/src/repository.test.ts b/libs/scheduler/src/repository.test.ts index a41f15a5..d3a411a2 100644 --- a/libs/scheduler/src/repository.test.ts +++ b/libs/scheduler/src/repository.test.ts @@ -49,60 +49,62 @@ describe('memory repository', () => { { every: 'week', dayOfWeek: [4], next: '2026-01-08T09:00:00Z' }, { every: 'month', day: 1, next: '2026-02-01T09:00:00Z' }, { every: 'year', day: 1, month: 1, next: '2027-01-01T09:00:00Z' } - ] as const)('executes periodic $every jobs with independent dispatcher and worker lifecycles', async ({ - next, - ...rule - }) => { - let now = Date.parse('2026-01-01T09:00:00Z'); - const jobs = new JobScheduler({ - storageRepository: new InMemoryJobRepository({ now: () => now }), - pollIntervalMs: 2 - }); - const job = testJob(); - const worker = jobs.createWorker({ - pollIntervalMs: 2, - jobs: [ - job.handle(async (input, context) => { - await context.report({ percent: 100 }); - return { url: '/' + input.id }; - }) - ] - }); - await jobs.upsertSchedule( - 'periodic', - job, - { id: 'one' }, - { - schedule: { - ...rule, - ...('dayOfWeek' in rule - ? { dayOfWeek: [...rule.dayOfWeek] } - : {}), - maxOccurrences: 2 + ] as const)( + 'executes periodic $every jobs with independent dispatcher and worker lifecycles', + async ({ next, ...rule }) => { + let now = Date.parse('2026-01-01T09:00:00Z'); + const jobs = new JobScheduler({ + storageRepository: new InMemoryJobRepository({ + now: () => now + }), + pollIntervalMs: 2 + }); + const job = testJob(); + const worker = jobs.createWorker({ + pollIntervalMs: 2, + jobs: [ + job.handle(async (input, context) => { + await context.report({ percent: 100 }); + return { url: '/' + input.id }; + }) + ] + }); + await jobs.upsertSchedule( + 'periodic', + job, + { id: 'one' }, + { + schedule: { + ...rule, + ...('dayOfWeek' in rule + ? { dayOfWeek: [...rule.dayOfWeek] } + : {}), + maxOccurrences: 2 + } } - } - ); - try { - await worker.start(); - expect((await jobs.health()).counts.succeeded ?? 0).toBe(0); - await jobs.start(); - await vi.waitFor(async () => - expect((await jobs.health()).counts.succeeded).toBe(1) ); - now = Date.parse(next) - 1; - expect(await jobs.dispatch()).toBe(0); - now++; - await vi.waitFor(async () => - expect((await jobs.health()).counts.succeeded).toBe(2) - ); - expect(await jobs.dispatch()).toBe(0); - expect(worker.lastError).toBeUndefined(); - expect(jobs.lastError).toBeUndefined(); - } finally { - await jobs.stop(); - await worker.stop(); + try { + await worker.start(); + expect((await jobs.health()).counts.succeeded ?? 0).toBe(0); + await jobs.start(); + await vi.waitFor(async () => + expect((await jobs.health()).counts.succeeded).toBe(1) + ); + now = Date.parse(next) - 1; + expect(await jobs.dispatch()).toBe(0); + now++; + await vi.waitFor(async () => + expect((await jobs.health()).counts.succeeded).toBe(2) + ); + expect(await jobs.dispatch()).toBe(0); + expect(worker.lastError).toBeUndefined(); + expect(jobs.lastError).toBeUndefined(); + } finally { + await jobs.stop(); + await worker.stop(); + } } - }); + ); it('shares detached state across repository instances', async () => { const storage = new InMemoryJobStorage(); const a = new JobScheduler({ @@ -117,30 +119,31 @@ describe('memory repository', () => { id: 'one' }); }); - it.each([ - 'coalesce', - 'skip', - 'replay' - ] as const)('applies missed policy %s', async missed => { - const repository = new InMemoryJobRepository(); - const scheduler = new JobScheduler({ storageRepository: repository }); - await scheduler.upsertSchedule( - 'one', - testJob(), - { id: 'one' }, - { - schedule: { - every: 'minute', - startsOn: new Date(Date.now() - 600000) - }, - missed - } - ); - expect(await scheduler.dispatch()).toBe( - missed === 'coalesce' ? 1 : missed === 'skip' ? 0 : 11 - ); - expect(await scheduler.dispatch()).toBe(0); - }); + it.each(['coalesce', 'skip', 'replay'] as const)( + 'applies missed policy %s', + async missed => { + const repository = new InMemoryJobRepository(); + const scheduler = new JobScheduler({ + storageRepository: repository + }); + await scheduler.upsertSchedule( + 'one', + testJob(), + { id: 'one' }, + { + schedule: { + every: 'minute', + startsOn: new Date(Date.now() - 600000) + }, + missed + } + ); + expect(await scheduler.dispatch()).toBe( + missed === 'coalesce' ? 1 : missed === 'skip' ? 0 : 11 + ); + expect(await scheduler.dispatch()).toBe(0); + } + ); it('skips overlap including already queued occurrences', async () => { const scheduler = new JobScheduler({ storageRepository: new InMemoryJobRepository() diff --git a/libs/scheduler/src/repository.ts b/libs/scheduler/src/repository.ts index 74b8bea2..94116c6c 100644 --- a/libs/scheduler/src/repository.ts +++ b/libs/scheduler/src/repository.ts @@ -213,8 +213,7 @@ export class JobRepository { const run = await tx.run(namespace, id, true); const now = await tx.now(); if ( - !run || - run.status !== 'running' || + run?.status !== 'running' || run.leaseToken !== token || run.leaseExpiresAt! <= now ) diff --git a/libs/scheduler/src/schedule-schemas.test.ts b/libs/scheduler/src/schedule-schemas.test.ts index 681a0900..21380aa0 100644 --- a/libs/scheduler/src/schedule-schemas.test.ts +++ b/libs/scheduler/src/schedule-schemas.test.ts @@ -97,22 +97,25 @@ describe('schedule schemas', () => { { every: 'day', skipFirst: Number.MAX_SAFE_INTEGER + 1 }, { every: 'day', timeZone: '+02:00' }, { every: 'day', timeZone: 'Wrong/Zone' } - ])('rejects %j at validation, calculation and registration boundaries', async input => { - expect(ScheduleSchema.validate(input).valid).toBe(false); - expect(() => new ScheduleCalculator(input as any)).toThrow(); - const jobs = new JobScheduler({ - storageRepository: new InMemoryJobRepository() - }); - await expect( - jobs.upsertSchedule( - 'bad', - testJob(), - { id: 'one' }, - { schedule: input as any } - ) - ).rejects.toThrow(); - expect((await jobs.health()).counts.queued ?? 0).toBe(0); - }); + ])( + 'rejects %j at validation, calculation and registration boundaries', + async input => { + expect(ScheduleSchema.validate(input).valid).toBe(false); + expect(() => new ScheduleCalculator(input as any)).toThrow(); + const jobs = new JobScheduler({ + storageRepository: new InMemoryJobRepository() + }); + await expect( + jobs.upsertSchedule( + 'bad', + testJob(), + { id: 'one' }, + { schedule: input as any } + ) + ).rejects.toThrow(); + expect((await jobs.health()).counts.queued ?? 0).toBe(0); + } + ); it('keeps shared constraints after schema composition', () => { for (const every of [ 'minute', diff --git a/libs/schema-json/LICENSE b/libs/schema-json/LICENSE new file mode 100644 index 00000000..053547c1 --- /dev/null +++ b/libs/schema-json/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (C) 2024 by Andrew Zolotuhkin + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libs/schema-json/README.md b/libs/schema-json/README.md index 3084461a..bf82d64e 100644 --- a/libs/schema-json/README.md +++ b/libs/schema-json/README.md @@ -1,6 +1,6 @@ # @cleverbrush/schema-json -![Coverage](https://img.shields.io/badge/coverage-96.8%25-brightgreen) +![Unit coverage](https://img.shields.io/badge/unit_coverage-97%25-brightgreen) Bidirectional JSON Schema (Draft 7 / 2020-12) interop for diff --git a/libs/schema-json/package.json b/libs/schema-json/package.json index e03e0f20..99777769 100644 --- a/libs/schema-json/package.json +++ b/libs/schema-json/package.json @@ -15,7 +15,7 @@ "validation", "cleverbrush" ], - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "main": "./dist/index.js", "exports": { ".": { @@ -25,6 +25,12 @@ }, "sideEffects": false, "name": "@cleverbrush/schema-json", + "engines": { + "node": ">=24" + }, + "devDependencies": { + "@cleverbrush/schema": "*" + }, "peerDependencies": { "@cleverbrush/schema": "^4.0.0", "@standard-schema/spec": "^1.1.0" diff --git a/libs/schema-json/src/boundaries.test.ts b/libs/schema-json/src/boundaries.test.ts index c79286aa..5ab0cbfa 100644 --- a/libs/schema-json/src/boundaries.test.ts +++ b/libs/schema-json/src/boundaries.test.ts @@ -4,38 +4,38 @@ import { withStandardJsonSchema } from './standardJsonSchema.js'; import { toJsonSchema } from './toJsonSchema.js'; describe('named reference JSON Schema', () => { - it.each([ - '2020-12', - '07' - ] as const)('keeps annotations outside the definition in draft %s', draft => { - const user = object({ name: string() }).schemaName('User'); - const schema = object({ - user: user, - previous: user - .nullable() - .optional() - .describe('Previous user') - .example(null) - }); - const json = toJsonSchema(schema, { - draft, - $schema: false, - nameResolver: s => (s === user ? 'User' : null) - }) as any; - expect(json.required).toEqual(['user']); - expect(json.properties.user).toEqual({ - $ref: '#/components/schemas/User' - }); - expect(json.properties.previous).toMatchObject({ - description: 'Previous user', - examples: [null], - anyOf: [ - { allOf: [{ $ref: '#/components/schemas/User' }] }, - { type: 'null' } - ] - }); - expect(user.introspect().description).toBeUndefined(); - }); + it.each(['2020-12', '07'] as const)( + 'keeps annotations outside the definition in draft %s', + draft => { + const user = object({ name: string() }).schemaName('User'); + const schema = object({ + user: user, + previous: user + .nullable() + .optional() + .describe('Previous user') + .example(null) + }); + const json = toJsonSchema(schema, { + draft, + $schema: false, + nameResolver: s => (s === user ? 'User' : null) + }) as any; + expect(json.required).toEqual(['user']); + expect(json.properties.user).toEqual({ + $ref: '#/components/schemas/User' + }); + expect(json.properties.previous).toMatchObject({ + description: 'Previous user', + examples: [null], + anyOf: [ + { allOf: [{ $ref: '#/components/schemas/User' }] }, + { type: 'null' } + ] + }); + expect(user.introspect().description).toBeUndefined(); + } + ); it('keeps final local default and nullability modifiers', () => { const target = string().nullable().schemaName('Name'); @@ -60,45 +60,47 @@ describe('named reference JSON Schema', () => { ); }); - it.each([ - '2020-12', - '07' - ] as const)('retains local modifiers before name resolution in draft %s', draft => { - const target = string() - .nullable() - .describe('Canonical') - .schemaName('Name'); - const local = target - .notNullable() - .describe('Use') - .example('Ada') - .readonly(); - const nameResolver = (s: typeof target) => - s.introspect().schemaName ?? null; - expect( - toJsonSchema(local, { draft, $schema: false, nameResolver }) - ).toEqual({ - allOf: [ - { $ref: '#/components/schemas/Name' }, + it.each(['2020-12', '07'] as const)( + 'retains local modifiers before name resolution in draft %s', + draft => { + const target = string() + .nullable() + .describe('Canonical') + .schemaName('Name'); + const local = target + .notNullable() + .describe('Use') + .example('Ada') + .readonly(); + const nameResolver = (s: typeof target) => + s.introspect().schemaName ?? null; + expect( + toJsonSchema(local, { draft, $schema: false, nameResolver }) + ).toEqual({ + allOf: [ + { $ref: '#/components/schemas/Name' }, + { not: { type: 'null' } } + ], + description: 'Use', + examples: ['Ada'], + readOnly: true + }); + expect( + toJsonSchema(local, { draft, $schema: false }).allOf + ).toEqual([ + { type: ['string', 'null'], description: 'Canonical' }, { not: { type: 'null' } } - ], - description: 'Use', - examples: ['Ada'], - readOnly: true - }); - expect(toJsonSchema(local, { draft, $schema: false }).allOf).toEqual([ - { type: ['string', 'null'], description: 'Canonical' }, - { not: { type: 'null' } } - ]); - expect( - toJsonSchema(local.nullable(), { - draft, - $schema: false, - nameResolver - }).allOf - ).toEqual([{ $ref: '#/components/schemas/Name' }]); - expect(target.introspect().description).toBe('Canonical'); - }); + ]); + expect( + toJsonSchema(local.nullable(), { + draft, + $schema: false, + nameResolver + }).allOf + ).toEqual([{ $ref: '#/components/schemas/Name' }]); + expect(target.introspect().description).toBe('Canonical'); + } + ); it('exports shape/rule derivatives inline while keeping nested named children', () => { const name = string().schemaName('Name'); diff --git a/libs/schema/LICENSE b/libs/schema/LICENSE new file mode 100644 index 00000000..053547c1 --- /dev/null +++ b/libs/schema/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (C) 2024 by Andrew Zolotuhkin + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libs/schema/README.md b/libs/schema/README.md index 601743e6..a2166073 100644 --- a/libs/schema/README.md +++ b/libs/schema/README.md @@ -7,7 +7,7 @@ [![License: BSD-3-Clause](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)](../../LICENSE) -![Coverage](https://img.shields.io/badge/coverage-97.7%25-brightgreen) +![Unit coverage](https://img.shields.io/badge/unit_coverage-97.7%25-brightgreen) A schema definition and validation library for TypeScript — faster than Zod in 14/15 benchmarks (up to 204× faster on invalid input), 3× smaller than Zod v4, and compatible with 50+ ecosystem tools via [Standard Schema v1](https://standardschema.dev/). diff --git a/libs/schema/package.json b/libs/schema/package.json index c89f8fcc..873b752b 100644 --- a/libs/schema/package.json +++ b/libs/schema/package.json @@ -20,7 +20,7 @@ "standard-schema", "cleverbrush" ], - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "main": "./dist/index.js", "exports": { ".": { @@ -94,6 +94,9 @@ }, "sideEffects": false, "name": "@cleverbrush/schema", + "engines": { + "node": ">=24" + }, "readme": "https://github.com/cleverbrush/framework/tree/master/libs/schema#readme", "repository": { "type": "git", diff --git a/libs/schema/src/builders/AnySchemaBuilder.ts b/libs/schema/src/builders/AnySchemaBuilder.ts index 85341c0c..44431abf 100644 --- a/libs/schema/src/builders/AnySchemaBuilder.ts +++ b/libs/schema/src/builders/AnySchemaBuilder.ts @@ -15,8 +15,8 @@ type AnySchemaBuilderCreateProps = Partial< * in TypeScript. Allows to define a schema for `any` value. * Use it when you don't know the type of the value. * - * **NOTE** this class is exported only to give opportunity to extend it - * by inheriting. It is not recommended to create an instance of this class + * **NOTE** this class is exported for type annotations and advanced use. + * Customize via extensions rather than subclassing. Avoid instantiating it * directly. Use `any()` function instead. * * @example diff --git a/libs/schema/src/builders/ArraySchemaBuilder.ts b/libs/schema/src/builders/ArraySchemaBuilder.ts index 0cca75e6..e97414fe 100644 --- a/libs/schema/src/builders/ArraySchemaBuilder.ts +++ b/libs/schema/src/builders/ArraySchemaBuilder.ts @@ -86,8 +86,8 @@ type ArraySchemaBuilderCreateProps< * Also you can limit the length of the array by using `minLength` * and `maxLength` methods. * - * **NOTE** this class is exported only to give opportunity to extend it - * by inheriting. It is not recommended to create an instance of this class + * **NOTE** this class is exported for type annotations and advanced use. + * Customize via extensions rather than subclassing. Avoid instantiating it * directly. Use {@link array | array()} function instead. * @see {@link array} */ @@ -387,7 +387,7 @@ export class ArraySchemaBuilder< } /** - * Performs synchronous validation of the schema over `object`. {@inheritDoc SchemaBuilder.validate} + * {@inheritDoc SchemaBuilder.validate} */ public validate( object: TResult, @@ -400,7 +400,7 @@ export class ArraySchemaBuilder< } /** - * Performs asynchronous validation of the schema over `object`. {@inheritDoc SchemaBuilder.validateAsync} + * {@inheritDoc SchemaBuilder.validateAsync} */ public async validateAsync( object: TResult, diff --git a/libs/schema/src/builders/BooleanSchemaBuilder.ts b/libs/schema/src/builders/BooleanSchemaBuilder.ts index 6b9a83d2..d28a36a6 100644 --- a/libs/schema/src/builders/BooleanSchemaBuilder.ts +++ b/libs/schema/src/builders/BooleanSchemaBuilder.ts @@ -15,23 +15,26 @@ type BooleanSchemaBuilderCreateProps = Partial< * Allows to define a schema for a boolean value. It can be required or optional. * It can be restricted to be equal to a certain value. * - * **NOTE** this class is exported only to give opportunity to extend it - * by inheriting. It is not recommended to create an instance of this class + * **NOTE** this class is exported for type annotations and advanced use. + * Customize via extensions rather than subclassing. Avoid instantiating it * directly. Use {@link boolean | boolean()} function instead. * - * @example ```ts + * @example + * ```ts * const schema = boolean().equals(true); * const result = schema.validate(true); * // result.valid === true * // result.object === true * ``` - * @example ```ts + * @example + * ```ts * const schema = boolean().equals(false); * const result = schema.validate(true); * // result.valid === false * // result.errors[0].message === 'is expected to be equal to 'false'' * ``` - * @example ```ts + * @example + * ```ts * const schema = boolean().equals(true).optional(); * const result = schema.validate(undefined); * // result.valid === true @@ -513,7 +516,8 @@ export class BooleanSchemaBuilder< * Accepts `"true"` → `true` and `"false"` → `false`; other values are * left unchanged so the boolean schema rejects them. * - * @example ```ts + * @example + * ```ts * const schema = boolean().coerce(); * const result = schema.validate('true'); * // result.valid === true diff --git a/libs/schema/src/builders/DateSchemaBuilder.ts b/libs/schema/src/builders/DateSchemaBuilder.ts index 006eb8d7..cf0f3dde 100644 --- a/libs/schema/src/builders/DateSchemaBuilder.ts +++ b/libs/schema/src/builders/DateSchemaBuilder.ts @@ -38,33 +38,37 @@ const parseFromEpochPreprocessor = (value: any) => { * It can be restricted to be: equal to a certain value, in future, in past, in a certain range. * Supports parsing from JSON string and UNIX epoch (using preprocessors). * - * **NOTE** this class is exported only to give opportunity to extend it - * by inheriting. It is not recommended to create an instance of this class + * **NOTE** this class is exported for type annotations and advanced use. + * Customize via extensions rather than subclassing. Avoid instantiating it * directly. Use {@link date | date()} function instead. * - * @example ```ts - * const date = new Date(2020, 0, 2); + * @example + * ```ts + * const value = new Date(2020, 0, 2); * const schema = date().min(new Date(2020, 0, 1)); - * const result = schema.validate(date); + * const result = schema.validate(value); * // result.valid === true - * // result.object === date + * // result.object === value * ``` * - * @example ```ts + * @example + * ```ts * const schema = date(); * const result = schema.validate('2020-01-01'); * // result.valid === false * // result.errors[0].message === 'is expected to be a date' * ``` * - * @example ```ts + * @example + * ```ts * const schema = date().parseFromJson(); * const result = schema.validate('2020-01-01T00:00:00.000Z'); * // result.valid === true * // result.object is equal to corresponding Date object * ``` * - * @example ```ts + * @example + * ```ts * const schema = date().parseFromEpoch(); * const result = schema.validate(1577836800000); * // result.valid === true @@ -896,7 +900,8 @@ export class DateSchemaBuilder< * For more specific parsing, see {@link acceptJsonString} and * {@link acceptEpoch}. * - * @example ```ts + * @example + * ```ts * const schema = date().coerce(); * const result = schema.validate('2024-01-15'); * // result.valid === true diff --git a/libs/schema/src/builders/ExternSchemaBuilder.ts b/libs/schema/src/builders/ExternSchemaBuilder.ts index 65cad914..d664c154 100644 --- a/libs/schema/src/builders/ExternSchemaBuilder.ts +++ b/libs/schema/src/builders/ExternSchemaBuilder.ts @@ -63,8 +63,8 @@ function formatIssuePath( * so `getErrorsFor(t => t.order.id)` works without any additional * configuration. * - * **NOTE** this class is exported only to give opportunity to extend it - * by inheriting. It is not recommended to create an instance of this class + * **NOTE** this class is exported for type annotations and advanced use. + * Customize via extensions rather than subclassing. Avoid instantiating it * directly. Use the {@link extern | extern()} factory function instead. * * @example diff --git a/libs/schema/src/builders/FunctionSchemaBuilder.ts b/libs/schema/src/builders/FunctionSchemaBuilder.ts index 31d85da2..5923bc7f 100644 --- a/libs/schema/src/builders/FunctionSchemaBuilder.ts +++ b/libs/schema/src/builders/FunctionSchemaBuilder.ts @@ -21,8 +21,8 @@ type InferParameters[]> = * It can be required or optional, and may carry typed parameter and return-type * schemas so that the inferred TypeScript function signature is fully typed. * - * **NOTE** this class is exported only to give opportunity to extend it - * by inheriting. It is not recommended to create an instance of this class + * **NOTE** this class is exported for type annotations and advanced use. + * Customize via extensions rather than subclassing. Avoid instantiating it * directly. Use {@link func | func()} function instead. * * @example Basic validation diff --git a/libs/schema/src/builders/GenericSchemaBuilder.ts b/libs/schema/src/builders/GenericSchemaBuilder.ts index 4e6b40e2..26d761fd 100644 --- a/libs/schema/src/builders/GenericSchemaBuilder.ts +++ b/libs/schema/src/builders/GenericSchemaBuilder.ts @@ -16,8 +16,8 @@ type GenericSchemaBuilderCreateProps = * builder whose TypeScript type is inferred from the template function's * generic signature. * - * **NOTE** this class is exported only to give opportunity to extend it - * by inheriting. It is not recommended to create an instance of this class + * **NOTE** this class is exported for type annotations and advanced use. + * Customize via extensions rather than subclassing. Avoid instantiating it * directly. Use {@link generic | generic()} function instead. * * @example Single type parameter @@ -59,7 +59,7 @@ type GenericSchemaBuilderCreateProps = * // InferType → { ok: boolean; value?: string; error?: number } * ``` * - * @example With default arguments (enables direct `.validate()` on the template) + * @example With default arguments * ```ts * const AnyList = generic( * [any()], // default args — one per template parameter @@ -149,7 +149,7 @@ export class GenericSchemaBuilder< * ``` */ // Set to the actual function in the constructor; declared here for TypeScript. - public declare readonly apply: TFn; + declare public readonly apply: TFn; /** * @hidden diff --git a/libs/schema/src/builders/IndexedProperties.test.ts b/libs/schema/src/builders/IndexedProperties.test.ts index 24132499..adee4910 100644 --- a/libs/schema/src/builders/IndexedProperties.test.ts +++ b/libs/schema/src/builders/IndexedProperties.test.ts @@ -39,35 +39,39 @@ test('indexed descriptors are stable, lazy, typed and preserve arrays when setti expect((tree.addresses as any)['01']).toBeUndefined(); expect((tree.addresses as any)[1.5]).toBeUndefined(); }); -test.each([ - 'sync', - 'async' -] as const)('indexed errors use precise descriptors (%s)', async mode => { - const value = { - addresses: [{ city: '' }, { city: 'Paris' }], - tags: [''], - matrix: [['']] - }; - const result = await (mode === 'sync' - ? schema.validate(value, { doNotStopOnFirstError: true }) - : schema.validateAsync(value, { doNotStopOnFirstError: true })); - expect(result.valid).toBe(false); - expect(result.getErrorsFor(t => t.addresses[0].city).isValid).toBe(false); - expect(result.getErrorsFor(t => t.addresses[1].city).isValid).toBe(true); - expect(result.getErrorsFor(t => t.tags[0]).isValid).toBe(false); - expect(result.getErrorsFor(t => t.matrix[0][0]).isValid).toBe(false); - expect( - result.getInvalidProperties().map(p => p.descriptor.toJsonPointer()) - ).toEqual( - expect.arrayContaining([ - '/addresses', - '/addresses/0/city', - '/tags', - '/tags/0', - '/matrix/0/0' - ]) - ); -}); +test.each(['sync', 'async'] as const)( + 'indexed errors use precise descriptors (%s)', + async mode => { + const value = { + addresses: [{ city: '' }, { city: 'Paris' }], + tags: [''], + matrix: [['']] + }; + const result = await (mode === 'sync' + ? schema.validate(value, { doNotStopOnFirstError: true }) + : schema.validateAsync(value, { doNotStopOnFirstError: true })); + expect(result.valid).toBe(false); + expect(result.getErrorsFor(t => t.addresses[0].city).isValid).toBe( + false + ); + expect(result.getErrorsFor(t => t.addresses[1].city).isValid).toBe( + true + ); + expect(result.getErrorsFor(t => t.tags[0]).isValid).toBe(false); + expect(result.getErrorsFor(t => t.matrix[0][0]).isValid).toBe(false); + expect( + result.getInvalidProperties().map(p => p.descriptor.toJsonPointer()) + ).toEqual( + expect.arrayContaining([ + '/addresses', + '/addresses/0/city', + '/tags', + '/tags/0', + '/matrix/0/0' + ]) + ); + } +); test('indexed validation works below a nested object and with escaped keys', () => { const nested = object({ profile: object({ diff --git a/libs/schema/src/builders/NullSchemaBuilder.ts b/libs/schema/src/builders/NullSchemaBuilder.ts index d4c735cf..cbcdabea 100644 --- a/libs/schema/src/builders/NullSchemaBuilder.ts +++ b/libs/schema/src/builders/NullSchemaBuilder.ts @@ -21,8 +21,8 @@ type NullSchemaBuilderCreateProps = Partial< * in a typed schema, for example in discriminated-union branches or when * modelling a JSON payload that may carry a JSON `null` value. * - * **NOTE** this class is exported only to give opportunity to extend it - * by inheriting. It is not recommended to create an instance of this class + * **NOTE** this class is exported for type annotations and advanced use. + * Customize via extensions rather than subclassing. Avoid instantiating it * directly. Use {@link nul | nul()} function instead. * * @example @@ -161,7 +161,6 @@ export class NullSchemaBuilder< /** * Performs synchronous validation of the schema over `object`. - * @param context Optional `ValidationContext` settings. */ protected _validate( object: null, @@ -172,7 +171,6 @@ export class NullSchemaBuilder< /** * Performs async validation of the schema over `object`. - * @param context Optional `ValidationContext` settings. */ protected async _validateAsync( object: null, diff --git a/libs/schema/src/builders/NumberSchemaBuilder.ts b/libs/schema/src/builders/NumberSchemaBuilder.ts index d15d4b22..c3fe85e9 100644 --- a/libs/schema/src/builders/NumberSchemaBuilder.ts +++ b/libs/schema/src/builders/NumberSchemaBuilder.ts @@ -18,30 +18,34 @@ type NumberSchemaBuilderCreateProps< * Can be required or optional, can be restricted to be equal to a certain value, * can be restricted to be in a certain range, can be restricted to be integer. * - * **NOTE** this class is exported only to give opportunity to extend it - * by inheriting. It is not recommended to create an instance of this class + * **NOTE** this class is exported for type annotations and advanced use. + * Customize via extensions rather than subclassing. Avoid instantiating it * directly. Use {@link number | number()} function instead. * - * @example ```ts + * @example + * ```ts * const schema = number().equals(42); * const result = schema.validate(42); * // result.valid === true * // result.object === 42 * ``` - * @example ```ts + * @example + * ```ts * const schema = number(); * const result = schema.validate('42'); * // result.valid === false * // result.errors[0].message === 'is expected to be a number' * ``` - * @example ```ts + * @example + * ```ts * const schema = number().min(0).max(100); * const result = schema.validate(42); * // result.valid === true * // result.object === 42 * ``` * - * @example ```ts + * @example + * ```ts * const schema = number().min(0).max(100); * const result = schema.validate(142.5); * // result.valid === false @@ -843,7 +847,8 @@ export class NumberSchemaBuilder< * a string source (e.g. a parse-string schema, URL parameter, * or form input). * - * @example ```ts + * @example + * ```ts * const schema = number().coerce(); * const result = schema.validate('42'); * // result.valid === true diff --git a/libs/schema/src/builders/ObjectSchemaBuilder.ts b/libs/schema/src/builders/ObjectSchemaBuilder.ts index 5473c8d2..286ebbcb 100644 --- a/libs/schema/src/builders/ObjectSchemaBuilder.ts +++ b/libs/schema/src/builders/ObjectSchemaBuilder.ts @@ -279,8 +279,8 @@ export type ObjectSchemaValidationResult< * Which means that you can define nested objects and arrays of * any complexity. * - * **NOTE** this class is exported only to give opportunity to extend it - * by inheriting. It is not recommended to create an instance of this class + * **NOTE** this class is exported for type annotations and advanced use. + * Customize via extensions rather than subclassing. Avoid instantiating it * directly. Use {@link object | object()} function instead. * * @example diff --git a/libs/schema/src/builders/PromiseSchemaBuilder.ts b/libs/schema/src/builders/PromiseSchemaBuilder.ts index 61da354d..c04ec861 100644 --- a/libs/schema/src/builders/PromiseSchemaBuilder.ts +++ b/libs/schema/src/builders/PromiseSchemaBuilder.ts @@ -17,8 +17,8 @@ type PromiseSchemaBuilderCreateProps = Partial< * function) and optionally carries a typed resolved-value schema so that * the inferred TypeScript type is `Promise` instead of `Promise`. * - * **NOTE** this class is exported only to give opportunity to extend it - * by inheriting. It is not recommended to create an instance of this class + * **NOTE** this class is exported for type annotations and advanced use. + * Customize via extensions rather than subclassing. Avoid instantiating it * directly. Use {@link promise | promise()} function instead. * * @example Basic validation diff --git a/libs/schema/src/builders/SchemaBuilder.ts b/libs/schema/src/builders/SchemaBuilder.ts index bd6e9595..4cb2ada8 100644 --- a/libs/schema/src/builders/SchemaBuilder.ts +++ b/libs/schema/src/builders/SchemaBuilder.ts @@ -63,7 +63,7 @@ export type InferType = T extends { * * When returned from an object-level validator (via {@link SchemaBuilder.addValidator | addValidator}), * the optional `property` selector can route the error to a specific property - * so that {@link ObjectSchemaValidationResult.getErrorsFor | getErrorsFor()} reports it + * so that `ObjectSchemaValidationResult.getErrorsFor()` reports it * on that property rather than only on the root object. * * ```ts @@ -767,8 +767,8 @@ type ResolvedSchemaType< * Base class for all schema builders. Provides basic functionality for schema building. * * **Note:** this class is not intended to be used directly, use one of the subclasses instead. - * @typeparam TResult Type of the object that will be returned by `validate()` method. - * @typeparam TRequired If `true`, object will be required. If `false`, object will be optional. + * @typeParam TResult Type of the object that will be returned by `validate()` method. + * @typeParam TRequired If `true`, object will be required. If `false`, object will be optional. */ export abstract class SchemaBuilder< TResult = any, diff --git a/libs/schema/src/builders/StringSchemaBuilder.ts b/libs/schema/src/builders/StringSchemaBuilder.ts index 166b9406..55c3eabf 100644 --- a/libs/schema/src/builders/StringSchemaBuilder.ts +++ b/libs/schema/src/builders/StringSchemaBuilder.ts @@ -19,46 +19,52 @@ type StringSchemaBuilderCreateProps< * length, restricted to start with a certain value, restricted to end with * a certain value, restricted to match a certain regular expression. * - * **NOTE** this class is exported only to give opportunity to extend it - * by inheriting. It is not recommended to create an instance of this class + * **NOTE** this class is exported for type annotations and advanced use. + * Customize via extensions rather than subclassing. Avoid instantiating it * directly. Use {@link string | string()} function instead. * - * @example ```ts + * @example + * ```ts * const schema = string().equals('hello'); * const result = schema.validate('hello'); * // result.valid === true * // result.object === 'hello' * ``` * - * @example ```ts + * @example + * ```ts * const schema = string().equals('hello'); * const result = schema.validate('world'); * // result.valid === false * // result.errors[0].message === "is expected to be equal to 'hello'" * ``` * - * @example ```ts + * @example + * ```ts * const schema = string().minLength(5); * const result = schema.validate('hello'); * // result.valid === true * // result.object === 'hello' * ``` * - * @example ```ts + * @example + * ```ts * const schema = string().minLength(5); * const result = schema.validate('hi'); * // result.valid === false * // result.errors[0].message === 'is expected to have a length of at least 5' * ``` * - * @example ```ts + * @example + * ```ts * const schema = string().minLength(2).maxLength(5); * const result = schema.validate('yes'); * // result.valid === true * // result.object === 'yes' * ``` * - * @example ```ts + * @example + * ```ts * const schema = string('no'); * const result = schema.validate('yes'); * // result.valid === false diff --git a/libs/schema/src/builders/TupleSchemaBuilder.ts b/libs/schema/src/builders/TupleSchemaBuilder.ts index 0fb1c9d1..1ed51672 100644 --- a/libs/schema/src/builders/TupleSchemaBuilder.ts +++ b/libs/schema/src/builders/TupleSchemaBuilder.ts @@ -95,8 +95,8 @@ type TupleSchemaBuilderCreateProps< * Use it when you need to validate function arguments, CSV rows, coordinate * pairs, structured event payloads, or any other fixed-structure array. * - * **NOTE** this class is exported only to give opportunity to extend it - * by inheriting. It is not recommended to create an instance of this class + * **NOTE** this class is exported for type annotations and advanced use. + * Customize via extensions rather than subclassing. Avoid instantiating it * directly. Use {@link tuple | tuple()} function instead. * * @example @@ -399,7 +399,7 @@ export class TupleSchemaBuilder< } /** - * Performs synchronous validation of the schema over `object`. {@inheritDoc SchemaBuilder.validate} + * {@inheritDoc SchemaBuilder.validate} */ public validate( object: TResult, @@ -412,7 +412,7 @@ export class TupleSchemaBuilder< } /** - * Performs asynchronous validation of the schema over `object`. {@inheritDoc SchemaBuilder.validateAsync} + * {@inheritDoc SchemaBuilder.validateAsync} */ public async validateAsync( object: TResult, diff --git a/libs/schema/src/builders/UnionSchemaBuilder.ts b/libs/schema/src/builders/UnionSchemaBuilder.ts index ede1d90e..b5c51d28 100644 --- a/libs/schema/src/builders/UnionSchemaBuilder.ts +++ b/libs/schema/src/builders/UnionSchemaBuilder.ts @@ -118,8 +118,8 @@ type TakeExceptIndex< * Which means that you are not limited to primitive types and * can construct complex types as well, e.g. object | array. * - * **NOTE** this class is exported only to give opportunity to extend it - * by inheriting. It is not recommended to create an instance of this class + * **NOTE** this class is exported for type annotations and advanced use. + * Customize via extensions rather than subclassing. Avoid instantiating it * directly. Use {@link union | union()} function instead. * * @example diff --git a/libs/schema/src/extension.ts b/libs/schema/src/extension.ts index ba53cf5a..5f6c9478 100644 --- a/libs/schema/src/extension.ts +++ b/libs/schema/src/extension.ts @@ -913,7 +913,6 @@ export function withExtensions< // Create a dynamic subclass const ExtendedClass = class extends BaseClass { - // biome-ignore lint/complexity/noUselessConstructor: required constructor(...args: any[]) { super(...args); } diff --git a/libs/schema/src/extensions/string.ts b/libs/schema/src/extensions/string.ts index ab7bc8de..b969d49f 100644 --- a/libs/schema/src/extensions/string.ts +++ b/libs/schema/src/extensions/string.ts @@ -71,8 +71,7 @@ export interface StringBuiltinExtensions { * By default only `http` and `https` protocols are accepted. * Pass `opts.protocols` to restrict or expand the allowed set. * - * @param opts - optional configuration - * @param opts.protocols - allowed URL protocols (default: `['http', 'https']`) + * The options overload accepts a `protocols` array (default: http/https). * @param errorMessage - custom error message or function to generate one * @returns a new schema builder with the URL validator applied * @@ -303,8 +302,7 @@ export const stringExtensions = defineExtension({ * By default only `http` and `https` protocols are accepted. * Pass `opts.protocols` to restrict or expand the allowed set. * - * @param opts - optional configuration - * @param opts.protocols - allowed URL protocols (default: `['http', 'https']`) + * @param optsOrError - protocol options or a custom error message * @param errorMessage - custom error message or function to generate one * @returns a new schema builder with the URL validator applied * diff --git a/libs/server-integration-tests/tests/uploads.test.ts b/libs/server-integration-tests/tests/uploads.test.ts index 4cf50efe..c2285ebc 100644 --- a/libs/server-integration-tests/tests/uploads.test.ts +++ b/libs/server-integration-tests/tests/uploads.test.ts @@ -187,22 +187,25 @@ it.each([ { maxFieldNameSize: 5 } ], ['text encoded as file name', [['images', 'not a file']], 400, {}] -] as const)('rejects %s without running the handler', async (_name, entries, status, limits) => { - const { base, handler } = await setup(limits); - const response = await fetch(`${base}/files`, { - method: 'POST', - body: form(entries as any) - }); - expect(response.status).toBe(status); - expect(response.headers.get('content-type')).toContain( - 'application/problem+json' - ); - expect(await response.json()).toMatchObject({ - status, - errors: expect.any(Array) - }); - expect(handler).not.toHaveBeenCalled(); -}); +] as const)( + 'rejects %s without running the handler', + async (_name, entries, status, limits) => { + const { base, handler } = await setup(limits); + const response = await fetch(`${base}/files`, { + method: 'POST', + body: form(entries as any) + }); + expect(response.status).toBe(status); + expect(response.headers.get('content-type')).toContain( + 'application/problem+json' + ); + expect(await response.json()).toMatchObject({ + status, + errors: expect.any(Array) + }); + expect(handler).not.toHaveBeenCalled(); + } +); it('allows exact file, field and count limits without truncation', async () => { const { base } = await setup({ diff --git a/libs/server-openapi/LICENSE b/libs/server-openapi/LICENSE new file mode 100644 index 00000000..053547c1 --- /dev/null +++ b/libs/server-openapi/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (C) 2024 by Andrew Zolotuhkin + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libs/server-openapi/README.md b/libs/server-openapi/README.md index 9fcf13ed..5102b438 100644 --- a/libs/server-openapi/README.md +++ b/libs/server-openapi/README.md @@ -2,6 +2,9 @@ [![CI](https://github.com/cleverbrush/framework/actions/workflows/ci.yml/badge.svg)](https://github.com/cleverbrush/framework/actions/workflows/ci.yml) [![License: BSD-3-Clause](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)](../../LICENSE) + +![Unit coverage](https://img.shields.io/badge/unit_coverage-96.4%25-brightgreen) + OpenAPI 3.1 specification generation for [`@cleverbrush/server`](../server). Converts endpoint registrations, schema definitions, and authentication configuration into a fully-formed OpenAPI document — no annotations, no decorators. Also generates **AsyncAPI 3.0** documents for WebSocket subscription endpoints. diff --git a/libs/server-openapi/package.json b/libs/server-openapi/package.json index b021b59e..163e5cf6 100644 --- a/libs/server-openapi/package.json +++ b/libs/server-openapi/package.json @@ -4,6 +4,12 @@ "url": "https://github.com/cleverbrush/framework/issues", "email": "andrew_zol@cleverbrush.com" }, + "devDependencies": { + "@cleverbrush/server": "*", + "@cleverbrush/schema": "*", + "@cleverbrush/schema-json": "*", + "@cleverbrush/auth": "*" + }, "peerDependencies": { "@cleverbrush/server": "^4.0.0", "@cleverbrush/schema": "^4.0.0", @@ -23,7 +29,7 @@ "documentation", "cleverbrush" ], - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "main": "./dist/index.js", "exports": { ".": { @@ -33,6 +39,9 @@ }, "sideEffects": false, "name": "@cleverbrush/server-openapi", + "engines": { + "node": ">=24" + }, "readme": "https://github.com/cleverbrush/framework/tree/master/libs/server-openapi#readme", "repository": { "type": "git", diff --git a/libs/server/LICENSE b/libs/server/LICENSE new file mode 100644 index 00000000..053547c1 --- /dev/null +++ b/libs/server/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (C) 2024 by Andrew Zolotuhkin + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libs/server/README.md b/libs/server/README.md index ef34ec7d..7884bc30 100644 --- a/libs/server/README.md +++ b/libs/server/README.md @@ -2,6 +2,9 @@ [![CI](https://github.com/cleverbrush/framework/actions/workflows/ci.yml/badge.svg)](https://github.com/cleverbrush/framework/actions/workflows/ci.yml) [![License: BSD-3-Clause](https://img.shields.io/badge/license-BSD--3--Clause-blue.svg)](../../LICENSE) + +![Unit coverage](https://img.shields.io/badge/unit_coverage-81.6%25-green) + A schema-first HTTP server framework for Node.js. Combines [`@cleverbrush/schema`](../schema) for request validation, [`@cleverbrush/di`](../di) for dependency injection, and [`@cleverbrush/auth`](../auth) for authentication — all wired together through a fluent builder API. @@ -911,6 +914,34 @@ apps/ backend/ // imports fullApi — handles all routes ``` +## Response replay and caching + +`idempotency({ scope })` coalesces concurrent mutations and replays their completed +responses within one middleware instance. Install it after authentication and +authorization. Derive `scope(ctx)` from verified identity/tenant context; returning +`undefined` bypasses replay. A constant scope is appropriate only for intentionally +public operations. Keys also include method and full URL. Reusing a key asserts +identical input; request bodies are not fingerprinted. + +Defaults: `ttl: 86400000`, `maxEntries: 1000`, `maxResponseBytes: 65536`. +Capacity exhaustion returns 503 before execution. Oversized or incomplete responses +reserve the key; retries return 409 so the caller can verify the outcome. This is +process-local replay, not durable exactly-once execution: crashes, expiry or thrown +handler errors can permit a later execution. Use database deduplication for durable +business guarantees. + +`cacheResponse()` retains at most 1000 keys and 65,536-byte complete response bodies +by default (`maxEntries`, `maxResponseBytes`); oldest keys are evicted at capacity. +It bypasses `Set-Cookie` and `Cache-Control: private`/`no-store` responses. +`defaultTtl` and `ttlByTag` are finite, non-negative milliseconds; retention uses +the longest TTL among an endpoint's tags (all zero disables retention). +Tags must distinguish endpoint shapes, verified identities, +tenants and all representation inputs. Both middleware capture `write()`/`end()` +chunks and restore response hooks on failure. + +See [security guidance](../../SECURITY.md) and the +[v4.x-to-v5 migration guide](../../docs/MIGRATION-v5.md). + ## License BSD-3-Clause — see [LICENSE](../../LICENSE). diff --git a/libs/server/package.json b/libs/server/package.json index 4fdc5423..96c83d44 100644 --- a/libs/server/package.json +++ b/libs/server/package.json @@ -8,8 +8,8 @@ "@cleverbrush/auth": "^4.4.3", "@cleverbrush/di": "^4.4.3", "@cleverbrush/schema": "^4.4.3", - "@fastify/busboy": "^3.2.0", - "ws": "^8.20.0" + "@fastify/busboy": "^3.2.2", + "ws": "^8.22.0" }, "devDependencies": { "@types/busboy": "1.5.4", @@ -29,7 +29,7 @@ "typescript", "cleverbrush" ], - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "main": "./dist/index.js", "exports": { ".": { @@ -43,6 +43,9 @@ }, "sideEffects": false, "name": "@cleverbrush/server", + "engines": { + "node": ">=24" + }, "readme": "https://github.com/cleverbrush/framework/tree/master/libs/server#readme", "repository": { "type": "git", diff --git a/libs/server/src/ContentNegotiator.security.test.ts b/libs/server/src/ContentNegotiator.security.test.ts index 51ba8380..6664fa8a 100644 --- a/libs/server/src/ContentNegotiator.security.test.ts +++ b/libs/server/src/ContentNegotiator.security.test.ts @@ -10,7 +10,7 @@ describe('ContentNegotiator — security', () => { ) as any; expect(result.safe).toBe('ok'); - expect(result.__proto__).toBe(Object.prototype); + expect(Object.getPrototypeOf(result)).toBe(Object.prototype); expect(({} as any).polluted).toBeUndefined(); }); @@ -85,7 +85,7 @@ describe('ContentNegotiator — security', () => { ) as Record; expect(result.safe).toBe('ok'); - expect(result.__proto__).toBe(Object.prototype); + expect(Object.getPrototypeOf(result)).toBe(Object.prototype); expect(result.constructor).toBe(Object); expect(({} as any).polluted).toBeUndefined(); }); diff --git a/libs/server/src/Cors.test.ts b/libs/server/src/Cors.test.ts index c919429c..b5795009 100644 --- a/libs/server/src/Cors.test.ts +++ b/libs/server/src/Cors.test.ts @@ -93,14 +93,19 @@ describe('CORS policy parsing', () => { 'https://app.example.test, https://other.example.test', 'https://app.example.test/path', 'null https://app.example.test' - ])('rejects malformed Origin without calling a predicate: %s', async value => { - const predicate = vi.fn(() => true); - const { req, res } = exchange({ origin: value }); - await new CorsPolicy({ origin: predicate }).handle(req, res, match); - expect(res.statusCode).toBe(403); - expect(predicate).not.toHaveBeenCalled(); - expect(res.getHeader('access-control-allow-origin')).toBeUndefined(); - }); + ])( + 'rejects malformed Origin without calling a predicate: %s', + async value => { + const predicate = vi.fn(() => true); + const { req, res } = exchange({ origin: value }); + await new CorsPolicy({ origin: predicate }).handle(req, res, match); + expect(res.statusCode).toBe(403); + expect(predicate).not.toHaveBeenCalled(); + expect( + res.getHeader('access-control-allow-origin') + ).toBeUndefined(); + } + ); it.each([ { 'access-control-request-method': '' }, { 'access-control-request-method': 'POST, DELETE' }, diff --git a/libs/server/src/Endpoint.ts b/libs/server/src/Endpoint.ts index bf9bf01f..04b9cd67 100644 --- a/libs/server/src/Endpoint.ts +++ b/libs/server/src/Endpoint.ts @@ -2185,7 +2185,7 @@ export class EndpointBuilder< * Declare a cache group for this endpoint. * * Use on GET / query endpoints to group responses into a named cache. - * The client-side {@code cacheTags} middleware caches responses keyed + * The client-side `cacheTags` middleware caches responses keyed * by this tag and flushes matching entries when a mutation calls * {@link clearsCacheTag}. * @@ -2251,7 +2251,7 @@ export class EndpointBuilder< * Declare which cache groups are cleared when this mutation succeeds. * * Use on POST / PUT / PATCH / DELETE endpoints. When the mutation - * completes, the {@code cacheTags} client middleware invalidates all + * completes, the `cacheTags` client middleware invalidates all * cache entries matching the declared tag names (prefix match). * * @overload Simple tag (clears all entries prefixed with the name). diff --git a/libs/server/src/ErrorMap.test.ts b/libs/server/src/ErrorMap.test.ts index b7b7858b..97f8d891 100644 --- a/libs/server/src/ErrorMap.test.ts +++ b/libs/server/src/ErrorMap.test.ts @@ -69,21 +69,19 @@ describe('errorMap and withErrors', () => { }); }); - it.each([ - new Error('Database password'), - 'non-error', - undefined, - null - ])('rethrows unmatched values without changing identity: %s', async error => { - const wrapped = withErrors( - ep, - errorMap().on(Missing, () => ActionResult.noContent()), - () => { - throw error; - } - ); - await expect(wrapped({} as never)).rejects.toBe(error); - }); + it.each([new Error('Database password'), 'non-error', undefined, null])( + 'rethrows unmatched values without changing identity: %s', + async error => { + const wrapped = withErrors( + ep, + errorMap().on(Missing, () => ActionResult.noContent()), + () => { + throw error; + } + ); + await expect(wrapped({} as never)).rejects.toBe(error); + } + ); it('does not feed translator failures back into the policy', async () => { const failure = new SpecialMissing('translator failed'); diff --git a/libs/server/src/RequestContext.security.test.ts b/libs/server/src/RequestContext.security.test.ts index 62feddbf..95edaa68 100644 --- a/libs/server/src/RequestContext.security.test.ts +++ b/libs/server/src/RequestContext.security.test.ts @@ -83,6 +83,38 @@ function createStreamingContext( } describe('RequestContext — security', () => { + it('shares a body read started after the first chunk has arrived', async () => { + const { req, res } = createReqRes(); + const ctx = new RequestContext(req, res); + const first = ctx.body(); + req.emit('data', Buffer.from('first')); + const second = ctx.body(); + req.emit('data', Buffer.from('second')); + req.emit('end'); + expect((await first).toString()).toBe('firstsecond'); + expect(await second).toBe(await first); + expect(req.listenerCount('data')).toBe(0); + expect(req.listenerCount('aborted')).toBe(0); + }); + + it('rejects interrupted reads and remembers the failure', async () => { + const { req, res } = createReqRes(); + const ctx = new RequestContext(req, res); + const pending = ctx.body(); + req.emit('aborted'); + await expect(pending).rejects.toMatchObject({ status: 400 }); + await expect(ctx.body()).rejects.toMatchObject({ status: 400 }); + expect(req.listenerCount('data')).toBe(0); + }); + + it('does not inherit query parameter names from Object.prototype', () => { + const { req, res } = createReqRes({ url: '/?__proto__=value' }); + const params = new RequestContext(req, res).queryParams; + expect( + Object.getOwnPropertyDescriptor(params, '__proto__')?.value + ).toBe('value'); + expect(params.constructor).toBeUndefined(); + }); it('rejects chunked body exceeding maxBodySize with HttpError 413', async () => { const chunk = Buffer.alloc(512, 'x'); const ctx = createStreamingContext([chunk, chunk], 512); @@ -118,7 +150,7 @@ describe('RequestContext — security', () => { const result = (await ctx.json()) as any; expect(result.safe).toBe('ok'); - expect(result.__proto__).toBe(Object.prototype); + expect(Object.getPrototypeOf(result)).toBe(Object.prototype); expect(({} as any).polluted).toBeUndefined(); }); diff --git a/libs/server/src/RequestContext.ts b/libs/server/src/RequestContext.ts index f7816964..194fdc28 100644 --- a/libs/server/src/RequestContext.ts +++ b/libs/server/src/RequestContext.ts @@ -19,7 +19,7 @@ import { checkJsonDepth, safeJsonParse } from './safeJson.js'; */ export const IRequestContext = object({ method: string(), - url: string(), + url: any().hasType(), pathParams: record(string(), string()), queryParams: record(string(), string()), headers: record(string(), string()), @@ -59,8 +59,7 @@ export class RequestContext { /** @internal — overridable for testing */ _queryParams?: Record; #services?: IServiceProvider; - #bodyBuffer: Buffer | null = null; - #bodyRead = false; + #bodyPromise?: Promise; #jsonCache: unknown = undefined; #jsonParsed = false; responded = false; @@ -91,7 +90,7 @@ export class RequestContext { ); // Build headers record (lowercased keys, string values) - const headers: Record = {}; + const headers: Record = Object.create(null); for (const [key, value] of Object.entries(request.headers)) { if (typeof value === 'string') { headers[key] = value; @@ -114,7 +113,7 @@ export class RequestContext { /** Parsed query string parameters from the request URL. */ get queryParams(): Record { if (this._queryParams) return this._queryParams; - const params: Record = {}; + const params: Record = Object.create(null); for (const [key, value] of this.url.searchParams) { params[key] = value; } @@ -130,27 +129,59 @@ export class RequestContext { this.#services = value; } - /** Read and buffer the raw request body. Result is cached after the first call. */ + /** Read once; concurrent readers share the same result or failure. */ async body(): Promise { - if (this.#bodyRead) return this.#bodyBuffer!; - - this.#bodyBuffer = await new Promise((resolve, reject) => { + if (this.#bodyPromise) return this.#bodyPromise; + this.#bodyPromise = new Promise((resolve, reject) => { const chunks: Buffer[] = []; let totalSize = 0; - this.request.on('data', (chunk: Buffer) => { + let settled = false; + const cleanup = () => { + this.request.off('data', data); + this.request.off('end', end); + this.request.off('error', error); + this.request.off('aborted', aborted); + this.request.off('close', closed); + }; + const error = (reason: Error) => { + if (settled) return; + settled = true; + cleanup(); + chunks.length = 0; + reject(reason); + }; + const aborted = () => + error(new HttpError(400, 'Request body interrupted')); + const closed = () => { + if (!this.request.readableEnded) aborted(); + }; + const data = (chunk: Buffer) => { + if (settled) return; totalSize += chunk.length; if (totalSize > this.maxBodySize) { + error(new HttpError(413, 'Payload Too Large')); this.request.destroy(); - reject(new HttpError(413, 'Payload Too Large')); return; } chunks.push(chunk); - }); - this.request.on('end', () => resolve(Buffer.concat(chunks))); - this.request.on('error', reject); + }; + const end = () => { + if (settled) return; + settled = true; + cleanup(); + resolve(Buffer.concat(chunks)); + }; + if (this.request.destroyed || this.request.readableEnded) { + aborted(); + return; + } + this.request.on('data', data); + this.request.on('end', end); + this.request.on('error', error); + this.request.on('aborted', aborted); + this.request.on('close', closed); }); - this.#bodyRead = true; - return this.#bodyBuffer; + return this.#bodyPromise; } /** Read, buffer, and JSON-parse the request body. Result is cached after the first call. */ diff --git a/libs/server/src/Server.cors.test.ts b/libs/server/src/Server.cors.test.ts index 90df54bc..d8354427 100644 --- a/libs/server/src/Server.cors.test.ts +++ b/libs/server/src/Server.cors.test.ts @@ -326,69 +326,74 @@ describe('HTTP CORS preflights', () => { }); describe('HTTP CORS origin policies', () => { - it.each([ - false, - true - ])('evaluates %s async predicates on every request and skips absent origins', async asynchronous => { - let allowed = true; - const predicate = vi.fn((value: string) => - asynchronous - ? Promise.resolve(allowed && value === origin) - : allowed && value === origin - ); - const handler = vi.fn(() => ({})); - const url = await start( - new ServerBuilder() - .useCors({ origin: predicate }) - .handle(endpoint.get('/records'), handler) - ); - expect((await preflight(`${url}/records`, 'GET')).status).toBe(204); - expect( - (await send(`${url}/records`, { headers: { origin } })).status - ).toBe(200); - allowed = false; - expect((await preflight(`${url}/records`, 'GET')).status).toBe(403); - expect( - (await send(`${url}/records`, { headers: { origin } })).status - ).toBe(403); - const absent = await send(`${url}/records`); - expect(absent.status).toBe(200); - expect(absent.headers.get('access-control-allow-origin')).toBeNull(); - expect(absent.headers.get('vary')).toBe('Origin'); - expect(predicate).toHaveBeenCalledTimes(4); - expect(handler).toHaveBeenCalledTimes(2); - }); - - it.each([ - false, - true - ])('fails closed with a generic 500 on %s async callback errors', async asynchronous => { - const handler = vi.fn(() => ({})); - const predicate = () => { - const error = new HttpError(418, 'private origin lookup failure'); - if (asynchronous) return Promise.reject(error); - throw error; - }; - const url = await start( - new ServerBuilder() - .useCors({ origin: predicate }) - .handle(endpoint.post('/records'), handler) - ); - for (const response of [ - await preflight(`${url}/records`), - await send(`${url}/records`, { - method: 'POST', - headers: { origin } - }) - ]) { - expect(response.status).toBe(500); + it.each([false, true])( + 'evaluates %s async predicates on every request and skips absent origins', + async asynchronous => { + let allowed = true; + const predicate = vi.fn((value: string) => + asynchronous + ? Promise.resolve(allowed && value === origin) + : allowed && value === origin + ); + const handler = vi.fn(() => ({})); + const url = await start( + new ServerBuilder() + .useCors({ origin: predicate }) + .handle(endpoint.get('/records'), handler) + ); + expect((await preflight(`${url}/records`, 'GET')).status).toBe(204); expect( - response.headers.get('access-control-allow-origin') + (await send(`${url}/records`, { headers: { origin } })).status + ).toBe(200); + allowed = false; + expect((await preflight(`${url}/records`, 'GET')).status).toBe(403); + expect( + (await send(`${url}/records`, { headers: { origin } })).status + ).toBe(403); + const absent = await send(`${url}/records`); + expect(absent.status).toBe(200); + expect( + absent.headers.get('access-control-allow-origin') ).toBeNull(); - expect(response.body).not.toContain('private'); + expect(absent.headers.get('vary')).toBe('Origin'); + expect(predicate).toHaveBeenCalledTimes(4); + expect(handler).toHaveBeenCalledTimes(2); } - expect(handler).not.toHaveBeenCalled(); - }); + ); + + it.each([false, true])( + 'fails closed with a generic 500 on %s async callback errors', + async asynchronous => { + const handler = vi.fn(() => ({})); + const predicate = () => { + const error = new HttpError( + 418, + 'private origin lookup failure' + ); + if (asynchronous) return Promise.reject(error); + throw error; + }; + const url = await start( + new ServerBuilder() + .useCors({ origin: predicate }) + .handle(endpoint.post('/records'), handler) + ); + for (const response of [ + await preflight(`${url}/records`), + await send(`${url}/records`, { + method: 'POST', + headers: { origin } + }) + ]) { + expect(response.status).toBe(500); + expect( + response.headers.get('access-control-allow-origin') + ).toBeNull(); + expect(response.body).not.toContain('private'); + } + expect(handler).not.toHaveBeenCalled(); + } + ); it('supports explicit public wildcard and opaque-origin policies', async () => { const wildcard = await start( @@ -530,71 +535,70 @@ describe('HTTP CORS response headers', () => { } }); - it.each([ - 'implicit', - 'object', - 'array', - 'message-object', - 'message-array' - ])('merges Vary and preserves cookies with native %s header writes', async mode => { - const url = await start( - new ServerBuilder() - .useCors({ origin }) - .handle(endpoint.get('/records'), () => - ActionResult.raw((_req, res) => { - res.setHeader('vary', 'Accept-Encoding, origin'); - res.setHeader('access-control-allow-origin', '*'); - res.setHeader( - 'access-control-allow-credentials', - 'true' - ); - const headers = { - Vary: 'Accept, ORIGIN', - 'Set-Cookie': ['a=1', 'b=2'], - 'Access-Control-Allow-Origin': otherOrigin - }; - const raw = [ - 'Vary', - 'Accept, ORIGIN', - 'Set-Cookie', - 'a=1', - 'Set-Cookie', - 'b=2', - 'Access-Control-Allow-Origin', - otherOrigin - ]; - if (mode === 'object') res.writeHead(202, headers); - else if (mode === 'array') res.writeHead(202, raw); - else if (mode === 'message-object') - res.writeHead(202, 'Accepted', headers); - else if (mode === 'message-array') - res.writeHead(202, 'Accepted', raw); - else { - res.statusCode = 202; - res.setHeader('set-cookie', ['a=1', 'b=2']); + it.each(['implicit', 'object', 'array', 'message-object', 'message-array'])( + 'merges Vary and preserves cookies with native %s header writes', + async mode => { + const url = await start( + new ServerBuilder() + .useCors({ origin }) + .handle(endpoint.get('/records'), () => + ActionResult.raw((_req, res) => { + res.setHeader('vary', 'Accept-Encoding, origin'); + res.setHeader('access-control-allow-origin', '*'); res.setHeader( - 'vary', - 'Accept-Encoding, origin, Accept' + 'access-control-allow-credentials', + 'true' ); - } - res.end('raw'); - }) - ) - ); - const response = await send(`${url}/records`, { headers: { origin } }); - expect(response.status).toBe(202); - expect(response.body).toBe('raw'); - expect(response.headers.get('access-control-allow-origin')).toBe( - origin - ); - expect( - response.headers.get('access-control-allow-credentials') - ).toBeNull(); - expect(response.headers.get('vary')).toBe( - 'Accept-Encoding, origin, Accept' - ); - expect(response.headers.getSetCookie()).toEqual(['a=1', 'b=2']); - }); + const headers = { + Vary: 'Accept, ORIGIN', + 'Set-Cookie': ['a=1', 'b=2'], + 'Access-Control-Allow-Origin': otherOrigin + }; + const raw = [ + 'Vary', + 'Accept, ORIGIN', + 'Set-Cookie', + 'a=1', + 'Set-Cookie', + 'b=2', + 'Access-Control-Allow-Origin', + otherOrigin + ]; + if (mode === 'object') res.writeHead(202, headers); + else if (mode === 'array') res.writeHead(202, raw); + else if (mode === 'message-object') + res.writeHead(202, 'Accepted', headers); + else if (mode === 'message-array') + res.writeHead(202, 'Accepted', raw); + else { + res.statusCode = 202; + res.setHeader('set-cookie', ['a=1', 'b=2']); + res.setHeader( + 'vary', + 'Accept-Encoding, origin, Accept' + ); + } + res.end('raw'); + }) + ) + ); + const response = await send(`${url}/records`, { + headers: { origin } + }); + expect(response.status).toBe(202); + expect(response.body).toBe('raw'); + expect(response.headers.get('access-control-allow-origin')).toBe( + origin + ); + expect( + response.headers.get('access-control-allow-credentials') + ).toBeNull(); + expect(response.headers.get('vary')).toBe( + 'Accept-Encoding, origin, Accept' + ); + expect(response.headers.getSetCookie()).toEqual(['a=1', 'b=2']); + } + ); it('preserves Vary wildcard and CORS on streamed results', async () => { const url = await start( @@ -619,64 +623,70 @@ describe('HTTP CORS response headers', () => { ); }); - it.each([ - 'cache', - 'idempotency' - ])('recomputes CORS on %s replay without mutating stored headers', async mode => { - let calls = 0; - const savedHeaders = Object.freeze({ - 'content-type': 'text/plain', - Vary: 'Accept', - 'Access-Control-Allow-Origin': origin, - 'Access-Control-Allow-Credentials': 'true' - }); - const url = await start( - new ServerBuilder() - .useCors({ origin: [origin, otherOrigin] }) - .handle( - mode === 'cache' - ? endpoint.get('/records').cacheTag('records') - : endpoint.post('/records'), - () => - ActionResult.raw((_req, res) => { - calls++; - res.writeHead(200, savedHeaders); - res.end('saved body'); - }), - { - middlewares: [ - mode === 'cache' ? cacheResponse() : idempotency() - ] + it.each(['cache', 'idempotency'])( + 'recomputes CORS on %s replay without mutating stored headers', + async mode => { + let calls = 0; + const savedHeaders = Object.freeze({ + 'content-type': 'text/plain', + Vary: 'Accept', + 'Access-Control-Allow-Origin': origin, + 'Access-Control-Allow-Credentials': 'true' + }); + const url = await start( + new ServerBuilder() + .useCors({ origin: [origin, otherOrigin] }) + .handle( + mode === 'cache' + ? endpoint.get('/records').cacheTag('records') + : endpoint.post('/records'), + () => + ActionResult.raw((_req, res) => { + calls++; + res.writeHead(200, savedHeaders); + res.end('saved body'); + }), + { + middlewares: [ + mode === 'cache' + ? cacheResponse() + : idempotency({ + scope: () => 'public-test' + }) + ] + } + ) + ); + for (const value of [origin, otherOrigin, undefined]) { + const response = await send(`${url}/records`, { + method: mode === 'cache' ? 'GET' : 'POST', + headers: { + ...(value ? { origin: value } : {}), + 'x-idempotency-key': 'same-key' } - ) - ); - for (const value of [origin, otherOrigin, undefined]) { - const response = await send(`${url}/records`, { + }); + expect(response.body).toBe('saved body'); + expect( + response.headers.get('access-control-allow-origin') + ).toBe(value ?? null); + expect( + response.headers.get('access-control-allow-credentials') + ).toBeNull(); + expect(response.headers.get('vary')).toBe('Accept, Origin'); + } + const denied = await send(`${url}/records`, { method: mode === 'cache' ? 'GET' : 'POST', headers: { - ...(value ? { origin: value } : {}), + origin: 'https://denied.example.test', 'x-idempotency-key': 'same-key' } }); - expect(response.body).toBe('saved body'); - expect(response.headers.get('access-control-allow-origin')).toBe( - value ?? null - ); + expect(denied.status).toBe(403); expect( - response.headers.get('access-control-allow-credentials') + denied.headers.get('access-control-allow-origin') ).toBeNull(); - expect(response.headers.get('vary')).toBe('Accept, Origin'); + expect(savedHeaders.Vary).toBe('Accept'); + expect(calls).toBe(1); } - const denied = await send(`${url}/records`, { - method: mode === 'cache' ? 'GET' : 'POST', - headers: { - origin: 'https://denied.example.test', - 'x-idempotency-key': 'same-key' - } - }); - expect(denied.status).toBe(403); - expect(denied.headers.get('access-control-allow-origin')).toBeNull(); - expect(savedHeaders.Vary).toBe('Accept'); - expect(calls).toBe(1); - }); + ); }); diff --git a/libs/server/src/Server.ts b/libs/server/src/Server.ts index 195af3c4..0eab94ef 100644 --- a/libs/server/src/Server.ts +++ b/libs/server/src/Server.ts @@ -195,7 +195,7 @@ export class ServerBuilder { * Enable the `GET /health` endpoint that returns `{ ok: true }` (200). * Useful for load balancer and container readiness probes. * - * The path is available as {@link HEALTHCHECK_PATH}. + * The path is available as `HEALTHCHECK_PATH`. */ withHealthcheck(): this { this.#healthcheck = true; diff --git a/libs/server/src/VirtualHttp.test.ts b/libs/server/src/VirtualHttp.test.ts new file mode 100644 index 00000000..c617d983 --- /dev/null +++ b/libs/server/src/VirtualHttp.test.ts @@ -0,0 +1,19 @@ +import { describe, expect, it } from 'vitest'; +import { VirtualServerResponse } from './VirtualHttp.js'; + +describe('virtual HTTP response', () => { + it('preserves directly assigned status codes in batch results', () => { + const response = new VirtualServerResponse(); + response.statusCode = 202; + response.end('accepted'); + expect(response.toResult().status).toBe(202); + }); + + it('provides a header snapshot for response middleware', () => { + const response = new VirtualServerResponse(); + response.setHeader('Cache-Control', 'private'); + const headers = response.getHeaders(); + headers['cache-control'] = 'public'; + expect(response.getHeaders()).toEqual({ 'cache-control': 'private' }); + }); +}); diff --git a/libs/server/src/VirtualHttp.ts b/libs/server/src/VirtualHttp.ts index e7d18b47..9c608be5 100644 --- a/libs/server/src/VirtualHttp.ts +++ b/libs/server/src/VirtualHttp.ts @@ -114,7 +114,6 @@ export class VirtualServerResponse extends Writable { readonly #chunks: Buffer[] = []; readonly #customHeaders: Record = {}; - #customStatus = 200; // ----------------------------------------------------------------------- // Writable interface — captures data written via readable.pipe(res) @@ -141,7 +140,6 @@ export class VirtualServerResponse extends Writable { status: number, headers?: Record | string | string[] ): this { - this.#customStatus = status; this.statusCode = status; if ( headers != null && @@ -179,6 +177,11 @@ export class VirtualServerResponse extends Writable { return this.#customHeaders[name.toLowerCase()]; } + /** Return a snapshot of response headers, matching ServerResponse. */ + getHeaders(): Record { + return { ...this.#customHeaders }; + } + /** * Captures the body and signals the end of the response. * @@ -215,7 +218,7 @@ export class VirtualServerResponse extends Writable { */ toResult(): VirtualResult { return { - status: this.#customStatus, + status: this.statusCode, headers: { ...this.#customHeaders }, body: Buffer.concat(this.#chunks).toString('utf-8') }; diff --git a/libs/server/src/WebSocketProtocol.test.ts b/libs/server/src/WebSocketProtocol.test.ts index 15e6c063..c95a9c59 100644 --- a/libs/server/src/WebSocketProtocol.test.ts +++ b/libs/server/src/WebSocketProtocol.test.ts @@ -101,7 +101,7 @@ describe('WebSocketProtocol', () => { if (frame!.type === 'message') { const data = frame?.data as any; expect(data?.safe).toBe('ok'); - expect(data?.__proto__).toBe(Object.prototype); + expect(Object.getPrototypeOf(data)).toBe(Object.prototype); expect(({} as any).polluted).toBeUndefined(); } }); diff --git a/libs/server/src/cacheKey.test.ts b/libs/server/src/cacheKey.test.ts index 042ecb3d..fefa926d 100644 --- a/libs/server/src/cacheKey.test.ts +++ b/libs/server/src/cacheKey.test.ts @@ -82,15 +82,12 @@ describe('versioned cache keys', () => { key({ value: [{ id: 1 }, { id: 1 }] }) ); }); - test.each([ - new Map(), - new Set(), - new Date(NaN), - Symbol(), - () => 1 - ])('rejects unsupported values explicitly: %s', value => { - expect(() => key({ value })).toThrow(TypeError); - }); + test.each([new Map(), new Set(), new Date(NaN), Symbol(), () => 1])( + 'rejects unsupported values explicitly: %s', + value => { + expect(() => key({ value })).toThrow(TypeError); + } + ); test('rejects cycles and getters', () => { const cyclic: any = {}; cyclic.self = cyclic; diff --git a/libs/server/src/middlewares/Idempotency.test.ts b/libs/server/src/middlewares/Idempotency.test.ts new file mode 100644 index 00000000..9d56ae79 --- /dev/null +++ b/libs/server/src/middlewares/Idempotency.test.ts @@ -0,0 +1,107 @@ +import { IncomingMessage, ServerResponse } from 'node:http'; +import { Socket } from 'node:net'; +import { describe, expect, it, vi } from 'vitest'; +import { RequestContext } from '../RequestContext.js'; +import { idempotency } from './Idempotency.js'; + +function context(scope = 'user-a', url = '/records', method = 'POST') { + const request = new IncomingMessage(new Socket()); + request.method = method; + request.url = url; + request.headers = { 'x-idempotency-key': 'same-key' }; + const ctx = new RequestContext(request, new ServerResponse(request)); + ctx.items.set('verified-scope', scope); + return ctx; +} + +describe('idempotent replay isolation and concurrency', () => { + const scoped = (options = {}) => + idempotency({ + scope: ctx => ctx.items.get('verified-scope') as string, + ...options + }); + + it('separates callers, methods and URLs using the same client key', async () => { + const middleware = scoped(); + for (const ctx of [ + context(), + context('user-b'), + context('user-a', '/other'), + context('user-a', '/records', 'PATCH') + ]) { + const handler = vi.fn(async () => { + ctx.response.end('ok'); + }); + await middleware(ctx, handler); + expect(handler).toHaveBeenCalledOnce(); + } + const handler = vi.fn(); + await middleware(context(), handler); + expect(handler).not.toHaveBeenCalled(); + }); + + it('coalesces simultaneous requests before the first response exists', async () => { + const middleware = scoped(); + const first = context(); + let release!: () => void; + const wait = new Promise(resolve => { + release = resolve; + }); + const original = middleware(first, async () => { + await wait; + first.response.end('done'); + }); + const duplicate = vi.fn(); + const replay = middleware(context(), duplicate); + release(); + await Promise.all([original, replay]); + expect(duplicate).not.toHaveBeenCalled(); + }); + + it('propagates failures to waiters and releases the reservation', async () => { + const middleware = scoped(); + const original = middleware(context(), async () => { + throw new Error('failed'); + }); + const waiter = middleware(context(), vi.fn()); + const results = await Promise.allSettled([original, waiter]); + expect(results.map(result => result.status)).toEqual([ + 'rejected', + 'rejected' + ]); + const ctx = context(); + const handler = vi.fn(async () => { + ctx.response.end('ok'); + }); + await middleware(ctx, handler); + expect(handler).toHaveBeenCalledOnce(); + }); + + it('does not re-execute a completed operation whose response was too large', async () => { + const middleware = scoped({ maxResponseBytes: 1 }); + const ctx = context(); + await middleware(ctx, async () => { + ctx.response.end('large'); + }); + const handler = vi.fn(); + await expect(middleware(context(), handler)).rejects.toMatchObject({ + status: 409 + }); + expect(handler).not.toHaveBeenCalled(); + }); + + it('rejects capacity overflow without executing and allows unscoped pass-through', async () => { + const middleware = scoped({ maxEntries: 1 }); + const ctx = context(); + await middleware(ctx, async () => { + ctx.response.end('ok'); + }); + const handler = vi.fn(); + await expect( + middleware(context('user-b'), handler) + ).rejects.toMatchObject({ status: 503 }); + expect(handler).not.toHaveBeenCalled(); + await idempotency({ scope: () => undefined })(context(), handler); + expect(handler).toHaveBeenCalledOnce(); + }); +}); diff --git a/libs/server/src/middlewares/Idempotency.ts b/libs/server/src/middlewares/Idempotency.ts index b30bb8b2..4636c183 100644 --- a/libs/server/src/middlewares/Idempotency.ts +++ b/libs/server/src/middlewares/Idempotency.ts @@ -1,185 +1,123 @@ -/** - * Server-side idempotency middleware. - * - * Ensures mutating requests with the same idempotency key produce the - * same result exactly once — subsequent replays return the stored - * response without re-executing the handler. - * - * @module - */ - -import type { ServerResponse } from 'node:http'; +import { createHash } from 'node:crypto'; +import { HttpError } from '../HttpError.js'; import type { RequestContext } from '../RequestContext.js'; import type { Middleware } from '../types.js'; +import { captureResponse, type ResponseSnapshot } from './ResponseSnapshot.js'; -// --------------------------------------------------------------------------- -// Types -// --------------------------------------------------------------------------- - -/** - * Configuration for {@link idempotency}. - */ +/** Configuration for bounded, process-local mutation response replay. */ export interface ServerIdempotencyOptions { /** - * TTL in milliseconds for stored responses. - * Defaults to `86_400_000` (24 hours). + * Explicit authorization/tenant scope. Run after authentication and + * authorization. Return undefined to skip; use a fixed string only for + * deliberately public operations. Never use an unverified identity header. */ + scope: (ctx: RequestContext) => string | undefined; + /** Stored response lifetime in milliseconds. Default: 86,400,000. */ ttl?: number; - - /** - * Header name to read the idempotency key from. - * Defaults to `"x-idempotency-key"`. - */ + /** Case-insensitive request header. Default: x-idempotency-key. */ headerName?: string; - - /** - * Predicate that decides whether a request should be skipped. - * Defaults to skipping non-mutating requests (GET, HEAD, OPTIONS). - */ + /** Default: skip GET, HEAD, OPTIONS and other non-mutating methods. */ skip?: (ctx: RequestContext) => boolean; + /** Maximum retained keys, including pending requests. Default: 1000. */ + maxEntries?: number; + /** Maximum response body retained per key. Default: 65,536 bytes. */ + maxResponseBytes?: number; } -// --------------------------------------------------------------------------- -// Internals -// --------------------------------------------------------------------------- - -interface StoredResponse { - status: number; - headers: Record; - body: Buffer; +interface Entry { + result: Promise; expiresAt: number; } -function isMutating(method: string): boolean { - return ['POST', 'PUT', 'DELETE', 'PATCH'].includes(method.toUpperCase()); -} - -const CLEANUP_INTERVAL = 60_000; - -// --------------------------------------------------------------------------- -// Middleware -// --------------------------------------------------------------------------- - /** - * Server-side idempotency middleware. - * - * Reads the `x-idempotency-key` header from mutating requests. If a - * response has already been stored for that key, it is returned - * immediately — the handler is never called. Otherwise the handler - * executes and its response is stored for future replays. - * - * GET, HEAD, and OPTIONS requests pass through without checking. - * - * @param options - Configuration. - * @returns A server-side {@link Middleware}. - * - * @example - * ```ts - * server.handle(CreateTodo, createHandler, { - * middlewares: [idempotency({ ttl: 86_400_000 })] - * }); - * ``` + * Replay completed mutations and coalesce concurrent requests within one + * middleware instance. Keys include explicit scope, method and full request URL. + * Reusing a key means retrying the same input; bodies are not fingerprinted. + * This is not durable exactly-once execution: restarts, expiry and thrown + * failures may permit another execution. Use database deduplication for that. + * Oversized/incomplete responses retain a non-replayable reservation (409). + * Capacity exhaustion returns 503 without executing the operation. */ -export function idempotency( - options: ServerIdempotencyOptions = {} -): Middleware { +export function idempotency(options: ServerIdempotencyOptions): Middleware { + if (typeof options?.scope !== 'function') + throw new TypeError('idempotency requires an explicit scope'); const { + scope, ttl = 86_400_000, headerName = 'x-idempotency-key', - skip = (ctx: RequestContext) => !isMutating(ctx.method) + skip = ctx => !['POST', 'PUT', 'DELETE', 'PATCH'].includes(ctx.method), + maxEntries = 1000, + maxResponseBytes = 65_536 } = options; - - const store = new Map(); - - // Periodic cleanup of expired entries - const cleanupTimer = setInterval(() => { - const now = Date.now(); - for (const [key, entry] of store) { - if (entry.expiresAt <= now) { - store.delete(key); - } - } - }, CLEANUP_INTERVAL); - - if (cleanupTimer.unref) { - cleanupTimer.unref(); + for (const [name, value] of Object.entries({ + ttl, + maxEntries, + maxResponseBytes + })) { + if (!Number.isSafeInteger(value) || value <= 0) + throw new TypeError(name + ' must be a positive safe integer'); } - - return async (ctx: RequestContext, next: () => Promise) => { - if (skip(ctx)) { - return next(); - } - - const key = - ctx.headers[headerName] ?? ctx.headers[headerName.toLowerCase()]; - if (!key || typeof key !== 'string' || key.length === 0) { - return next(); - } - - // Check if we already have a stored response for this key - const stored = store.get(key); - if (stored) { - if (stored.expiresAt <= Date.now()) { - store.delete(key); - // Expired — fall through to handler - } else { - // Replay stored response - const res = ctx.response as ServerResponse; - res.writeHead(stored.status, stored.headers); - res.end(stored.body); - ctx.responded = true; - return; - } - } - - // Capture the handler's response for future replays - const originalWriteHead = ( - ctx.response as ServerResponse - ).writeHead.bind(ctx.response); - const originalEnd = (ctx.response as ServerResponse).end.bind( - ctx.response - ); - - let capturedStatus = 200; - let capturedHeaders: Record = {}; - const chunks: Buffer[] = []; - - (ctx.response as ServerResponse).writeHead = function ( - this: ServerResponse, - statusCode: number, - ...args: any[] - ) { - capturedStatus = statusCode; - if (args.length > 0) { - capturedHeaders = args[0]; - } - return originalWriteHead(statusCode, ...args) as ServerResponse; - } as any; - - (ctx.response as ServerResponse).end = function ( - this: ServerResponse, - chunk?: any, - ...args: any[] - ) { - if (chunk) { - chunks.push( - Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk) + const store = new Map(); + return async (ctx, next) => { + const supplied = + ctx.headers[headerName.toLowerCase()] ?? ctx.headers[headerName]; + if (skip(ctx) || !supplied) return next(); + if (supplied.length > 256) + throw new HttpError(400, 'Idempotency key is too long'); + const identity = scope(ctx); + if (identity === undefined) return next(); + if (typeof identity !== 'string') + throw new TypeError('Idempotency scope must be a string'); + const key = createHash('sha256') + .update( + JSON.stringify([ + identity, + ctx.method, + ctx.url?.href ?? '/', + supplied + ]) + ) + .digest('hex'); + const now = Date.now(); + for (const [k, entry] of store) + if (entry.expiresAt <= now) store.delete(k); + const existing = store.get(key); + if (existing) { + const snapshot = await existing.result; + if (!snapshot) + throw new HttpError( + 409, + 'The previous response cannot be replayed; verify the operation outcome' ); - } - return originalEnd(chunk, ...args) as ServerResponse; - } as any; - - await next(); - - // Store the response for future replays - if (capturedStatus >= 200 && capturedStatus < 500) { - const body = Buffer.concat(chunks); - store.set(key, { - status: capturedStatus, - headers: capturedHeaders, - body, - expiresAt: Date.now() + ttl - }); + ctx.response.writeHead(snapshot.status, snapshot.headers); + ctx.response.end(snapshot.body); + ctx.responded = true; + return; + } + if (store.size >= maxEntries) + throw new HttpError(503, 'Idempotency capacity reached'); + let resolve!: (value: ResponseSnapshot | undefined) => void; + let reject!: (error: unknown) => void; + const result = new Promise((yes, no) => { + resolve = yes; + reject = no; + }); + // The first caller observes errors directly even when no waiter exists. + void result.catch(() => undefined); + const entry: Entry = { result, expiresAt: Infinity }; + store.set(key, entry); + try { + const snapshot = await captureResponse( + ctx.response, + next, + maxResponseBytes + ); + entry.expiresAt = Date.now() + ttl; + resolve(snapshot); + } catch (error) { + store.delete(key); + reject(error); + throw error; } }; } diff --git a/libs/server/src/middlewares/ResponseCache.test.ts b/libs/server/src/middlewares/ResponseCache.test.ts index f77dbace..d9675efe 100644 --- a/libs/server/src/middlewares/ResponseCache.test.ts +++ b/libs/server/src/middlewares/ResponseCache.test.ts @@ -34,6 +34,44 @@ function send(ctx: RequestContext, body: string, status = 200) { } describe('server response cache generations', () => { + test.each([ + { 'Cache-Control': 'private, max-age=60' }, + { 'cache-control': 'no-store' }, + { 'Set-Cookie': 'session=private' } + ])('does not replay private response headers %j', async headers => { + const cache = cacheResponse(); + const initial = context(); + await cache(initial, async () => { + initial.response.writeHead(200, headers); + initial.response.end('private data'); + }); + const handler = vi.fn(); + await cache(context(), handler); + expect(handler).toHaveBeenCalledOnce(); + }); + test('evicts oldest keys at capacity and bypasses oversized bodies', async () => { + const cache = cacheResponse({ maxEntries: 1, maxResponseBytes: 3 }); + for (const name of ['first', 'second']) { + const ctx = context('GET', [name]); + await cache(ctx, async () => send(ctx, 'ok')); + } + const miss = vi.fn(); + await cache(context('GET', ['first']), miss); + expect(miss).toHaveBeenCalledOnce(); + const hit = vi.fn(); + await cache(context('GET', ['second']), hit); + expect(hit).not.toHaveBeenCalled(); + const large = context('GET', ['large']); + await cache(large, async () => send(large, 'too large')); + await cache(context('GET', ['large']), miss); + expect(miss).toHaveBeenCalledTimes(2); + }); + test('rejects unbounded or invalid retention settings', () => { + expect(() => cacheResponse({ maxEntries: 0 })).toThrow(); + expect(() => cacheResponse({ maxResponseBytes: Infinity })).toThrow(); + expect(() => cacheResponse({ defaultTtl: NaN })).toThrow(); + expect(() => cacheResponse({ ttlByTag: { records: -1 } })).toThrow(); + }); test('preserves HTTP body, status and headers across cached responses', async () => { const cache = cacheResponse(); let reads = 0; @@ -89,18 +127,19 @@ describe('server response cache generations', () => { await cache(context(), handler); expect(handler).toHaveBeenCalledTimes(1); }); - test.each([ - 400, 500 - ])('failed write %s preserves cached entries', async status => { - const cache = cacheResponse(); - const initial = context(); - await cache(initial, async () => send(initial, 'old')); - const write = context('PATCH'); - await cache(write, async () => send(write, '', status)); - const handler = vi.fn(); - await cache(context(), handler); - expect(handler).not.toHaveBeenCalled(); - }); + test.each([400, 500])( + 'failed write %s preserves cached entries', + async status => { + const cache = cacheResponse(); + const initial = context(); + await cache(initial, async () => send(initial, 'old')); + const write = context('PATCH'); + await cache(write, async () => send(write, '', status)); + const handler = vi.fn(); + await cache(context(), handler); + expect(handler).not.toHaveBeenCalled(); + } + ); test('a thrown write preserves entries', async () => { const cache = cacheResponse(); const initial = context(); diff --git a/libs/server/src/middlewares/ResponseCache.ts b/libs/server/src/middlewares/ResponseCache.ts index 3edd4147..314e5b44 100644 --- a/libs/server/src/middlewares/ResponseCache.ts +++ b/libs/server/src/middlewares/ResponseCache.ts @@ -9,11 +9,12 @@ * @module */ -import type { ServerResponse } from 'node:http'; +import type { OutgoingHttpHeaders, ServerResponse } from 'node:http'; import type { CacheTagDefinition } from '../CacheTag.js'; import { computeCacheKey } from '../cacheKey.js'; import type { RequestContext } from '../RequestContext.js'; import type { Middleware } from '../types.js'; +import { captureResponse } from './ResponseSnapshot.js'; // --------------------------------------------------------------------------- // Types @@ -33,6 +34,10 @@ export interface ServerCacheOptions { * Per-tag TTL overrides: `{ [tagName]: ttlMs }`. */ ttlByTag?: Record; + /** Maximum retained cache keys. Default: 1000 (oldest keys are evicted). */ + maxEntries?: number; + /** Maximum retained response body size. Default: 65,536 bytes. */ + maxResponseBytes?: number; } // --------------------------------------------------------------------------- @@ -41,7 +46,7 @@ export interface ServerCacheOptions { interface CacheEntry { status: number; - headers: Record; + headers: OutgoingHttpHeaders; body: Buffer; expiresAt: number; generations: ReadonlyArray; @@ -68,6 +73,9 @@ function isMutating(method: string): boolean { * - **Mutation (POST/PUT/PATCH/DELETE)**: Lets the handler run, then * invalidates all cache entries whose tag names start with any of the * endpoint's cache tag names. Older in-flight reads cannot refill them. + * - By default, retain at most 1000 keys and bodies up to 65,536 bytes. Private/no-store and + * Set-Cookie responses are not cached. Install after authorization and encode + * every identity/tenant/representation dimension in tag properties. * * @param options - Cache configuration. * @returns A server-side {@link Middleware}. @@ -80,7 +88,23 @@ function isMutating(method: string): boolean { * ``` */ export function cacheResponse(options: ServerCacheOptions = {}): Middleware { - const { ttlByTag = {}, defaultTtl = 60_000 } = options; + const { + defaultTtl = 60_000, + maxEntries = 1000, + maxResponseBytes = 65_536 + } = options; + const ttlByTag = { ...options.ttlByTag }; + for (const [name, value] of Object.entries({ + maxEntries, + maxResponseBytes + })) { + if (!Number.isSafeInteger(value) || value <= 0) + throw new TypeError(name + ' must be a positive safe integer'); + } + for (const value of [defaultTtl, ...Object.values(ttlByTag)]) { + if (!Number.isFinite(value) || value < 0) + throw new TypeError('Cache TTL must be finite and non-negative'); + } const cache = new Map(); const generations = new Map(); @@ -119,6 +143,8 @@ export function cacheResponse(options: ServerCacheOptions = {}): Middleware { } if (ctx.method === 'GET') { + for (const [key, entry] of cache) + if (entry.expiresAt <= Date.now()) cache.delete(key); // Build root for key computation const root = { params: ctx.pathParams ?? {}, @@ -153,45 +179,29 @@ export function cacheResponse(options: ServerCacheOptions = {}): Middleware { } } - // Cache miss — run handler, then capture the response - const originalWriteHead = ( - ctx.response as ServerResponse - ).writeHead.bind(ctx.response); - const originalEnd = (ctx.response as ServerResponse).end.bind( - ctx.response + const snapshotResponse = await captureResponse( + ctx.response, + next, + maxResponseBytes ); - - let capturedStatus = 200; - let capturedHeaders: Record = - {}; - const chunks: Buffer[] = []; - - (ctx.response as ServerResponse).writeHead = function ( - this: ServerResponse, - statusCode: number, - ...args: any[] - ) { - capturedStatus = statusCode; - if (args.length > 0) { - capturedHeaders = args[0]; - } - return originalWriteHead(statusCode, ...args) as ServerResponse; - } as any; - - (ctx.response as ServerResponse).end = function ( - this: ServerResponse, - chunk?: any, - ...args: any[] - ) { - if (chunk) { - chunks.push( - Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk) - ); - } - return originalEnd(chunk, ...args) as ServerResponse; - } as any; - - await next(); + if (!snapshotResponse) return; + const { + status: capturedStatus, + headers: capturedHeaders, + body + } = snapshotResponse; + if ( + Object.keys(capturedHeaders).some( + name => name.toLowerCase() === 'set-cookie' + ) + ) + return; + if ( + /\b(?:private|no-store)\b/i.test( + String(capturedHeaders['cache-control'] ?? '') + ) + ) + return; // Store in cache on success if ( @@ -199,7 +209,6 @@ export function cacheResponse(options: ServerCacheOptions = {}): Middleware { capturedStatus < 300 && isCurrent(snapshot) ) { - const body = Buffer.concat(chunks); const ttl = tags.reduce((max, tag) => { const t = ttlByTag[tag.name] !== undefined @@ -210,6 +219,8 @@ export function cacheResponse(options: ServerCacheOptions = {}): Middleware { if (ttl > 0) { for (const key of keys) { + if (!cache.has(key) && cache.size >= maxEntries) + cache.delete(cache.keys().next().value!); cache.set(key, { status: capturedStatus, headers: capturedHeaders, diff --git a/libs/server/src/middlewares/ResponseSnapshot.test.ts b/libs/server/src/middlewares/ResponseSnapshot.test.ts new file mode 100644 index 00000000..3fee79af --- /dev/null +++ b/libs/server/src/middlewares/ResponseSnapshot.test.ts @@ -0,0 +1,100 @@ +import { IncomingMessage, ServerResponse } from 'node:http'; +import { Socket } from 'node:net'; +import { describe, expect, it } from 'vitest'; +import { captureResponse } from './ResponseSnapshot.js'; + +describe('response snapshots', () => { + const response = () => + new ServerResponse(new IncomingMessage(new Socket())); + + it('preserves native status, setHeader, writeHead overloads and every body chunk', async () => { + const res = response(); + const methods = { + write: res.write, + writeHead: res.writeHead, + end: res.end + }; + const snapshot = await captureResponse( + res, + async () => { + res.setHeader('x-before', 'yes'); + res.writeHead(201, 'Created', { 'content-type': 'text/plain' }); + res.write('a'); + res.write('e9', 'hex'); + res.end(new Uint8Array([98])); + }, + 10 + ); + expect(snapshot).toMatchObject({ + status: 201, + headers: { 'x-before': 'yes', 'content-type': 'text/plain' } + }); + expect(snapshot?.body).toEqual(Buffer.from([97, 233, 98])); + expect(res.write).toBe(methods.write); + expect(res.end).toBe(methods.end); + expect(res.writeHead).toBe(methods.writeHead); + }); + + it('handles implicit headers and end(callback)', async () => { + const res = response(); + const snapshot = await captureResponse( + res, + async () => { + res.statusCode = 202; + res.setHeader('x-before', 'yes'); + res.write('body'); + res.end(() => {}); + }, + 10 + ); + expect(snapshot).toMatchObject({ + status: 202, + headers: { 'x-before': 'yes' }, + body: Buffer.from('body') + }); + }); + + it('preserves duplicate raw headers such as Set-Cookie', async () => { + const res = response(); + const snapshot = await captureResponse( + res, + async () => { + res.writeHead(200, ['Set-Cookie', 'a=1', 'Set-Cookie', 'b=2']); + res.end('ok'); + }, + 10 + ); + expect(snapshot?.headers['set-cookie']).toEqual(['a=1', 'b=2']); + }); + + it('does not retain oversized or incomplete responses', async () => { + expect( + await captureResponse(response(), async () => {}, 1) + ).toBeUndefined(); + const res = response(); + expect( + await captureResponse( + res, + async () => { + res.end('large'); + }, + 1 + ) + ).toBeUndefined(); + }); + + it('restores hooks after handler failure', async () => { + const res = response(); + const end = res.end; + await expect( + captureResponse( + res, + async () => { + throw new Error('failure'); + }, + 10 + ) + ).rejects.toThrow('failure'); + expect(res.end).toBe(end); + }); +}); diff --git a/libs/server/src/middlewares/ResponseSnapshot.ts b/libs/server/src/middlewares/ResponseSnapshot.ts new file mode 100644 index 00000000..817a06cd --- /dev/null +++ b/libs/server/src/middlewares/ResponseSnapshot.ts @@ -0,0 +1,100 @@ +import type { OutgoingHttpHeaders, ServerResponse } from 'node:http'; + +/** @internal A complete, bounded response suitable for replay. */ +export interface ResponseSnapshot { + status: number; + headers: OutgoingHttpHeaders; + body: Buffer; +} + +/** @internal Capture all write/end overloads without changing their behavior. */ +export async function captureResponse( + response: ServerResponse, + next: () => Promise, + maxBytes: number +): Promise { + const { write, writeHead, end } = response; + const chunks: Buffer[] = []; + let size = 0; + let ended = false; + let headers: OutgoingHttpHeaders = {}; + const capture = (chunk: unknown, encoding?: unknown) => { + if (chunk == null || typeof chunk === 'function' || size > maxBytes) + return; + const buffer = + typeof chunk === 'string' + ? Buffer.from( + chunk, + typeof encoding === 'string' + ? (encoding as BufferEncoding) + : 'utf8' + ) + : Buffer.from(chunk as Uint8Array); + size += buffer.length; + if (size > maxBytes) chunks.length = 0; + else chunks.push(buffer); + }; + response.writeHead = function ( + this: ServerResponse, + status: number, + ...args: any[] + ) { + const supplied = typeof args[0] === 'string' ? args[1] : args[0]; + if (Array.isArray(supplied)) { + const raw: OutgoingHttpHeaders = {}; + for (let i = 0; i < supplied.length; i += 2) { + const name = supplied[i].toLowerCase(); + const previous = raw[name]; + raw[name] = + previous === undefined + ? supplied[i + 1] + : [ + ...(Array.isArray(previous) + ? previous + : [String(previous)]), + supplied[i + 1] + ]; + } + headers = { ...headers, ...raw }; + } else if (supplied) { + for (const [name, value] of Object.entries(supplied)) + headers[name.toLowerCase()] = + value as OutgoingHttpHeaders[string]; + } + const result = Reflect.apply(writeHead, this, [status, ...args]); + headers = { ...headers, ...this.getHeaders?.() }; + return result; + } as typeof writeHead; + response.write = function ( + this: ServerResponse, + chunk: any, + ...args: any[] + ) { + capture(chunk, args[0]); + return Reflect.apply(write, this, [chunk, ...args]); + } as typeof write; + response.end = function ( + this: ServerResponse, + chunk?: any, + ...args: any[] + ) { + capture(chunk, args[0]); + const result = Reflect.apply(end, this, [chunk, ...args]); + ended = true; + headers = { ...headers, ...this.getHeaders?.() }; + return result; + } as typeof end; + try { + await next(); + if (!ended || size > maxBytes) return undefined; + return { + status: response.statusCode, + headers, + body: Buffer.concat(chunks) + }; + } finally { + response.write = write; + response.writeHead = writeHead; + response.end = end; + } +} diff --git a/libs/server/src/safeJson.test.ts b/libs/server/src/safeJson.test.ts index a2e282cb..669e0605 100644 --- a/libs/server/src/safeJson.test.ts +++ b/libs/server/src/safeJson.test.ts @@ -29,7 +29,7 @@ describe('safeJsonParse', () => { '{"__proto__":{"polluted":true},"safe":"value"}' ) as any; expect(result.safe).toBe('value'); - expect(result.__proto__).toBe(Object.prototype); + expect(Object.getPrototypeOf(result)).toBe(Object.prototype); expect(({} as any).polluted).toBeUndefined(); }); @@ -38,7 +38,7 @@ describe('safeJsonParse', () => { '{"a":{"__proto__":{"polluted":true},"b":1}}' ) as any; expect(result.a.b).toBe(1); - expect(result.a.__proto__).toBe(Object.prototype); + expect(Object.getPrototypeOf(result.a)).toBe(Object.prototype); expect(({} as any).polluted).toBeUndefined(); }); @@ -55,7 +55,7 @@ describe('safeJsonParse', () => { '[{"__proto__":{"polluted":true},"ok":1}]' ) as any[]; expect(result[0].ok).toBe(1); - expect(result[0].__proto__).toBe(Object.prototype); + expect(Object.getPrototypeOf(result[0])).toBe(Object.prototype); expect(({} as any).polluted).toBeUndefined(); }); diff --git a/libs/server/tests/Idempotency.test.ts b/libs/server/tests/Idempotency.test.ts index 184dc1de..02bb3d41 100644 --- a/libs/server/tests/Idempotency.test.ts +++ b/libs/server/tests/Idempotency.test.ts @@ -49,7 +49,7 @@ describe('idempotency middleware', () => { }); test('passes through GET requests', async () => { - const mw = idempotency({ ttl: 5000 }); + const mw = idempotency({ scope: () => 'public-test', ttl: 5000 }); const ctx = makeContext({ 'x-idempotency-key': 'key-1' }, 'GET'); const next = vi.fn().mockResolvedValue(undefined); await mw(ctx, next); @@ -57,7 +57,7 @@ describe('idempotency middleware', () => { }); test('passes through mutations without idempotency key', async () => { - const mw = idempotency({ ttl: 5000 }); + const mw = idempotency({ scope: () => 'public-test', ttl: 5000 }); const ctx = makeContext({}, 'POST'); const next = vi.fn().mockResolvedValue(undefined); await mw(ctx, next); @@ -65,7 +65,7 @@ describe('idempotency middleware', () => { }); test('passes through on first request with key', async () => { - const mw = idempotency({ ttl: 5000 }); + const mw = idempotency({ scope: () => 'public-test', ttl: 5000 }); const ctx = makeContext({ 'x-idempotency-key': 'key-1' }, 'POST'); let handlerCalled = false; await mw(ctx, async () => { @@ -77,7 +77,7 @@ describe('idempotency middleware', () => { }); test('returns stored response on duplicate key', async () => { - const mw = idempotency({ ttl: 10_000 }); + const mw = idempotency({ scope: () => 'public-test', ttl: 10_000 }); // First request const ctx1 = makeContext({ 'x-idempotency-key': 'key-dup' }, 'POST'); @@ -100,7 +100,7 @@ describe('idempotency middleware', () => { }); test('different keys store independently', async () => { - const mw = idempotency({ ttl: 10_000 }); + const mw = idempotency({ scope: () => 'public-test', ttl: 10_000 }); const ctx1 = makeContext({ 'x-idempotency-key': 'key-a' }, 'POST'); await mw(ctx1, async () => { @@ -118,7 +118,7 @@ describe('idempotency middleware', () => { }); test('expired key calls handler again', async () => { - const mw = idempotency({ ttl: 1000 }); + const mw = idempotency({ scope: () => 'public-test', ttl: 1000 }); const ctx1 = makeContext({ 'x-idempotency-key': 'key-exp' }, 'POST'); await mw(ctx1, async () => { @@ -140,6 +140,7 @@ describe('idempotency middleware', () => { test('handles case-insensitive header name', async () => { const mw = idempotency({ + scope: () => 'public-test', ttl: 10_000, headerName: 'X-Idempotency-Key' }); @@ -160,7 +161,7 @@ describe('idempotency middleware', () => { }); test('stores error responses too (non-2xx under 500)', async () => { - const mw = idempotency({ ttl: 10_000 }); + const mw = idempotency({ scope: () => 'public-test', ttl: 10_000 }); const ctx1 = makeContext({ 'x-idempotency-key': 'key-err' }, 'POST'); await mw(ctx1, async () => { @@ -179,6 +180,7 @@ describe('idempotency middleware', () => { test('custom skip predicate', async () => { const mw = idempotency({ + scope: () => 'public-test', ttl: 10_000, skip: ctx => ctx.method === 'DELETE' }); diff --git a/libs/server/vitest.config.mts b/libs/server/vitest.config.mts index c2329925..27d94728 100644 --- a/libs/server/vitest.config.mts +++ b/libs/server/vitest.config.mts @@ -2,7 +2,7 @@ import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { - include: ['src/**/*.test.ts'], + include: ['src/**/*.test.ts', 'tests/**/*.test.ts'], typecheck: { enabled: true, include: ['src/**/*.test-d.ts'], diff --git a/libs/storage-s3/LICENSE b/libs/storage-s3/LICENSE new file mode 100644 index 00000000..053547c1 --- /dev/null +++ b/libs/storage-s3/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (C) 2024 by Andrew Zolotuhkin + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libs/storage-s3/README.md b/libs/storage-s3/README.md index 9b9bc4f2..a78d65d5 100644 --- a/libs/storage-s3/README.md +++ b/libs/storage-s3/README.md @@ -1,9 +1,12 @@ # @cleverbrush/storage-s3 + +![Unit coverage](https://img.shields.io/badge/unit_coverage-90.7%25-brightgreen) + An implementation of [`ObjectStorage`](../storage) for S3-compatible services. Uses the modular AWS SDK as a protocol client; an AWS account is not required. Explicit endpoints and credentials are required, with no ambient AWS credential -lookup. The package is server-only and requires Node.js 20+. +lookup. The package is server-only and requires Node.js 24+. ## Configure a provider diff --git a/libs/storage-s3/package.json b/libs/storage-s3/package.json index 8699e4e5..76e0cc08 100644 --- a/libs/storage-s3/package.json +++ b/libs/storage-s3/package.json @@ -6,7 +6,7 @@ "sideEffects": false, "license": "BSD-3-Clause", "engines": { - "node": ">=20" + "node": ">=24" }, "files": [ "dist" diff --git a/libs/storage-s3/src/S3Storage.test.ts b/libs/storage-s3/src/S3Storage.test.ts index 098a70ad..360be676 100644 --- a/libs/storage-s3/src/S3Storage.test.ts +++ b/libs/storage-s3/src/S3Storage.test.ts @@ -112,36 +112,36 @@ describe('S3 storage', () => { await rejected; expect(input.destroyed).toBe(true); }); - it.each([ - 'normal exit', - 'exception', - 'explicit close' - ])('await using closes unread downloads exactly once on %s', async exit => { - const source = new PassThrough(); - vi.spyOn(S3Client.prototype, 'send').mockResolvedValue({ - Body: source, - ContentLength: 20 - } as never); - const destroy = vi.spyOn(S3Client.prototype, 'destroy'); - const storage = create(); - const failure = new Error('scope failed'); - let body: Readable | undefined; - const scoped = (async () => { - await using owned: ObjectStorage = storage; - body = (await owned.get('unread')).body; - if (exit === 'explicit close') await owned.close(); - if (exit === 'exception') throw failure; - })(); - if (exit === 'exception') await expect(scoped).rejects.toBe(failure); - else await scoped; - expect(source.destroyed).toBe(true); - expect(body?.destroyed).toBe(true); - await expect(storage.stat('after-disposal')).rejects.toMatchObject({ - code: 'closed' - }); - await storage.close(); - expect(destroy).toHaveBeenCalledTimes(1); - }); + it.each(['normal exit', 'exception', 'explicit close'])( + 'await using closes unread downloads exactly once on %s', + async exit => { + const source = new PassThrough(); + vi.spyOn(S3Client.prototype, 'send').mockResolvedValue({ + Body: source, + ContentLength: 20 + } as never); + const destroy = vi.spyOn(S3Client.prototype, 'destroy'); + const storage = create(); + const failure = new Error('scope failed'); + let body: Readable | undefined; + const scoped = (async () => { + await using owned: ObjectStorage = storage; + body = (await owned.get('unread')).body; + if (exit === 'explicit close') await owned.close(); + if (exit === 'exception') throw failure; + })(); + if (exit === 'exception') + await expect(scoped).rejects.toBe(failure); + else await scoped; + expect(source.destroyed).toBe(true); + expect(body?.destroyed).toBe(true); + await expect(storage.stat('after-disposal')).rejects.toMatchObject({ + code: 'closed' + }); + await storage.close(); + expect(destroy).toHaveBeenCalledTimes(1); + } + ); it('normalizes failed input streams and aborts their multipart state', async () => { const source = new PassThrough(); const send = vi diff --git a/libs/storage/LICENSE b/libs/storage/LICENSE new file mode 100644 index 00000000..053547c1 --- /dev/null +++ b/libs/storage/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (C) 2024 by Andrew Zolotuhkin + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/libs/storage/README.md b/libs/storage/README.md index 8f8b5d08..ebf6613e 100644 --- a/libs/storage/README.md +++ b/libs/storage/README.md @@ -1,6 +1,9 @@ # @cleverbrush/storage + +![Unit coverage](https://img.shields.io/badge/unit_coverage-100%25-brightgreen) + -Provider-neutral object storage contracts and key/URL helpers for Node.js 20+. +Provider-neutral object storage contracts and key/URL helpers for Node.js 24+. The core has no runtime dependencies. Use an adapter such as [`@cleverbrush/storage-s3`](../storage-s3) for self-hosted or hosted S3 services. diff --git a/libs/storage/package.json b/libs/storage/package.json index 17c05bd2..0566ad45 100644 --- a/libs/storage/package.json +++ b/libs/storage/package.json @@ -6,7 +6,7 @@ "sideEffects": false, "license": "BSD-3-Clause", "engines": { - "node": ">=20" + "node": ">=24" }, "files": [ "dist" diff --git a/package-lock.json b/package-lock.json index 4ad80dfa..b5dd75c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,7 +7,7 @@ "": { "name": "cleverbrush-framework", "version": "4.2.0", - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "workspaces": [ "./libs/*", "./demos/*", @@ -17,17 +17,21 @@ ], "devDependencies": { "@biomejs/biome": "^2.4.9", - "@changesets/cli": "^2.30.0", + "@changesets/cli": "^3.0.3", "@playwright/mcp": "^0.0.70", "@testing-library/react": "^16.3.2", - "@vitest/coverage-v8": "^4.1.2", + "@vitest/coverage-v8": "^4.1.11", "concurrently": "^9.1.0", + "esbuild": "^0.28.2", "jsdom": "^29.0.1", "tsup": "^8.5.1", - "turbo": "^2.8.21", + "turbo": "^2.9.14", "typedoc": "^0.28.18", "typescript": "^6.0.2", - "vitest": "^4.1.2" + "vitest": "^4.1.11" + }, + "engines": { + "node": ">=24" } }, "demos/e2e": { @@ -36,64 +40,47 @@ "dependencies": { "pg": "^8.13.3", "playwright": "^1.49.1", - "ws": "^8.18.0" + "ws": "^8.22.0" }, "devDependencies": { "@types/node": "^22.0.0", "@types/pg": "^8.11.10", "@types/ws": "^8.5.13", "typescript": "^6.0.2", - "vitest": "^4.1.2" - } - }, - "demos/e2e/node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "vitest": "^4.1.11" } }, "demos/e2e/node_modules/playwright": { - "version": "1.59.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz", - "integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==", + "version": "1.63.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.63.0.tgz", + "integrity": "sha512-+7ziBLidS4NaNCdt57SUDT+wYmmd5fmiQejUic/kb+YsYSCPyOOE9sebzMjNmQrsnNpDJqd4WHvV/8lfKfUDUg==", "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.59.1" + "playwright-core": "1.63.0" }, "bin": { "playwright": "cli.js" }, "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "fsevents": "2.3.2" + "node": ">=20" } }, "demos/e2e/node_modules/playwright-core": { - "version": "1.59.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz", - "integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==", + "version": "1.63.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.63.0.tgz", + "integrity": "sha512-rYCsBF/M5HjUch52bbtVONEFjv6Xu8sm8h72dNlR5bzIE1fvC/bxgspzkjSfU+MweEMmPM8KJebG6nnyxo5mCg==", "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" }, "engines": { - "node": ">=18" + "node": ">=20" } }, "demos/todo-backend": { "name": "@cleverbrush/todo-backend", "version": "1.0.0", - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "dependencies": { "@cleverbrush/auth": "*", "@cleverbrush/di": "*", @@ -108,7 +95,7 @@ "@cleverbrush/schema-json": "*", "@cleverbrush/server": "*", "@cleverbrush/server-openapi": "*", - "@opentelemetry/instrumentation-http": "^0.215.0", + "@opentelemetry/instrumentation-http": "^0.222.0", "@opentelemetry/instrumentation-runtime-node": "^0.28.0", "@opentelemetry/instrumentation-undici": "^0.25.0", "google-auth-library": "^9.15.0", @@ -137,40 +124,46 @@ "@tanstack/react-query": "^5.80.7", "react": "^19.0.0", "react-dom": "^19.0.0", - "react-router": "^7.5.3" + "react-router": "^7.18.2" }, "devDependencies": { "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", "@vitejs/plugin-react": "^4.3.4", "typescript": "^6.0.2", - "vite": "^6.3.3" + "vite": "^6.4.3" } }, "libs/async": { "name": "@cleverbrush/async", "version": "4.4.3", - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "devDependencies": { "@types/node": "^25.4.0" + }, + "engines": { + "node": ">=24" } }, "libs/async/node_modules/@types/node": { - "version": "25.6.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz", - "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==", + "version": "25.9.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.9.tgz", + "integrity": "sha512-b4e2xxj/yMeT2hNlD6x7zNFiQK2Dlmkb9CQ6v3/1G2RezKN4YS2lcEv8lzGm1mhs4f2lM4yNAMI/zUDBZBBt7w==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.19.0" + "undici-types": ">=7.24.0 <7.24.7" } }, "libs/auth": { "name": "@cleverbrush/auth", "version": "4.4.3", - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "dependencies": { "@cleverbrush/schema": "^4.4.3" + }, + "engines": { + "node": ">=24" } }, "libs/benchmarks": { @@ -178,7 +171,7 @@ "version": "2.0.2", "dependencies": { "@cleverbrush/schema": "^4.0.0", - "joi": "^17.13.3", + "joi": "^17.13.7", "yup": "^1.6.1", "zod": "^3.24.4" } @@ -186,7 +179,7 @@ "libs/client": { "name": "@cleverbrush/client", "version": "4.4.3", - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "dependencies": { "@cleverbrush/schema": "^4.4.3", "@cleverbrush/server": "^4.4.3" @@ -198,6 +191,9 @@ "react": "^19.0.0", "react-dom": "^19.0.0" }, + "engines": { + "node": ">=24" + }, "peerDependencies": { "@tanstack/react-query": "^5.0.0", "react": ">=18.0.0" @@ -214,49 +210,62 @@ "libs/deep": { "name": "@cleverbrush/deep", "version": "4.4.3", - "license": "BSD 3-Clause" + "license": "BSD-3-Clause", + "engines": { + "node": ">=24" + } }, "libs/di": { "name": "@cleverbrush/di", "version": "4.4.3", - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "dependencies": { "@cleverbrush/schema": "^4.4.3" + }, + "engines": { + "node": ">=24" } }, "libs/env": { "name": "@cleverbrush/env", "version": "4.4.3", - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "dependencies": { "@cleverbrush/deep": "^4.4.3" }, "devDependencies": { + "@cleverbrush/schema": "*", "@types/node": "^25.4.0" }, + "engines": { + "node": ">=24" + }, "peerDependencies": { "@cleverbrush/schema": "^4.0.0" } }, "libs/env/node_modules/@types/node": { - "version": "25.6.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz", - "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==", + "version": "25.9.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.9.tgz", + "integrity": "sha512-b4e2xxj/yMeT2hNlD6x7zNFiQK2Dlmkb9CQ6v3/1G2RezKN4YS2lcEv8lzGm1mhs4f2lM4yNAMI/zUDBZBBt7w==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.19.0" + "undici-types": ">=7.24.0 <7.24.7" } }, "libs/knex-clickhouse": { "name": "@cleverbrush/knex-clickhouse", "version": "4.4.3", - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "dependencies": { "@cleverbrush/async": "^4.4.3", "@cleverbrush/deep": "^4.4.3", "@clickhouse/client": "^1.18.2" }, + "engines": { + "node": ">=24" + }, "peerDependencies": { "knex": ">=3.1.0" } @@ -264,10 +273,13 @@ "libs/knex-schema": { "name": "@cleverbrush/knex-schema", "version": "4.4.3", - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "dependencies": { "@cleverbrush/schema": "^4.4.3" }, + "engines": { + "node": ">=24" + }, "peerDependencies": { "knex": ">=3.1.0" } @@ -275,14 +287,20 @@ "libs/log": { "name": "@cleverbrush/log", "version": "4.4.3", - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "dependencies": { "@cleverbrush/async": "^4.4.3", "@cleverbrush/schema": "^4.4.3" }, "devDependencies": { + "@cleverbrush/di": "*", + "@cleverbrush/knex-clickhouse": "*", + "@cleverbrush/server": "*", "@types/node": "^25.4.0" }, + "engines": { + "node": ">=24" + }, "peerDependencies": { "@cleverbrush/di": "^4.0.0", "@cleverbrush/knex-clickhouse": "^4.0.0", @@ -301,31 +319,37 @@ } }, "libs/log/node_modules/@types/node": { - "version": "25.6.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz", - "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==", + "version": "25.9.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.9.tgz", + "integrity": "sha512-b4e2xxj/yMeT2hNlD6x7zNFiQK2Dlmkb9CQ6v3/1G2RezKN4YS2lcEv8lzGm1mhs4f2lM4yNAMI/zUDBZBBt7w==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.19.0" + "undici-types": ">=7.24.0 <7.24.7" } }, "libs/mapper": { "name": "@cleverbrush/mapper", "version": "4.4.3", - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "dependencies": { "@cleverbrush/schema": "^4.4.3" + }, + "engines": { + "node": ">=24" } }, "libs/orm": { "name": "@cleverbrush/orm", "version": "4.4.3", - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "dependencies": { "@cleverbrush/knex-schema": "^4.4.3", "@cleverbrush/schema": "^4.4.3" }, + "engines": { + "node": ">=24" + }, "peerDependencies": { "knex": ">=3.1.0" } @@ -333,7 +357,7 @@ "libs/orm-cli": { "name": "@cleverbrush/orm-cli", "version": "4.4.3", - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "dependencies": { "@cleverbrush/knex-schema": "*", "tsx": "^4.19.0" @@ -341,6 +365,9 @@ "bin": { "cb-orm": "dist/bin.js" }, + "engines": { + "node": ">=24" + }, "peerDependencies": { "knex": ">=3.1.0" } @@ -348,37 +375,40 @@ "libs/otel": { "name": "@cleverbrush/otel", "version": "4.4.3", - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "dependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/api-logs": "^0.215.0", - "@opentelemetry/context-async-hooks": "^2.7.0", - "@opentelemetry/core": "^2.7.0", - "@opentelemetry/exporter-logs-otlp-http": "^0.215.0", - "@opentelemetry/exporter-metrics-otlp-http": "^0.215.0", - "@opentelemetry/exporter-trace-otlp-http": "^0.215.0", - "@opentelemetry/resources": "^2.7.0", - "@opentelemetry/sdk-logs": "^0.215.0", - "@opentelemetry/sdk-metrics": "^2.7.0", - "@opentelemetry/sdk-node": "^0.215.0", - "@opentelemetry/sdk-trace-node": "^2.7.0", + "@opentelemetry/api-logs": "^0.222.0", + "@opentelemetry/context-async-hooks": "^2.11.0", + "@opentelemetry/core": "^2.11.0", + "@opentelemetry/exporter-logs-otlp-http": "^0.222.0", + "@opentelemetry/exporter-metrics-otlp-http": "^0.222.0", + "@opentelemetry/exporter-trace-otlp-http": "^0.222.0", + "@opentelemetry/resources": "^2.11.0", + "@opentelemetry/sdk-logs": "^0.222.0", + "@opentelemetry/sdk-metrics": "^2.11.0", + "@opentelemetry/sdk-node": "^0.222.0", + "@opentelemetry/sdk-trace-node": "^2.11.0", "@opentelemetry/semantic-conventions": "^1.40.0" }, "devDependencies": { "@cleverbrush/di": "*", "@cleverbrush/log": "*", "@cleverbrush/server": "*", - "@opentelemetry/instrumentation-http": "^0.215.0", + "@opentelemetry/instrumentation-http": "^0.222.0", "@opentelemetry/instrumentation-runtime-node": "^0.28.0", "@opentelemetry/instrumentation-undici": "^0.25.0", "@types/node": "^25.4.0", "knex": "^3.1.0" }, + "engines": { + "node": ">=24" + }, "peerDependencies": { "@cleverbrush/di": "^4.0.0", "@cleverbrush/log": "^4.0.0", "@cleverbrush/server": "^4.0.0", - "@opentelemetry/instrumentation-http": "^0.215.0", + "@opentelemetry/instrumentation-http": "^0.222.0", "@opentelemetry/instrumentation-runtime-node": "^0.28.0", "@opentelemetry/instrumentation-undici": "^0.25.0", "knex": "^3.0.0" @@ -408,19 +438,19 @@ } }, "libs/otel/node_modules/@types/node": { - "version": "25.6.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz", - "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==", + "version": "25.9.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.9.tgz", + "integrity": "sha512-b4e2xxj/yMeT2hNlD6x7zNFiQK2Dlmkb9CQ6v3/1G2RezKN4YS2lcEv8lzGm1mhs4f2lM4yNAMI/zUDBZBBt7w==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.19.0" + "undici-types": ">=7.24.0 <7.24.7" } }, "libs/react-form": { "name": "@cleverbrush/react-form", "version": "4.4.3", - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "dependencies": { "@cleverbrush/deep": "^4.4.3", "@cleverbrush/schema": "^4.4.3" @@ -429,6 +459,9 @@ "@types/react": "^19.0.0", "react": "^19.0.0" }, + "engines": { + "node": ">=24" + }, "peerDependencies": { "react": ">=18.0.0" } @@ -436,12 +469,15 @@ "libs/scheduler": { "name": "@cleverbrush/scheduler", "version": "4.4.3", - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "dependencies": { "@cleverbrush/schema": "^4.4.3" }, "devDependencies": { "@types/node": "^25.4.0" + }, + "engines": { + "node": ">=24" } }, "libs/scheduler-postgres": { @@ -457,45 +493,44 @@ "devDependencies": { "@types/node": "^25.4.0" }, + "engines": { + "node": ">=24" + }, "peerDependencies": { "knex": ">=3.1.0", "pg": ">=8" } }, "libs/scheduler-postgres/node_modules/@types/node": { - "version": "25.9.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.8.tgz", - "integrity": "sha512-VfMrScDmMhUJQmd5hArdQnFvK0OIeD36uN2Va1FcpYaLB/BgkgM8Ulc50XtYISPMz7APJ30+N0T5EM0jlcdfRw==", + "version": "25.9.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.9.tgz", + "integrity": "sha512-b4e2xxj/yMeT2hNlD6x7zNFiQK2Dlmkb9CQ6v3/1G2RezKN4YS2lcEv8lzGm1mhs4f2lM4yNAMI/zUDBZBBt7w==", "dev": true, "license": "MIT", "dependencies": { "undici-types": ">=7.24.0 <7.24.7" } }, - "libs/scheduler-postgres/node_modules/undici-types": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", - "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", - "dev": true, - "license": "MIT" - }, "libs/scheduler/node_modules/@types/node": { - "version": "25.6.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz", - "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==", + "version": "25.9.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.9.tgz", + "integrity": "sha512-b4e2xxj/yMeT2hNlD6x7zNFiQK2Dlmkb9CQ6v3/1G2RezKN4YS2lcEv8lzGm1mhs4f2lM4yNAMI/zUDBZBBt7w==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.19.0" + "undici-types": ">=7.24.0 <7.24.7" } }, "libs/schema": { "name": "@cleverbrush/schema", "version": "4.4.3", - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "devDependencies": { "@cleverbrush/deep": "^4.4.3" }, + "engines": { + "node": ">=24" + }, "peerDependencies": { "@standard-schema/spec": "^1.1.0" } @@ -503,7 +538,13 @@ "libs/schema-json": { "name": "@cleverbrush/schema-json", "version": "4.4.3", - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", + "devDependencies": { + "@cleverbrush/schema": "*" + }, + "engines": { + "node": ">=24" + }, "peerDependencies": { "@cleverbrush/schema": "^4.0.0", "@standard-schema/spec": "^1.1.0" @@ -512,17 +553,20 @@ "libs/server": { "name": "@cleverbrush/server", "version": "4.4.3", - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "dependencies": { "@cleverbrush/auth": "^4.4.3", "@cleverbrush/di": "^4.4.3", "@cleverbrush/schema": "^4.4.3", - "@fastify/busboy": "^3.2.0", - "ws": "^8.20.0" + "@fastify/busboy": "^3.2.2", + "ws": "^8.22.0" }, "devDependencies": { "@types/busboy": "1.5.4", "@types/ws": "^8.18.1" + }, + "engines": { + "node": ">=24" } }, "libs/server-integration-tests": { @@ -538,7 +582,16 @@ "libs/server-openapi": { "name": "@cleverbrush/server-openapi", "version": "4.4.3", - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", + "devDependencies": { + "@cleverbrush/auth": "*", + "@cleverbrush/schema": "*", + "@cleverbrush/schema-json": "*", + "@cleverbrush/server": "*" + }, + "engines": { + "node": ">=24" + }, "peerDependencies": { "@cleverbrush/auth": "^4.0.0", "@cleverbrush/schema": "^4.0.0", @@ -554,7 +607,7 @@ "@types/node": "^25.4.0" }, "engines": { - "node": ">=20" + "node": ">=24" } }, "libs/storage-s3": { @@ -570,7 +623,7 @@ "@types/node": "^25.4.0" }, "engines": { - "node": ">=20" + "node": ">=24" } }, "libs/storage-s3/node_modules/@types/node": { @@ -583,13 +636,6 @@ "undici-types": ">=7.24.0 <7.24.7" } }, - "libs/storage-s3/node_modules/undici-types": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", - "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", - "dev": true, - "license": "MIT" - }, "libs/storage/node_modules/@types/node": { "version": "25.9.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.9.tgz", @@ -600,13 +646,6 @@ "undici-types": ">=7.24.0 <7.24.7" } }, - "libs/storage/node_modules/undici-types": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", - "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", - "dev": true, - "license": "MIT" - }, "node_modules/@asamuzakjp/css-color": { "version": "5.1.11", "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz", @@ -625,9 +664,9 @@ } }, "node_modules/@asamuzakjp/dom-selector": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.1.0.tgz", - "integrity": "sha512-ASf825+5vsGuYWoyFyNsex2mNtPTXpCvYTR942+w/eNw7PqS0Lhl/PE1hC7bajneI3m/Oxi+yrP3vTOPxfwM8A==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.1.1.tgz", + "integrity": "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==", "dev": true, "license": "MIT", "dependencies": { @@ -675,9 +714,9 @@ } }, "node_modules/@aws-sdk/client-s3": { - "version": "3.1145.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.1145.0.tgz", - "integrity": "sha512-MPKAQdx8qCZW1/ChDPILNehtAn/BMP4GNTnaH/xhGCWg3v50ADVq1K2WzxIrFfeANdWUyjwiY0CXDBhclsNWgQ==", + "version": "3.1146.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.1146.0.tgz", + "integrity": "sha512-WY0YCBzxc4muFfY6UbGpA+oib0nGT/Px2aEPGYX7pp1la2OmDLChnK81mu8H7LdYvwGU1pLAsLIa/qgad0Wd7g==", "license": "Apache-2.0", "dependencies": { "@aws-sdk/checksums": "^3.1001.1", @@ -864,9 +903,9 @@ } }, "node_modules/@aws-sdk/lib-storage": { - "version": "3.1145.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/lib-storage/-/lib-storage-3.1145.0.tgz", - "integrity": "sha512-sM6n9AYlOLpRt+qovDJWYZk2dDGS18dHaOB1QCJIibXKbEuM1lz/flf1WstHCbyrteX4ldgMRFLw0xxjbfSkAQ==", + "version": "3.1146.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/lib-storage/-/lib-storage-3.1146.0.tgz", + "integrity": "sha512-Fqnbc3xayxP0GfsQoxcLl9wublbVC2eo5uWFds6tTUejAyHKYcMFvgziBmZBFOKA04oYY8kgXMjAuJGQoPFk5A==", "license": "Apache-2.0", "dependencies": { "@smithy/core": "^3.35.0", @@ -880,7 +919,7 @@ "node": ">=20.0.0" }, "peerDependencies": { - "@aws-sdk/client-s3": "^3.1145.0" + "@aws-sdk/client-s3": "^3.1146.0" } }, "node_modules/@aws-sdk/middleware-sdk-s3": { @@ -987,13 +1026,13 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", + "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -1002,9 +1041,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", - "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", "dev": true, "license": "MIT", "engines": { @@ -1012,21 +1051,21 @@ } }, "node_modules/@babel/core": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", - "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -1053,14 +1092,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.29.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", - "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -1070,14 +1109,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -1107,9 +1146,9 @@ } }, "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", "dev": true, "license": "MIT", "engines": { @@ -1117,29 +1156,29 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1149,9 +1188,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", - "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", "dev": true, "license": "MIT", "engines": { @@ -1159,9 +1198,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", "dev": true, "license": "MIT", "engines": { @@ -1169,9 +1208,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "dev": true, "license": "MIT", "engines": { @@ -1179,9 +1218,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", "dev": true, "license": "MIT", "engines": { @@ -1189,27 +1228,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", - "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0" + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", - "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "version": "7.29.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.9.tgz", + "integrity": "sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.29.0" + "@babel/types": "^7.29.8" }, "bin": { "parser": "bin/babel-parser.js" @@ -1219,13 +1258,13 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", - "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz", + "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1235,13 +1274,13 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", - "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz", + "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1251,9 +1290,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", - "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", "dev": true, "license": "MIT", "engines": { @@ -1261,33 +1300,33 @@ } }, "node_modules/@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz", + "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.8", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.8", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", "debug": "^4.3.1" }, "engines": { @@ -1295,14 +1334,14 @@ } }, "node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1319,9 +1358,9 @@ } }, "node_modules/@biomejs/biome": { - "version": "2.4.12", - "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.4.12.tgz", - "integrity": "sha512-Rro7adQl3NLq/zJCIL98eElXKI8eEiBtoeu5TbXF/U3qbjuSc7Jb5rjUbeHHcquDWeSf3HnGP7XI5qGrlRk/pA==", + "version": "2.5.15", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.5.15.tgz", + "integrity": "sha512-WZTW4slm/pdkh92K6t/3aEN++44JD1PQ7squ7RCMLI1flHGl43DVOIGXvCjHiZgBMO1V4uYxoFNtqrIA4qGuIQ==", "dev": true, "license": "MIT OR Apache-2.0", "bin": { @@ -1335,20 +1374,20 @@ "url": "https://opencollective.com/biome" }, "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "2.4.12", - "@biomejs/cli-darwin-x64": "2.4.12", - "@biomejs/cli-linux-arm64": "2.4.12", - "@biomejs/cli-linux-arm64-musl": "2.4.12", - "@biomejs/cli-linux-x64": "2.4.12", - "@biomejs/cli-linux-x64-musl": "2.4.12", - "@biomejs/cli-win32-arm64": "2.4.12", - "@biomejs/cli-win32-x64": "2.4.12" + "@biomejs/cli-darwin-arm64": "2.5.15", + "@biomejs/cli-darwin-x64": "2.5.15", + "@biomejs/cli-linux-arm64": "2.5.15", + "@biomejs/cli-linux-arm64-musl": "2.5.15", + "@biomejs/cli-linux-x64": "2.5.15", + "@biomejs/cli-linux-x64-musl": "2.5.15", + "@biomejs/cli-win32-arm64": "2.5.15", + "@biomejs/cli-win32-x64": "2.5.15" } }, "node_modules/@biomejs/cli-darwin-arm64": { - "version": "2.4.12", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.12.tgz", - "integrity": "sha512-BnMU4Pc3ciEVteVpZ0BK33MLr7X57F5w1dwDLDn+/iy/yTrA4Q/N2yftidFtsA4vrDh0FMXDpacNV/Tl3fbmng==", + "version": "2.5.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.5.15.tgz", + "integrity": "sha512-BZVzFhJ/mUvTLMYbc9x6el0o2Uv5zP7bACYyhFb6fSc1giroXm7PL0a5KqMJ9F+/BnkYRjagD/ROSDVOZ6eapg==", "cpu": [ "arm64" ], @@ -1363,9 +1402,9 @@ } }, "node_modules/@biomejs/cli-darwin-x64": { - "version": "2.4.12", - "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.12.tgz", - "integrity": "sha512-x9uJ0bI1rJsWICp3VH8w/5PnAVD3A7SqzDpbrfoUQX1QyWrK5jSU4fRLo/wSgGeplCivbxBRKmt5Xq4/nWvq8A==", + "version": "2.5.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.5.15.tgz", + "integrity": "sha512-V5Kw63V+fVGNFhFrizDxMbGXAzZCh8kYzJpy3GI6gTapWDg7bZK9oYC9CbVgQSEdTnupz0U5Efz8Ev+5vdBBXw==", "cpu": [ "x64" ], @@ -1380,13 +1419,16 @@ } }, "node_modules/@biomejs/cli-linux-arm64": { - "version": "2.4.12", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.12.tgz", - "integrity": "sha512-tOwuCuZZtKi1jVzbk/5nXmIsziOB6yqN8c9r9QM0EJYPU6DpQWf11uBOSCfFKKM4H3d9ZoarvlgMfbcuD051Pw==", + "version": "2.5.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.5.15.tgz", + "integrity": "sha512-XaG7P7eeSLYETD3K9grfB0mQmpLQZjygdbsxekWBJAg4am79fGAtRpfRyNnTvXB2fZFNnJZG3nXm7HBat0+VUw==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -1397,13 +1439,16 @@ } }, "node_modules/@biomejs/cli-linux-arm64-musl": { - "version": "2.4.12", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.12.tgz", - "integrity": "sha512-FhfpkAAlKL6kwvcVap0Hgp4AhZmtd3YImg0kK1jd7C/aSoh4SfsB2f++yG1rU0lr8Y5MCFJrcSkmssiL9Xnnig==", + "version": "2.5.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.5.15.tgz", + "integrity": "sha512-tGzZUTcJCV7tj5Adh/Gn6nR4MycqA3iohq2LEcrarAgRBkU0h0OKd2UzCRkOsOYOuUGaUJb4YmqRqyy2feGGSw==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -1414,13 +1459,16 @@ } }, "node_modules/@biomejs/cli-linux-x64": { - "version": "2.4.12", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.12.tgz", - "integrity": "sha512-8pFeAnLU9QdW9jCIslB/v82bI0lhBmz2ZAKc8pVMFPO0t0wAHsoEkrUQUbMkIorTRIjbqyNZHA3lEXavsPWYSw==", + "version": "2.5.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.5.15.tgz", + "integrity": "sha512-xE4iEW/3LqlYj9GFgGrFsFSH51dEEpUbYWBfeOv0q87WkUDxK2o/HhreSb7qMJqck70RVM6Lg96hgedcAYcOkA==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -1431,13 +1479,16 @@ } }, "node_modules/@biomejs/cli-linux-x64-musl": { - "version": "2.4.12", - "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.12.tgz", - "integrity": "sha512-dwTIgZrGutzhkQCuvHynCkyW6hJxUuyZqKKO0YNfaS2GUoRO+tOvxXZqZB6SkWAOdfZTzwaw8IEdUnIkHKHoew==", + "version": "2.5.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.5.15.tgz", + "integrity": "sha512-IlxUcyxilVGPsE008x13pWdkTbU3nQpP2i9b5UrbOYj6goBkKsRX1XB7yJLcV+O1H9LBchIIm4adOaVLZBX0ZQ==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -1448,9 +1499,9 @@ } }, "node_modules/@biomejs/cli-win32-arm64": { - "version": "2.4.12", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.12.tgz", - "integrity": "sha512-B0DLnx0vA9ya/3v7XyCaP+/lCpnbWbMOfUFFve+xb5OxyYvdHaS55YsSddr228Y+JAFk58agCuZTsqNiw2a6ig==", + "version": "2.5.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.5.15.tgz", + "integrity": "sha512-2kPKzhNlm8C+Ru3GcO0Me2ODkCBLrVOUNuyi84RJyWDVKAA4+Kjj3jMjL938lF6BRfvltC4vB2nPY9SvN51Ovg==", "cpu": [ "arm64" ], @@ -1465,9 +1516,9 @@ } }, "node_modules/@biomejs/cli-win32-x64": { - "version": "2.4.12", - "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.12.tgz", - "integrity": "sha512-yMckRzTyZ83hkk8iDFWswqSdU8tvZxspJKnYNh7JZr/zhZNOlzH13k4ecboU6MurKExCe2HUkH75pGI/O2JwGA==", + "version": "2.5.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.5.15.tgz", + "integrity": "sha512-yAzh4UqEImV6Hcy0zjUqNfJAsUhoD64FBQCdTG8Md/Z2wQeVP/ZmJ3XRYAs1g8J9fjTFyl2fCtc5FB5til47+g==", "cpu": [ "x64" ], @@ -1495,245 +1546,276 @@ } }, "node_modules/@changesets/apply-release-plan": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@changesets/apply-release-plan/-/apply-release-plan-7.1.1.tgz", - "integrity": "sha512-9qPCm/rLx/xoOFXIHGB229+4GOL76S4MC+7tyOuTsR6+1jYlfFDQORdvwR5hDA6y4FL2BPt3qpbcQIS+dW85LA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@changesets/config": "^3.1.4", - "@changesets/get-version-range-type": "^0.4.0", - "@changesets/git": "^3.0.4", - "@changesets/should-skip-package": "^0.1.2", - "@changesets/types": "^6.1.0", - "@manypkg/get-packages": "^1.1.3", - "detect-indent": "^6.0.0", - "fs-extra": "^7.0.1", - "lodash.startcase": "^4.4.0", - "outdent": "^0.5.0", - "prettier": "^2.7.1", - "resolve-from": "^5.0.0", - "semver": "^7.5.3" + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@changesets/apply-release-plan/-/apply-release-plan-8.1.1.tgz", + "integrity": "sha512-nk2Hbq3M9NeHMdaIRBDvawKrQJzXB9sGyBUE5yWnIKtvdcNCPj75zBekJYr0gTT2ecJH3eFeTrc8eisPImcv0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/config": "^4.0.1", + "@changesets/format": "^0.1.2", + "@changesets/git": "^4.0.1", + "@changesets/should-skip-package": "^1.0.0", + "@changesets/types": "^7.0.0", + "import-meta-resolve": "^4.2.0", + "jsonc-parser": "^3.3.1", + "semver": "^7.8.1" + }, + "engines": { + "node": "^22.11 || ^24 || >=26" } }, "node_modules/@changesets/assemble-release-plan": { - "version": "6.0.10", - "resolved": "https://registry.npmjs.org/@changesets/assemble-release-plan/-/assemble-release-plan-6.0.10.tgz", - "integrity": "sha512-rSDcqdJ9KbVyjpBIuCidhvZNIiVt1XaIYp73ycVQRIA5n/j6wQaEk0ChRLMUQ1vkxZe51PTQ9OIhbg6HQMW45A==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@changesets/assemble-release-plan/-/assemble-release-plan-7.0.0.tgz", + "integrity": "sha512-oEW8BxdA604kGGtDSCiHr5w9Tv4UWe9I2k61IBNZzCOE1kbYaJj4v+lFQNgcEZFkUc2pV/+hASErGDvpJOZCTg==", "dev": true, "license": "MIT", "dependencies": { - "@changesets/errors": "^0.2.0", - "@changesets/get-dependents-graph": "^2.1.4", - "@changesets/should-skip-package": "^0.1.2", - "@changesets/types": "^6.1.0", - "@manypkg/get-packages": "^1.1.3", - "semver": "^7.5.3" + "@changesets/errors": "^1.0.0", + "@changesets/get-dependents-graph": "^3.0.0", + "@changesets/should-skip-package": "^1.0.0", + "@changesets/types": "^7.0.0", + "semver": "^7.8.1" + }, + "engines": { + "node": "^22.11 || ^24 || >=26" } }, "node_modules/@changesets/changelog-git": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@changesets/changelog-git/-/changelog-git-0.2.1.tgz", - "integrity": "sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@changesets/changelog-git/-/changelog-git-1.0.0.tgz", + "integrity": "sha512-3Dst2Ime2Op5nd4XmWJLPIgp11ZFqJqSkVug9izK6TDcIV4YlhPS4ECbEVR+eGI0bk0r1ItogD4j2Oli87bJrA==", "dev": true, "license": "MIT", "dependencies": { - "@changesets/types": "^6.1.0" + "@changesets/types": "^7.0.0" + }, + "engines": { + "node": "^22.11 || ^24 || >=26" } }, "node_modules/@changesets/cli": { - "version": "2.31.0", - "resolved": "https://registry.npmjs.org/@changesets/cli/-/cli-2.31.0.tgz", - "integrity": "sha512-AhI4enNTgHu2IZr6K4WZyf0EPch4XVMn1yOMFmCD9gsfBGqMYaHXls5HyDv6/CL5axVQABz68eG30eCtbr2wFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@changesets/apply-release-plan": "^7.1.1", - "@changesets/assemble-release-plan": "^6.0.10", - "@changesets/changelog-git": "^0.2.1", - "@changesets/config": "^3.1.4", - "@changesets/errors": "^0.2.0", - "@changesets/get-dependents-graph": "^2.1.4", - "@changesets/get-release-plan": "^4.0.16", - "@changesets/git": "^3.0.4", - "@changesets/logger": "^0.1.1", - "@changesets/pre": "^2.0.2", - "@changesets/read": "^0.6.7", - "@changesets/should-skip-package": "^0.1.2", - "@changesets/types": "^6.1.0", - "@changesets/write": "^0.4.0", - "@inquirer/external-editor": "^1.0.2", - "@manypkg/get-packages": "^1.1.3", - "ansi-colors": "^4.1.3", - "enquirer": "^2.4.1", - "fs-extra": "^7.0.1", - "mri": "^1.2.0", - "package-manager-detector": "^0.2.0", - "picocolors": "^1.1.0", - "resolve-from": "^5.0.0", - "semver": "^7.5.3", - "spawndamnit": "^3.0.1", - "term-size": "^2.1.0" + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@changesets/cli/-/cli-3.0.3.tgz", + "integrity": "sha512-L1i67bMfkK6iC/kDejuNO4pFgTRcWIObsD6Qof2woP1I4M8tj17DGlcpRaTzTv1DYSP5EMwWdEtR8X0Pj5Jg3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/apply-release-plan": "^8.1.1", + "@changesets/assemble-release-plan": "^7.0.0", + "@changesets/changelog-git": "^1.0.0", + "@changesets/config": "^4.0.1", + "@changesets/errors": "^1.0.0", + "@changesets/get-dependents-graph": "^3.0.0", + "@changesets/git": "^4.0.1", + "@changesets/pre": "^3.0.0", + "@changesets/read": "^1.0.1", + "@changesets/should-skip-package": "^1.0.0", + "@changesets/types": "^7.0.0", + "@changesets/write": "^1.0.1", + "@clack/prompts": "^1.7.0", + "@manypkg/get-packages": "^3.1.0", + "@pnpm/deps.graph-sequencer": "^1100.0.1", + "cac": "^7.0.0", + "import-meta-resolve": "^4.2.0", + "launch-editor": "^2.14.1", + "package-manager-detector": "^1.6.0", + "semver": "^7.8.1", + "tinyexec": "^1.3.1" }, "bin": { "changeset": "bin.js" + }, + "engines": { + "node": "^22.11 || ^24 || >=26", + "npm": ">=10.9.0", + "pnpm": ">=10.0.0", + "yarn": ">=4.5.2" } }, "node_modules/@changesets/config": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@changesets/config/-/config-3.1.4.tgz", - "integrity": "sha512-pf0bvD/v6WI2cRlZ6hzpjtZdSlXDXMAJ+Iz7xfFzV4ZxJ8OGGAON+1qYc99ZPrijnt4xp3VGG7eNvAOGS24V1Q==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@changesets/config/-/config-4.0.1.tgz", + "integrity": "sha512-PxpxSQLQSLDjceAgRq+4FWlU+HflDIw03mmPVipxoQx9xrXFUjmqTy3SE+n/2t+DqPdpPiLWVr5NRiLZvjyLwg==", "dev": true, "license": "MIT", "dependencies": { - "@changesets/errors": "^0.2.0", - "@changesets/get-dependents-graph": "^2.1.4", - "@changesets/logger": "^0.1.1", - "@changesets/should-skip-package": "^0.1.2", - "@changesets/types": "^6.1.0", - "@manypkg/get-packages": "^1.1.3", - "fs-extra": "^7.0.1", - "micromatch": "^4.0.8" + "@changesets/get-dependents-graph": "^3.0.0", + "@changesets/should-skip-package": "^1.0.0", + "@changesets/types": "^7.0.0", + "@manypkg/get-packages": "^3.1.0", + "picomatch": "^4.0.7" + }, + "engines": { + "node": "^22.11 || ^24 || >=26" } }, "node_modules/@changesets/errors": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@changesets/errors/-/errors-0.2.0.tgz", - "integrity": "sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@changesets/errors/-/errors-1.0.0.tgz", + "integrity": "sha512-ElN/mEzn6zmETgjwf5MclCMa9ef59sAR0lfO8VSYIsiRvbC2FbLB/92EoYw10Sl0kGixxHFiJZUSv7dA+YpR8g==", "dev": true, "license": "MIT", - "dependencies": { - "extendable-error": "^0.1.5" + "engines": { + "node": "^22.11 || ^24 || >=26" } }, - "node_modules/@changesets/get-dependents-graph": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@changesets/get-dependents-graph/-/get-dependents-graph-2.1.4.tgz", - "integrity": "sha512-ZsS00x6WvmHq3sQv8oCMwL0f/z3wbXCVuSVTJwCnnmbC/iBdNJGFx1EcbMG4PC6sXRyH69liM4A2WKXzn/kRPg==", + "node_modules/@changesets/format": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@changesets/format/-/format-0.1.2.tgz", + "integrity": "sha512-Caez5XtNXCFS/G5bwyav3wuXL0tMxVd2ZGbaumWbzN08tyzO21asCw7JZhNtVsAZDCvDRUzZN+Iit9SyRITSYA==", "dev": true, "license": "MIT", "dependencies": { - "@changesets/types": "^6.1.0", - "@manypkg/get-packages": "^1.1.3", - "picocolors": "^1.1.0", - "semver": "^7.5.3" + "package-manager-detector": "^1.8.0", + "tinyexec": "^1.3.0" + }, + "engines": { + "node": "^22.11 || ^24 || >=26" } }, - "node_modules/@changesets/get-release-plan": { - "version": "4.0.16", - "resolved": "https://registry.npmjs.org/@changesets/get-release-plan/-/get-release-plan-4.0.16.tgz", - "integrity": "sha512-2K5Om6CrMPm45rtvckfzWo7e9jOVCKLCnXia5eUPaURH7/LWzri7pK1TycdzAuAtehLkW7VPbWLCSExTHmiI6g==", + "node_modules/@changesets/get-dependents-graph": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@changesets/get-dependents-graph/-/get-dependents-graph-3.0.0.tgz", + "integrity": "sha512-ji/t5wFA1zREKXRUePE6Qi+Qu2UgxCeSSGQrphezwvQZrp49B7sJ+8+wvM0tA7zPeSxYKCojDy3WWgrl+s+awg==", "dev": true, "license": "MIT", "dependencies": { - "@changesets/assemble-release-plan": "^6.0.10", - "@changesets/config": "^3.1.4", - "@changesets/pre": "^2.0.2", - "@changesets/read": "^0.6.7", - "@changesets/types": "^6.1.0", - "@manypkg/get-packages": "^1.1.3" + "@changesets/types": "^7.0.0", + "semver": "^7.8.1" + }, + "engines": { + "node": "^22.11 || ^24 || >=26" } }, - "node_modules/@changesets/get-version-range-type": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@changesets/get-version-range-type/-/get-version-range-type-0.4.0.tgz", - "integrity": "sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==", - "dev": true, - "license": "MIT" - }, "node_modules/@changesets/git": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@changesets/git/-/git-3.0.4.tgz", - "integrity": "sha512-BXANzRFkX+XcC1q/d27NKvlJ1yf7PSAgi8JG6dt8EfbHFHi4neau7mufcSca5zRhwOL8j9s6EqsxmT+s+/E6Sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@changesets/errors": "^0.2.0", - "@manypkg/get-packages": "^1.1.3", - "is-subdir": "^1.1.1", - "micromatch": "^4.0.8", - "spawndamnit": "^3.0.1" - } - }, - "node_modules/@changesets/logger": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@changesets/logger/-/logger-0.1.1.tgz", - "integrity": "sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@changesets/git/-/git-4.0.1.tgz", + "integrity": "sha512-6vWpIAC4LkpmlqaIu37ViT5enyd9+uBwAiGqCGhASvkSHBgx4PrtTGXWTMFYpDmZbjgyonyVgMciPrW4MqtJsA==", "dev": true, "license": "MIT", "dependencies": { - "picocolors": "^1.1.0" + "@changesets/errors": "^1.0.0", + "@changesets/types": "^7.0.0", + "@manypkg/get-packages": "^3.1.0", + "picomatch": "^4.0.4", + "tinyexec": "^1.3.0" + }, + "engines": { + "node": "^22.11 || ^24 || >=26" } }, "node_modules/@changesets/parse": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@changesets/parse/-/parse-0.4.3.tgz", - "integrity": "sha512-ZDmNc53+dXdWEv7fqIUSgRQOLYoUom5Z40gmLgmATmYR9NbL6FJJHwakcCpzaeCy+1D0m0n7mT4jj2B/MQPl7A==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@changesets/parse/-/parse-1.0.0.tgz", + "integrity": "sha512-P0iaMb9p9CRYZiTgAllEIF9AUMQHIy1G72tKlcIqJp61icZSsKQNiOPdxAMZG8m/DvZwt/oz5xEbTpike//dWg==", "dev": true, "license": "MIT", "dependencies": { - "@changesets/types": "^6.1.0", - "js-yaml": "^4.1.1" + "@changesets/types": "^7.0.0", + "yaml": "^2.9.0" + }, + "engines": { + "node": "^22.11 || ^24 || >=26" } }, "node_modules/@changesets/pre": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@changesets/pre/-/pre-2.0.2.tgz", - "integrity": "sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@changesets/pre/-/pre-3.0.0.tgz", + "integrity": "sha512-Zm/6YliV/a2oeWTqHJf6KxLrQwgcK1i/BRDl2m0EKZvbnxV5fG9QRhwJJGshjsZTUTS6dkfURQ2K6aAvgNw/3Q==", "dev": true, "license": "MIT", "dependencies": { - "@changesets/errors": "^0.2.0", - "@changesets/types": "^6.1.0", - "@manypkg/get-packages": "^1.1.3", - "fs-extra": "^7.0.1" + "@changesets/errors": "^1.0.0", + "@changesets/types": "^7.0.0", + "@manypkg/get-packages": "^3.1.0" + }, + "engines": { + "node": "^22.11 || ^24 || >=26" } }, "node_modules/@changesets/read": { - "version": "0.6.7", - "resolved": "https://registry.npmjs.org/@changesets/read/-/read-0.6.7.tgz", - "integrity": "sha512-D1G4AUYGrBEk8vj8MGwf75k9GpN6XL3wg8i42P2jZZwFLXnlr2Pn7r9yuQNbaMCarP7ZQWNJbV6XLeysAIMhTA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@changesets/read/-/read-1.0.1.tgz", + "integrity": "sha512-nzvSC6RcTiWf/dsuwZFXpLzGGsRHYCL68U3uHV7srsulU93aVWY7u2GEJiDZ+4GIIElfaW5EqtKC0UHroY+LTQ==", "dev": true, "license": "MIT", "dependencies": { - "@changesets/git": "^3.0.4", - "@changesets/logger": "^0.1.1", - "@changesets/parse": "^0.4.3", - "@changesets/types": "^6.1.0", - "fs-extra": "^7.0.1", - "p-filter": "^2.1.0", - "picocolors": "^1.1.0" + "@changesets/git": "^4.0.1", + "@changesets/parse": "^1.0.0", + "@changesets/types": "^7.0.0" + }, + "engines": { + "node": "^22.11 || ^24 || >=26" } }, "node_modules/@changesets/should-skip-package": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@changesets/should-skip-package/-/should-skip-package-0.1.2.tgz", - "integrity": "sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@changesets/should-skip-package/-/should-skip-package-1.0.0.tgz", + "integrity": "sha512-pwqoJmbONn1XgXmZXPEExgAaT+HdZLjALFTDgIm+PnS5KeO2nLtzA2/Q+4aMFY14kFMuXKG30MObhvDzWgzDgg==", "dev": true, "license": "MIT", "dependencies": { - "@changesets/types": "^6.1.0", - "@manypkg/get-packages": "^1.1.3" + "@changesets/types": "^7.0.0" + }, + "engines": { + "node": "^22.11 || ^24 || >=26" } }, "node_modules/@changesets/types": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@changesets/types/-/types-6.1.0.tgz", - "integrity": "sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@changesets/types/-/types-7.0.0.tgz", + "integrity": "sha512-c5GoiQyt3pxiXjrWSNoP8/GRf4kG+VnKzovx1OQM8dYYALlSwgedmkPmJ+ZqGxqwg9D3Bkj85Uo4KLd5BN3A0w==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": "^22.11 || ^24 || >=26" + } }, "node_modules/@changesets/write": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@changesets/write/-/write-0.4.0.tgz", - "integrity": "sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@changesets/write/-/write-1.0.1.tgz", + "integrity": "sha512-q/ThtP9gcnEP6xlv7LrY26C2mHqdGBti/MmOVngt/M/oIGYkssmQGxPK9WzBNt2juVcH/vml2WQ+ra8LXYOTaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/format": "^0.1.1", + "@changesets/types": "^7.0.0", + "human-id": "^4.2.0" + }, + "engines": { + "node": "^22.11 || ^24 || >=26" + } + }, + "node_modules/@clack/core": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@clack/core/-/core-1.5.1.tgz", + "integrity": "sha512-iHTrHA8MtVuLl2TfZySmcKv1qO2PoyC9Z7pfSDozEuV5vtY3/wcOPKJXlqJ5Oq2Cx5DDGQGAMVx6HZfRRoVEbQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-wrap-ansi": "^0.2.0", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 20.12.0" + } + }, + "node_modules/@clack/prompts": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.8.1.tgz", + "integrity": "sha512-dlT1m5e/0yUL0kRNcQn7yGLVThkgbB0Ga/1AmfDDC/8ik6AIiSf2QLQO2zPYvefsHP0aFgxO93cVLCCfDp7kzQ==", "dev": true, "license": "MIT", "dependencies": { - "@changesets/types": "^6.1.0", - "fs-extra": "^7.0.1", - "human-id": "^4.1.1", - "prettier": "^2.7.1" + "@clack/core": "1.5.1", + "fast-string-width": "^3.0.2", + "fast-wrap-ansi": "^0.2.0", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 20.12.0" } }, "node_modules/@cleverbrush/async": { @@ -1849,27 +1931,18 @@ "link": true }, "node_modules/@clickhouse/client": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/@clickhouse/client/-/client-1.18.2.tgz", - "integrity": "sha512-fuquQswRSHWM6D079ZeuGqkMOsqtcUPL06UdTnowmoeeYjVrqisfVmvnw8pc3OeKS4kVb91oygb/MfLDiMs0TQ==", + "version": "1.23.1", + "resolved": "https://registry.npmjs.org/@clickhouse/client/-/client-1.23.1.tgz", + "integrity": "sha512-vs3/Zc1dHvT171btW5nMoPsPCJ6QVJ5pp7obxzO5sjqwFx/jjz9wwCAqcFOdc2DhprugDBaVn+4dVY8hG3A9nw==", "license": "Apache-2.0", - "dependencies": { - "@clickhouse/client-common": "1.18.2" - }, "engines": { - "node": ">=16" + "node": ">=20" } }, - "node_modules/@clickhouse/client-common": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/@clickhouse/client-common/-/client-common-1.18.2.tgz", - "integrity": "sha512-J0SG6q9V31ydxonglpj9xhNRsUxCsF71iEZ784yldqMYwsHixj/9xHFDgBDX3DuMiDx/kPDfXnf+pimp08wIBA==", - "license": "Apache-2.0" - }, "node_modules/@csstools/color-helpers": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz", - "integrity": "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.1.2.tgz", + "integrity": "sha512-grhRy3OKmniaAEKXMjua5z/EODX0MSqBGjunw8+j/3HQjOnahs2AGhvEOIYVUWcU6ScApbhLhVrQTX8XqrMrow==", "dev": true, "funding": [ { @@ -1887,9 +1960,9 @@ } }, "node_modules/@csstools/css-calc": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.2.0.tgz", - "integrity": "sha512-bR9e6o2BDB12jzN/gIbjHa5wLJ4UjD1CB9pM7ehlc0ddk6EBz+yYS1EV2MF55/HUxrHcB/hehAyt5vhsA3hx7w==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.4.3.tgz", + "integrity": "sha512-iex20d8CHVkyvg6B7UKV7uHnI2Bqo9g+EFfT9E0y+GvTvhZ/DwONJ+9aKb1dlqm0ZiGsL5RXjp0fCoJYnkeDjA==", "dev": true, "funding": [ { @@ -1906,14 +1979,14 @@ "node": ">=20.19.0" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^4.0.0", - "@csstools/css-tokenizer": "^4.0.0" + "@csstools/css-parser-algorithms": "^4.0.2", + "@csstools/css-tokenizer": "^4.0.2" } }, "node_modules/@csstools/css-color-parser": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.0.tgz", - "integrity": "sha512-U0KhLYmy2GVj6q4T3WaAe6NPuFYCPQoE3b0dRGxejWDgcPp8TP7S5rVdM5ZrFaqu4N67X8YaPBw14dQSYx3IyQ==", + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.2.6.tgz", + "integrity": "sha512-iiPQ3iRWwnJkeEn6RIu6SJPr7hYrLz6XZ9s/QZl+2/LI5KQVjpl2fdmDSZKuD4xP6GMmMPgHFFXg6k1Wkz0Trg==", "dev": true, "funding": [ { @@ -1927,21 +2000,21 @@ ], "license": "MIT", "dependencies": { - "@csstools/color-helpers": "^6.0.2", - "@csstools/css-calc": "^3.2.0" + "@csstools/color-helpers": "^6.1.2", + "@csstools/css-calc": "^3.4.3" }, "engines": { "node": ">=20.19.0" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^4.0.0", - "@csstools/css-tokenizer": "^4.0.0" + "@csstools/css-parser-algorithms": "^4.0.2", + "@csstools/css-tokenizer": "^4.0.2" } }, "node_modules/@csstools/css-parser-algorithms": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", - "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.2.tgz", + "integrity": "sha512-40cSKyMvK+tq4qz6Awrlye2WGuOKt3FwPgtGg6KTfbHOWNw+Rk1rzbAtZnZ6IBhsY491HLRnDXwoyBAijmmILA==", "dev": true, "funding": [ { @@ -1958,13 +2031,13 @@ "node": ">=20.19.0" }, "peerDependencies": { - "@csstools/css-tokenizer": "^4.0.0" + "@csstools/css-tokenizer": "^4.0.2" } }, "node_modules/@csstools/css-syntax-patches-for-csstree": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.3.tgz", - "integrity": "sha512-SH60bMfrRCJF3morcdk57WklujF4Jr/EsQUzqkarfHXEFcAR1gg7fS/chAE922Sehgzc1/+Tz5H3Ypa1HiEKrg==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.15.tgz", + "integrity": "sha512-J0u7HkVl2nzSlhsiTOp4AmwcUQ3D+mGEEKfBy/7To5/y7F2OHwyLrXfrhR0SMgr4p5Lo+eaMVSeai24zUcBIxA==", "dev": true, "funding": [ { @@ -1987,9 +2060,9 @@ } }, "node_modules/@csstools/css-tokenizer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", - "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.2.tgz", + "integrity": "sha512-OoKoR0f76dCY666JlcbhmVTs2drYj1GUXZTYTcbUgJjh9Nv41aFfZ21bPQTERm5+L5cBDo466NltB2lplS5GBw==", "dev": true, "funding": [ { @@ -2007,9 +2080,9 @@ } }, "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", "license": "MIT", "optional": true, "dependencies": { @@ -2017,9 +2090,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", - "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", "cpu": [ "ppc64" ], @@ -2033,9 +2106,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", - "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", "cpu": [ "arm" ], @@ -2049,9 +2122,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", - "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", "cpu": [ "arm64" ], @@ -2065,9 +2138,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", - "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", "cpu": [ "x64" ], @@ -2081,9 +2154,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", - "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", "cpu": [ "arm64" ], @@ -2097,9 +2170,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", - "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", "cpu": [ "x64" ], @@ -2113,9 +2186,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", - "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", "cpu": [ "arm64" ], @@ -2129,9 +2202,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", - "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", "cpu": [ "x64" ], @@ -2145,9 +2218,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", - "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", "cpu": [ "arm" ], @@ -2161,9 +2234,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", - "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", "cpu": [ "arm64" ], @@ -2177,9 +2250,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", - "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", "cpu": [ "ia32" ], @@ -2193,9 +2266,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", - "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", "cpu": [ "loong64" ], @@ -2209,9 +2282,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", - "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", "cpu": [ "mips64el" ], @@ -2225,9 +2298,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", - "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", "cpu": [ "ppc64" ], @@ -2241,9 +2314,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", - "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", "cpu": [ "riscv64" ], @@ -2257,9 +2330,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", - "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", "cpu": [ "s390x" ], @@ -2273,9 +2346,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", - "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", "cpu": [ "x64" ], @@ -2289,9 +2362,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", - "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", "cpu": [ "arm64" ], @@ -2305,9 +2378,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", - "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", "cpu": [ "x64" ], @@ -2321,9 +2394,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", - "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", "cpu": [ "arm64" ], @@ -2337,9 +2410,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", - "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", "cpu": [ "x64" ], @@ -2353,9 +2426,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", - "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", "cpu": [ "arm64" ], @@ -2369,9 +2442,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", - "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", "cpu": [ "x64" ], @@ -2385,9 +2458,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", - "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", "cpu": [ "arm64" ], @@ -2401,9 +2474,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", - "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", "cpu": [ "ia32" ], @@ -2417,9 +2490,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", - "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", "cpu": [ "x64" ], @@ -2433,9 +2506,9 @@ } }, "node_modules/@exodus/bytes": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.0.tgz", - "integrity": "sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.16.0.tgz", + "integrity": "sha512-IcpW84uEn3N7ETtNZMlxKhfl6Pec8rUNGOTBtWbK1FKhJxIFAptZyVrvVRVBimAJxJCgc3PxepxkdWWG4DVzfA==", "dev": true, "license": "MIT", "engines": { @@ -2451,37 +2524,37 @@ } }, "node_modules/@fastify/busboy": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.2.0.tgz", - "integrity": "sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.2.2.tgz", + "integrity": "sha512-yXSS27qPExaXeuLvMRMXOLtpipzfQYNjG3FkunDWKGfMYjKuhFXko9CVzqxm8jcF+lmtS9Fd89QNdh9XDjnbNg==", "license": "MIT" }, "node_modules/@floating-ui/core": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz", - "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.8.0.tgz", + "integrity": "sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==", "license": "MIT", "dependencies": { - "@floating-ui/utils": "^0.2.11" + "@floating-ui/utils": "^0.2.12" } }, "node_modules/@floating-ui/dom": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz", - "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.8.0.tgz", + "integrity": "sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==", "license": "MIT", "dependencies": { - "@floating-ui/core": "^1.7.5", - "@floating-ui/utils": "^0.2.11" + "@floating-ui/core": "^1.8.0", + "@floating-ui/utils": "^0.2.12" } }, "node_modules/@floating-ui/react-dom": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.8.tgz", - "integrity": "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==", + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.9.tgz", + "integrity": "sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==", "license": "MIT", "dependencies": { - "@floating-ui/dom": "^1.7.6" + "@floating-ui/dom": "^1.8.0" }, "peerDependencies": { "react": ">=16.8.0", @@ -2489,9 +2562,9 @@ } }, "node_modules/@floating-ui/utils": { - "version": "0.2.11", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz", - "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.12.tgz", + "integrity": "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==", "license": "MIT" }, "node_modules/@gerrit0/mini-shiki": { @@ -2509,9 +2582,9 @@ } }, "node_modules/@grpc/grpc-js": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.3.tgz", - "integrity": "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==", + "version": "1.14.5", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.5.tgz", + "integrity": "sha512-7VZM+SVdEcUUqSQeNI3zM8Qs/BhQKZndPo2h5VkYkAM8Iz0wJIa8mKV5ekQGqG8UUsnkQ0NMxIxwkIHYvj0qOw==", "license": "Apache-2.0", "dependencies": { "@grpc/proto-loader": "^0.8.0", @@ -2522,14 +2595,14 @@ } }, "node_modules/@grpc/proto-loader": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.0.tgz", - "integrity": "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.1.tgz", + "integrity": "sha512-wtF6h+DY6M3YaDBPAmvuuA6jV8Sif9MjtOI5euKFWRgCDl5PeDpPsHR9u2l6St5ceY8AZgoNDww5+HvEsXFsGg==", "license": "Apache-2.0", "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", - "protobufjs": "^7.5.3", + "protobufjs": "^7.5.5", "yargs": "^17.7.2" }, "bin": { @@ -2539,30 +2612,6 @@ "node": ">=6" } }, - "node_modules/@grpc/proto-loader/node_modules/protobufjs": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.5.tgz", - "integrity": "sha512-3wY1AxV+VBNW8Yypfd1yQY9pXnqTAN+KwQxL8iYm3/BjKYMNg4i0owhEe26PWDOMaIrzeeF98Lqd5NGz4omiIg==", - "hasInstallScript": true, - "license": "BSD-3-Clause", - "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" - }, - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/@hapi/hoek": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", @@ -2589,9 +2638,9 @@ } }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.5.tgz", + "integrity": "sha512-QRUlFQ0WxvdWyqqG/WtI3iupfD5rBzmCHXSdPsY91sAtVtTo7Q4cb6zOccZ3gqEqkr0f1As1ehLqmEpDsRf+lg==", "cpu": [ "arm64" ], @@ -2601,19 +2650,19 @@ "darwin" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" + "@img/sharp-libvips-darwin-arm64": "1.3.4" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.5.tgz", + "integrity": "sha512-+BR255RhDlpygUpOc/Jdt1nT6DQ3XG/ERo5wbcdOf5Q320dKtPCKPLR1LJs9VGXRaMa8l1uUa0tkCNOXiAxZUw==", "cpu": [ "x64" ], @@ -2623,19 +2672,38 @@ "darwin" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" + "@img/sharp-libvips-darwin-x64": "1.3.4" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.5.tgz", + "integrity": "sha512-Y/z91nEZ4uIBX5X3nfTovjU9lHNKFYbL2lpHCLVNmXQK03VIZvXBBt0KxbPGp2SdGSF+2mQU4e+hQaWOt86iAw==", + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.5" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.4.tgz", + "integrity": "sha512-5R89nBYiRdUlSWJxPhO+GVtaXzXSxKnRu/xqMn3KTA3L9EB9Oy/P+Nn2f2vlhPuUdy/Zusb2DarbyTpGCfEDuw==", "cpu": [ "arm64" ], @@ -2649,9 +2717,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.4.tgz", + "integrity": "sha512-iR2OKH80yi0U+dUplyh3/xdpFvps6YkCwsXenIJxqxR1v9o+xtKTGbS9H7cps+2Vxjc8B1j96p75NmTGjIhtpQ==", "cpu": [ "x64" ], @@ -2665,12 +2733,15 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.4.tgz", + "integrity": "sha512-LmRtTsOHuvM2+wlO2Db37dx5MiZhB0FvSunciw48YjdOkZz9KAiRbm8ujeMOA1INqmei5NapFxYEK1D1ZSidmw==", "cpu": [ "arm" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2681,12 +2752,15 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.4.tgz", + "integrity": "sha512-Y3dgX/6lE2QhQb+Gxy0WZxfg9MEm/JBjamZpS2IklP7xIQoKN4hzAm7KcMVGtaVDt3neE9OKBC7vAfonA/Lr1A==", "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2697,12 +2771,15 @@ } }, "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.4.tgz", + "integrity": "sha512-Le6boB8Tai0Nis+gIxIpKx68UDVVIqdR8Tin5Yf1z2LJJQLDJvCDRqRu+jC2qCoD+eIomonmOwB4smBRxfVpYQ==", "cpu": [ "ppc64" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2713,12 +2790,15 @@ } }, "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.4.tgz", + "integrity": "sha512-aHkkIEHPRdQEegJN20MLmGtxYD9R2wQr3Cwpddnu5+YKMt6Uzax7S9h5gpZTo8wyrGuZSlfQ63OevL5mTyOC7Q==", "cpu": [ "riscv64" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2729,12 +2809,15 @@ } }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.4.tgz", + "integrity": "sha512-ra/mB6MikESDUO7Yg+Mi95bFBb9GsObURuhnOv3OqknjGe9sZrG8tCe9q0xSIGrtLgvgw0gKnFWcK4blSgQOuQ==", "cpu": [ "s390x" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2745,12 +2828,15 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.4.tgz", + "integrity": "sha512-GJ//SSXbnwSDes02umB3nDJLFcQzw8a18V8fyhqr6tV515tOEMdImjjxj1AoafMRz56F3PHgftnj1QEKSU1zkw==", "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2761,12 +2847,15 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.4.tgz", + "integrity": "sha512-hvulFwtjUcagsis6BBxHwGFwWoNZjgYmULGVrZcyfNbjA8hKILbRxGg15/7w5HDyXHXUos/j6baAWqnCyQ2DWA==", "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2777,12 +2866,15 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.4.tgz", + "integrity": "sha512-6zXKeE/p39I1AmA3cJG35eyBGNqNddLnUXjhwBnsGjFPWqf5VKkDBEqaEkPDoTEtkxwi2vv8Tcr2mDyP4So7Fg==", "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -2793,204 +2885,244 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.5.tgz", + "integrity": "sha512-LEaXK2WdXVK5ykcw0buWyPMsmLLL2vpHLD6yrNSW+JGEL3BZPA4tpKN6iaMc4AxTTAoaX/sU1rOL51lcIz48ZQ==", "cpu": [ "arm" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" + "@img/sharp-libvips-linux-arm": "1.3.4" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.5.tgz", + "integrity": "sha512-LYVx5JTsOM2CBzmxreh+nl64/3H6Xb09iSLknqH47z2T2DFFxDeFLP5y4dJwe6H7uGQlHPyEEtIqyo3DYsRwdQ==", "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" + "@img/sharp-libvips-linux-arm64": "1.3.4" } }, "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.5.tgz", + "integrity": "sha512-QVxAAq8evVRI9ia2vqgwrmWucn5Dfv+JdWzj75pD8omHLPSP7f8p20O8jxzjCcuCEQEOtYOZUmX1hkiZ0kdevA==", "cpu": [ "ppc64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" + "@img/sharp-libvips-linux-ppc64": "1.3.4" } }, "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.5.tgz", + "integrity": "sha512-LtdreXguaavKODPIfzJ4kffx7UNt1omwtK0rch4EBbbSTXPnxWmYSayXdLJw0fJzQ97kHt1gL/yh4tvU+nCyRQ==", "cpu": [ "riscv64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" + "@img/sharp-libvips-linux-riscv64": "1.3.4" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.5.tgz", + "integrity": "sha512-UZasTOFiYzotTsGOCu42BfUzP6Tu6Do/947iRm1RsLKvlllxwGcn4RN27LibGWceix4Y+Pmw3jsnTcCQIgWjqA==", "cpu": [ "s390x" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" + "@img/sharp-libvips-linux-s390x": "1.3.4" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.5.tgz", + "integrity": "sha512-SxFtLTeJInhAA9Q836kux2vZNeOBQEx658qvbboZScr0wIARym3IcGmW7KpVD5sbVg0Ojy+udFQdayYIZyoNog==", "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" + "@img/sharp-libvips-linux-x64": "1.3.4" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.5.tgz", + "integrity": "sha512-9HbMclmI1zlNkFRs3z9/eBtDjfD0sGlrX1z6b1qwmiFY5ElDLh4BC0LPBdVp7z1DXFiKlIcznf+ZlsuZzLxQqg==", "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + "@img/sharp-libvips-linuxmusl-arm64": "1.3.4" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.5.tgz", + "integrity": "sha512-4KOphqB035HrVdqLZfCgMzzERrQkkzOwRhl4OAkRO1YCldbaFjySXMaK534Mo0V+LndnlJk+sbUyLeU0ULyD1A==", "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + "@img/sharp-libvips-linuxmusl-x64": "1.3.4" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.5.tgz", + "integrity": "sha512-Ptsga1su4tQx+LLF1ECS9U6nz5kmrXKo6XVbtR48Ke3ZRxxgaWBu7IDtEe1quo8hiupwm6WFqxVlXaSf7IINGQ==", + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.11.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.5.tgz", + "integrity": "sha512-hfhF/FmoQyTUkA0bIKFOtw536BQSeBMe6BF6QyWlrPxT754+TFLaZ7sKKTfvvM0yJgKgaYTwnFCIZ/GuDw5SUA==", "cpu": [ "wasm32" ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "license": "Apache-2.0", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.7.0" + "@img/sharp-wasm32": "0.35.5" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.5.tgz", + "integrity": "sha512-X4t7g+7ZA5DKblCBEXGjUqqemj4vczING/5viFwAL8h4N3qYeyjwdCvRLHi4EdOUI+2Z7UFlp1VM+p/AuEtm6Q==", "cpu": [ "arm64" ], @@ -3000,16 +3132,16 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.5.tgz", + "integrity": "sha512-5Zm82LoBc43nhwNybZlG7Y1KO//Zhsn306fQl29ZOuStHLGTo3BWL83q3cznX0poxSAMuYL1On/BHBxkBeKr6A==", "cpu": [ "ia32" ], @@ -3019,16 +3151,16 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": "^20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.5.tgz", + "integrity": "sha512-x76eH0vEiHlcMQu8Y8IenntaACtddpT6W0wmXtWrnKcnKI7ME5DdgqhAD6SEWOEl1v2zDvkZDhFA9KnURwpfqg==", "cpu": [ "x64" ], @@ -3038,34 +3170,12 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, - "node_modules/@inquirer/external-editor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", - "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", - "dev": true, - "license": "MIT", - "dependencies": { - "chardet": "^2.1.1", - "iconv-lite": "^0.7.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/node": ">=18" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -3099,9 +3209,9 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", "dev": true, "license": "MIT" }, @@ -3127,75 +3237,45 @@ } }, "node_modules/@manypkg/find-root": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@manypkg/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.5.5", - "@types/node": "^12.7.1", - "find-up": "^4.1.0", - "fs-extra": "^8.1.0" - } - }, - "node_modules/@manypkg/find-root/node_modules/@types/node": { - "version": "12.20.55", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", - "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@manypkg/find-root/node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@manypkg/find-root/-/find-root-3.1.0.tgz", + "integrity": "sha512-BcSqCyKhBVZ5YkSzOiheMCV41kqAFptW6xGqYSTjkVTl9XQpr+pqHhwgGCOHQtjDCv7Is6EFyA14Sm5GVbVABA==", "dev": true, "license": "MIT", "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "@manypkg/tools": "^2.1.0" }, "engines": { - "node": ">=6 <7 || >=8" + "node": ">=20.0.0" } }, "node_modules/@manypkg/get-packages": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@manypkg/get-packages/-/get-packages-1.1.3.tgz", - "integrity": "sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@manypkg/get-packages/-/get-packages-3.1.0.tgz", + "integrity": "sha512-0TbBVyvPrP7xGYBI/cP8UP+yl/z+HtbTttAD7FMAJgn/kXOTwh5/60TsqP9ZYY710forNfyV0N8P/IE/ujGZJg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/runtime": "^7.5.5", - "@changesets/types": "^4.0.1", - "@manypkg/find-root": "^1.1.0", - "fs-extra": "^8.1.0", - "globby": "^11.0.0", - "read-yaml-file": "^1.1.0" + "@manypkg/find-root": "^3.1.0", + "@manypkg/tools": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" } }, - "node_modules/@manypkg/get-packages/node_modules/@changesets/types": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@changesets/types/-/types-4.1.0.tgz", - "integrity": "sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@manypkg/get-packages/node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "node_modules/@manypkg/tools": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@manypkg/tools/-/tools-2.1.2.tgz", + "integrity": "sha512-6QEf6yqFbETdwGITKq57aYoPfX/3K8XFNwsAlx0C1M7o8cb79sv1M3w+tWuWvIcSbNqrLF7OD7YpZMVVz335hQ==", "dev": true, "license": "MIT", "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "jju": "^1.4.0", + "tinyglobby": "^0.2.13", + "yaml": "^2.9.0" }, "engines": { - "node": ">=6 <7 || >=8" + "node": ">=20.0.0" } }, "node_modules/@monaco-editor/loader": { @@ -3221,16 +3301,36 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, "node_modules/@next/env": { - "version": "16.2.4", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.4.tgz", - "integrity": "sha512-dKkkOzOSwFYe5RX6y26fZgkSpVAlIOJKQHIiydQcrWH6y/97+RceSOAdjZ14Qa3zLduVUy0TXcn+EiM6t4rPgw==", + "version": "16.3.8", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.3.8.tgz", + "integrity": "sha512-Al9zqHVV7TJv0eFuOU4U7Lvv74PTih4Ch63sk2xCIpSTkE3udFnaOcnzP2lQVymiL7yS9Cj2iClUXlR3EQ5sEw==", "license": "MIT" }, "node_modules/@next/swc-darwin-arm64": { - "version": "16.2.4", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.4.tgz", - "integrity": "sha512-OXTFFox5EKN1Ym08vfrz+OXxmCcEjT4SFMbNRsWZE99dMqt2Kcusl5MqPXcW232RYkMLQTy0hqgAMEsfEd/l2A==", + "version": "16.3.8", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.3.8.tgz", + "integrity": "sha512-2JPRMh2nmQG5CiL7cXGL9AGwnPWJQ//cTtAUCT+w511QHk79SYz3LGv/pc5X643B/WEO0rvu3Yww0hqwt3kgeA==", "cpu": [ "arm64" ], @@ -3244,9 +3344,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "16.2.4", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.4.tgz", - "integrity": "sha512-XhpVnUfmYWvD3YrXu55XdcAkQtOnvaI6wtQa8fuF5fGoKoxIUZ0kWPtcOfqJEWngFF/lOS9l3+O9CcownhiQxQ==", + "version": "16.3.8", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.3.8.tgz", + "integrity": "sha512-GZtCCOBKJ4leVIT/Th0llWKhD1ca92lzbQiS5R5ON9QkoiFnilFsebDae1JU2a3HWoKMEmEZWGs1AGLavVM72Q==", "cpu": [ "x64" ], @@ -3260,12 +3360,15 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.2.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.4.tgz", - "integrity": "sha512-Mx/tjlNA3G8kg14QvuGAJ4xBwPk1tUHq56JxZ8CXnZwz1Etz714soCEzGQQzVMz4bEnGPowzkV6Xrp6wAkEWOQ==", + "version": "16.3.8", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.3.8.tgz", + "integrity": "sha512-O659ygeQYqneJ1fBKMpFxIFqYkYswu8IAS1OCKK/4f3ZgJJm1dRz4fVJZRi/kLLWjnBKnebOePA4WNv+sV1pVA==", "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -3276,12 +3379,15 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.2.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.4.tgz", - "integrity": "sha512-iVMMp14514u7Nup2umQS03nT/bN9HurK8ufylC3FZNykrwjtx7V1A7+4kvhbDSCeonTVqV3Txnv0Lu+m2oDXNg==", + "version": "16.3.8", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.3.8.tgz", + "integrity": "sha512-dSjKSyWpzxoO1d3DIZZcP4XJcNaKeLmxQMFOiYl5vuBRMmweIqnAhty8tAmRsvTss779cK1FtYnDMj40e4TQlg==", "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -3292,12 +3398,15 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.2.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.4.tgz", - "integrity": "sha512-EZOvm1aQWgnI/N/xcWOlnS3RQBk0VtVav5Zo7n4p0A7UKyTDx047k8opDbXgBpHl4CulRqRfbw3QrX2w5UOXMQ==", + "version": "16.3.8", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.3.8.tgz", + "integrity": "sha512-lbqOuz3RPRcv+o9msNsJw5x4+Y1ZwPTs6vmL6DCf7i0fZfvng/F59wyeDwqHIvV0mK//RBy/jJkZ+nCKsSMXjQ==", "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -3308,12 +3417,15 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "16.2.4", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.4.tgz", - "integrity": "sha512-h9FxsngCm9cTBf71AR4fGznDEDx1hS7+kSEiIRjq5kO1oXWm07DxVGZjCvk0SGx7TSjlUqhI8oOyz7NfwAdPoA==", + "version": "16.3.8", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.3.8.tgz", + "integrity": "sha512-+316WswI8ScVgZeUd+1KGaXkHhaYQzCjvH/05TZSpJ8zBizb1a4G7DtO7F12jcBIqMOtsz9ji1t48fmKtzqsGA==", "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -3324,9 +3436,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.2.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.4.tgz", - "integrity": "sha512-3NdJV5OXMSOeJYijX+bjaLge3mJBlh4ybydbT4GFoB/2hAojWHtMhl3CYlYoMrjPuodp0nzFVi4Tj2+WaMg+Ow==", + "version": "16.3.8", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.3.8.tgz", + "integrity": "sha512-ji0gd4kMYUxO+1fJBIbiBVRCjzG/lloiyCccnlebvb1ZJ5qXCPZqYg4Jl1DrrixnWNMKylzgpmMWx0yNDYXlzw==", "cpu": [ "arm64" ], @@ -3340,9 +3452,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.2.4", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.4.tgz", - "integrity": "sha512-kMVGgsqhO5YTYODD9IPGGhA6iprWidQckK3LmPeW08PIFENRmgfb4MjXHO+p//d+ts2rpjvK5gXWzXSMrPl9cw==", + "version": "16.3.8", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.3.8.tgz", + "integrity": "sha512-WcTlaKt/TWkh5kUjdJcUmB1XgZ+1c6fz4Y9fDHL73YNSdGaUWjceeWrrlwF0nv19iABYWC4iAq1oX1w4Bn0vfg==", "cpu": [ "x64" ], @@ -3355,44 +3467,6 @@ "node": ">= 10" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/@opentelemetry/api": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz", @@ -3403,9 +3477,9 @@ } }, "node_modules/@opentelemetry/api-logs": { - "version": "0.215.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.215.0.tgz", - "integrity": "sha512-xrFlqhdhUyO8wSRn6DjE0145/HPWSJ5Nm0C7vWua6TdL/FSEAZvEyvdsa9CRXuxo9ebb7j/NEPhEcO62IJ0qUA==", + "version": "0.222.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.222.0.tgz", + "integrity": "sha512-9mb1If+IF6u0ZVXkHQ6ogEae5HwA6ajIVUgpSDQyRASxft6BSXHvBvPooRle3yFN/fKnCdSOnuu0OC3PLcF6+g==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/api": "^1.3.0" @@ -3415,13 +3489,13 @@ } }, "node_modules/@opentelemetry/configuration": { - "version": "0.215.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/configuration/-/configuration-0.215.0.tgz", - "integrity": "sha512-FSWvDryxjinHROfzEVbJGBw10FqGzLEm2C1LPX6Lot6hvxq3lFJzNLlue8vm64C5yIbqSQVjWsPhYu56ThQS4Q==", + "version": "0.222.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/configuration/-/configuration-0.222.0.tgz", + "integrity": "sha512-zNWGmEvyX7CUbS6F6PKi7hf3CF/SffzeBYyWVQAfadZ8hODrvLmqV3IUGVt1SiqyYanRPHc8VuhoO0Qh9OEuug==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.7.0", - "yaml": "^2.0.0" + "@opentelemetry/core": "2.11.0", + "yaml": "^2.8.3" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3431,9 +3505,9 @@ } }, "node_modules/@opentelemetry/context-async-hooks": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.7.0.tgz", - "integrity": "sha512-MWXggArM+Y11mPS8VOrqxOj+YMGQSRuvhM91eSBX4xFpJa05mpkeVvM8pPux5ElkEjV5RMgrkisrlP/R83SpBQ==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.11.0.tgz", + "integrity": "sha512-Tr79DyWI8itsBdg+jH+opjfrwLzX+erk1/ExkIwhWoAVjVrJIn2y5+cGjTC0Vy8fyNIA/y8wuJPZwr1T3xCZeQ==", "license": "Apache-2.0", "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3443,9 +3517,9 @@ } }, "node_modules/@opentelemetry/core": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.0.tgz", - "integrity": "sha512-DT12SXVwV2eoJrGf4nnsvZojxxeQo+LlNAsoYGRRObPWTeN6APiqZ2+nqDCQDvQX40eLi1AePONS0onoASp3yQ==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.11.0.tgz", + "integrity": "sha512-7YP44XH0tV6+Mb54x2YGf84i7yi+31MBZlE8JwvozkxyTvXbSp10X7cI7YE49ChJ3shMJoBmCJF3+1QFBJctGA==", "license": "Apache-2.0", "dependencies": { "@opentelemetry/semantic-conventions": "^1.29.0" @@ -3458,17 +3532,15 @@ } }, "node_modules/@opentelemetry/exporter-logs-otlp-grpc": { - "version": "0.215.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-grpc/-/exporter-logs-otlp-grpc-0.215.0.tgz", - "integrity": "sha512-MVq+9ma/63XRXc0AcnS+XyWSD6VBYn39OucsvpzjqxTpzTOiGXNxTwsbV3zbnvgUexb5hc2ZjJlZUK2W/19UUw==", + "version": "0.222.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-grpc/-/exporter-logs-otlp-grpc-0.222.0.tgz", + "integrity": "sha512-HH4Oi/E8HXBKtcjgcVPJGsrg+lSstdw1m93brRdpVj5CjMpyTkv/O0ZYMXP6QXVEaLjjFivtzUu4Y8+i1UJpjw==", "license": "Apache-2.0", "dependencies": { - "@grpc/grpc-js": "^1.14.3", - "@opentelemetry/core": "2.7.0", - "@opentelemetry/otlp-exporter-base": "0.215.0", - "@opentelemetry/otlp-grpc-exporter-base": "0.215.0", - "@opentelemetry/otlp-transformer": "0.215.0", - "@opentelemetry/sdk-logs": "0.215.0" + "@opentelemetry/otlp-exporter-base": "0.222.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.222.0", + "@opentelemetry/otlp-transformer": "0.222.0", + "@opentelemetry/sdk-logs": "0.222.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3478,16 +3550,14 @@ } }, "node_modules/@opentelemetry/exporter-logs-otlp-http": { - "version": "0.215.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.215.0.tgz", - "integrity": "sha512-U7Qb+TVX2GZH5RSC+Gx9aE5zChKP1kPg87X3PlI/41lWVPJdBIzmgMmuE28MmQlrK84nLHCIqUOOben8YkSzBw==", + "version": "0.222.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.222.0.tgz", + "integrity": "sha512-GqwLZohJTf/sttYlOpT1oFTJ9ScYaKnk0sOx4Qe/+KmXYVOa+v94cfnkUkFKDuvTODWhCL50Hz3gvsZD6ngQpg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.215.0", - "@opentelemetry/core": "2.7.0", - "@opentelemetry/otlp-exporter-base": "0.215.0", - "@opentelemetry/otlp-transformer": "0.215.0", - "@opentelemetry/sdk-logs": "0.215.0" + "@opentelemetry/otlp-exporter-base": "0.222.0", + "@opentelemetry/otlp-transformer": "0.222.0", + "@opentelemetry/sdk-logs": "0.222.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3497,18 +3567,14 @@ } }, "node_modules/@opentelemetry/exporter-logs-otlp-proto": { - "version": "0.215.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-proto/-/exporter-logs-otlp-proto-0.215.0.tgz", - "integrity": "sha512-vs2xKKTdt/vKWMuBzw+LZYYCKqulodCRoonWWiyToIQfa6JgbyWjTu/iy6qpBLhLi+t6fNc1bwJGwu3vkot2Jg==", + "version": "0.222.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-proto/-/exporter-logs-otlp-proto-0.222.0.tgz", + "integrity": "sha512-LlCDJuaznfFPzjHoKXjN/r9T5pgCZpmVheZ/EmwMUhMvpxI7RXDmsdLXoYpl5suYQNkwyXAUZhVEXKe3lev8Hg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.215.0", - "@opentelemetry/core": "2.7.0", - "@opentelemetry/otlp-exporter-base": "0.215.0", - "@opentelemetry/otlp-transformer": "0.215.0", - "@opentelemetry/resources": "2.7.0", - "@opentelemetry/sdk-logs": "0.215.0", - "@opentelemetry/sdk-trace-base": "2.7.0" + "@opentelemetry/otlp-exporter-base": "0.222.0", + "@opentelemetry/otlp-transformer": "0.222.0", + "@opentelemetry/sdk-logs": "0.222.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3518,19 +3584,14 @@ } }, "node_modules/@opentelemetry/exporter-metrics-otlp-grpc": { - "version": "0.215.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.215.0.tgz", - "integrity": "sha512-1TAMliHQvzc+v1OtnLMHSk5sU8BSkJbxIKrWzuCWcQjajWrvem/r5ugLK6agI0PjPz/ADfZju5AVYedlNyeO9g==", + "version": "0.222.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.222.0.tgz", + "integrity": "sha512-nPU2gpQWJfQttf+A71aLkKEOQoO6n6QGs5QTtPagi/rb3Lc1I55bqRXoLQEZsWqWimc8eas2Vo168NkTaByxQw==", "license": "Apache-2.0", "dependencies": { - "@grpc/grpc-js": "^1.14.3", - "@opentelemetry/core": "2.7.0", - "@opentelemetry/exporter-metrics-otlp-http": "0.215.0", - "@opentelemetry/otlp-exporter-base": "0.215.0", - "@opentelemetry/otlp-grpc-exporter-base": "0.215.0", - "@opentelemetry/otlp-transformer": "0.215.0", - "@opentelemetry/resources": "2.7.0", - "@opentelemetry/sdk-metrics": "2.7.0" + "@opentelemetry/exporter-metrics-otlp-http": "0.222.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.222.0", + "@opentelemetry/otlp-transformer": "0.222.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3540,16 +3601,16 @@ } }, "node_modules/@opentelemetry/exporter-metrics-otlp-http": { - "version": "0.215.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.215.0.tgz", - "integrity": "sha512-FRydO5j7MWnXK9ghfykKxiSM8I5UeiicK/UNl3/mv86xoEKkb+LKz1I3WXgkuYVOQf22VNqbPO58s2W1mVWtEQ==", + "version": "0.222.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.222.0.tgz", + "integrity": "sha512-6Ko+0bgNP6V4G/RsmNBSk5lO1B/lo9z6tvLpFY3ykPPevXNzFmOW3C9hEmTt0zKCvHovyrol3TYlg4HVb5gwjg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.7.0", - "@opentelemetry/otlp-exporter-base": "0.215.0", - "@opentelemetry/otlp-transformer": "0.215.0", - "@opentelemetry/resources": "2.7.0", - "@opentelemetry/sdk-metrics": "2.7.0" + "@opentelemetry/core": "2.11.0", + "@opentelemetry/otlp-exporter-base": "0.222.0", + "@opentelemetry/otlp-transformer": "0.222.0", + "@opentelemetry/resources": "2.11.0", + "@opentelemetry/sdk-metrics": "2.11.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3559,17 +3620,14 @@ } }, "node_modules/@opentelemetry/exporter-metrics-otlp-proto": { - "version": "0.215.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-proto/-/exporter-metrics-otlp-proto-0.215.0.tgz", - "integrity": "sha512-d8/Sys9MtxLbn0S+RE1pUNcuoI9ZyI4SPfOO+yskSEQiPFoKCTMwwthB8MTY4S8qxCBAWyM+P7QMX+vEIT7PZw==", + "version": "0.222.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-proto/-/exporter-metrics-otlp-proto-0.222.0.tgz", + "integrity": "sha512-s1ZPwS8Ig7cpRmL3kCobPaFbemM81dJ03LNPrsuN32h3mCDvNF2NQ1pRHruOju0P0o1TZ24LkZA685rtQal1KA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.7.0", - "@opentelemetry/exporter-metrics-otlp-http": "0.215.0", - "@opentelemetry/otlp-exporter-base": "0.215.0", - "@opentelemetry/otlp-transformer": "0.215.0", - "@opentelemetry/resources": "2.7.0", - "@opentelemetry/sdk-metrics": "2.7.0" + "@opentelemetry/exporter-metrics-otlp-http": "0.222.0", + "@opentelemetry/otlp-exporter-base": "0.222.0", + "@opentelemetry/otlp-transformer": "0.222.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3579,14 +3637,14 @@ } }, "node_modules/@opentelemetry/exporter-prometheus": { - "version": "0.215.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.215.0.tgz", - "integrity": "sha512-7ghCl1G84jccmxG3B8UwUMZ1OlequBzB1jt5tZ4DDiAyVKeA4Roz5D6VK8SQ0ZyBQffVyX/rtXrpVXKVzRCGfg==", + "version": "0.222.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-prometheus/-/exporter-prometheus-0.222.0.tgz", + "integrity": "sha512-I7nalnnBZrhlTgjlqsVeEjJGcQCFK5kKAYVk7XTz/cbubvj5fvjLOal/Many0taHF2GIkOWmegsbQ8vbQJjhFg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.7.0", - "@opentelemetry/resources": "2.7.0", - "@opentelemetry/sdk-metrics": "2.7.0", + "@opentelemetry/core": "2.11.0", + "@opentelemetry/resources": "2.11.0", + "@opentelemetry/sdk-metrics": "2.11.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -3597,18 +3655,15 @@ } }, "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { - "version": "0.215.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.215.0.tgz", - "integrity": "sha512-+SuWfPFVjPTvHJhlzTCBetLsPVu86xSFPR3fv8TN+H7lpe5aZzF96TUsfMHDR0lwpIwlJpG57CJnGalIfrpXkg==", + "version": "0.222.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.222.0.tgz", + "integrity": "sha512-uisilePEdOMa3hIfXW82XXU6FofF8gw3iv+PoboIMWEUBC7XbJB3XnIHSdrSDsqCw3njR/If08qD0yzOY1U9Sg==", "license": "Apache-2.0", "dependencies": { - "@grpc/grpc-js": "^1.14.3", - "@opentelemetry/core": "2.7.0", - "@opentelemetry/otlp-exporter-base": "0.215.0", - "@opentelemetry/otlp-grpc-exporter-base": "0.215.0", - "@opentelemetry/otlp-transformer": "0.215.0", - "@opentelemetry/resources": "2.7.0", - "@opentelemetry/sdk-trace-base": "2.7.0" + "@opentelemetry/otlp-exporter-base": "0.222.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.222.0", + "@opentelemetry/otlp-transformer": "0.222.0", + "@opentelemetry/sdk-trace": "2.11.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3618,16 +3673,14 @@ } }, "node_modules/@opentelemetry/exporter-trace-otlp-http": { - "version": "0.215.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.215.0.tgz", - "integrity": "sha512-k4J9ISeGpb0Bm/wCrlcrbroMFTkiWMrdhNxQGrlktxLy127Yzd4/7nrTawn5d/ApktYTknvdixsE6++34Qfi1w==", + "version": "0.222.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.222.0.tgz", + "integrity": "sha512-RCnPWcHppwiquQ+cV3nWvNwdf0MG1w26e5jewW2T83nTZOlXgcg88sY9ulCVgagGHcq1mj0L0GP6YHgzk2v8oA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.7.0", - "@opentelemetry/otlp-exporter-base": "0.215.0", - "@opentelemetry/otlp-transformer": "0.215.0", - "@opentelemetry/resources": "2.7.0", - "@opentelemetry/sdk-trace-base": "2.7.0" + "@opentelemetry/otlp-exporter-base": "0.222.0", + "@opentelemetry/otlp-transformer": "0.222.0", + "@opentelemetry/sdk-trace": "2.11.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3637,16 +3690,14 @@ } }, "node_modules/@opentelemetry/exporter-trace-otlp-proto": { - "version": "0.215.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.215.0.tgz", - "integrity": "sha512-+QclHuJmlp/I3Z2fNn+j1dAajMjJqJ4Sgo8ajwiK6Tzmg5SNwBGmBX66AZvTLe/3/bc3L7bo90m9gsaJBrzEsA==", + "version": "0.222.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.222.0.tgz", + "integrity": "sha512-imLrLzNhnABJt3tUyT63h6MqfZrG3Gdv3TE7+OUQTvwD088RfCUx3rF01lcgfu2KXBFk13vwi0yKZLvFEalkIA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.7.0", - "@opentelemetry/otlp-exporter-base": "0.215.0", - "@opentelemetry/otlp-transformer": "0.215.0", - "@opentelemetry/resources": "2.7.0", - "@opentelemetry/sdk-trace-base": "2.7.0" + "@opentelemetry/otlp-exporter-base": "0.222.0", + "@opentelemetry/otlp-transformer": "0.222.0", + "@opentelemetry/sdk-trace": "2.11.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3656,14 +3707,14 @@ } }, "node_modules/@opentelemetry/exporter-zipkin": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-2.7.0.tgz", - "integrity": "sha512-tbzcYDmZWtX4hgJn15qP7/iYFVd1yzbUloBuSYsQtn0XQTxJsG7vgwkPKEBellriH0XJmlZJxYtWkHpwzHBhaQ==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-2.11.0.tgz", + "integrity": "sha512-mFibA45yXLEETIj+VcTVycFFe4R91iD41d+r3CU0f3DDWJCrICiHxUIjmYBTCZIuywXEeNo++2lncQeWnH7lWQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.7.0", - "@opentelemetry/resources": "2.7.0", - "@opentelemetry/sdk-trace-base": "2.7.0", + "@opentelemetry/core": "2.11.0", + "@opentelemetry/resources": "2.11.0", + "@opentelemetry/sdk-trace": "2.11.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -3674,12 +3725,12 @@ } }, "node_modules/@opentelemetry/instrumentation": { - "version": "0.215.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.215.0.tgz", - "integrity": "sha512-SyJONuqypQ2xWdYMy99vF7JhZ2kDTGx4oRmM/jZV+kRtZ96JTnJmEINbIJgHz7Gnhtw0bimHwbPy/pguA5wpPQ==", + "version": "0.222.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.222.0.tgz", + "integrity": "sha512-fhbRDuAgzPKpq1k5+hX3uS+3QAYNca0on0Ngot/R8dDsQtuFaADeZnG2uSgxABuvcI7S2phAaRhbct9jieeKZw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.215.0", + "@opentelemetry/api-logs": "0.222.0", "import-in-the-middle": "^3.0.0", "require-in-the-middle": "^8.0.0" }, @@ -3691,13 +3742,13 @@ } }, "node_modules/@opentelemetry/instrumentation-http": { - "version": "0.215.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.215.0.tgz", - "integrity": "sha512-ip9iNoRRVxDyP8LVfdqqI6OwbOwzxTl4SaP1WDKJq0sDsgpOr7rIOFj7gV8yKl4F5PdDOUYy8VqdgIOWZRlGBw==", + "version": "0.222.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.222.0.tgz", + "integrity": "sha512-HhMHW32LyGzIJEmUElxaML4oUe6JiCq0Hvjt/Awxb60SlMsSFetFLFjed45g/0wrPr1n7z+xW+x1B/584WBAkg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.7.0", - "@opentelemetry/instrumentation": "0.215.0", + "@opentelemetry/core": "2.11.0", + "@opentelemetry/instrumentation": "0.222.0", "@opentelemetry/semantic-conventions": "^1.29.0", "forwarded-parse": "2.1.2" }, @@ -3723,6 +3774,35 @@ "@opentelemetry/api": "^1.3.0" } }, + "node_modules/@opentelemetry/instrumentation-runtime-node/node_modules/@opentelemetry/api-logs": { + "version": "0.215.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.215.0.tgz", + "integrity": "sha512-xrFlqhdhUyO8wSRn6DjE0145/HPWSJ5Nm0C7vWua6TdL/FSEAZvEyvdsa9CRXuxo9ebb7j/NEPhEcO62IJ0qUA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/instrumentation-runtime-node/node_modules/@opentelemetry/instrumentation": { + "version": "0.215.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.215.0.tgz", + "integrity": "sha512-SyJONuqypQ2xWdYMy99vF7JhZ2kDTGx4oRmM/jZV+kRtZ96JTnJmEINbIJgHz7Gnhtw0bimHwbPy/pguA5wpPQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.215.0", + "import-in-the-middle": "^3.0.0", + "require-in-the-middle": "^8.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, "node_modules/@opentelemetry/instrumentation-undici": { "version": "0.25.0", "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.25.0.tgz", @@ -3740,14 +3820,43 @@ "@opentelemetry/api": "^1.7.0" } }, - "node_modules/@opentelemetry/otlp-exporter-base": { + "node_modules/@opentelemetry/instrumentation-undici/node_modules/@opentelemetry/api-logs": { + "version": "0.215.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.215.0.tgz", + "integrity": "sha512-xrFlqhdhUyO8wSRn6DjE0145/HPWSJ5Nm0C7vWua6TdL/FSEAZvEyvdsa9CRXuxo9ebb7j/NEPhEcO62IJ0qUA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/instrumentation-undici/node_modules/@opentelemetry/instrumentation": { "version": "0.215.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.215.0.tgz", - "integrity": "sha512-lHrfbmeLSmesGSkkHiqDwOzfaEMSWXdc7q6UoLfbW8byONCb+bE/zkAr0kapN4US1baT/2nbpNT7Cn9XoB96Vg==", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.215.0.tgz", + "integrity": "sha512-SyJONuqypQ2xWdYMy99vF7JhZ2kDTGx4oRmM/jZV+kRtZ96JTnJmEINbIJgHz7Gnhtw0bimHwbPy/pguA5wpPQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.215.0", + "import-in-the-middle": "^3.0.0", + "require-in-the-middle": "^8.0.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.222.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.222.0.tgz", + "integrity": "sha512-YbywG3veEm2Fb6TbdxRkuquWob6eVWXuA8/Ba1tXz9jHfUqpdE3keilOHEtPboC4CvS1bjeeVfNkWGOOrLj+lw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.7.0", - "@opentelemetry/otlp-transformer": "0.215.0" + "@opentelemetry/core": "2.11.0", + "@opentelemetry/otlp-transformer": "0.222.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3757,15 +3866,15 @@ } }, "node_modules/@opentelemetry/otlp-grpc-exporter-base": { - "version": "0.215.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.215.0.tgz", - "integrity": "sha512-WkuHkUrhwNxTKrm7Xuf6S+HmLNbk2T8S2YiZhN606RfgetSQb9xLp4NizWLwXvw63uxGsBaK262dirFO2yht2g==", + "version": "0.222.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.222.0.tgz", + "integrity": "sha512-Kw5WqDBYYpCgY+edHWZS/Jp3bdguqNkCyEWOsqFhf8RmtcEuWYzPFA9i+L6UsNoJXTs8ngeQB6dwELH1czNYjA==", "license": "Apache-2.0", "dependencies": { "@grpc/grpc-js": "^1.14.3", - "@opentelemetry/core": "2.7.0", - "@opentelemetry/otlp-exporter-base": "0.215.0", - "@opentelemetry/otlp-transformer": "0.215.0" + "@opentelemetry/core": "2.11.0", + "@opentelemetry/otlp-exporter-base": "0.222.0", + "@opentelemetry/otlp-transformer": "0.222.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3775,18 +3884,17 @@ } }, "node_modules/@opentelemetry/otlp-transformer": { - "version": "0.215.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.215.0.tgz", - "integrity": "sha512-cWwBvaV+vkXHkSoTYR8hGw+AW03UlgTr6xtrUKOMeum3T+8vffYXIfXu6KY5MLu8O9QtoBKqaKWw9I5xoOepng==", + "version": "0.222.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.222.0.tgz", + "integrity": "sha512-/F3BZ89+CJQnZkMh2tCrtcdB+XT2Dxhj4FFE+WPQ//413hmFL0/RfEX6vgOIWGhiSzrkHWTK3+6SiT7K5/g/jQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.215.0", - "@opentelemetry/core": "2.7.0", - "@opentelemetry/resources": "2.7.0", - "@opentelemetry/sdk-logs": "0.215.0", - "@opentelemetry/sdk-metrics": "2.7.0", - "@opentelemetry/sdk-trace-base": "2.7.0", - "protobufjs": "^8.0.1" + "@opentelemetry/api-logs": "0.222.0", + "@opentelemetry/core": "2.11.0", + "@opentelemetry/resources": "2.11.0", + "@opentelemetry/sdk-logs": "0.222.0", + "@opentelemetry/sdk-metrics": "2.11.0", + "@opentelemetry/sdk-trace": "2.11.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3796,12 +3904,12 @@ } }, "node_modules/@opentelemetry/propagator-b3": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-2.7.0.tgz", - "integrity": "sha512-HNm+tdXY5i8dzAo4YankchNWdZ4Z1Boop7lhbb3wltWT0MwEMo0QADRJwrF83pXEeDT+5Bmq4J8sStFaUywE3g==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-2.11.0.tgz", + "integrity": "sha512-ldyRZXx6qsqu9Spqd7rVxo6SNAUJqP8Ir3vwIi4N8xyZLXNl/2XeQmIHvFneiVOqNwWkU+JGFg2O9/T1fefU0w==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.7.0" + "@opentelemetry/core": "2.11.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3811,12 +3919,12 @@ } }, "node_modules/@opentelemetry/propagator-jaeger": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-2.7.0.tgz", - "integrity": "sha512-lKMAjekRkFYWrjmPTaxUJt+V8Mr1iB94sP3HDZZCmdZ/LUV/wtqAGqXhgnkIbdlnWxxvEs9MGEIMdJC+xObMFg==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-2.11.0.tgz", + "integrity": "sha512-RUOqgoIqXOPQTOhpBrs07Y8sBlxVoLY5Fka6ueZ0prW+k6fWQsba/nHDY4gm+gmfuwtRqbX5Y9YKhDvktjbIqQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.7.0" + "@opentelemetry/core": "2.11.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3826,12 +3934,12 @@ } }, "node_modules/@opentelemetry/resources": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.7.0.tgz", - "integrity": "sha512-K+oi0hNMv94EpZbnW3eyu2X6SGVpD3O5DhG2NIp65Hc7lhAj9brRXTAVzh3wB82+q3ThakEf7Zd7RsFUqcTc7A==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.11.0.tgz", + "integrity": "sha512-Ie7+8q8MDF4FAEQCKVMTx3ReUvxiIAgIiiW3c9JdmP8+HMcDy20puT+AHjexnExgnbvBxjQ9fjkFDWrikJ2jQA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.7.0", + "@opentelemetry/core": "2.11.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -3842,14 +3950,14 @@ } }, "node_modules/@opentelemetry/sdk-logs": { - "version": "0.215.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.215.0.tgz", - "integrity": "sha512-y3ucOmphzc4vgBTyIGchs+N/1rkACmoka8QalT2z1LBNM232Z17zMYayHcMl+dgMoOadZ0b72UZv7mDtqy1cFA==", + "version": "0.222.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.222.0.tgz", + "integrity": "sha512-+19YHODIjaUCArxleaJtuufFZVpz/xvvK+VllQqE+W8hHolxdoRwHfK/s667zezwh1hkx6FFF+oYzetYgqK+Bg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.215.0", - "@opentelemetry/core": "2.7.0", - "@opentelemetry/resources": "2.7.0", + "@opentelemetry/api-logs": "0.222.0", + "@opentelemetry/core": "2.11.0", + "@opentelemetry/resources": "2.11.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -3860,13 +3968,13 @@ } }, "node_modules/@opentelemetry/sdk-metrics": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.7.0.tgz", - "integrity": "sha512-Vd7h95av/LYRsAVN7wbprvvJnHkq7swMXAo7Uad0Uxf9jl6NSReLa0JNivrcc5BVIx/vl2t+cgdVQQbnVhsR9w==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.11.0.tgz", + "integrity": "sha512-7GXXcObyHyDUUSG+L+kJoquty01bzm7ivE7+SSgXXJcHuPzGviptxwARmI2c+bnnxjexGQbJnyNlN8HxBP/Y7A==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.7.0", - "@opentelemetry/resources": "2.7.0" + "@opentelemetry/core": "2.11.0", + "@opentelemetry/resources": "2.11.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3876,35 +3984,54 @@ } }, "node_modules/@opentelemetry/sdk-node": { - "version": "0.215.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.215.0.tgz", - "integrity": "sha512-YunKvZOMhYNMBJ66YRjbGShuoV/w1y21U7MGPRx0iPJenPszOddtYEQFJv8piAEOn94BUFIfJHtHjptrHsGiIA==", + "version": "0.222.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.222.0.tgz", + "integrity": "sha512-t8tlFN/ezYV7iI4k6u3t2jFbIReX99Z0iVMVSYLJscE4McxIW9YGHhdOldeY1elHeoq8mjEO4B9s4Fk7+zJbsg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.215.0", - "@opentelemetry/configuration": "0.215.0", - "@opentelemetry/context-async-hooks": "2.7.0", - "@opentelemetry/core": "2.7.0", - "@opentelemetry/exporter-logs-otlp-grpc": "0.215.0", - "@opentelemetry/exporter-logs-otlp-http": "0.215.0", - "@opentelemetry/exporter-logs-otlp-proto": "0.215.0", - "@opentelemetry/exporter-metrics-otlp-grpc": "0.215.0", - "@opentelemetry/exporter-metrics-otlp-http": "0.215.0", - "@opentelemetry/exporter-metrics-otlp-proto": "0.215.0", - "@opentelemetry/exporter-prometheus": "0.215.0", - "@opentelemetry/exporter-trace-otlp-grpc": "0.215.0", - "@opentelemetry/exporter-trace-otlp-http": "0.215.0", - "@opentelemetry/exporter-trace-otlp-proto": "0.215.0", - "@opentelemetry/exporter-zipkin": "2.7.0", - "@opentelemetry/instrumentation": "0.215.0", - "@opentelemetry/otlp-exporter-base": "0.215.0", - "@opentelemetry/propagator-b3": "2.7.0", - "@opentelemetry/propagator-jaeger": "2.7.0", - "@opentelemetry/resources": "2.7.0", - "@opentelemetry/sdk-logs": "0.215.0", - "@opentelemetry/sdk-metrics": "2.7.0", - "@opentelemetry/sdk-trace-base": "2.7.0", - "@opentelemetry/sdk-trace-node": "2.7.0", + "@opentelemetry/api-logs": "0.222.0", + "@opentelemetry/configuration": "0.222.0", + "@opentelemetry/context-async-hooks": "2.11.0", + "@opentelemetry/core": "2.11.0", + "@opentelemetry/exporter-logs-otlp-grpc": "0.222.0", + "@opentelemetry/exporter-logs-otlp-http": "0.222.0", + "@opentelemetry/exporter-logs-otlp-proto": "0.222.0", + "@opentelemetry/exporter-metrics-otlp-grpc": "0.222.0", + "@opentelemetry/exporter-metrics-otlp-http": "0.222.0", + "@opentelemetry/exporter-metrics-otlp-proto": "0.222.0", + "@opentelemetry/exporter-prometheus": "0.222.0", + "@opentelemetry/exporter-trace-otlp-grpc": "0.222.0", + "@opentelemetry/exporter-trace-otlp-http": "0.222.0", + "@opentelemetry/exporter-trace-otlp-proto": "0.222.0", + "@opentelemetry/exporter-zipkin": "2.11.0", + "@opentelemetry/instrumentation": "0.222.0", + "@opentelemetry/otlp-exporter-base": "0.222.0", + "@opentelemetry/otlp-grpc-exporter-base": "0.222.0", + "@opentelemetry/propagator-b3": "2.11.0", + "@opentelemetry/propagator-jaeger": "2.11.0", + "@opentelemetry/resources": "2.11.0", + "@opentelemetry/sdk-logs": "0.222.0", + "@opentelemetry/sdk-metrics": "2.11.0", + "@opentelemetry/sdk-trace": "2.11.0", + "@opentelemetry/sdk-trace-base": "2.11.0", + "@opentelemetry/sdk-trace-node": "2.11.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace/-/sdk-trace-2.11.0.tgz", + "integrity": "sha512-fFnTqGm8/G73GQVnxYi7LXa1ZVYEUvgL6XI1LpvV0bPC7WQ/ZGgKxCSl8FnlZBKto9JHHEFTO6s6CUpvvtwFrA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.11.0", + "@opentelemetry/resources": "2.11.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -3915,13 +4042,14 @@ } }, "node_modules/@opentelemetry/sdk-trace-base": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.7.0.tgz", - "integrity": "sha512-Yg9zEXJB50DLVLpsKPk7NmNqlPlS+OvqhJGh0A8oawIOTPOwlm4eXs9BMJV7L79lvEwI+dWtAj+YjTyddV336A==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.11.0.tgz", + "integrity": "sha512-H19x/TX/LZdqiYOjM7fqtSxwlplC5pgelavqbQdHbhdq0q/AI/TGkM2dfGuuynTXmJPeF2HoZVoPDu+TGoW78A==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.7.0", - "@opentelemetry/resources": "2.7.0", + "@opentelemetry/core": "2.11.0", + "@opentelemetry/resources": "2.11.0", + "@opentelemetry/sdk-trace": "2.11.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -3932,14 +4060,14 @@ } }, "node_modules/@opentelemetry/sdk-trace-node": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.7.0.tgz", - "integrity": "sha512-RrFHOXw0IYp/OThew6QORdybnnLitUAUMCJKcQNBYS0hDkCYarO2vTkVxfrGxCIqd5XHSMvbCpBd/T8ZMw8oSg==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.11.0.tgz", + "integrity": "sha512-CuvCMJmZxswhNLlM2LfuLOW3h3fZujA4hsG4B+Sz4dX2zvaXO8Ng74cnDHWD64gLszTlhiG3c0iNUjj4g+0/sA==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/context-async-hooks": "2.7.0", - "@opentelemetry/core": "2.7.0", - "@opentelemetry/sdk-trace-base": "2.7.0" + "@opentelemetry/context-async-hooks": "2.11.0", + "@opentelemetry/core": "2.11.0", + "@opentelemetry/sdk-trace-base": "2.11.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -3949,9 +4077,9 @@ } }, "node_modules/@opentelemetry/semantic-conventions": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.40.0.tgz", - "integrity": "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw==", + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.43.0.tgz", + "integrity": "sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==", "license": "Apache-2.0", "engines": { "node": ">=14" @@ -3974,6 +4102,19 @@ "node": ">=18" } }, + "node_modules/@pnpm/deps.graph-sequencer": { + "version": "1100.0.1", + "resolved": "https://registry.npmjs.org/@pnpm/deps.graph-sequencer/-/deps.graph-sequencer-1100.0.1.tgz", + "integrity": "sha512-pOr5+q1fLYKwFN3LAJuGZEnfXDcQ73zqgDHMtGy+K+uIoUqyY+6MeDCWFwfu+4EFuq76I5EPFofoNAI+Bmmq4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.13" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -3987,25 +4128,24 @@ "license": "BSD-3-Clause" }, "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", + "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz", + "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==", "license": "BSD-3-Clause" }, "node_modules/@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", + "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", "license": "BSD-3-Clause", "dependencies": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" + "@protobufjs/aspromise": "^1.1.1" } }, "node_modules/@protobufjs/float": { @@ -4014,12 +4154,6 @@ "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", "license": "BSD-3-Clause" }, - "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", - "license": "BSD-3-Clause" - }, "node_modules/@protobufjs/path": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", @@ -4033,9 +4167,9 @@ "license": "BSD-3-Clause" }, "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.2.tgz", + "integrity": "sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==", "license": "BSD-3-Clause" }, "node_modules/@radix-ui/colors": { @@ -4045,24 +4179,24 @@ "license": "MIT" }, "node_modules/@radix-ui/number": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", - "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.3.tgz", + "integrity": "sha512-Road2bidD0uu/1BGDOWNdPI06g0lIRy6IF9GZcIrDK2KGItfor8IQwQa+yM2ERgHM1MmHxaxpTzk0/Jp42lNfA==", "license": "MIT" }, "node_modules/@radix-ui/primitive": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", - "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.7.tgz", + "integrity": "sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q==", "license": "MIT" }, "node_modules/@radix-ui/react-accessible-icon": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-accessible-icon/-/react-accessible-icon-1.1.7.tgz", - "integrity": "sha512-XM+E4WXl0OqUJFovy6GjmxxFyx9opfCAIUku4dlKRd5YEPqt4kALOkQOp0Of6reHuUkJuiPBEc5k0o4z4lTC8A==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-accessible-icon/-/react-accessible-icon-1.1.15.tgz", + "integrity": "sha512-WTQwcAvQf5sOcuUyi90lKPbhwcvQ+j55cjrSmeaN+L2vKU3DooOvlKw2MDeiJ5IkV5N905KW0/fGojKOBhD11A==", "license": "MIT", "dependencies": { - "@radix-ui/react-visually-hidden": "1.2.3" + "@radix-ui/react-visually-hidden": "1.2.11" }, "peerDependencies": { "@types/react": "*", @@ -4080,20 +4214,20 @@ } }, "node_modules/@radix-ui/react-accordion": { - "version": "1.2.12", - "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.12.tgz", - "integrity": "sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA==", + "version": "1.2.20", + "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.20.tgz", + "integrity": "sha512-jDhG9FvAEnlhnjrsINbNXcUa4G+L1KqSkJSunkbKEzFRcAb52jvM0PjPxPRvhe1HNc5F5yc0yzzWeeqlH4yBIg==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-collapsible": "1.1.12", - "@radix-ui/react-collection": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-controllable-state": "1.2.2" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-collapsible": "1.1.20", + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-controllable-state": "1.2.6" }, "peerDependencies": { "@types/react": "*", @@ -4111,17 +4245,16 @@ } }, "node_modules/@radix-ui/react-alert-dialog": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.15.tgz", - "integrity": "sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw==", + "version": "1.1.23", + "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.23.tgz", + "integrity": "sha512-VAYOiQRqj3GPpYJE0I9J+X8Ip05cyVlNdKOFeiGS2Ou1HHGfpl0BxOyZm6nmVDyU+W+NF3/XLzmjHmVGydhwgA==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dialog": "1.1.15", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-dialog": "1.1.23", + "@radix-ui/react-primitive": "2.1.10" }, "peerDependencies": { "@types/react": "*", @@ -4139,12 +4272,12 @@ } }, "node_modules/@radix-ui/react-arrow": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", - "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.15.tgz", + "integrity": "sha512-v4zggRcjadnI+ClKDuijlQEW4tw3NoaeHc/PwpKnLoLLKNUG4InLegkstooLcRIUWCs+8L22dGURCVuFfOKfnA==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.1.3" + "@radix-ui/react-primitive": "2.1.10" }, "peerDependencies": { "@types/react": "*", @@ -4162,12 +4295,12 @@ } }, "node_modules/@radix-ui/react-aspect-ratio": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-aspect-ratio/-/react-aspect-ratio-1.1.7.tgz", - "integrity": "sha512-Yq6lvO9HQyPwev1onK1daHCHqXVLzPhSVjmsNjCa2Zcxy2f7uJD2itDtxknv6FzAKCwD1qQkeVDmX/cev13n/g==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-aspect-ratio/-/react-aspect-ratio-1.1.15.tgz", + "integrity": "sha512-fy+dyVR+90nelK8rqIznFlxzx7uPcGbhxH8Nfr2bHb4UfSe+e3hklOC0luK0hDwVwnRX7xTRySpsrQVeW+/oNQ==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.1.3" + "@radix-ui/react-primitive": "2.1.10" }, "peerDependencies": { "@types/react": "*", @@ -4185,16 +4318,17 @@ } }, "node_modules/@radix-ui/react-avatar": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.10.tgz", - "integrity": "sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.2.6.tgz", + "integrity": "sha512-4ULOTJ/mqy2hT9GlWa/MFHxHSvH3nJzHnZM1waNsc5Bonv7i70aNenghXmD97S6OJ81ekXONGGt4nT1r0PfEdA==", "license": "MIT", "dependencies": { - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-is-hydrated": "0.1.0", - "@radix-ui/react-use-layout-effect": "1.1.1" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-is-hydrated": "0.1.3", + "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -4212,19 +4346,18 @@ } }, "node_modules/@radix-ui/react-checkbox": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.3.tgz", - "integrity": "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==", + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.11.tgz", + "integrity": "sha512-Gnptr9pDDQxD3hgq2dtPbtrp/c2qH1mBwIzw3X/ivrMb2e1t0jMTi606fVEqFPaQR1ggXIVQWKj3P2WW9v7zGQ==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-use-previous": "1.1.1", - "@radix-ui/react-use-size": "1.1.1" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-size": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -4242,19 +4375,19 @@ } }, "node_modules/@radix-ui/react-collapsible": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.12.tgz", - "integrity": "sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==", + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.20.tgz", + "integrity": "sha512-mcGesGplBnzN2sbvJETzpCNfSMyPnb29q1GRLU+Ib7bJrpIG2ywmRoh2V5VbA2uNvKikKUlVbAPks7JDjz4A8Q==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-use-layout-effect": "1.1.1" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -4272,15 +4405,15 @@ } }, "node_modules/@radix-ui/react-collection": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", - "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.15.tgz", + "integrity": "sha512-9W+B9NPF0NaaPh/1NJd3+KqsnlLqU9H7T2rvww+fp+T/evVXdNAyYcnfRQZFOjkR1ajQp3yORlqnI8soawLvNA==", "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3" + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-slot": "1.3.3" }, "peerDependencies": { "@types/react": "*", @@ -4298,9 +4431,9 @@ } }, "node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", - "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.5.tgz", + "integrity": "sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -4313,9 +4446,9 @@ } }, "node_modules/@radix-ui/react-context": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", - "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.2.2.tgz", + "integrity": "sha512-RHCUGwKHDr0hDGg4X7ma4JG4/+12qxw8rkh5QKdDldlCvtja6nUx1Ef/8HVrJze81lEsgLQlqjzjGNHantgnQA==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -4328,17 +4461,16 @@ } }, "node_modules/@radix-ui/react-context-menu": { - "version": "2.2.16", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context-menu/-/react-context-menu-2.2.16.tgz", - "integrity": "sha512-O8morBEW+HsVG28gYDZPTrT9UUovQUlJue5YO836tiTJhuIWBm/zQHc7j388sHWtdH/xUZurK9olD2+pcqx5ww==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context-menu/-/react-context-menu-2.3.7.tgz", + "integrity": "sha512-CtXP35dxaB5T3zXSd+E3uHe/QpXcpYnZmxp6OaIbfthtfW4wyb77M23BG+bwIJDtsMwEP/YssdsmNyZu7jhWew==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-menu": "2.1.16", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-controllable-state": "1.2.2" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-menu": "2.1.24", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-controllable-state": "1.2.6" }, "peerDependencies": { "@types/react": "*", @@ -4356,25 +4488,26 @@ } }, "node_modules/@radix-ui/react-dialog": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", - "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-focus-guards": "1.1.3", - "@radix-ui/react-focus-scope": "1.1.7", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-use-controllable-state": "1.2.2", + "version": "1.1.23", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.23.tgz", + "integrity": "sha512-Ksw4WeROkO4rC9k/onilX/Ao2Cr1ku1unMNH+XSCcP4jSXYu7HDsg9n4ojMjVb22XpYjAQ9qfrFlVbru1vXDUA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-focus-guards": "1.1.6", + "@radix-ui/react-focus-scope": "1.1.16", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-slot": "1.3.3", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-layout-effect": "1.1.4", "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" + "react-remove-scroll": "^2.7.2" }, "peerDependencies": { "@types/react": "*", @@ -4392,9 +4525,9 @@ } }, "node_modules/@radix-ui/react-direction": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", - "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.4.tgz", + "integrity": "sha512-5pzg4FGQNpExhnhT2zlrP1wZFaYCd1K0nYWoFAdcYoYK868IEigqMX3B3f8yIoRlAhAeDWciLI6ZdCKHF9P4Vg==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -4407,16 +4540,16 @@ } }, "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", - "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.19.tgz", + "integrity": "sha512-8g4pfOL9HoKKLWGiypT+dphVqjFfmcXO5GBnhsG6zI+lxAx/8feQpr+1LSN8Re3hiZ+XkLNS4O9ztK11/LzQ6w==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-escape-keydown": "1.1.1" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-effect-event": "0.0.5" }, "peerDependencies": { "@types/react": "*", @@ -4434,18 +4567,18 @@ } }, "node_modules/@radix-ui/react-dropdown-menu": { - "version": "2.1.16", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.16.tgz", - "integrity": "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==", + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.24.tgz", + "integrity": "sha512-geq8l2rJkxvkXsT9RMgtUE3P8pITFpTsvYpbySi1IH4fZEABD/Gp85myayFgxk0ktljGMJnCbeFkyTusvSvv7g==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-menu": "2.1.16", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-controllable-state": "1.2.2" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-menu": "2.1.24", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-controllable-state": "1.2.6" }, "peerDependencies": { "@types/react": "*", @@ -4463,9 +4596,9 @@ } }, "node_modules/@radix-ui/react-focus-guards": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", - "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.6.tgz", + "integrity": "sha512-RNOJjfZMTyBM6xYmV3IVGXkPjIhcBAuv48POevAXwrGJhkWZ9p1rFoIS1JFooPuT193AZmRsCPhpoVJxx6OPoQ==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -4478,14 +4611,14 @@ } }, "node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", - "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.16.tgz", + "integrity": "sha512-wmRZ2WWLvmt6KHy2rNPOdPUjwq5xOHY02+m+udwJTn0aNIox/rkskAvJTyTLGhPK6KgrUjlJUJpgmx/+wFiFIQ==", "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1" + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -4503,17 +4636,17 @@ } }, "node_modules/@radix-ui/react-form": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-form/-/react-form-0.1.8.tgz", - "integrity": "sha512-QM70k4Zwjttifr5a4sZFts9fn8FzHYvQ5PiB19O2HsYibaHSVt9fH9rzB0XZo/YcM+b7t/p7lYCT/F5eOeF5yQ==", + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-form/-/react-form-0.1.16.tgz", + "integrity": "sha512-Q4TLEn2A7TAypxwmd6R9EwrlXDvkfYSDMrq9/887AXAGh+G1rH+kYJKSTv+Si9Y0JPKTwKYv6PviAJosysNimA==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-label": "2.1.7", - "@radix-ui/react-primitive": "2.1.3" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-label": "2.1.15", + "@radix-ui/react-primitive": "2.1.10" }, "peerDependencies": { "@types/react": "*", @@ -4531,20 +4664,20 @@ } }, "node_modules/@radix-ui/react-hover-card": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.1.15.tgz", - "integrity": "sha512-qgTkjNT1CfKMoP0rcasmlH2r1DAiYicWsDsufxl940sT2wHNEWWv6FMWIQXWhVdmC1d/HYfbhQx60KYyAtKxjg==", + "version": "1.1.23", + "resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.1.23.tgz", + "integrity": "sha512-H8qONfZd3ltrU3+jHCIgITbWo6e1iTKvP9DHdrvYbX48ooRM5FjEDTn16AMwdfuOGkWdZEhpl3PLL/Wk/AnHDQ==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-popper": "1.2.8", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-controllable-state": "1.2.2" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-popper": "1.3.7", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-controllable-state": "1.2.6" }, "peerDependencies": { "@types/react": "*", @@ -4571,12 +4704,12 @@ } }, "node_modules/@radix-ui/react-id": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", - "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.4.tgz", + "integrity": "sha512-TMQp2llA+RYn7JcjnrMnz7wN4pcVttPZnRZo52PLQsoLVKzNlVwUeHmfePgTgRluXFvlD3GD5g5MOVVTJCO0qA==", "license": "MIT", "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" + "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -4589,12 +4722,12 @@ } }, "node_modules/@radix-ui/react-label": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.7.tgz", - "integrity": "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==", + "version": "2.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.15.tgz", + "integrity": "sha512-o/rdYEwZTTo5tjknnPeyQFU45kUC4i/XyeDPP+HGyi6XqpOP6Zf5Ya5vh/Yfe9Id5JiuWnnAx2XqIeD3UYZt0g==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.1.3" + "@radix-ui/react-primitive": "2.1.10" }, "peerDependencies": { "@types/react": "*", @@ -4612,29 +4745,29 @@ } }, "node_modules/@radix-ui/react-menu": { - "version": "2.1.16", - "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.16.tgz", - "integrity": "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-collection": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-focus-guards": "1.1.3", - "@radix-ui/react-focus-scope": "1.1.7", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.8", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-roving-focus": "1.1.11", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-use-callback-ref": "1.1.1", + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.24.tgz", + "integrity": "sha512-uW7RVuU6Lp/ZtfeY4b3kL32zccgEWvPv1+cf17ubYzHa9cL8AHokmk36cG/XEiH/smbQvumnieXX9j/e9RqJWA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-focus-guards": "1.1.6", + "@radix-ui/react-focus-scope": "1.1.16", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-popper": "1.3.7", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-roving-focus": "1.1.19", + "@radix-ui/react-slot": "1.3.3", + "@radix-ui/react-use-callback-ref": "1.1.4", "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" + "react-remove-scroll": "^2.7.2" }, "peerDependencies": { "@types/react": "*", @@ -4652,21 +4785,21 @@ } }, "node_modules/@radix-ui/react-menubar": { - "version": "1.1.16", - "resolved": "https://registry.npmjs.org/@radix-ui/react-menubar/-/react-menubar-1.1.16.tgz", - "integrity": "sha512-EB1FktTz5xRRi2Er974AUQZWg2yVBb1yjip38/lgwtCVRd3a+maUoGHN/xs9Yv8SY8QwbSEb+YrxGadVWbEutA==", + "version": "1.1.24", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menubar/-/react-menubar-1.1.24.tgz", + "integrity": "sha512-eeVs0vf7cuqXaM0qLQCPcufImiJNVBXdJDLu7ZGYl2732UH23Qat/foNGrr6vYV3/DdTsBqASoggUFgH14OcZA==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-collection": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-menu": "2.1.16", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-roving-focus": "1.1.11", - "@radix-ui/react-use-controllable-state": "1.2.2" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-menu": "2.1.24", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-roving-focus": "1.1.19", + "@radix-ui/react-use-controllable-state": "1.2.6" }, "peerDependencies": { "@types/react": "*", @@ -4684,25 +4817,25 @@ } }, "node_modules/@radix-ui/react-navigation-menu": { - "version": "1.2.14", - "resolved": "https://registry.npmjs.org/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.2.14.tgz", - "integrity": "sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-collection": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-use-layout-effect": "1.1.1", - "@radix-ui/react-use-previous": "1.1.1", - "@radix-ui/react-visually-hidden": "1.2.3" + "version": "1.2.22", + "resolved": "https://registry.npmjs.org/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.2.22.tgz", + "integrity": "sha512-ou7iLEJ+yrhQndkkA4U21XIdS/CS45F4iXIkTZcb6/Ne9EMsOuDudVmCwmDnfFZZ+y1FZqXRNSIgBy+YMvZVZg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-layout-effect": "1.1.4", + "@radix-ui/react-use-previous": "1.1.4", + "@radix-ui/react-visually-hidden": "1.2.11" }, "peerDependencies": { "@types/react": "*", @@ -4720,23 +4853,23 @@ } }, "node_modules/@radix-ui/react-one-time-password-field": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-one-time-password-field/-/react-one-time-password-field-0.1.8.tgz", - "integrity": "sha512-ycS4rbwURavDPVjCb5iS3aG4lURFDILi6sKI/WITUMZ13gMmn/xGjpLoqBAalhJaDk8I3UbCM5GzKHrnzwHbvg==", + "version": "0.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-one-time-password-field/-/react-one-time-password-field-0.1.16.tgz", + "integrity": "sha512-Tj9P6ntAJEw52oq/F0AGknXR4XncxEt7XU47O3xJQOiWfLzEy3d9gtgKfvjSzGxzHkfL+VzvxGu2KTFsloJqXw==", "license": "MIT", "dependencies": { - "@radix-ui/number": "1.1.1", - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-collection": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-roving-focus": "1.1.11", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-use-effect-event": "0.0.2", - "@radix-ui/react-use-is-hydrated": "0.1.0", - "@radix-ui/react-use-layout-effect": "1.1.1" + "@radix-ui/number": "1.1.3", + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-roving-focus": "1.1.19", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-effect-event": "0.0.5", + "@radix-ui/react-use-is-hydrated": "0.1.3", + "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -4754,19 +4887,19 @@ } }, "node_modules/@radix-ui/react-password-toggle-field": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-password-toggle-field/-/react-password-toggle-field-0.1.3.tgz", - "integrity": "sha512-/UuCrDBWravcaMix4TdT+qlNdVwOM1Nck9kWx/vafXsdfj1ChfhOdfi3cy9SGBpWgTXwYCuboT/oYpJy3clqfw==", + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-password-toggle-field/-/react-password-toggle-field-0.1.11.tgz", + "integrity": "sha512-4gvFnmDXu3dgj21CqsufzIameRvlRd4SBqaWhcrlrNhRo0Y5i/49AmRJYe1fdAM3G2VNBbmin4b0D6cdQocwgw==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-use-effect-event": "0.0.2", - "@radix-ui/react-use-is-hydrated": "0.1.0" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-effect-event": "0.0.5", + "@radix-ui/react-use-is-hydrated": "0.1.3" }, "peerDependencies": { "@types/react": "*", @@ -4784,26 +4917,26 @@ } }, "node_modules/@radix-ui/react-popover": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.15.tgz", - "integrity": "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-focus-guards": "1.1.3", - "@radix-ui/react-focus-scope": "1.1.7", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.8", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-use-controllable-state": "1.2.2", + "version": "1.1.23", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.23.tgz", + "integrity": "sha512-mw58MrBlyHWFisTOYignD0vf/3gdcgAR+9of1s9G/38CbFiUwH1nCDkc0AUM9IrXFgN5Ue8n45j9WCgyM1sbiQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-focus-guards": "1.1.6", + "@radix-ui/react-focus-scope": "1.1.16", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-popper": "1.3.7", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-slot": "1.3.3", + "@radix-ui/react-use-controllable-state": "1.2.6", "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" + "react-remove-scroll": "^2.7.2" }, "peerDependencies": { "@types/react": "*", @@ -4821,21 +4954,21 @@ } }, "node_modules/@radix-ui/react-popper": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", - "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.3.7.tgz", + "integrity": "sha512-UsJrrd7w4wuKKTdvd/DNERVlwSlUcyXzjhyDwBk+3aPOsCjOY6ZSbxuw8E6lZTjjfP8Cpd0J8VVkrYUWyGYXyg==", "license": "MIT", "dependencies": { "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-layout-effect": "1.1.1", - "@radix-ui/react-use-rect": "1.1.1", - "@radix-ui/react-use-size": "1.1.1", - "@radix-ui/rect": "1.1.1" + "@radix-ui/react-arrow": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-layout-effect": "1.1.4", + "@radix-ui/react-use-rect": "1.1.4", + "@radix-ui/react-use-size": "1.1.4", + "@radix-ui/rect": "1.1.3" }, "peerDependencies": { "@types/react": "*", @@ -4853,13 +4986,13 @@ } }, "node_modules/@radix-ui/react-portal": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", - "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", + "version": "1.1.17", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.17.tgz", + "integrity": "sha512-vKQLcWypUnwZVvfV7UkGahH2g6ySe8M8R+zYBwPrv5byZ9QAW6cQVvNKo7GgmD+p8aYb6D9JBuvy8/WhOno2wQ==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-layout-effect": "1.1.1" + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -4877,13 +5010,12 @@ } }, "node_modules/@radix-ui/react-presence": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", - "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.10.tgz", + "integrity": "sha512-3wyzCQ6+ubRA+D4uv9m95JYLXxmOHp05qjrkjeA7uKHHtjpPggQzc6DAb0URl7j67oR0K2foO4ip27TiX037Bw==", "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-use-layout-effect": "1.1.1" + "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -4901,12 +5033,12 @@ } }, "node_modules/@radix-ui/react-primitive": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", - "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.10.tgz", + "integrity": "sha512-MucOnzh6hR5mid6VpkbglRAMYMjKLqRnGBbjXkzjK52fuQDd1qbkx78a5P40mkcnVXJdEVxm26E9OPAiUq7nBg==", "license": "MIT", "dependencies": { - "@radix-ui/react-slot": "1.2.3" + "@radix-ui/react-slot": "1.3.3" }, "peerDependencies": { "@types/react": "*", @@ -4924,13 +5056,13 @@ } }, "node_modules/@radix-ui/react-progress": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.7.tgz", - "integrity": "sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.16.tgz", + "integrity": "sha512-5XnomAsoZZCY+KNTxbIghpGqPruZvKFNlvcAljVAOdDRDsH4/OZQxhtwo5wdtoDM5R6MhJBb2sPnDuRFep3lzg==", "license": "MIT", "dependencies": { - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.3" + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-primitive": "2.1.10" }, "peerDependencies": { "@types/react": "*", @@ -4948,21 +5080,20 @@ } }, "node_modules/@radix-ui/react-radio-group": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.3.8.tgz", - "integrity": "sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ==", + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.4.7.tgz", + "integrity": "sha512-cgYFEkntCxppHZgtSZ+7vh0wbZQ+IC7PPMw8DSnRG27B6kDd32/Zw0OJt7dGDigCoprMuWHjg2PvUn3PYvPFoQ==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-roving-focus": "1.1.11", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-use-previous": "1.1.1", - "@radix-ui/react-use-size": "1.1.1" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-roving-focus": "1.1.19", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-size": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -4980,20 +5111,22 @@ } }, "node_modules/@radix-ui/react-roving-focus": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", - "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.19.tgz", + "integrity": "sha512-V9jI6hDjT7l3jsCQD9bLNvDLM3tH/gdbOTp7Tefp3hbbgCGQoK7tUvrWiRlcoBHIZ809ElXwNQwVo0B98LuTXQ==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-collection": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-controllable-state": "1.2.2" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-is-hydrated": "0.1.3", + "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -5011,20 +5144,20 @@ } }, "node_modules/@radix-ui/react-scroll-area": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.10.tgz", - "integrity": "sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==", + "version": "1.2.18", + "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.18.tgz", + "integrity": "sha512-Zn5Cd171wxsO3Dfg8HaW6RifTb9CYTKQJHs/G4+LN1GfmJpaQMZQyQxMprVPHpaz7QY4l9BxK2JwQuzHsXC8nA==", "license": "MIT", "dependencies": { - "@radix-ui/number": "1.1.1", - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-layout-effect": "1.1.1" + "@radix-ui/number": "1.1.3", + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -5042,32 +5175,33 @@ } }, "node_modules/@radix-ui/react-select": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz", - "integrity": "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/number": "1.1.1", - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-collection": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-focus-guards": "1.1.3", - "@radix-ui/react-focus-scope": "1.1.7", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.8", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-use-layout-effect": "1.1.1", - "@radix-ui/react-use-previous": "1.1.1", - "@radix-ui/react-visually-hidden": "1.2.3", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.3.7.tgz", + "integrity": "sha512-WFGImkmbzcfxeIwq/+4HvRN0pizBwbwQUED4I13ezQsDdfl38ZntN6TmR8XaSzPBqoCToe8rF75j6NPNDSzhbg==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.3", + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-focus-guards": "1.1.6", + "@radix-ui/react-focus-scope": "1.1.16", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-popper": "1.3.7", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-slot": "1.3.3", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-layout-effect": "1.1.4", + "@radix-ui/react-use-previous": "1.1.4", + "@radix-ui/react-visually-hidden": "1.2.11", "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.6.3" + "react-remove-scroll": "^2.7.2" }, "peerDependencies": { "@types/react": "*", @@ -5085,12 +5219,12 @@ } }, "node_modules/@radix-ui/react-separator": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.7.tgz", - "integrity": "sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.15.tgz", + "integrity": "sha512-jOLO4lssEzWpoDu7G+Ze4VjwMRUBt291pnZD0gmalREZipnTX3wadQo7Fy48GCTfe14/YRN6rw/rOJqrE85Wxw==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.1.3" + "@radix-ui/react-primitive": "2.1.10" }, "peerDependencies": { "@types/react": "*", @@ -5108,22 +5242,22 @@ } }, "node_modules/@radix-ui/react-slider": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.3.6.tgz", - "integrity": "sha512-JPYb1GuM1bxfjMRlNLE+BcmBC8onfCi60Blk7OBqi2MLTFdS+8401U4uFjnwkOr49BLmXxLC6JHkvAsx5OJvHw==", + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.4.7.tgz", + "integrity": "sha512-mTSLf1GC/C0moWjTbvCM6Qn/gBjvlFt1azuWF2v7MN5C3Zq2U2J2lN3ZEYkpujuOU5Ro7A28wkviSxaKnG0BYg==", "license": "MIT", "dependencies": { - "@radix-ui/number": "1.1.1", - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-collection": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-use-layout-effect": "1.1.1", - "@radix-ui/react-use-previous": "1.1.1", - "@radix-ui/react-use-size": "1.1.1" + "@radix-ui/number": "1.1.3", + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-layout-effect": "1.1.4", + "@radix-ui/react-use-previous": "1.1.4", + "@radix-ui/react-use-size": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -5141,12 +5275,12 @@ } }, "node_modules/@radix-ui/react-slot": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", - "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.3.3.tgz", + "integrity": "sha512-qx7oqnYbxnK9kYI9m317qmFmEgo6ywqWvbTogdj7cL9p3/yx4M48p7Rnw5z3H890cL/ow/EeWJsuTykeZVXP5Q==", "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" + "@radix-ui/react-compose-refs": "1.1.5" }, "peerDependencies": { "@types/react": "*", @@ -5159,18 +5293,17 @@ } }, "node_modules/@radix-ui/react-switch": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.2.6.tgz", - "integrity": "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.3.7.tgz", + "integrity": "sha512-48tB/4dn2UVLBCYhTu9AuR63IHl73l/qLbLgxd86noTUor4/K4LFDAcYjK+isP5313qxaFpjPVogE7+Y0/V3Kw==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-use-previous": "1.1.1", - "@radix-ui/react-use-size": "1.1.1" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-size": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -5188,19 +5321,19 @@ } }, "node_modules/@radix-ui/react-tabs": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz", - "integrity": "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==", + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.21.tgz", + "integrity": "sha512-UKxJlZid7FVtsk/WTxj4i4uSEgj2Au+KBbS7SQyTlzMhhn+86Cz3tISZdTa87bfEfcuvZezf2ZsxD4xuEKtkog==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-roving-focus": "1.1.11", - "@radix-ui/react-use-controllable-state": "1.2.2" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-roving-focus": "1.1.19", + "@radix-ui/react-use-controllable-state": "1.2.6" }, "peerDependencies": { "@types/react": "*", @@ -5218,23 +5351,23 @@ } }, "node_modules/@radix-ui/react-toast": { - "version": "1.2.15", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.15.tgz", - "integrity": "sha512-3OSz3TacUWy4WtOXV38DggwxoqJK4+eDkNMl5Z/MJZaoUPaP4/9lf81xXMe1I2ReTAptverZUpbPY4wWwWyL5g==", + "version": "1.2.23", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.23.tgz", + "integrity": "sha512-ofhyAsYaocRGOs/n0XWdUOSVzEAG6BfrMVM8z0c0kLEWY38w/0WuMFPTJP/HVaZPYkMvHZoKIIhNcjbTCBILPg==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-collection": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-use-layout-effect": "1.1.1", - "@radix-ui/react-visually-hidden": "1.2.3" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-layout-effect": "1.1.4", + "@radix-ui/react-visually-hidden": "1.2.11" }, "peerDependencies": { "@types/react": "*", @@ -5252,14 +5385,14 @@ } }, "node_modules/@radix-ui/react-toggle": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.10.tgz", - "integrity": "sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.18.tgz", + "integrity": "sha512-7lonPlKfSacd20GlOBx2ltuVKz9oqWYZz+oMQyOltw6t1y2nyftj2ZmwwUHYn49kqfDWcp8dNZm5NgV+5Z+mug==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-use-controllable-state": "1.2.2" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-controllable-state": "1.2.6" }, "peerDependencies": { "@types/react": "*", @@ -5277,18 +5410,18 @@ } }, "node_modules/@radix-ui/react-toggle-group": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.1.11.tgz", - "integrity": "sha512-5umnS0T8JQzQT6HbPyO7Hh9dgd82NmS36DQr+X/YJ9ctFNCiiQd6IJAYYZ33LUwm8M+taCz5t2ui29fHZc4Y6Q==", + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.1.19.tgz", + "integrity": "sha512-OtnwuSVjd1Ofi+AdnvhsjQdyuhCDwYs1w9RyB5BN/OavXOVQo42SYqQjwUnbPnaiPFBpQ9aX70dWeee+v2oBLA==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-roving-focus": "1.1.11", - "@radix-ui/react-toggle": "1.1.10", - "@radix-ui/react-use-controllable-state": "1.2.2" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-roving-focus": "1.1.19", + "@radix-ui/react-toggle": "1.1.18", + "@radix-ui/react-use-controllable-state": "1.2.6" }, "peerDependencies": { "@types/react": "*", @@ -5306,18 +5439,18 @@ } }, "node_modules/@radix-ui/react-toolbar": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toolbar/-/react-toolbar-1.1.11.tgz", - "integrity": "sha512-4ol06/1bLoFu1nwUqzdD4Y5RZ9oDdKeiHIsntug54Hcr1pgaHiPqHFEaXI1IFP/EsOfROQZ8Mig9VTIRza6Tjg==", + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toolbar/-/react-toolbar-1.1.19.tgz", + "integrity": "sha512-Ph0IvtYw4VB12ZnZg+YtrGs8yJQsnizwo/zu0R4Y/nWugtJzA7Pg1eWeuDR9+LSqn+xjamss+UOSOJJJ4gx8jw==", "license": "MIT", "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-roving-focus": "1.1.11", - "@radix-ui/react-separator": "1.1.7", - "@radix-ui/react-toggle-group": "1.1.11" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-roving-focus": "1.1.19", + "@radix-ui/react-separator": "1.1.15", + "@radix-ui/react-toggle-group": "1.1.19" }, "peerDependencies": { "@types/react": "*", @@ -5335,23 +5468,24 @@ } }, "node_modules/@radix-ui/react-tooltip": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz", - "integrity": "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.8", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-visually-hidden": "1.2.3" + "version": "1.2.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.16.tgz", + "integrity": "sha512-6EamKFRRnlpdadndbZ6LMwycfwkwPte1B42hs6QA0gYhjaOKqW4PZ4pjaW9UrlDX5eVt/OjncE7BFTPL5nmZhg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-popper": "1.3.7", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-slot": "1.3.3", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-layout-effect": "1.1.4", + "@radix-ui/react-visually-hidden": "1.2.11" }, "peerDependencies": { "@types/react": "*", @@ -5369,9 +5503,9 @@ } }, "node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", - "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.4.tgz", + "integrity": "sha512-R6OUY2e2fA6Yn6s+VSx5KBV6Nx8LQEhu+cz7LCej18rQ1HLyg9PSC9jP/ZNx0o6FAIK9c0F1kHylzSxKsdlkrQ==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -5384,13 +5518,14 @@ } }, "node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", - "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.6.tgz", + "integrity": "sha512-uEQJGT97ZA/TgP/Hydw47lHu+/vQj6z/0jA+WeTbK1o9Rx45GImjpD0tc3W5ad3D6XTSR6e1yEO0FvGq6WQfVQ==", "license": "MIT", "dependencies": { - "@radix-ui/react-use-effect-event": "0.0.2", - "@radix-ui/react-use-layout-effect": "1.1.1" + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-use-effect-event": "0.0.5", + "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -5403,12 +5538,12 @@ } }, "node_modules/@radix-ui/react-use-effect-event": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", - "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.5.tgz", + "integrity": "sha512-7cshFL8HGS/7HEiHH+9kL9HBwp2sa9yX18Knwek6KYWmXwM7pegMgta2AXMQKI+rq3JnfSj9x8wYqFMTdG1Jgg==", "license": "MIT", "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" + "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -5421,12 +5556,12 @@ } }, "node_modules/@radix-ui/react-use-escape-keydown": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", - "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.5.tgz", + "integrity": "sha512-ge3ipobwSXTj4JyVtswQ7qZj0ZHdtbGuOno/LrgAAeSxtsJ6Vs4Gz5IkPH2bmqpjcLUFoqGhA/mueuIf63UXlA==", "license": "MIT", "dependencies": { - "@radix-ui/react-use-callback-ref": "1.1.1" + "@radix-ui/react-use-callback-ref": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -5439,13 +5574,10 @@ } }, "node_modules/@radix-ui/react-use-is-hydrated": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.0.tgz", - "integrity": "sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==", + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.3.tgz", + "integrity": "sha512-umO/aJ+82CpOnhDZUTbILCQf7kU/g0iv+oGs/Q8jw7IkhWBzaEP4sA268PhFAJTFetbwp3ICc6ktpI4TqtxcIw==", "license": "MIT", - "dependencies": { - "use-sync-external-store": "^1.5.0" - }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -5457,9 +5589,9 @@ } }, "node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", - "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.4.tgz", + "integrity": "sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -5472,9 +5604,9 @@ } }, "node_modules/@radix-ui/react-use-previous": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", - "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.4.tgz", + "integrity": "sha512-XoSLhbRbqxFtgJoi2fNHA3C6pDlY34x508vUpUGoFZfvePfHXHbE1lC4FYFMnJWgiCRroSTw6fOsXQoVS9RwZg==", "license": "MIT", "peerDependencies": { "@types/react": "*", @@ -5487,12 +5619,12 @@ } }, "node_modules/@radix-ui/react-use-rect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", - "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.4.tgz", + "integrity": "sha512-cSOCh6JlkmfjLyNcLiu2nB4v+nm+dkZ+Q5KHWk/soo4U7ZLiEQFKHK9/YmtBHjfCEaU43IBKQOc4/uJmCaiCTQ==", "license": "MIT", "dependencies": { - "@radix-ui/rect": "1.1.1" + "@radix-ui/rect": "1.1.3" }, "peerDependencies": { "@types/react": "*", @@ -5505,12 +5637,12 @@ } }, "node_modules/@radix-ui/react-use-size": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", - "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.4.tgz", + "integrity": "sha512-D3anSY15EJoxrihpsXI6SMrmmonnQtR2ni7arO+Lfdg3O95b9hNXxONk8jA5C8ANdF/h5HMAxejgs8PWJ6rlhw==", "license": "MIT", "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.1" + "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", @@ -5523,12 +5655,12 @@ } }, "node_modules/@radix-ui/react-visually-hidden": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz", - "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==", + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.11.tgz", + "integrity": "sha512-NFS86RYYZb4/exihaESBGOpMJFz8MGLAfu3mOBSGByVnVPC9JPASfYubxd/8KbkQK0sYAv8lVQDEQukDX/qXvQ==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.1.3" + "@radix-ui/react-primitive": "2.1.10" }, "peerDependencies": { "@types/react": "*", @@ -5546,9 +5678,9 @@ } }, "node_modules/@radix-ui/rect": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", - "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.3.tgz", + "integrity": "sha512-JtyZR+mqgBibTo8xea3B6ZRmzZiM/YeVBtUkas6zMuXjAlfIFIW2FgqeM9eLyvEaYX66vr6DJMK+4U6LV0KhNw==", "license": "MIT" }, "node_modules/@radix-ui/themes": { @@ -5595,9 +5727,9 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", - "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==", + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.64.0.tgz", + "integrity": "sha512-FLLpX941CD/7aBvX3GqLGBM9OvY5PGXTryPJkG5r5/OuBpZpV5k45Uo3RxAvqd35tsyc3hH1bhtz921IKzoIzg==", "cpu": [ "arm" ], @@ -5609,9 +5741,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz", - "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==", + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.64.0.tgz", + "integrity": "sha512-iI32XM/jnDYJsvx5ai/za/7gWO9XoSeFFtU06/ZCaRgyilfJOjaopD3nOpk2WrTCGOyeDt4hleUiGOAnd5tHNg==", "cpu": [ "arm64" ], @@ -5623,9 +5755,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz", - "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==", + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.64.0.tgz", + "integrity": "sha512-dRBZlbt2sEtF8Mn4MODThze9fIPdDoOhVSlXMAKy5dQbOYK9pOOoPY4Fat3OTtr0+8zbJo5TTZwlsfxY/kDNJg==", "cpu": [ "arm64" ], @@ -5637,9 +5769,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz", - "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==", + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.64.0.tgz", + "integrity": "sha512-Y3whrJOMucfkhgNkvcM93M6j8C53iEAMmPMYD99C6G1kUy4ELigLWk9ar2/4aCBSRgsql0zdlOemLgW2m5GZgQ==", "cpu": [ "x64" ], @@ -5651,9 +5783,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz", - "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==", + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.64.0.tgz", + "integrity": "sha512-S6S1GEzPjFF6IONYJX3AKt/799+8RgBgCcwRnUo+Ep1IvBxUbw5M51iU0TDdK2NWWVnSvCQ8yi6Em0fGASMOng==", "cpu": [ "arm64" ], @@ -5665,9 +5797,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz", - "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==", + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.64.0.tgz", + "integrity": "sha512-Ed1DPoFENkXj4IGzSB12SesV40iP6LxCCrbTIkSGIQBpOOxpBM+0kj62lmaeIHfMWTDoddqTOtqc7s8LYfn+Pw==", "cpu": [ "x64" ], @@ -5679,13 +5811,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz", - "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==", + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.64.0.tgz", + "integrity": "sha512-J/6xhutJPtFyIkmHdf4olOdHHKH5kOJNumojdWn+Elv5ICEYTVOa362Vw27TxsK0BLzb1le3EDlO3UbvFK1lnQ==", "cpu": [ "arm" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5693,13 +5828,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz", - "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==", + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.64.0.tgz", + "integrity": "sha512-G4L/Nnx59d2LvDoV/P4S5lZ7oVLT6hodOHMacqjiq2LL2gts86KbkEBTYL53+fBt0odhf7XPIbIATGoYiJctxg==", "cpu": [ "arm" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -5707,13 +5845,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz", - "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==", + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.64.0.tgz", + "integrity": "sha512-zF16pvJDEUksGYRbfnXne7pdklAyj2XDmJeYKgP93oq0cUyX9Y8O7lUo1R+IY1GagP/YBvCEDCe7eEcAKpw//g==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5721,13 +5862,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz", - "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==", + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.64.0.tgz", + "integrity": "sha512-nIggWn8n+w12+R5lsV+PYYoUY8vuObxPAQqJI7WES+9v0qspYuAHoZMfYM0yLxlipasT5BY9ht5W8t5c/rSCkg==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -5735,13 +5879,16 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz", - "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==", + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.64.0.tgz", + "integrity": "sha512-WLOTiHPzszP0dxIMsHpFeDSOSB5I49Mj8K4+yKE3H1X/D0JRG5XZ5jrctJLMjnUc/LUzaKiwq6EHWNJ8pup8PA==", "cpu": [ "loong64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5749,13 +5896,16 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz", - "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==", + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.64.0.tgz", + "integrity": "sha512-35RK9aRhh7g7Pmeu5gQ01aCvrhXzj9LoBE6xtUcEH1ns+nyTaSzxSpADmBQINu4QiTDGvmQYwo1j5hlg1rRXFw==", "cpu": [ "loong64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -5763,13 +5913,16 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz", - "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==", + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.64.0.tgz", + "integrity": "sha512-RqTfnpoTjuWfPAmqK5p2edt2AMCDJWm6oTn7sKfmyx+6tRga0ip05SJ977o6udTcIiDeF/CJMUfkaN4gtWt6Aw==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5777,13 +5930,16 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz", - "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==", + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.64.0.tgz", + "integrity": "sha512-RQjZPigjxFvFZDijG6rjlSXxhRaTf9zLYVpGK+lVcga64oMqP1GbgVqCb46eHTTceSIgp89Ja0y5aPe3GFBYHg==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -5791,13 +5947,16 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz", - "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==", + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.64.0.tgz", + "integrity": "sha512-tjL58mgQcTRnDRpeky+Sth1MKQeYi2Ct6r3Luq9E5aTSjnM/i/JbozqZkKqp9thM5+oDSZxo+05f9kdQedfTGA==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5805,13 +5964,16 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz", - "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==", + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.64.0.tgz", + "integrity": "sha512-ictoRyVaoZ8C67myHef+8hhCa6IplXJJ/xUCkVCxziV4zxMZh+jvhkhGsPBLqj1S72fnqD0QH5zL4lcaa4X7DQ==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -5819,13 +5981,16 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz", - "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==", + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.64.0.tgz", + "integrity": "sha512-8aVvZ2hfr9RLFzBQkQNmYeHLDja8fLeQAmLt757zdKN/B+8L809KlLJKctQjS22wZotptV8G6o7fVPuAs8S1tQ==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5833,13 +5998,16 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz", - "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==", + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.64.0.tgz", + "integrity": "sha512-2dEF8GAcDKwshUfydD+GhosppNyBzhVUHkdFF3CXd3FpOkzj/RZzF5aoEtzklujklT5qFVRU2GK/cFeLaTNKVg==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -5847,13 +6015,16 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz", - "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==", + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.64.0.tgz", + "integrity": "sha512-3XHTvoo2hKkh0MiOfS7SkhHjMaaWF7P4F3drocZZaho/Z8w7h/jpnuBHm91BOQLfvrKHs1yR+zir4E8RI3XzTQ==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -5861,9 +6032,9 @@ ] }, "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz", - "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==", + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.64.0.tgz", + "integrity": "sha512-HKtkzodL4a3KFWDXDoO61cRynAWTR3LnAE0oGtfG9lcNFpNuVYUFOByxDgNqEvic9rvaQbAj+SFwTEWDptRaIw==", "cpu": [ "x64" ], @@ -5875,9 +6046,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz", - "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==", + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.64.0.tgz", + "integrity": "sha512-JBsxqiY16uyyCujtoC3s7zL+SrYyNElptD7K4V+KUoJ2V8/XG3hPWpheK+SuPwNh1cikJvIZmIdsrYXvD6PLJw==", "cpu": [ "arm64" ], @@ -5889,9 +6060,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz", - "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==", + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.64.0.tgz", + "integrity": "sha512-S5Qrijh37qhnUYQ0ay8WN3LuWeKjow7wTUyCOPMZtnXiBXYXdsZETI6ZLfYnQAKovltckPLBFdNeD61P2rJc7g==", "cpu": [ "arm64" ], @@ -5903,9 +6074,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz", - "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==", + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.64.0.tgz", + "integrity": "sha512-zbZ6iMmLFEsCtTdc2/y1F/cDFVNNCPF7Ae8PCT/wdPE//hGwYp6r+MyCLMFX+kOeIe09Pz6ASfnnfpSTN1FJQQ==", "cpu": [ "ia32" ], @@ -5917,9 +6088,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz", - "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==", + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.64.0.tgz", + "integrity": "sha512-L6Hw69oNpuYaOr1zCG30v0/DqKYCSRwodPGwIcVZ/6f8SOzq9rxtL/xNVI5MO4wjli+Ko0ErK9uHJL3mNPt8wQ==", "cpu": [ "x64" ], @@ -5931,9 +6102,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz", - "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==", + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.64.0.tgz", + "integrity": "sha512-nl+CTWIgOGszQmUz/kanfgrfFZ4qiwxKIfT/ryl3N3Ewk5tqizmYtNXiqb7Bu/fDtNgu1kyxdFiAUJNur3iysg==", "cpu": [ "x64" ], @@ -6102,9 +6273,9 @@ "license": "MIT" }, "node_modules/@swc/helpers": { - "version": "0.5.15", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", - "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "version": "0.5.23", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.23.tgz", + "integrity": "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.8.0" @@ -6166,9 +6337,9 @@ } }, "node_modules/@tanstack/devtools-event-client": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@tanstack/devtools-event-client/-/devtools-event-client-0.4.3.tgz", - "integrity": "sha512-OZI6QyULw0FI0wjgmeYzCIfbgPsOEzwJtCpa69XrfLMtNXLGnz3d/dIabk7frg0TmHo+Ah49w5I4KC7Tufwsvw==", + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@tanstack/devtools-event-client/-/devtools-event-client-0.4.4.tgz", + "integrity": "sha512-6T5Yop/793YI+H+5J8Hsyj4kCih9sl4t3ElLgKioW5hk3ocn+ZdSJ94tT7vL7uabxSugWYBZlOTMPzEw2puvQw==", "license": "MIT", "bin": { "intent": "bin/intent.js" @@ -6182,14 +6353,14 @@ } }, "node_modules/@tanstack/form-core": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/@tanstack/form-core/-/form-core-1.29.0.tgz", - "integrity": "sha512-uyeKEdJBfbj0bkBSwvSYVRtWLOaXvfNX3CeVw1HqGOXVLxpBBGAqWdYLc+UoX/9xcoFwFXrjR9QqMPzvwm2yyQ==", + "version": "1.33.5", + "resolved": "https://registry.npmjs.org/@tanstack/form-core/-/form-core-1.33.5.tgz", + "integrity": "sha512-3dfx9MBP0aq5sXKteikG629X9oviptrQj0IFRk9YGcb+lB7Kv5x8S17oOSk1wUWgjQZ4xVJEMbKwOAODymocgA==", "license": "MIT", "dependencies": { "@tanstack/devtools-event-client": "^0.4.1", "@tanstack/pacer-lite": "^0.1.1", - "@tanstack/store": "^0.9.1" + "@tanstack/store": "^0.11.0" }, "funding": { "type": "github", @@ -6210,9 +6381,9 @@ } }, "node_modules/@tanstack/query-core": { - "version": "5.99.0", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.99.0.tgz", - "integrity": "sha512-3Jv3WQG0BCcH7G+7lf/bP8QyBfJOXeY+T08Rin3GZ1bshvwlbPt7NrDHMEzGdKIOmOzvIQmxjk28YEQX60k7pQ==", + "version": "5.104.1", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.104.1.tgz", + "integrity": "sha512-TiRghcrGkUTE+M4JBxvpPSpBhxvIzQairw+cksrHYGr+2uwC77yW1SY9nYxnYRYOppvucf8rEg6b5byoz4rTpw==", "license": "MIT", "funding": { "type": "github", @@ -6220,13 +6391,13 @@ } }, "node_modules/@tanstack/react-form": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/@tanstack/react-form/-/react-form-1.29.0.tgz", - "integrity": "sha512-jj425NNX0QKqbUzqSNiYI3HCPHSk2df47acXCJyXczWOTmG81ECZGkgofgqamFsSU9kMiH6Di5RLUnftrlhWSw==", + "version": "1.33.5", + "resolved": "https://registry.npmjs.org/@tanstack/react-form/-/react-form-1.33.5.tgz", + "integrity": "sha512-LlRB28qJwO/QCGaHvWnbdh4haBgTFiZVmzA2uzxSBS3YA7/IqrQ6HOBK70CkFQ+DbflZ7NawsmSln13h5iIdTA==", "license": "MIT", "dependencies": { - "@tanstack/form-core": "1.29.0", - "@tanstack/react-store": "^0.9.1" + "@tanstack/form-core": "1.33.5", + "@tanstack/react-store": "^0.11.0" }, "funding": { "type": "github", @@ -6242,12 +6413,12 @@ } }, "node_modules/@tanstack/react-query": { - "version": "5.99.0", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.99.0.tgz", - "integrity": "sha512-OY2bCqPemT1LlqJ8Y2CUau4KELnIhhG9Ol3ZndPbdnB095pRbPo1cHuXTndg8iIwtoHTgwZjyaDnQ0xD0mYwAw==", + "version": "5.104.1", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.104.1.tgz", + "integrity": "sha512-Zz70EgjahNI7aCz8BZxM3vUGU44ycxEAzMuCUjLeA7SVhhOkg3ZgPjPJPvHjs6Rmky/dtmnu1WzjGHNHmhCoZQ==", "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.99.0" + "@tanstack/query-core": "5.104.1" }, "funding": { "type": "github", @@ -6258,12 +6429,12 @@ } }, "node_modules/@tanstack/react-store": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@tanstack/react-store/-/react-store-0.9.3.tgz", - "integrity": "sha512-y2iHd/N9OkoQbFJLUX1T9vbc2O9tjH0pQRgTcx1/Nz4IlwLvkgpuglXUx+mXt0g5ZDFrEeDnONPqkbfxXJKwRg==", + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/@tanstack/react-store/-/react-store-0.11.2.tgz", + "integrity": "sha512-oj5O5DmiMWE+ih+SioX2ayC1kbK7d3fpLx0B75fPJr9nY3zU+hoW010yr64qXsY/9fyiLVE2bn5mGNV3owm8RQ==", "license": "MIT", "dependencies": { - "@tanstack/store": "0.9.3", + "@tanstack/store": "0.11.2", "use-sync-external-store": "^1.6.0" }, "funding": { @@ -6276,9 +6447,9 @@ } }, "node_modules/@tanstack/store": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/@tanstack/store/-/store-0.9.3.tgz", - "integrity": "sha512-8reSzl/qGWGGVKhBoxXPMWzATSbZLZFWhwBAFO9NAyp0TxzfBP0mIrGb8CP8KrQTmvzXlR/vFPPUrHTLBGyFyw==", + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/@tanstack/store/-/store-0.11.2.tgz", + "integrity": "sha512-sJ4mjol8uQsHV0gOJzzjwXfh2Fwm+Sz0+8deqiTm4jGbMdjzNSW+xZCFm0kUa870uhd8yi+DpKnZb5Kc4apa0Q==", "license": "MIT", "funding": { "type": "github", @@ -6286,9 +6457,9 @@ } }, "node_modules/@testing-library/dom": { - "version": "10.4.1", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", - "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", + "version": "10.4.2", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.2.tgz", + "integrity": "sha512-yzr2S9HyAIdhz2/6qHgbs665Q7PKVcDF05vsOlHPxG1mo36gKVesdYVeDLnXgfjJ03CrKRk08knc6+E/9m8v2Q==", "dev": true, "license": "MIT", "peer": true, @@ -6307,9 +6478,9 @@ } }, "node_modules/@testing-library/react": { - "version": "16.3.2", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.2.tgz", - "integrity": "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==", + "version": "16.3.3", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.3.tgz", + "integrity": "sha512-Uo193NgQbPMz6lrrhtRQQFcMC6Re/ELLFbbuVL30WDlZxlpZf9/lMHTAVxPRLw1q1iu9OJmR1c2BLiENRstdBg==", "dev": true, "license": "MIT", "dependencies": { @@ -6335,9 +6506,9 @@ } }, "node_modules/@turbo/darwin-64": { - "version": "2.9.6", - "resolved": "https://registry.npmjs.org/@turbo/darwin-64/-/darwin-64-2.9.6.tgz", - "integrity": "sha512-X/56SnVXIQZBLKwniGTwEQTGmtE5brSACnKMBWpY3YafuxVYefrC2acamfjgxP7BG5w3I+6jf0UrLoSzgPcSJg==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@turbo/darwin-64/-/darwin-64-2.11.7.tgz", + "integrity": "sha512-Z2LFOxwbiJueD1miByXJS/BrW21vv6YaQXZustty5jEX4vQYw6zyh101Sz1QO7WG0mqBzKagZ/DoAeeujI0/Sw==", "cpu": [ "x64" ], @@ -6349,9 +6520,9 @@ ] }, "node_modules/@turbo/darwin-arm64": { - "version": "2.9.6", - "resolved": "https://registry.npmjs.org/@turbo/darwin-arm64/-/darwin-arm64-2.9.6.tgz", - "integrity": "sha512-aalBeSl4agT/QtYGDyf/XLajedWzUC9Vg/pm/YO6QQ93vkQ91Vz5uK1ta5RbVRDozQSz4njxUNqRNmOXDzW+qw==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@turbo/darwin-arm64/-/darwin-arm64-2.11.7.tgz", + "integrity": "sha512-Vhv8ybgIflosNDSSRD9fNXHlCFA60dsGyTqQmc7l28SEX5jOaweCCD4BBYNd8/p0WGkw3e7tZbfA9w6FsujGvw==", "cpu": [ "arm64" ], @@ -6363,9 +6534,9 @@ ] }, "node_modules/@turbo/linux-64": { - "version": "2.9.6", - "resolved": "https://registry.npmjs.org/@turbo/linux-64/-/linux-64-2.9.6.tgz", - "integrity": "sha512-YKi05jnNHaD7vevgYwahpzGwbsNNTwzU2c7VZdmdFm7+cGDP4oREUWSsainiMfRqjRuolQxBwRn8wf1jmu+YZA==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@turbo/linux-64/-/linux-64-2.11.7.tgz", + "integrity": "sha512-rAqRp3blQKk1+ylMGlZHAW0eJjVNuuGUm4ZrC6RU1Im8tLryvaO4a2zLQ/FQi1TRlmKqKV9YXBki2YeytlLbXA==", "cpu": [ "x64" ], @@ -6373,13 +6544,14 @@ "license": "MIT", "optional": true, "os": [ + "android", "linux" ] }, "node_modules/@turbo/linux-arm64": { - "version": "2.9.6", - "resolved": "https://registry.npmjs.org/@turbo/linux-arm64/-/linux-arm64-2.9.6.tgz", - "integrity": "sha512-02o/ZS69cOYEDczXvOB2xmyrtzjQ2hVFtWZK1iqxXUfzMmTjZK4UumrfNnjckSg+gqeBfnPRHa0NstA173Ik3g==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@turbo/linux-arm64/-/linux-arm64-2.11.7.tgz", + "integrity": "sha512-thVYGov7S+llXAxrl3kBb/efalav0xhrjl5NHCR6AfKx14JkbqaetvbVwyss4FvZ9rt+Pv3xsTb+8SIYix8DTg==", "cpu": [ "arm64" ], @@ -6387,13 +6559,14 @@ "license": "MIT", "optional": true, "os": [ + "android", "linux" ] }, "node_modules/@turbo/windows-64": { - "version": "2.9.6", - "resolved": "https://registry.npmjs.org/@turbo/windows-64/-/windows-64-2.9.6.tgz", - "integrity": "sha512-wVdQjvnBI15wB6JrA+43CtUtagjIMmX6XYO758oZHAsCNSxqRlJtdyujih0D8OCnwCRWiGWGI63zAxR0hO6s9g==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@turbo/windows-64/-/windows-64-2.11.7.tgz", + "integrity": "sha512-ZIoKT7J7KutgYl4bGxmmgeUQjG+7E0nY8BF9dxUOiMH2vdTsM4JQ2uSwTYFkTR2Llbg6ghGjSmuZ8LQtkLEYjg==", "cpu": [ "x64" ], @@ -6405,9 +6578,9 @@ ] }, "node_modules/@turbo/windows-arm64": { - "version": "2.9.6", - "resolved": "https://registry.npmjs.org/@turbo/windows-arm64/-/windows-arm64-2.9.6.tgz", - "integrity": "sha512-1XUUyWW0W6FTSqGEhU8RHVqb2wP1SPkr7hIvBlMEwH9jr+sJQK5kqeosLJ/QaUv4ecSAd1ZhIrLoW7qslAzT4A==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@turbo/windows-arm64/-/windows-arm64-2.11.7.tgz", + "integrity": "sha512-88Xv2f7SLGVJ68lRRx8o375dKmnZYNdDtvlKcPA0kZpcF93eAQd8l96BKQ0/AMHG0bMJA0Z5QqoqtetxzNsC+g==", "cpu": [ "arm64" ], @@ -6500,16 +6673,16 @@ "license": "MIT" }, "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "dev": true, "license": "MIT" }, "node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz", + "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==", "dev": true, "license": "MIT", "dependencies": { @@ -6517,9 +6690,9 @@ } }, "node_modules/@types/node": { - "version": "22.19.17", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.17.tgz", - "integrity": "sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==", + "version": "22.20.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.5.tgz", + "integrity": "sha512-U2+DNr+wSjpsTS/wZGYHq7GcwfuSmKiKvoPvK22zwTlRhU91yOniN4qRR5KhIjvif7ysw/dz/hKmfDH0Ris4aA==", "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -6532,9 +6705,9 @@ "license": "MIT" }, "node_modules/@types/pg": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.20.0.tgz", - "integrity": "sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow==", + "version": "8.23.1", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.23.1.tgz", + "integrity": "sha512-fKVHpikPdg4GKks3JuLEhvwSyvwzF23hnabPy6DD8ljVbC7+6J5dQzdv4arV6jqq57djnMgs1HKBxX4P8aBI3A==", "dev": true, "license": "MIT", "dependencies": { @@ -6544,9 +6717,9 @@ } }, "node_modules/@types/react": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", - "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.3.0.tgz", + "integrity": "sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==", "devOptional": true, "license": "MIT", "dependencies": { @@ -6554,13 +6727,13 @@ } }, "node_modules/@types/react-dom": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", - "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.3.0.tgz", + "integrity": "sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q==", "devOptional": true, "license": "MIT", "peerDependencies": { - "@types/react": "^19.2.0" + "@types/react": "^19.3.0" } }, "node_modules/@types/trusted-types": { @@ -6579,9 +6752,9 @@ "license": "MIT" }, "node_modules/@types/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.2.tgz", + "integrity": "sha512-67MQl+fpWKVTT1NYdnmo3U4sc/xPo/zQBncVnI74qmQa0z/b+1g6iYqNmGCPbxO+zz2aklb08a0oHfegiVd0/w==", "dev": true, "license": "MIT", "dependencies": { @@ -6610,14 +6783,14 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.4.tgz", - "integrity": "sha512-x7FptB5oDruxNPDNY2+S8tCh0pcq7ymCe1gTHcsp733jYjrJl8V1gMUlVysuCD9Kz46Xz9t1akkv08dPcYDs1w==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.11.tgz", + "integrity": "sha512-8MVGEFnJIcdGjcbfKmeq8z0pZHH0JlVtoVZH9Q/qwUp6wyFnEJUBMrw9DCaj+ra3vShGmhavjalMIhPNxZAUcw==", "dev": true, "license": "MIT", "dependencies": { "@bcoe/v8-coverage": "^1.0.2", - "@vitest/utils": "4.1.4", + "@vitest/utils": "4.1.11", "ast-v8-to-istanbul": "^1.0.0", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", @@ -6631,8 +6804,8 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "4.1.4", - "vitest": "4.1.4" + "@vitest/browser": "4.1.11", + "vitest": "4.1.11" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -6641,16 +6814,16 @@ } }, "node_modules/@vitest/expect": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.4.tgz", - "integrity": "sha512-iPBpra+VDuXmBFI3FMKHSFXp3Gx5HfmSCE8X67Dn+bwephCnQCaB7qWK2ldHa+8ncN8hJU8VTMcxjPpyMkUjww==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.11.tgz", + "integrity": "sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.4", - "@vitest/utils": "4.1.4", + "@vitest/spy": "4.1.11", + "@vitest/utils": "4.1.11", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" }, @@ -6659,13 +6832,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.4.tgz", - "integrity": "sha512-R9HTZBhW6yCSGbGQnDnH3QHfJxokKN4KB+Yvk9Q1le7eQNYwiCyKxmLmurSpFy6BzJanSLuEUDrD+j97Q+ZLPg==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.11.tgz", + "integrity": "sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.4", + "@vitest/spy": "4.1.11", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -6686,9 +6859,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.4.tgz", - "integrity": "sha512-ddmDHU0gjEUyEVLxtZa7xamrpIefdEETu3nZjWtHeZX4QxqJ7tRxSteHVXJOcr8jhiLoGAhkK4WJ3WqBpjx42A==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.11.tgz", + "integrity": "sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==", "dev": true, "license": "MIT", "dependencies": { @@ -6699,13 +6872,13 @@ } }, "node_modules/@vitest/runner": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.4.tgz", - "integrity": "sha512-xTp7VZ5aXP5ZJrn15UtJUWlx6qXLnGtF6jNxHepdPHpMfz/aVPx+htHtgcAL2mDXJgKhpoo2e9/hVJsIeFbytQ==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.11.tgz", + "integrity": "sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.4", + "@vitest/utils": "4.1.11", "pathe": "^2.0.3" }, "funding": { @@ -6713,14 +6886,14 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.4.tgz", - "integrity": "sha512-MCjCFgaS8aZz+m5nTcEcgk/xhWv0rEH4Yl53PPlMXOZ1/Ka2VcZU6CJ+MgYCZbcJvzGhQRjVrGQNZqkGPttIKw==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.11.tgz", + "integrity": "sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.4", - "@vitest/utils": "4.1.4", + "@vitest/pretty-format": "4.1.11", + "@vitest/utils": "4.1.11", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -6729,9 +6902,9 @@ } }, "node_modules/@vitest/spy": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.4.tgz", - "integrity": "sha512-XxNdAsKW7C+FLydqFJLb5KhJtl3PGCMmYwFRfhvIgxJvLSXhhVI1zM8f1qD3Zg7RCjTSzDVyct6sghs9UEgBEQ==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.11.tgz", + "integrity": "sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==", "dev": true, "license": "MIT", "funding": { @@ -6739,13 +6912,13 @@ } }, "node_modules/@vitest/utils": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.4.tgz", - "integrity": "sha512-13QMT+eysM5uVGa1rG4kegGYNp6cnQcsTc67ELFbhNLQO+vgsygtYJx2khvdt4gVQqSSpC/KT5FZZxUpP3Oatw==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.11.tgz", + "integrity": "sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.4", + "@vitest/pretty-format": "4.1.11", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, @@ -6754,9 +6927,10 @@ } }, "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", + "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -6765,15 +6939,6 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-import-attributes": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", - "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", - "license": "MIT", - "peerDependencies": { - "acorn": "^8" - } - }, "node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", @@ -6783,16 +6948,6 @@ "node": ">= 14" } }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -6803,14 +6958,15 @@ } }, "node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "license": "MIT", - "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" @@ -6853,16 +7009,6 @@ "dequal": "^2.0.3" } }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/assertion-error": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", @@ -6874,9 +7020,9 @@ } }, "node_modules/ast-v8-to-istanbul": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.0.tgz", - "integrity": "sha512-1fSfIwuDICFA4LKkCzRPO7F0hzFf0B7+Xqrl27ynQaa+Rh0e1Es0v6kWHPott3lU10AyAr7oKHa65OppjLn3Rg==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.7.tgz", + "integrity": "sha512-kFL68AG6ajd8fg248zwM9GQrUWEp79gsmjum34OEXjs4yHuUMZfYKwOLW9GMmB4oNvVrj+EAGxsP7ye2UR9UlA==", "dev": true, "license": "MIT", "dependencies": { @@ -6923,9 +7069,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.10.19", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.19.tgz", - "integrity": "sha512-qCkNLi2sfBOn8XhZQ0FXsT1Ki/Yo5P90hrkRamVFRS7/KV9hpfA4HkoWNU152+8w0zPjnxo5psx5NL3PSGgv5g==", + "version": "2.11.27", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.27.tgz", + "integrity": "sha512-ElY12DaROGuan+lMmZ8Cvo/ZUbXPe7Enc/9VU/b1T3Kp4dwytRcNdR8DoSJN5SNJT/CuvcCA0DHDVmMOCePdRQ==", "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.cjs" @@ -6934,23 +7080,10 @@ "node": ">=6.0.0" } }, - "node_modules/better-path-resolve": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/better-path-resolve/-/better-path-resolve-1.0.0.tgz", - "integrity": "sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-windows": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/bidi-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", - "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.1.0.tgz", + "integrity": "sha512-fX1Onk0tdVPC7obPWB5EbJ1z7NVhLq4m2xZLq2YXBkxzMXIGRpNMU88n0EPgWseKl12J7zXs7qrDxPK4sRs2fg==", "dev": true, "license": "MIT", "dependencies": { @@ -6973,35 +7106,22 @@ "license": "MIT" }, "node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" + "node": "20 || >=22" } }, "node_modules/browserslist": { - "version": "4.28.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", - "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "version": "4.29.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.29.3.tgz", + "integrity": "sha512-1R4kiYKXGViqEN0CnoDrXc1StD9niAwu+j2dukWzrD4bJgsD4lDmEp0CRbc6E/vYJIfTHwPmwyaKtVSudICdPA==", "dev": true, "funding": [ { @@ -7019,11 +7139,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.10.12", - "caniuse-lite": "^1.0.30001782", - "electron-to-chromium": "^1.5.328", - "node-releases": "^2.0.36", - "update-browserslist-db": "^1.2.3" + "baseline-browser-mapping": "^2.11.26", + "caniuse-lite": "^1.0.30001813", + "electron-to-chromium": "^1.5.439", + "node-releases": "^2.0.57", + "update-browserslist-db": "^1.3.3" }, "bin": { "browserslist": "cli.js" @@ -7065,19 +7185,19 @@ } }, "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cac/-/cac-7.0.0.tgz", + "integrity": "sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=20.19.0" } }, "node_modules/caniuse-lite": { - "version": "1.0.30001788", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001788.tgz", - "integrity": "sha512-6q8HFp+lOQtcf7wBK+uEenxymVWkGKkjFpCvw5W25cmMwEDU45p1xQFBQv8JDlMMry7eNxyBaR+qxgmTUZkIRQ==", + "version": "1.0.30001814", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001814.tgz", + "integrity": "sha512-/Uaf1lAzr59XcMpW0o96WoEfr+VXK2OX4U9AgFoiSHsVJ4HppnIFUjtYzsyDH2+tgANaQb2/oxYGwCPapN1FpA==", "funding": [ { "type": "opencollective", @@ -7095,9 +7215,9 @@ "license": "CC-BY-4.0" }, "node_modules/chai": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", - "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.3.0.tgz", + "integrity": "sha512-XWAtwJ6OHO+tj0EKCs0Y2UamnyOxseZWltU4x2U2wh8g4AigdjwvtUjvLP2tqkA/avxHEtzxNaqGq/YGNwckKg==", "dev": true, "license": "MIT", "engines": { @@ -7121,29 +7241,19 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chalk/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "has-flag": "^4.0.0" }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/chardet": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", - "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", - "dev": true, - "license": "MIT" - }, "node_modules/chokidar": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", @@ -7161,9 +7271,9 @@ } }, "node_modules/cjs-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", - "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.1.tgz", + "integrity": "sha512-Ca8swihM+/4yKecYHY52kgJd300hi2lADU/a1RxNTRe+RJ9jvqQlESpbz9DnG9mowez8qwXHB8qYdIUw9e+F5Q==", "license": "MIT" }, "node_modules/classnames": { @@ -7217,25 +7327,24 @@ "license": "MIT" }, "node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", "license": "MIT", "engines": { - "node": ">= 6" + "node": ">=14" } }, "node_modules/concurrently": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz", - "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==", + "version": "9.2.4", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.4.tgz", + "integrity": "sha512-TZ0CEhyzvFjgtAvHTusDMgj7wNdihCh7LLLrzdUOXIhdlnL2JBBGA9eJxR24rtqgmdjh3OA3hrN1rCHj6HM8qA==", "dev": true, "license": "MIT", "dependencies": { "chalk": "4.1.2", "rxjs": "7.8.2", - "shell-quote": "1.8.3", + "shell-quote": "1.9.0", "supports-color": "8.1.1", "tree-kill": "1.2.2", "yargs": "17.7.2" @@ -7251,20 +7360,23 @@ "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" } }, - "node_modules/concurrently/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/concurrently/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "node": ">=12" } }, "node_modules/confbox": { @@ -7304,21 +7416,6 @@ "url": "https://opencollective.com/express" } }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/css-tree": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", @@ -7355,12 +7452,12 @@ } }, "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "license": "MIT", "dependencies": { - "ms": "^2.1.3" + "ms": "2.1.2" }, "engines": { "node": ">=6.0" @@ -7389,16 +7486,6 @@ "node": ">=6" } }, - "node_modules/detect-indent": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", - "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -7415,19 +7502,6 @@ "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", "license": "MIT" }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/dom-accessibility-api": { "version": "0.5.16", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", @@ -7437,9 +7511,9 @@ "peer": true }, "node_modules/dompurify": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.7.tgz", - "integrity": "sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==", + "version": "3.4.15", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.15.tgz", + "integrity": "sha512-EUBjM+B+lkDE41iE82DDSCfkoPGfXx8IxFxPMjNzm/Uk4xDet77rTN9wqlxlVg71kK7XGuUMv6wUxJUwwv+Xyw==", "license": "(MPL-2.0 OR Apache-2.0)", "peer": true, "optionalDependencies": { @@ -7456,9 +7530,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.340", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.340.tgz", - "integrity": "sha512-908qahOGocRMinT2nM3ajCEM99H4iPdv84eagPP3FfZy/1ZGeOy2CZYzjhms81ckOPCXPlW7LkY4XpxD8r1DrA==", + "version": "1.5.444", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.444.tgz", + "integrity": "sha512-5ss/uJfoDYDHT0lfJzT6FbcskIzROIOPf0BbbFkGcvDzoJU7i//9GDrwwIHQVmIsrAGiF3ihpADBRIsrEFt1rQ==", "dev": true, "license": "ISC" }, @@ -7468,28 +7542,14 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "license": "MIT" }, - "node_modules/enquirer": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", - "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-colors": "^4.1.1", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/entities": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.1.0.tgz", + "integrity": "sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==", "dev": true, "license": "BSD-2-Clause", "engines": { - "node": ">=0.12" + "node": ">=20.19.0" }, "funding": { "url": "https://github.com/fb55/entities?sponsor=1" @@ -7505,16 +7565,15 @@ } }, "node_modules/es-module-lexer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", - "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", - "dev": true, + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-3.0.2.tgz", + "integrity": "sha512-BuIB67FngDSyQ/dpQNOZybwdEBDUGJQvOqwWr4ha/ufYiqzuEwPkKO2zLhRAgay28tStRIHUeWmszZAJo3GCOg==", "license": "MIT" }, "node_modules/esbuild": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", - "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", "hasInstallScript": true, "license": "MIT", "bin": { @@ -7524,32 +7583,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.7", - "@esbuild/android-arm": "0.27.7", - "@esbuild/android-arm64": "0.27.7", - "@esbuild/android-x64": "0.27.7", - "@esbuild/darwin-arm64": "0.27.7", - "@esbuild/darwin-x64": "0.27.7", - "@esbuild/freebsd-arm64": "0.27.7", - "@esbuild/freebsd-x64": "0.27.7", - "@esbuild/linux-arm": "0.27.7", - "@esbuild/linux-arm64": "0.27.7", - "@esbuild/linux-ia32": "0.27.7", - "@esbuild/linux-loong64": "0.27.7", - "@esbuild/linux-mips64el": "0.27.7", - "@esbuild/linux-ppc64": "0.27.7", - "@esbuild/linux-riscv64": "0.27.7", - "@esbuild/linux-s390x": "0.27.7", - "@esbuild/linux-x64": "0.27.7", - "@esbuild/netbsd-arm64": "0.27.7", - "@esbuild/netbsd-x64": "0.27.7", - "@esbuild/openbsd-arm64": "0.27.7", - "@esbuild/openbsd-x64": "0.27.7", - "@esbuild/openharmony-arm64": "0.27.7", - "@esbuild/sunos-x64": "0.27.7", - "@esbuild/win32-arm64": "0.27.7", - "@esbuild/win32-ia32": "0.27.7", - "@esbuild/win32-x64": "0.27.7" + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" } }, "node_modules/escalade": { @@ -7570,20 +7629,6 @@ "node": ">=6" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/estree-walker": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", @@ -7604,9 +7649,9 @@ } }, "node_modules/expect-type": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", - "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -7619,65 +7664,49 @@ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "license": "MIT" }, - "node_modules/extendable-error": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/extendable-error/-/extendable-error-0.1.7.tgz", - "integrity": "sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==", + "node_modules/fast-string-truncated-width": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", + "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==", "dev": true, "license": "MIT" }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "node_modules/fast-string-width": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", + "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", "dev": true, "license": "MIT", "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fastq": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", - "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", - "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" + "fast-string-truncated-width": "^3.0.2" } }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "node_modules/fast-wrap-ansi": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz", + "integrity": "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==", "dev": true, "license": "MIT", "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" + "fast-string-width": "^3.0.2" } }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, "engines": { - "node": ">=8" + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } } }, "node_modules/fix-dts-default-cjs-exports": { @@ -7698,25 +7727,11 @@ "integrity": "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==", "license": "MIT" }, - "node_modules/fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -7803,58 +7818,12 @@ "node": ">=8.0.0" } }, - "node_modules/get-tsconfig": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.0.tgz", - "integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==", - "license": "MIT", - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, "node_modules/getopts": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/getopts/-/getopts-2.3.0.tgz", "integrity": "sha512-5eDf9fuSXwxBL6q5HX+dhDj+dslFGWzU5thZ9kNKUkcPtaPdatmUFKwHFrLb/uf/WpA4BHET+AX3Scl56cAjpA==", "license": "MIT" }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/google-auth-library": { "version": "9.15.1", "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", @@ -7881,13 +7850,6 @@ "node": ">=14" } }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, "node_modules/gtoken": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", @@ -7912,9 +7874,9 @@ } }, "node_modules/hasown": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", - "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -7957,32 +7919,15 @@ } }, "node_modules/human-id": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/human-id/-/human-id-4.1.3.tgz", - "integrity": "sha512-tsYlhAYpjCKa//8rXZ9DqKEawhPoSytweBC2eNvcaDK+57RZLHGqNs3PZTQO6yekLFSuvA6AlnAfrw1uBvtb+Q==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/human-id/-/human-id-4.2.1.tgz", + "integrity": "sha512-zPGsiS+dWoTZtZ4AtpA9Y+BdSFSNWvnouNlWNoUFyAM6xHOHmdCvqO3k8AIbdamCOv4gUFUVNPf6rJFfc4UiJw==", "dev": true, "license": "MIT", "bin": { "human-id": "dist/cli.js" } }, - "node_modules/iconv-lite": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", - "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -8003,31 +7948,31 @@ ], "license": "BSD-3-Clause" }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, "node_modules/import-in-the-middle": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-3.0.1.tgz", - "integrity": "sha512-pYkiyXVL2Mf3pozdlDGV6NAObxQx13Ae8knZk1UJRJ6uRW/ZRmTGHlQYtrsSl7ubuE5F8CD1z+s1n4RHNuTtuA==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-3.5.2.tgz", + "integrity": "sha512-WseIA/4o56+GYO3RmlOPvejYi771OFeJEeK/ejROsr9bdjW+E5NXTOD1Di8TL40Qgg3JJX9aa97Hf8iiSVIKZw==", "license": "Apache-2.0", "dependencies": { - "acorn": "^8.15.0", - "acorn-import-attributes": "^1.9.5", "cjs-module-lexer": "^2.2.0", + "es-module-lexer": "^3.0.2", "module-details-from-path": "^1.0.4" }, "engines": { "node": ">=18" } }, + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -8044,12 +7989,12 @@ } }, "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.17.0.tgz", + "integrity": "sha512-J/vG0zBCbIKOQFfufSwyXdMrsohyJIUNkrnmo6WZGzoM7tr/lsbfW5b2BvisL6zsyMzK9UxV9L6c7AoFbyXHOA==", "license": "MIT", "dependencies": { - "hasown": "^2.0.2" + "hasown": "^2.0.4" }, "engines": { "node": ">= 0.4" @@ -8058,16 +8003,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -8077,29 +8012,6 @@ "node": ">=8" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, "node_modules/is-potential-custom-element-name": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", @@ -8119,36 +8031,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-subdir": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-subdir/-/is-subdir-1.2.0.tgz", - "integrity": "sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==", - "dev": true, - "license": "MIT", - "dependencies": { - "better-path-resolve": "1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -8174,9 +8056,22 @@ "node": ">=10" } }, - "node_modules/istanbul-reports": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", "dev": true, "license": "BSD-3-Clause", @@ -8188,10 +8083,17 @@ "node": ">=8" } }, + "node_modules/jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", + "dev": true, + "license": "MIT" + }, "node_modules/joi": { - "version": "17.13.3", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", - "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "version": "17.13.8", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.8.tgz", + "integrity": "sha512-iPKOGmiRw1jxf/JOPwxmCcUQAOdF359mdzYiP2DJ+TMX0YK2zjK3D+zYOaGjpumWxOFF/l2xVWjRVK5bGSLdEw==", "license": "BSD-3-Clause", "dependencies": { "@hapi/hoek": "^9.3.0", @@ -8218,42 +8120,29 @@ "dev": true, "license": "MIT" }, - "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/jsdom": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.0.2.tgz", - "integrity": "sha512-9VnGEBosc/ZpwyOsJBCQ/3I5p7Q5ngOY14a9bf5btenAORmZfDse1ZEheMiWcJ3h81+Fv7HmJFdS0szo/waF2w==", + "version": "29.1.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.1.1.tgz", + "integrity": "sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==", "dev": true, "license": "MIT", "dependencies": { - "@asamuzakjp/css-color": "^5.1.5", - "@asamuzakjp/dom-selector": "^7.0.6", + "@asamuzakjp/css-color": "^5.1.11", + "@asamuzakjp/dom-selector": "^7.1.1", "@bramus/specificity": "^2.4.2", - "@csstools/css-syntax-patches-for-csstree": "^1.1.1", + "@csstools/css-syntax-patches-for-csstree": "^1.1.3", "@exodus/bytes": "^1.15.0", "css-tree": "^3.2.1", "data-urls": "^7.0.0", "decimal.js": "^10.6.0", "html-encoding-sniffer": "^6.0.0", "is-potential-custom-element-name": "^1.0.1", - "lru-cache": "^11.2.7", - "parse5": "^8.0.0", + "lru-cache": "^11.3.5", + "parse5": "^8.0.1", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", "tough-cookie": "^6.0.1", - "undici": "^7.24.5", + "undici": "^7.25.0", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^8.0.1", "whatwg-mimetype": "^5.0.0", @@ -8307,15 +8196,12 @@ "node": ">=6" } }, - "node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", "dev": true, - "license": "MIT", - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } + "license": "MIT" }, "node_modules/jwa": { "version": "2.0.1", @@ -8339,9 +8225,9 @@ } }, "node_modules/knex": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/knex/-/knex-3.2.9.tgz", - "integrity": "sha512-dtAILTjBMaG8YloP5oBxohDIKyIsdQ/TkcVvSjhsksvsjeH63Y0PADyuMDfNZKbVT3Rlx3vEYVBlecbPT/KerA==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/knex/-/knex-3.3.0.tgz", + "integrity": "sha512-LgWl031hNuLv9Lhxdd9093zULa2aNcoP04Bk29e5NidgRcEr/T0rAr2WYi4BhjTiCDoQiRU56meUE5rppLKZzQ==", "license": "MIT", "dependencies": { "colorette": "2.0.19", @@ -8352,11 +8238,11 @@ "get-package-type": "^0.1.0", "getopts": "2.3.0", "interpret": "^2.2.0", - "lodash": "^4.17.21", + "lodash": "^4.18.1", "pg-connection-string": "2.6.2", "rechoir": "^0.8.0", "resolve-from": "^5.0.0", - "tarn": "^3.0.2", + "tarn": "^3.1.0", "tildify": "2.0.0" }, "bin": { @@ -8372,6 +8258,9 @@ "better-sqlite3": { "optional": true }, + "mariadb": { + "optional": true + }, "mysql": { "optional": true }, @@ -8395,38 +8284,17 @@ } } }, - "node_modules/knex/node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "license": "MIT", - "engines": { - "node": ">=14" - } - }, - "node_modules/knex/node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "node_modules/launch-editor": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.14.1.tgz", + "integrity": "sha512-QWBrQsMpH7gPr965dsKD/3cKWiNoTjpATQf++Xq63N6sKRGMwlVXz41O1IZTMfZQgBctD/K5Zt06+/I6pP6+HA==", + "dev": true, "license": "MIT", "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "picocolors": "^1.1.1", + "shell-quote": "^1.8.4" } }, - "node_modules/knex/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "license": "MIT" - }, "node_modules/lilconfig": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", @@ -8448,10 +8316,20 @@ "license": "MIT" }, "node_modules/linkify-it": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.2.tgz", + "integrity": "sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], "license": "MIT", "dependencies": { "uc.micro": "^2.0.0" @@ -8467,19 +8345,6 @@ "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/lodash": { "version": "4.18.1", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", @@ -8492,13 +8357,6 @@ "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", "license": "MIT" }, - "node_modules/lodash.startcase": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", - "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", - "dev": true, - "license": "MIT" - }, "node_modules/long": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", @@ -8506,9 +8364,9 @@ "license": "Apache-2.0" }, "node_modules/lru-cache": { - "version": "11.3.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.5.tgz", - "integrity": "sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==", + "version": "11.5.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.3.tgz", + "integrity": "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==", "dev": true, "license": "BlueOak-1.0.0", "engines": { @@ -8544,14 +8402,14 @@ } }, "node_modules/magicast": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.2.tgz", - "integrity": "sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.5.tgz", + "integrity": "sha512-UicdXN8zQ3JHlxVq+28afMXPr1z7WNY6+7EJnzTdQWkTAlMLF5fNCCKxJHBQwGaNGR11581EiQmQzx73+MvszA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", "source-map-js": "^1.2.1" } }, @@ -8572,15 +8430,25 @@ } }, "node_modules/markdown-it": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz", - "integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==", + "version": "14.3.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.3.2.tgz", + "integrity": "sha512-sHHjZ5fJKlgrG4qns2YwVcdNep35h5fERrfkD2YNsb9UFk0UIHarbiTaHKVMlPuWAoiilyK8Fv/jAm11slsY7Q==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1", - "entities": "^4.4.0", - "linkify-it": "^5.0.0", + "entities": "^4.5.0", + "linkify-it": "^5.0.2", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", "uc.micro": "^2.1.0" @@ -8623,44 +8491,20 @@ "license": "CC0-1.0" }, "node_modules/mdurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", - "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.1.0.tgz", + "integrity": "sha512-1+HBaOx0zi/dQWht8rNv9MYf9qqpqL/kxI0hXImU6Y547zM6Sni8BQibt7ifgMcYtQg41ao3Ivd6cnSM86inpg==", "dev": true, "license": "MIT" }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.5" + "brace-expansion": "^5.0.8" }, "engines": { "node": "18 || 20 || >=22" @@ -8689,30 +8533,20 @@ "license": "MIT" }, "node_modules/monaco-editor": { - "version": "0.55.1", - "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.55.1.tgz", - "integrity": "sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==", + "version": "0.57.0", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.57.0.tgz", + "integrity": "sha512-5BkI9KGoqrNvBGUe15/QlZq3OooZ8WLg1AxTpaqHRCP3HNpzPPZKE2EDz8M7c+VRmCeUw1Brp4cx/PWm3kI/5A==", "license": "MIT", "peer": true, "dependencies": { - "dompurify": "3.2.7", + "dompurify": "3.4.15", "marked": "14.0.0" } }, - "node_modules/mri": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", - "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "license": "MIT" }, "node_modules/mz": { @@ -8728,9 +8562,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.19", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz", + "integrity": "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==", "funding": [ { "type": "github", @@ -8746,16 +8580,16 @@ } }, "node_modules/next": { - "version": "16.2.4", - "resolved": "https://registry.npmjs.org/next/-/next-16.2.4.tgz", - "integrity": "sha512-kPvz56wF5frc+FxlHI5qnklCzbq53HTwORaWBGdT0vNoKh1Aya9XC8aPauH4NJxqtzbWsS5mAbctm4cr+EkQ2Q==", + "version": "16.3.8", + "resolved": "https://registry.npmjs.org/next/-/next-16.3.8.tgz", + "integrity": "sha512-U7QEZaTini6wKrb8A8hqLLqYQyCetegKjCpJOyxk642vWoMoU1x5PyZCJFvgYgiptA8xc5j/9xYlZFO7w9Sjmw==", "license": "MIT", "dependencies": { - "@next/env": "16.2.4", - "@swc/helpers": "0.5.15", + "@next/env": "16.3.8", + "@swc/helpers": "0.5.23", "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", - "postcss": "8.4.31", + "postcss": "8.5.23", "styled-jsx": "5.1.6" }, "bin": { @@ -8765,15 +8599,15 @@ "node": ">=20.9.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "16.2.4", - "@next/swc-darwin-x64": "16.2.4", - "@next/swc-linux-arm64-gnu": "16.2.4", - "@next/swc-linux-arm64-musl": "16.2.4", - "@next/swc-linux-x64-gnu": "16.2.4", - "@next/swc-linux-x64-musl": "16.2.4", - "@next/swc-win32-arm64-msvc": "16.2.4", - "@next/swc-win32-x64-msvc": "16.2.4", - "sharp": "^0.34.5" + "@next/swc-darwin-arm64": "16.3.8", + "@next/swc-darwin-x64": "16.3.8", + "@next/swc-linux-arm64-gnu": "16.3.8", + "@next/swc-linux-arm64-musl": "16.3.8", + "@next/swc-linux-x64-gnu": "16.3.8", + "@next/swc-linux-x64-musl": "16.3.8", + "@next/swc-win32-arm64-msvc": "16.3.8", + "@next/swc-win32-x64-msvc": "16.3.8", + "sharp": "^0.35.4" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -8841,11 +8675,14 @@ } }, "node_modules/node-releases": { - "version": "2.0.37", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz", - "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==", + "version": "2.0.57", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.57.tgz", + "integrity": "sha512-kQK9LGGFiHtrWiNhZtA7Qbw17AQz+dmsEKODRIVTXA9+e5MS/2gZEBhYJt13GrAz5/IOZKddH/0Z3TP/Zgo+yw==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/object-assign": { "version": "4.1.1", @@ -8858,144 +8695,45 @@ } }, "node_modules/obug": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", - "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.2.1.tgz", + "integrity": "sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==", "dev": true, "funding": [ "https://github.com/sponsors/sxzz", "https://opencollective.com/debug" ], - "license": "MIT" - }, - "node_modules/outdent": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/outdent/-/outdent-0.5.0.tgz", - "integrity": "sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/p-filter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-2.1.0.tgz", - "integrity": "sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-map": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, "license": "MIT", "engines": { - "node": ">=6" + "node": ">=12.20.0" } }, "node_modules/package-manager-detector": { - "version": "0.2.11", - "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.11.tgz", - "integrity": "sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.8.0.tgz", + "integrity": "sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==", "dev": true, - "license": "MIT", - "dependencies": { - "quansync": "^0.2.7" - } + "license": "MIT" }, "node_modules/parse5": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", - "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", + "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==", "dev": true, "license": "MIT", "dependencies": { - "entities": "^6.0.0" + "entities": "^8.0.0" }, "funding": { "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "license": "MIT" }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/pathe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", @@ -9014,14 +8752,14 @@ } }, "node_modules/pg": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.20.0.tgz", - "integrity": "sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==", + "version": "8.23.1", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.23.1.tgz", + "integrity": "sha512-aL96AHANtWjPLDOLqnhx+ngp9+UK7ETEU8VJrDCGvsSSi/mGLcWYsS6Herg7lmaBJe4uwrfqsa7gTEFaSizDoQ==", "license": "MIT", "dependencies": { - "pg-connection-string": "^2.12.0", - "pg-pool": "^3.13.0", - "pg-protocol": "^1.13.0", + "pg-connection-string": "^2.14.1", + "pg-pool": "^3.14.0", + "pg-protocol": "^1.16.1", "pg-types": "2.2.0", "pgpass": "1.0.5" }, @@ -9029,7 +8767,7 @@ "node": ">= 16.0.0" }, "optionalDependencies": { - "pg-cloudflare": "^1.3.0" + "pg-cloudflare": "^1.4.1" }, "peerDependencies": { "pg-native": ">=3.0.1" @@ -9041,9 +8779,9 @@ } }, "node_modules/pg-cloudflare": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.3.0.tgz", - "integrity": "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.4.1.tgz", + "integrity": "sha512-6PQbsFWZcp9EmJEwy5cGQ2La+AMWpP46lgbb8X+U/XsHIUweYDNCpeuKck5RxL2MdVFi7krbbEi5nX4Zh7JhrQ==", "license": "MIT", "optional": true }, @@ -9063,18 +8801,18 @@ } }, "node_modules/pg-pool": { - "version": "3.13.0", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.13.0.tgz", - "integrity": "sha512-gB+R+Xud1gLFuRD/QgOIgGOBE2KCQPaPwkzBBGC9oG69pHTkhQeIuejVIk3/cnDyX39av2AxomQiyPT13WKHQA==", + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.14.0.tgz", + "integrity": "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==", "license": "MIT", "peerDependencies": { "pg": ">=8.0" } }, "node_modules/pg-protocol": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.13.0.tgz", - "integrity": "sha512-zzdvXfS6v89r6v7OcFCHfHlyG/wvry1ALxZo4LqgUoy7W9xhBDMaqOuMiF3qEV45VqsN6rdlcehHrfDtlCPc8w==", + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.16.1.tgz", + "integrity": "sha512-p9VOFMiHB/ZbJATetbg+99PxssTVSQRnyuPSQ67mN1+1KBOjZaZ83ZQzltnxPhJwSsC3nwVjJ10DVJlerbFzLg==", "license": "MIT" }, "node_modules/pg-types": { @@ -9094,9 +8832,9 @@ } }, "node_modules/pg/node_modules/pg-connection-string": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.12.0.tgz", - "integrity": "sha512-U7qg+bpswf3Cs5xLzRqbXbQl85ng0mfSV/J0nnA31MCLgvEaAo7CIhmeyrmJpOr7o+zm0rXK+hNnT5l9RHkCkQ==", + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.14.1.tgz", + "integrity": "sha512-qR3kGNPBLpCNtz0evbKA0Y/MRFXwSSdT+pTJvYp/bXTcReZbvX1kzF0IyTc1QnxqF7AZbOeBhNL8R5mYQZV/MA==", "license": "MIT" }, "node_modules/pgpass": { @@ -9115,28 +8853,18 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", - "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "dev": true, "license": "MIT", "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/pirates": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", @@ -9191,25 +8919,10 @@ "node": ">=18" } }, - "node_modules/playwright/node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "version": "8.5.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz", + "integrity": "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==", "funding": [ { "type": "opencollective", @@ -9226,9 +8939,9 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "nanoid": "^3.3.16", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -9316,22 +9029,6 @@ "node": ">=0.10.0" } }, - "node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, "node_modules/pretty-format": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", @@ -9348,6 +9045,20 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/property-expr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz", @@ -9355,24 +9066,23 @@ "license": "MIT" }, "node_modules/protobufjs": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-8.0.1.tgz", - "integrity": "sha512-NWWCCscLjs+cOKF/s/XVNFRW7Yih0fdH+9brffR5NZCy8k42yRdl5KlWKMVXuI1vfCoy4o1z80XR/W/QUb3V3w==", + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.6.tgz", + "integrity": "sha512-dYDWdjSl5RNb7SgPxGQcRU+GtvP7s2fpkrY0r432PcOIaZ0/rBcxEZnQN67iJhFuQiVw754JDoPruPCNdGsbjg==", "hasInstallScript": true, "license": "BSD-3-Clause", "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/codegen": "^2.0.5", + "@protobufjs/eventemitter": "^1.1.1", + "@protobufjs/fetch": "^1.1.1", "@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", + "@protobufjs/utf8": "^1.1.1", "@types/node": ">=13.7.0", - "long": "^5.0.0" + "long": "^5.3.2" }, "engines": { "node": ">=12.0.0" @@ -9398,105 +9108,67 @@ "node": ">=6" } }, - "node_modules/quansync": { - "version": "0.2.11", - "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz", - "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/antfu" - }, - { - "type": "individual", - "url": "https://github.com/sponsors/sxzz" - } - ], - "license": "MIT" - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/radix-ui": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/radix-ui/-/radix-ui-1.4.3.tgz", - "integrity": "sha512-aWizCQiyeAenIdUbqEpXgRA1ya65P13NKn/W8rWkcN0OPkRDxdBVLWnIEDsS2RpwCK2nobI7oMUSmexzTDyAmA==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.3", - "@radix-ui/react-accessible-icon": "1.1.7", - "@radix-ui/react-accordion": "1.2.12", - "@radix-ui/react-alert-dialog": "1.1.15", - "@radix-ui/react-arrow": "1.1.7", - "@radix-ui/react-aspect-ratio": "1.1.7", - "@radix-ui/react-avatar": "1.1.10", - "@radix-ui/react-checkbox": "1.3.3", - "@radix-ui/react-collapsible": "1.1.12", - "@radix-ui/react-collection": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-context-menu": "2.2.16", - "@radix-ui/react-dialog": "1.1.15", - "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.11", - "@radix-ui/react-dropdown-menu": "2.1.16", - "@radix-ui/react-focus-guards": "1.1.3", - "@radix-ui/react-focus-scope": "1.1.7", - "@radix-ui/react-form": "0.1.8", - "@radix-ui/react-hover-card": "1.1.15", - "@radix-ui/react-label": "2.1.7", - "@radix-ui/react-menu": "2.1.16", - "@radix-ui/react-menubar": "1.1.16", - "@radix-ui/react-navigation-menu": "1.2.14", - "@radix-ui/react-one-time-password-field": "0.1.8", - "@radix-ui/react-password-toggle-field": "0.1.3", - "@radix-ui/react-popover": "1.1.15", - "@radix-ui/react-popper": "1.2.8", - "@radix-ui/react-portal": "1.1.9", - "@radix-ui/react-presence": "1.1.5", - "@radix-ui/react-primitive": "2.1.3", - "@radix-ui/react-progress": "1.1.7", - "@radix-ui/react-radio-group": "1.3.8", - "@radix-ui/react-roving-focus": "1.1.11", - "@radix-ui/react-scroll-area": "1.2.10", - "@radix-ui/react-select": "2.2.6", - "@radix-ui/react-separator": "1.1.7", - "@radix-ui/react-slider": "1.3.6", - "@radix-ui/react-slot": "1.2.3", - "@radix-ui/react-switch": "1.2.6", - "@radix-ui/react-tabs": "1.1.13", - "@radix-ui/react-toast": "1.2.15", - "@radix-ui/react-toggle": "1.1.10", - "@radix-ui/react-toggle-group": "1.1.11", - "@radix-ui/react-toolbar": "1.1.11", - "@radix-ui/react-tooltip": "1.2.8", - "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-use-effect-event": "0.0.2", - "@radix-ui/react-use-escape-keydown": "1.1.1", - "@radix-ui/react-use-is-hydrated": "0.1.0", - "@radix-ui/react-use-layout-effect": "1.1.1", - "@radix-ui/react-use-size": "1.1.1", - "@radix-ui/react-visually-hidden": "1.2.3" + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/radix-ui/-/radix-ui-1.6.7.tgz", + "integrity": "sha512-QBdhh1arIEUvPC0dQ5+nwWAxt7+N+oP/9jPwjJkGFoSk/sqxg32gJtSXGtFh8frAIcS6oC9cx2Q+7KYCQLOAeA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-accessible-icon": "1.1.15", + "@radix-ui/react-accordion": "1.2.20", + "@radix-ui/react-alert-dialog": "1.1.23", + "@radix-ui/react-arrow": "1.1.15", + "@radix-ui/react-aspect-ratio": "1.1.15", + "@radix-ui/react-avatar": "1.2.6", + "@radix-ui/react-checkbox": "1.3.11", + "@radix-ui/react-collapsible": "1.1.20", + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-context-menu": "2.3.7", + "@radix-ui/react-dialog": "1.1.23", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-dropdown-menu": "2.1.24", + "@radix-ui/react-focus-guards": "1.1.6", + "@radix-ui/react-focus-scope": "1.1.16", + "@radix-ui/react-form": "0.1.16", + "@radix-ui/react-hover-card": "1.1.23", + "@radix-ui/react-label": "2.1.15", + "@radix-ui/react-menu": "2.1.24", + "@radix-ui/react-menubar": "1.1.24", + "@radix-ui/react-navigation-menu": "1.2.22", + "@radix-ui/react-one-time-password-field": "0.1.16", + "@radix-ui/react-password-toggle-field": "0.1.11", + "@radix-ui/react-popover": "1.1.23", + "@radix-ui/react-popper": "1.3.7", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-progress": "1.1.16", + "@radix-ui/react-radio-group": "1.4.7", + "@radix-ui/react-roving-focus": "1.1.19", + "@radix-ui/react-scroll-area": "1.2.18", + "@radix-ui/react-select": "2.3.7", + "@radix-ui/react-separator": "1.1.15", + "@radix-ui/react-slider": "1.4.7", + "@radix-ui/react-slot": "1.3.3", + "@radix-ui/react-switch": "1.3.7", + "@radix-ui/react-tabs": "1.1.21", + "@radix-ui/react-toast": "1.2.23", + "@radix-ui/react-toggle": "1.1.18", + "@radix-ui/react-toggle-group": "1.1.19", + "@radix-ui/react-toolbar": "1.1.19", + "@radix-ui/react-tooltip": "1.2.16", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-effect-event": "0.0.5", + "@radix-ui/react-use-escape-keydown": "1.1.5", + "@radix-ui/react-use-is-hydrated": "0.1.3", + "@radix-ui/react-use-layout-effect": "1.1.4", + "@radix-ui/react-use-size": "1.1.4", + "@radix-ui/react-visually-hidden": "1.2.11" }, "peerDependencies": { "@types/react": "*", @@ -9514,24 +9186,24 @@ } }, "node_modules/react": { - "version": "19.2.5", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.5.tgz", - "integrity": "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==", + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.3.0.tgz", + "integrity": "sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "19.2.5", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.5.tgz", - "integrity": "sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==", + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.3.0.tgz", + "integrity": "sha512-JDk8dgif51OjFoDE70+OT9ICyYr+69HlmihNwp1+Nsfbna3t5sIiCa9ZJktDmQ4/1b/rn26hIAR2uYXDMr5r0Q==", "license": "MIT", "dependencies": { - "scheduler": "^0.27.0" + "scheduler": "^0.28.0" }, "peerDependencies": { - "react": "^19.2.5" + "react": "^19.3.0" } }, "node_modules/react-is": { @@ -9600,9 +9272,9 @@ } }, "node_modules/react-router": { - "version": "7.14.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.14.1.tgz", - "integrity": "sha512-5BCvFskyAAVumqhEKh/iPhLOIkfxcEUz8WqFIARCkMg8hZZzDYX9CtwxXA0e+qT8zAxmMC0x3Ckb9iMONwc5jg==", + "version": "7.18.4", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.4.tgz", + "integrity": "sha512-PUPQcMhMGRAslLcvtlPz/kmzBEWPhLdgLFrL7pLNepBL6dX0lWj4WD2cUYVgYCuT3jxvghYFg81cDTj44DhetQ==", "license": "MIT", "dependencies": { "cookie": "^1.0.1", @@ -9643,46 +9315,6 @@ } } }, - "node_modules/read-yaml-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-yaml-file/-/read-yaml-file-1.1.0.tgz", - "integrity": "sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.1.5", - "js-yaml": "^3.6.1", - "pify": "^4.0.1", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/read-yaml-file/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/read-yaml-file/node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -9755,6 +9387,29 @@ "node": ">=9.3.0 || >=8.10.0 <9.0.0" } }, + "node_modules/require-in-the-middle/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/require-in-the-middle/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "node_modules/resolve": { "version": "1.22.12", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", @@ -9785,34 +9440,14 @@ "node": ">=8" } }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "license": "MIT", - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, "node_modules/rollup": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", - "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", + "version": "4.64.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.64.0.tgz", + "integrity": "sha512-gjA/kFeDfffII8COoPsvLPvE16hWDTEc1+0QKQNKUkuV8iJjqsC4xXs8/FzuL0v9EV39kuxTbAgcFoTRyy2TGQ==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.8" + "@types/estree": "1.0.9" }, "bin": { "rollup": "dist/bin/rollup" @@ -9822,58 +9457,35 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.60.1", - "@rollup/rollup-android-arm64": "4.60.1", - "@rollup/rollup-darwin-arm64": "4.60.1", - "@rollup/rollup-darwin-x64": "4.60.1", - "@rollup/rollup-freebsd-arm64": "4.60.1", - "@rollup/rollup-freebsd-x64": "4.60.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", - "@rollup/rollup-linux-arm-musleabihf": "4.60.1", - "@rollup/rollup-linux-arm64-gnu": "4.60.1", - "@rollup/rollup-linux-arm64-musl": "4.60.1", - "@rollup/rollup-linux-loong64-gnu": "4.60.1", - "@rollup/rollup-linux-loong64-musl": "4.60.1", - "@rollup/rollup-linux-ppc64-gnu": "4.60.1", - "@rollup/rollup-linux-ppc64-musl": "4.60.1", - "@rollup/rollup-linux-riscv64-gnu": "4.60.1", - "@rollup/rollup-linux-riscv64-musl": "4.60.1", - "@rollup/rollup-linux-s390x-gnu": "4.60.1", - "@rollup/rollup-linux-x64-gnu": "4.60.1", - "@rollup/rollup-linux-x64-musl": "4.60.1", - "@rollup/rollup-openbsd-x64": "4.60.1", - "@rollup/rollup-openharmony-arm64": "4.60.1", - "@rollup/rollup-win32-arm64-msvc": "4.60.1", - "@rollup/rollup-win32-ia32-msvc": "4.60.1", - "@rollup/rollup-win32-x64-gnu": "4.60.1", - "@rollup/rollup-win32-x64-msvc": "4.60.1", + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.64.0", + "@rollup/rollup-android-arm64": "4.64.0", + "@rollup/rollup-darwin-arm64": "4.64.0", + "@rollup/rollup-darwin-x64": "4.64.0", + "@rollup/rollup-freebsd-arm64": "4.64.0", + "@rollup/rollup-freebsd-x64": "4.64.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.64.0", + "@rollup/rollup-linux-arm-musleabihf": "4.64.0", + "@rollup/rollup-linux-arm64-gnu": "4.64.0", + "@rollup/rollup-linux-arm64-musl": "4.64.0", + "@rollup/rollup-linux-loong64-gnu": "4.64.0", + "@rollup/rollup-linux-loong64-musl": "4.64.0", + "@rollup/rollup-linux-ppc64-gnu": "4.64.0", + "@rollup/rollup-linux-ppc64-musl": "4.64.0", + "@rollup/rollup-linux-riscv64-gnu": "4.64.0", + "@rollup/rollup-linux-riscv64-musl": "4.64.0", + "@rollup/rollup-linux-s390x-gnu": "4.64.0", + "@rollup/rollup-linux-x64-gnu": "4.64.0", + "@rollup/rollup-linux-x64-musl": "4.64.0", + "@rollup/rollup-openbsd-x64": "4.64.0", + "@rollup/rollup-openharmony-arm64": "4.64.0", + "@rollup/rollup-win32-arm64-msvc": "4.64.0", + "@rollup/rollup-win32-ia32-msvc": "4.64.0", + "@rollup/rollup-win32-x64-gnu": "4.64.0", + "@rollup/rollup-win32-x64-msvc": "4.64.0", "fsevents": "~2.3.2" } }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, "node_modules/rxjs": { "version": "7.8.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", @@ -9904,13 +9516,6 @@ ], "license": "MIT" }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, - "license": "MIT" - }, "node_modules/saxes": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", @@ -9925,15 +9530,15 @@ } }, "node_modules/scheduler": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", - "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.28.0.tgz", + "integrity": "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==", "license": "MIT" }, "node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "devOptional": true, "license": "ISC", "bin": { @@ -9950,77 +9555,59 @@ "license": "MIT" }, "node_modules/sharp": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", - "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", - "hasInstallScript": true, + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.5.tgz", + "integrity": "sha512-Ywn4OnzGukp7CDMrp08RQ50YKmuwG47brZgIVPTvBaaAfQlRlygrRqSrxdCiL9M+LlzLBiJ68IR1QqvzHyjC7g==", "license": "Apache-2.0", "optional": true, "dependencies": { - "@img/colour": "^1.0.0", + "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", - "semver": "^7.7.3" + "semver": "^7.8.5" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.5", - "@img/sharp-darwin-x64": "0.34.5", - "@img/sharp-libvips-darwin-arm64": "1.2.4", - "@img/sharp-libvips-darwin-x64": "1.2.4", - "@img/sharp-libvips-linux-arm": "1.2.4", - "@img/sharp-libvips-linux-arm64": "1.2.4", - "@img/sharp-libvips-linux-ppc64": "1.2.4", - "@img/sharp-libvips-linux-riscv64": "1.2.4", - "@img/sharp-libvips-linux-s390x": "1.2.4", - "@img/sharp-libvips-linux-x64": "1.2.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", - "@img/sharp-libvips-linuxmusl-x64": "1.2.4", - "@img/sharp-linux-arm": "0.34.5", - "@img/sharp-linux-arm64": "0.34.5", - "@img/sharp-linux-ppc64": "0.34.5", - "@img/sharp-linux-riscv64": "0.34.5", - "@img/sharp-linux-s390x": "0.34.5", - "@img/sharp-linux-x64": "0.34.5", - "@img/sharp-linuxmusl-arm64": "0.34.5", - "@img/sharp-linuxmusl-x64": "0.34.5", - "@img/sharp-wasm32": "0.34.5", - "@img/sharp-win32-arm64": "0.34.5", - "@img/sharp-win32-ia32": "0.34.5", - "@img/sharp-win32-x64": "0.34.5" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" + "@img/sharp-darwin-arm64": "0.35.5", + "@img/sharp-darwin-x64": "0.35.5", + "@img/sharp-freebsd-wasm32": "0.35.5", + "@img/sharp-libvips-darwin-arm64": "1.3.4", + "@img/sharp-libvips-darwin-x64": "1.3.4", + "@img/sharp-libvips-linux-arm": "1.3.4", + "@img/sharp-libvips-linux-arm64": "1.3.4", + "@img/sharp-libvips-linux-ppc64": "1.3.4", + "@img/sharp-libvips-linux-riscv64": "1.3.4", + "@img/sharp-libvips-linux-s390x": "1.3.4", + "@img/sharp-libvips-linux-x64": "1.3.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.4", + "@img/sharp-libvips-linuxmusl-x64": "1.3.4", + "@img/sharp-linux-arm": "0.35.5", + "@img/sharp-linux-arm64": "0.35.5", + "@img/sharp-linux-ppc64": "0.35.5", + "@img/sharp-linux-riscv64": "0.35.5", + "@img/sharp-linux-s390x": "0.35.5", + "@img/sharp-linux-x64": "0.35.5", + "@img/sharp-linuxmusl-arm64": "0.35.5", + "@img/sharp-linuxmusl-x64": "0.35.5", + "@img/sharp-webcontainers-wasm32": "0.35.5", + "@img/sharp-win32-arm64": "0.35.5", + "@img/sharp-win32-ia32": "0.35.5", + "@img/sharp-win32-x64": "0.35.5" }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, "node_modules/shell-quote": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", - "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.9.0.tgz", + "integrity": "sha512-Iov+JwFv/2HcTpcwNMKd8+IWNb8tboQJNQTkAY/LLVK7gGH9jy+LGkVqPxfekHl+yMmiqXszdGWXgkfml7hjqA==", "dev": true, "license": "MIT", "engines": { @@ -10037,28 +9624,12 @@ "dev": true, "license": "ISC" }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } + "license": "MIT" }, "node_modules/source-map": { "version": "0.7.6", @@ -10071,25 +9642,14 @@ } }, "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, - "node_modules/spawndamnit": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spawndamnit/-/spawndamnit-3.0.1.tgz", - "integrity": "sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==", - "dev": true, - "license": "SEE LICENSE IN LICENSE", - "dependencies": { - "cross-spawn": "^7.0.5", - "signal-exit": "^4.0.1" - } - }, "node_modules/split2": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", @@ -10099,13 +9659,6 @@ "node": ">= 10.x" } }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, - "license": "BSD-3-Clause" - }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", @@ -10120,9 +9673,9 @@ "license": "MIT" }, "node_modules/std-env": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", - "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.3.0.tgz", + "integrity": "sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag==", "dev": true, "license": "MIT" }, @@ -10171,16 +9724,6 @@ "node": ">=8" } }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/styled-jsx": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", @@ -10227,17 +9770,30 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, "node_modules/supports-preserve-symlinks-flag": { @@ -10260,27 +9816,14 @@ "license": "MIT" }, "node_modules/tarn": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.2.tgz", - "integrity": "sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/tarn/-/tarn-3.1.2.tgz", + "integrity": "sha512-3RTvqKZcK/17jnJ8rMKFXbyNogywTs1z0gVPPwFsJGX46rkmUHOdIaSQ/aVO1rS7nH+soiXiWk7rvUXxndm8Dg==", "license": "MIT", "engines": { "node": ">=8.0.0" } }, - "node_modules/term-size": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", - "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", @@ -10327,16 +9870,19 @@ "license": "MIT" }, "node_modules/tinyexec": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", + "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/tinyglobby": { - "version": "0.2.16", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", - "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "dev": true, "license": "MIT", "dependencies": { @@ -10350,41 +9896,10 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/tinyrainbow": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", - "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.2.0.tgz", + "integrity": "sha512-LgO3D9yZJjApUiuUfl9iFAwrtaX4+lok3wJIqttGoKCHlWUqHqbQpnxCf82L8FjgKsh4iGo78hqJwgL8F6To2A==", "dev": true, "license": "MIT", "engines": { @@ -10392,38 +9907,25 @@ } }, "node_modules/tldts": { - "version": "7.0.28", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.28.tgz", - "integrity": "sha512-+Zg3vWhRUv8B1maGSTFdev9mjoo8Etn2Ayfs4cnjlD3CsGkxXX4QyW3j2WJ0wdjYcYmy7Lx2RDsZMhgCWafKIw==", + "version": "7.4.16", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.16.tgz", + "integrity": "sha512-QwBER5KMR86IIjpIiO7H/Z3IMJPsZ1A6RKPAqzTTgOyUQUSt9FdnKcqhTaJmkY6HVrgouZHZR0ncK5QxvmnQeg==", "dev": true, "license": "MIT", "dependencies": { - "tldts-core": "^7.0.28" + "tldts-core": "^7.4.16" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "7.0.28", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.28.tgz", - "integrity": "sha512-7W5Efjhsc3chVdFhqtaU0KtK32J37Zcr9RKtID54nG+tIpcY79CQK/veYPODxtD/LJ4Lue66jvrQzIX2Z2/pUQ==", + "version": "7.4.16", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.16.tgz", + "integrity": "sha512-MDolfaSJtlSK5Y0A1xl3277ekubZwobpBjugknDizI9O5Rm60a1m8k4ICK+MRsCDzPygT81mp3BBf5RKDlFRfA==", "dev": true, "license": "MIT" }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, "node_modules/toposort": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", @@ -10431,115 +9933,640 @@ "license": "MIT" }, "node_modules/tough-cookie": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz", - "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.2.tgz", + "integrity": "sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { "tldts": "^7.0.5" }, "engines": { - "node": ">=16" + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tsup": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.5.1.tgz", + "integrity": "sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-require": "^5.1.0", + "cac": "^6.7.14", + "chokidar": "^4.0.3", + "consola": "^3.4.0", + "debug": "^4.4.0", + "esbuild": "^0.27.0", + "fix-dts-default-cjs-exports": "^1.0.0", + "joycon": "^3.1.1", + "picocolors": "^1.1.1", + "postcss-load-config": "^6.0.1", + "resolve-from": "^5.0.0", + "rollup": "^4.34.8", + "source-map": "^0.7.6", + "sucrase": "^3.35.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.11", + "tree-kill": "^1.2.2" + }, + "bin": { + "tsup": "dist/cli-default.js", + "tsup-node": "dist/cli-node.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@microsoft/api-extractor": "^7.36.0", + "@swc/core": "^1", + "postcss": "^8.4.12", + "typescript": ">=4.5.0" + }, + "peerDependenciesMeta": { + "@microsoft/api-extractor": { + "optional": true + }, + "@swc/core": { + "optional": true + }, + "postcss": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/tsup/node_modules/@esbuild/aix-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsup/node_modules/@esbuild/android-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsup/node_modules/@esbuild/android-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsup/node_modules/@esbuild/android-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsup/node_modules/@esbuild/darwin-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsup/node_modules/@esbuild/darwin-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsup/node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsup/node_modules/@esbuild/freebsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsup/node_modules/@esbuild/linux-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsup/node_modules/@esbuild/linux-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsup/node_modules/@esbuild/linux-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsup/node_modules/@esbuild/linux-loong64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsup/node_modules/@esbuild/linux-mips64el": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsup/node_modules/@esbuild/linux-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsup/node_modules/@esbuild/linux-riscv64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsup/node_modules/@esbuild/linux-s390x": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsup/node_modules/@esbuild/linux-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsup/node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsup/node_modules/@esbuild/netbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsup/node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsup/node_modules/@esbuild/openbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsup/node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsup/node_modules/@esbuild/sunos-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsup/node_modules/@esbuild/win32-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsup/node_modules/@esbuild/win32-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsup/node_modules/@esbuild/win32-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsup/node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" } }, - "node_modules/tr46": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", - "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "node_modules/tsup/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "license": "MIT", "dependencies": { - "punycode": "^2.3.1" + "ms": "^2.1.3" }, "engines": { - "node": ">=20" - } - }, - "node_modules/tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true, - "license": "MIT", - "bin": { - "tree-kill": "cli.js" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/tsup": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.5.1.tgz", - "integrity": "sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==", + "node_modules/tsup/node_modules/esbuild": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", "dev": true, + "hasInstallScript": true, "license": "MIT", - "dependencies": { - "bundle-require": "^5.1.0", - "cac": "^6.7.14", - "chokidar": "^4.0.3", - "consola": "^3.4.0", - "debug": "^4.4.0", - "esbuild": "^0.27.0", - "fix-dts-default-cjs-exports": "^1.0.0", - "joycon": "^3.1.1", - "picocolors": "^1.1.1", - "postcss-load-config": "^6.0.1", - "resolve-from": "^5.0.0", - "rollup": "^4.34.8", - "source-map": "^0.7.6", - "sucrase": "^3.35.0", - "tinyexec": "^0.3.2", - "tinyglobby": "^0.2.11", - "tree-kill": "^1.2.2" - }, "bin": { - "tsup": "dist/cli-default.js", - "tsup-node": "dist/cli-node.js" + "esbuild": "bin/esbuild" }, "engines": { "node": ">=18" }, - "peerDependencies": { - "@microsoft/api-extractor": "^7.36.0", - "@swc/core": "^1", - "postcss": "^8.4.12", - "typescript": ">=4.5.0" - }, - "peerDependenciesMeta": { - "@microsoft/api-extractor": { - "optional": true - }, - "@swc/core": { - "optional": true - }, - "postcss": { - "optional": true - }, - "typescript": { - "optional": true - } + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" } }, + "node_modules/tsup/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tsup/node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, "node_modules/tsx": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", - "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "version": "4.23.15", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.15.tgz", + "integrity": "sha512-Yiex1Ovn8z2xPpOWckIiysV1SSyRMY9BkLF++q0yKiDxCqRhosKfMg3janKkiLBwZ5c/YryloKwGZcrEmtwxKw==", "license": "MIT", "dependencies": { - "esbuild": "~0.27.0", - "get-tsconfig": "^4.7.5" + "esbuild": "~0.28.0" }, "bin": { "tsx": "dist/cli.mjs" @@ -10551,22 +10578,36 @@ "fsevents": "~2.3.3" } }, + "node_modules/tsx/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/turbo": { - "version": "2.9.6", - "resolved": "https://registry.npmjs.org/turbo/-/turbo-2.9.6.tgz", - "integrity": "sha512-+v2QJey7ZUeUiuigkU+uFfklvNUyPI2VO2vBpMYJA+a1hKFLFiKtUYlRHdb3P9CrAvMzi0upbjI4WT+zKtqkBg==", + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/turbo/-/turbo-2.11.7.tgz", + "integrity": "sha512-RhXHbqfvY6o3xt/zSHCwSNwLrZlXiqHzewi4eAj9PtZ1T1CqgMJ2yIDb7SKPTG9IikTm925mfzL6hldQVdyNWw==", "dev": true, "license": "MIT", "bin": { "turbo": "bin/turbo" }, "optionalDependencies": { - "@turbo/darwin-64": "2.9.6", - "@turbo/darwin-arm64": "2.9.6", - "@turbo/linux-64": "2.9.6", - "@turbo/linux-arm64": "2.9.6", - "@turbo/windows-64": "2.9.6", - "@turbo/windows-arm64": "2.9.6" + "@turbo/darwin-64": "2.11.7", + "@turbo/darwin-arm64": "2.11.7", + "@turbo/linux-64": "2.11.7", + "@turbo/linux-arm64": "2.11.7", + "@turbo/windows-64": "2.11.7", + "@turbo/windows-arm64": "2.11.7" } }, "node_modules/type-fest": { @@ -10582,17 +10623,17 @@ } }, "node_modules/typedoc": { - "version": "0.28.19", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.28.19.tgz", - "integrity": "sha512-wKh+lhdmMFivMlc6vRRcMGXeGEHGU2g8a2CkPTJjJlwRf1iXbimWIPcFolCqe4E0d/FRtGszpIrsp3WLpDB8Pw==", + "version": "0.28.20", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.28.20.tgz", + "integrity": "sha512-uSKqkh8Cr48vllnEy+jdaAgOeR6Y+QCBW7usgUsKj7gJEfR7stw9U/fE49LBnj2tPRKPY0c0EBJSWe9Appmplg==", "dev": true, "license": "Apache-2.0", "dependencies": { "@gerrit0/mini-shiki": "^3.23.0", "lunr": "^2.3.9", - "markdown-it": "^14.1.1", + "markdown-it": "^14.3.0", "minimatch": "^10.2.5", - "yaml": "^2.8.3" + "yaml": "^2.9.0" }, "bin": { "typedoc": "bin/typedoc" @@ -10627,16 +10668,16 @@ "license": "MIT" }, "node_modules/ufo": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", - "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", + "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", "dev": true, "license": "MIT" }, "node_modules/undici": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz", - "integrity": "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==", + "version": "7.30.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.30.0.tgz", + "integrity": "sha512-dkrQXeHSaoamnItlYbmzG0wFYrM0ZwDxCIg0A7aKjTyyhh9svRzCNFEzV+Vm05/yehjCzjDZ31KXfGEjYSztDQ==", "dev": true, "license": "MIT", "engines": { @@ -10644,26 +10685,16 @@ } }, "node_modules/undici-types": { - "version": "7.19.2", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz", - "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", "dev": true, "license": "MIT" }, - "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.3.tgz", + "integrity": "sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ==", "dev": true, "funding": [ { @@ -10735,9 +10766,9 @@ } }, "node_modules/use-sync-external-store": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", - "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.7.0.tgz", + "integrity": "sha512-6L+EeigHMQhdaIPNIFUKwfWJSwWFQ8gJbJ2DLOs5sDIegTwR9fRxvnM3uciHKjIZhFz+KAv2emhWMRvDmMcY8A==", "license": "MIT", "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" @@ -10753,6 +10784,7 @@ "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" @@ -10763,9 +10795,9 @@ } }, "node_modules/vite": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.2.tgz", - "integrity": "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==", + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz", + "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==", "dev": true, "license": "MIT", "dependencies": { @@ -11321,80 +11353,35 @@ "@esbuild/win32-x64": "0.25.12" } }, - "node_modules/vite/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/vite/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "node_modules/vite/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, + "hasInstallScript": true, "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/vite/node_modules/postcss": { - "version": "8.5.10", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz", - "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } + "optional": true, + "os": [ + "darwin" ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, "engines": { - "node": "^10 || ^12 || >=14" + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, "node_modules/vitest": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.4.tgz", - "integrity": "sha512-tFuJqTxKb8AvfyqMfnavXdzfy3h3sWZRWwfluGbkeR7n0HUev+FmNgZ8SDrRBTVrVCjgH5cA21qGbCffMNtWvg==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.11.tgz", + "integrity": "sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.4", - "@vitest/mocker": "4.1.4", - "@vitest/pretty-format": "4.1.4", - "@vitest/runner": "4.1.4", - "@vitest/snapshot": "4.1.4", - "@vitest/spy": "4.1.4", - "@vitest/utils": "4.1.4", + "@vitest/expect": "4.1.11", + "@vitest/mocker": "4.1.11", + "@vitest/pretty-format": "4.1.11", + "@vitest/runner": "4.1.11", + "@vitest/snapshot": "4.1.11", + "@vitest/spy": "4.1.11", + "@vitest/utils": "4.1.11", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -11422,12 +11409,12 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.4", - "@vitest/browser-preview": "4.1.4", - "@vitest/browser-webdriverio": "4.1.4", - "@vitest/coverage-istanbul": "4.1.4", - "@vitest/coverage-v8": "4.1.4", - "@vitest/ui": "4.1.4", + "@vitest/browser-playwright": "4.1.11", + "@vitest/browser-preview": "4.1.11", + "@vitest/browser-webdriverio": "4.1.11", + "@vitest/coverage-istanbul": "4.1.11", + "@vitest/coverage-v8": "4.1.11", + "@vitest/ui": "4.1.11", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -11471,28 +11458,12 @@ } } }, - "node_modules/vitest/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/vitest/node_modules/tinyexec": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.1.tgz", - "integrity": "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==", + "node_modules/vitest/node_modules/es-module-lexer": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", + "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } + "license": "MIT" }, "node_modules/w3c-xmlserializer": { "version": "5.0.0", @@ -11550,22 +11521,6 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" } }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/why-is-node-running": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", @@ -11600,25 +11555,10 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/ws": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", - "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.22.0.tgz", + "integrity": "sha512-Ydggc987+RO0AnWtZ/7Wq9FtNvcrL1b/RO0ud9mWjUPgDrsAAwQSF51sm2hm1XofbU/4jkpGEsLFsZZxU+1DOg==", "license": "MIT", "engines": { "node": ">=10.0.0" @@ -11679,9 +11619,9 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", - "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz", + "integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==", "license": "ISC", "bin": { "yaml": "bin.mjs" @@ -11694,9 +11634,9 @@ } }, "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "version": "17.7.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", "license": "MIT", "dependencies": { "cliui": "^8.0.1", @@ -11745,8 +11685,8 @@ "name": "website-docs", "version": "0.1.0", "dependencies": { - "@cleverbrush/website-shared": "file:../shared", - "next": "^16.2.1", + "@cleverbrush/website-shared": "*", + "next": "^16.3.6", "performative-ui": "^0.3.0", "react": "^19.1.0", "react-dom": "^19.1.0" @@ -11776,14 +11716,14 @@ "name": "website-schema", "version": "0.1.0", "dependencies": { - "@cleverbrush/mapper": "file:../../libs/mapper", - "@cleverbrush/schema": "file:../../libs/schema", - "@cleverbrush/website-shared": "file:../shared", + "@cleverbrush/mapper": "*", + "@cleverbrush/schema": "*", + "@cleverbrush/website-shared": "*", "@monaco-editor/react": "^4.7.0", "@t3-oss/env-core": "^0.13.11", "@t3-oss/env-nextjs": "^0.13.11", "@tanstack/react-form": "^1.28.6", - "next": "^16.2.1", + "next": "^16.3.6", "performative-ui": "^0.3.0", "react": "^19.2.4", "react-dom": "^19.2.4" @@ -11796,9 +11736,9 @@ } }, "websites/schema/node_modules/@types/node": { - "version": "20.19.39", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.39.tgz", - "integrity": "sha512-orrrD74MBUyK8jOAD/r0+lfa1I2MO6I+vAkmAWzMYbCcgrN4lCrmK52gRFQq/JRxfYPfonkr4b0jcY7Olqdqbw==", + "version": "20.19.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", + "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", "dev": true, "license": "MIT", "dependencies": { @@ -11816,7 +11756,7 @@ "name": "@cleverbrush/website-shared", "version": "0.0.0", "dependencies": { - "next": "^16.2.1", + "next": "^16.3.6", "performative-ui": "^0.3.0", "react": "^19.1.0" } diff --git a/package.json b/package.json index cf5de23f..ac7d08b2 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,15 @@ { "name": "cleverbrush-framework", - "private": "true", + "engines": { + "node": ">=24" + }, + "private": true, "version": "4.2.0", - "description": "Cleverbrush Framework - a set of libraries used by Cleverbrush, a web-based publishing application", - "packageManager": "npm@24.13.0", + "description": "Schema-first TypeScript libraries for application development", + "packageManager": "npm@11.12.1", "scripts": { "build": "turbo run build --filter=!website-schema --filter=!website-docs", + "postbuild": "npm rebuild --workspace @cleverbrush/orm-cli --ignore-scripts", "build:all": "npm run lint:fix && npm run build && node scripts/update-bundle-badge.js && node scripts/measure-bundle-sizes.js && npm run test && npm run docs && npm run test:coverage && npm run bench:json", "build:schema-site": "cd websites/schema && npm run build && cd ../..", "build:docs-site": "cd websites/docs && npm run build && cd ../..", @@ -26,7 +30,9 @@ "test": "vitest --run --typecheck", "test:queries:integration": "vitest run --config vitest.queries.config.mts", "test:scheduler:integration": "vitest run --config vitest.scheduler.config.mts", - "test:coverage": "vitest --run --coverage && node scripts/update-coverage-badges.js", + "test:coverage": "vitest --run --coverage && node scripts/check-coverage.mjs", + "coverage:badges": "node scripts/update-coverage-badges.js", + "test:packages": "node scripts/test-packages.mjs", "bench": "vitest bench --run", "bench:json": "BENCH_JSON=bench-results.json vitest bench --run --project benchmarks && node scripts/relativize-bench-paths.js bench-results.json", "dev:demo": "./scripts/dev-demo.sh", @@ -46,20 +52,21 @@ ], "keywords": [], "author": "Andrew Zolotukhin ", - "license": "BSD 3-Clause", + "license": "BSD-3-Clause", "type": "module", "devDependencies": { "@playwright/mcp": "^0.0.70", "@biomejs/biome": "^2.4.9", - "@changesets/cli": "^2.30.0", + "@changesets/cli": "^3.0.3", "@testing-library/react": "^16.3.2", - "@vitest/coverage-v8": "^4.1.2", + "@vitest/coverage-v8": "^4.1.11", "concurrently": "^9.1.0", + "esbuild": "^0.28.2", "jsdom": "^29.0.1", "tsup": "^8.5.1", - "turbo": "^2.8.21", + "turbo": "^2.9.14", "typedoc": "^0.28.18", "typescript": "^6.0.2", - "vitest": "^4.1.2" + "vitest": "^4.1.11" } } diff --git a/scripts/check-coverage.mjs b/scripts/check-coverage.mjs new file mode 100644 index 00000000..b41dbbcf --- /dev/null +++ b/scripts/check-coverage.mjs @@ -0,0 +1,49 @@ +import { readFileSync, readdirSync } from 'node:fs'; +import { resolve, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const root = fileURLToPath(new URL('../', import.meta.url)); +const summary = JSON.parse( + readFileSync(join(root, 'coverage/coverage-summary.json'), 'utf8') +); +const floors = JSON.parse( + readFileSync(join(root, 'coverage-thresholds.json'), 'utf8') +); +const metrics = ['statements', 'branches', 'functions', 'lines']; +let failed = false; +for (const directory of readdirSync(join(root, 'libs'))) { + const manifest = JSON.parse( + readFileSync(join(root, 'libs', directory, 'package.json'), 'utf8') + ); + if (manifest.private) continue; + const prefix = resolve(root, 'libs', directory, 'src') + '/'; + const files = Object.entries(summary).filter(([file]) => + file.startsWith(prefix) + ); + for (const metric of metrics) { + const covered = files.reduce( + (sum, [, data]) => sum + data[metric].covered, + 0 + ); + const total = files.reduce( + (sum, [, data]) => sum + data[metric].total, + 0 + ); + const percent = total ? (covered / total) * 100 : 100; + const floor = floors[directory]?.[metric]; + if ( + !files.length || + !Number.isFinite(floor) || + floor < 0 || + floor > 100 || + percent + 0.001 < floor + ) { + console.error( + `${manifest.name} ${metric}: ${percent.toFixed(2)}%, required ${floor ?? 'MISSING'}% (${files.length} files)` + ); + failed = true; + } + } +} +if (failed) process.exitCode = 1; +else console.log('All published-package coverage floors passed.'); diff --git a/scripts/package-license.mjs b/scripts/package-license.mjs new file mode 100644 index 00000000..b8e42b53 --- /dev/null +++ b/scripts/package-license.mjs @@ -0,0 +1,32 @@ +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; + +/** + * Check first-party source or installed-package licensing against the root + * LICENSE. This checks consistency, not the licensing of dependencies. + * + * @param {string} directory - Root, workspace or installed package directory. + * @param {string} expectedLicense - Canonical root LICENSE contents. + * @returns {void} + */ +export function assertPackageLicense(directory, expectedLicense) { + if (!/^BSD 3-Clause License\r?\n/.test(expectedLicense)) { + throw new Error('Root LICENSE must contain the BSD-3-Clause license'); + } + const pkg = JSON.parse( + readFileSync(join(directory, 'package.json'), 'utf8') + ); + if (pkg.license !== 'BSD-3-Clause') { + throw new Error(`${pkg.name}: package license must be BSD-3-Clause`); + } + let license; + try { + license = readFileSync(join(directory, 'LICENSE'), 'utf8'); + } catch (error) { + if (error.code !== 'ENOENT') throw error; + throw new Error(`${pkg.name}: missing LICENSE`, { cause: error }); + } + if (license !== expectedLicense) { + throw new Error(`${pkg.name}: LICENSE does not match root LICENSE`); + } +} diff --git a/scripts/package-license.test.mjs b/scripts/package-license.test.mjs new file mode 100644 index 00000000..786b78b9 --- /dev/null +++ b/scripts/package-license.test.mjs @@ -0,0 +1,93 @@ +import { + mkdirSync, + mkdtempSync, + readFileSync, + rmSync, + writeFileSync +} from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import { assertPackageLicense } from './package-license.mjs'; + +const canonicalLicense = readFileSync( + new URL('../LICENSE', import.meta.url), + 'utf8' +); +let directory; + +beforeEach(() => { + directory = mkdtempSync(join(tmpdir(), 'framework-license-test-')); +}); + +afterEach(() => { + rmSync(directory, { recursive: true, force: true }); +}); + +function fixture( + relativePath, + license = canonicalLicense, + identifier = 'BSD-3-Clause' +) { + const location = join(directory, relativePath); + mkdirSync(location, { recursive: true }); + writeFileSync( + join(location, 'package.json'), + JSON.stringify({ + name: '@cleverbrush/example', + license: identifier ?? undefined + }) + ); + if (license !== null) writeFileSync(join(location, 'LICENSE'), license); + return location; +} + +describe('package licensing', () => { + it.each(['.', 'libs/example', 'node_modules/@cleverbrush/example'])( + 'accepts canonical licensing in %s', + relativePath => { + expect(() => + assertPackageLicense(fixture(relativePath), canonicalLicense) + ).not.toThrow(); + } + ); + + it('rejects a missing LICENSE', () => { + expect(() => + assertPackageLicense(fixture('.', null), canonicalLicense) + ).toThrow('@cleverbrush/example: missing LICENSE'); + }); + + it('rejects a mismatched LICENSE even when metadata is correct', () => { + expect(() => + assertPackageLicense( + fixture('.', 'Unlicense or 0BSD'), + canonicalLicense + ) + ).toThrow('@cleverbrush/example: LICENSE does not match root LICENSE'); + }); + + it.each(['MIT', '0BSD', 'BSD 3-Clause', null])( + 'rejects incorrect or absent license metadata: %s', + identifier => { + expect(() => + assertPackageLicense( + fixture('.', canonicalLicense, identifier), + canonicalLicense + ) + ).toThrow( + '@cleverbrush/example: package license must be BSD-3-Clause' + ); + } + ); + + it('rejects an incorrect canonical license even when all copies agree', () => { + const incorrectLicense = 'Unlicense or 0BSD'; + expect(() => + assertPackageLicense( + fixture('.', incorrectLicense), + incorrectLicense + ) + ).toThrow('Root LICENSE must contain the BSD-3-Clause license'); + }); +}); diff --git a/scripts/test-packages.mjs b/scripts/test-packages.mjs new file mode 100644 index 00000000..f45b677c --- /dev/null +++ b/scripts/test-packages.mjs @@ -0,0 +1,173 @@ +import { execFileSync } from 'node:child_process'; +import { + mkdtempSync, + readdirSync, + readFileSync, + rmSync, + writeFileSync +} from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { build } from 'esbuild'; +import { assertPackageLicense } from './package-license.mjs'; + +const root = fileURLToPath(new URL('../', import.meta.url)); +const directory = mkdtempSync(join(tmpdir(), 'framework-packages-')); +const dependencies = {}; +const peers = {}; +const specifiers = []; +const binaries = []; +const execute = (command, args, cwd = directory) => + execFileSync(command, args, { + cwd, + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'pipe'], + env: { + ...process.env, + npm_config_audit: 'false', + npm_config_fund: 'false' + }, + timeout: 300_000, + maxBuffer: 10 * 1024 * 1024 + }); + +try { + const license = readFileSync(join(root, 'LICENSE'), 'utf8'); + assertPackageLicense(root, license); + for (const folder of readdirSync(join(root, 'libs'))) { + const workspace = join(root, 'libs', folder); + const pkg = JSON.parse( + readFileSync(join(workspace, 'package.json'), 'utf8') + ); + if (pkg.private) continue; + assertPackageLicense(workspace, license); + const [packed] = JSON.parse( + execute( + 'npm', + [ + 'pack', + '--ignore-scripts', + '--json', + '--pack-destination', + directory + ], + workspace + ) + ); + const files = new Set(packed.files.map(file => file.path)); + if (!files.has('LICENSE')) + throw new Error(`${pkg.name}: missing packaged license`); + for (const bin of Object.values(pkg.bin ?? {})) { + if (!files.has(bin.replace(/^\.\//, ''))) + throw new Error(`${pkg.name}: missing executable ${bin}`); + binaries.push(join(directory, 'node_modules', pkg.name, bin)); + } + if ( + [...files].some(file => + /(?:\.test(?:-d)?\.[cm]?tsx?$|\.env$)/.test(file) + ) + ) + throw new Error(`${pkg.name}: test or environment file in tarball`); + for (const [subpath, exported] of Object.entries(pkg.exports)) { + const target = + typeof exported === 'string' + ? { + import: exported, + types: exported.replace(/\.js$/, '.d.ts') + } + : exported; + for (const field of ['import', 'types']) { + if ( + !target[field] || + !files.has(target[field].replace(/^\.\//, '')) + ) + throw new Error( + `${pkg.name}${subpath}: missing ${field} target` + ); + } + specifiers.push( + pkg.name + (subpath === '.' ? '' : subpath.slice(1)) + ); + } + Object.assign(peers, pkg.peerDependencies); + dependencies[pkg.name] = `file:${join(directory, packed.filename)}`; + } + writeFileSync( + join(directory, 'package.json'), + JSON.stringify({ + private: true, + type: 'module', + dependencies: { + ...peers, + '@types/node': '^24', + '@types/react': '^19', + ...dependencies + } + }) + ); + // Generate a disposable consumer lockfile, then install reproducibly. + execute('npm', ['update', '--package-lock-only', '--ignore-scripts']); + execute('npm', ['ci', '--ignore-scripts']); + for (const name of Object.keys(dependencies)) { + assertPackageLicense(join(directory, 'node_modules', name), license); + } + for (const bin of binaries) execute(process.execPath, [bin, '--help']); + execute(process.execPath, [ + '--input-type=module', + '-e', + `for (const name of ${JSON.stringify(specifiers)}) await import(name);` + ]); + const declarations = specifiers + .map( + (name, i) => + `import type * as Entry${i} from ${JSON.stringify(name)};` + ) + .join('\n'); + const readmeExample = readFileSync(join(root, 'README.md'), 'utf8').match( + /## How The Pieces Fit[\s\S]*?```ts\n([\s\S]*?)```/ + )?.[1]; + if (!readmeExample) + throw new Error('Root README consumer example is missing'); + writeFileSync( + join(directory, 'consumer.ts'), + declarations + '\n' + readmeExample + ); + execute(process.execPath, [ + join(root, 'node_modules/typescript/bin/tsc'), + '--noEmit', + '--strict', + '--skipLibCheck', + 'false', + '--target', + 'ES2022', + '--lib', + 'ES2022,DOM,ESNext.Disposable', + '--types', + 'node,react', + '--module', + 'ESNext', + '--moduleResolution', + 'bundler', + 'consumer.ts' + ]); + await build({ + stdin: { + contents: `import * as schema from '@cleverbrush/schema'; import * as client from '@cleverbrush/client'; import * as form from '@cleverbrush/react-form'; import * as contract from '@cleverbrush/server/contract'; globalThis.framework = { schema, client, form, contract };`, + resolveDir: directory + }, + bundle: true, + platform: 'browser', + format: 'esm', + write: false + }); + console.log( + `Packed consumer passed: ${Object.keys(dependencies).length} packages, ${specifiers.length} entry points, BSD-3-Clause licenses, declarations and browser bundle.` + ); +} catch (error) { + if (error.stdout) console.error(error.stdout.toString()); + console.error(error.stderr?.toString() || error.message); + process.exitCode = 1; +} finally { + rmSync(directory, { recursive: true, force: true }); +} diff --git a/scripts/update-coverage-badges.js b/scripts/update-coverage-badges.js index 728a0069..06329e08 100644 --- a/scripts/update-coverage-badges.js +++ b/scripts/update-coverage-badges.js @@ -10,7 +10,7 @@ * * Usage: node scripts/update-coverage-badges.js */ -import { readFileSync, writeFileSync, existsSync } from 'fs'; +import { readFileSync, writeFileSync, existsSync, readdirSync } from 'fs'; import { resolve, join } from 'path'; const ROOT = resolve(import.meta.dirname, '..'); @@ -40,21 +40,17 @@ function getColor(pct) { function makeBadge(pct) { const label = `${pct}%25`; // %25 is URL-encoded % const color = getColor(pct); - return `![Coverage](https://img.shields.io/badge/coverage-${label}-${color})`; + return `![Unit coverage](https://img.shields.io/badge/unit_coverage-${label}-${color})`; } // --- Per-library coverage aggregation --- -const LIBS = [ - 'async', - 'deep', - 'knex-clickhouse', - 'mapper', - 'react-form', - 'scheduler', - 'schema', - 'schema-json' -]; +const LIBS = readdirSync(join(ROOT, 'libs')).filter(lib => { + const manifest = JSON.parse( + readFileSync(join(ROOT, 'libs', lib, 'package.json'), 'utf8') + ); + return !manifest.private; +}); function calcLibCoverage(lib) { const prefix = join(ROOT, 'libs', lib, 'src') + '/'; @@ -86,11 +82,14 @@ function updateReadme(readmePath, badge) { // Subsequent runs: replace between existing markers if (content.includes(BADGE_START)) { - const escaped = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const escaped = s => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); const regex = new RegExp( `${escaped(BADGE_START)}[\\s\\S]*?${escaped(BADGE_END)}` ); - content = content.replace(regex, `${BADGE_START}\n${badge}\n${BADGE_END}`); + content = content.replace( + regex, + `${BADGE_START}\n${badge}\n${BADGE_END}` + ); writeFileSync(readmePath, content); console.log(` updated: ${readmePath}`); return; diff --git a/typedoc.json b/typedoc.json index 57266890..9c75088c 100644 --- a/typedoc.json +++ b/typedoc.json @@ -1,6 +1,9 @@ -{ +{ "$schema": "https://typedoc.org/schema.json", "entryPointStrategy": "packages", + "packageOptions": { + "tsconfig": "tsconfig.build.json" + }, "entryPoints": [ "libs/async", "libs/auth", @@ -21,16 +24,18 @@ "libs/schema", "libs/schema-json", "libs/server", - "libs/server-openapi" + "libs/server-openapi", + "libs/storage", + "libs/storage-s3" ], "out": "websites/docs/public/api-docs/latest", "name": "Libraries", - "excludePrivate": true, - "exclude": [ - "**/node_modules/**", - "websites/**", - "demos/**" + "highlightLanguages": [ + "bash", "console", "css", "html", "javascript", "json", "jsonc", + "json5", "yaml", "tsx", "typescript", "sql" ], + "excludePrivate": true, + "exclude": ["**/node_modules/**", "websites/**", "demos/**"], "navigationLinks": { "← Back to Website": "/", "All Versions": "/api-docs/" diff --git a/vite.config.mts b/vite.config.mts index bb40cf29..e4c2546a 100644 --- a/vite.config.mts +++ b/vite.config.mts @@ -3,8 +3,19 @@ import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { + // Compiler-consumer tests allocate several GB; bound parallel workers. + maxWorkers: 4, // Use simple glob pattern for auto-discovery - projects: ['libs/*'], + projects: [ + 'libs/*', + { + test: { + name: 'scripts', + include: ['scripts/**/*.test.mjs'], + environment: 'node' + } + } + ], benchmark: { ...(process.env.BENCH_JSON ? { outputJson: process.env.BENCH_JSON } @@ -14,11 +25,12 @@ export default defineConfig({ 'libs/**/src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}' ], coverage: { - // Updated for v3: Define include patterns first - include: ['libs/**/src/**/*.{js,mjs,cjs,ts,mts,cts}'], + include: ['libs/**/src/**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], exclude: [ '**/node_modules/**', '**/dist/**', + '**/*.d.ts', + '**/*.test-d.ts', '**/src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}', 'libs/**/src/index.ts', 'libs/**/src/types.ts', diff --git a/websites/docs/app/client/sections/idempotency.tsx b/websites/docs/app/client/sections/idempotency.tsx index 60da6038..242995ec 100644 --- a/websites/docs/app/client/sections/idempotency.tsx +++ b/websites/docs/app/client/sections/idempotency.tsx @@ -41,7 +41,10 @@ await client.todos.create({ body: { title: 'Buy milk' } });

The server-side idempotency() middleware reads the header, stores the response, and replays it for - duplicate keys. + duplicate keys within an explicit scope, method and URL. + Install it after authentication and authorization. + Concurrent duplicates share one execution; this + process-local store is not durable exactly-once execution.

                      'public-todos', ttl: 86_400_000 })],
 });
 `)
                         }}
@@ -145,6 +149,37 @@ server.handle(CreateTodo, createHandler, {
                             
                         
                         
+                            
+                                
+                                    scope
+                                
+                                
+                                    (ctx) => string | undefined
+                                
+                                Required; undefined skips replay
+                            
+                            
+                                
+                                    maxEntries
+                                
+                                
+                                    number
+                                
+                                
+                                    1000 (pending and completed)
+                                
+                            
+                            
+                                
+                                    maxResponseBytes
+                                
+                                
+                                    number
+                                
+                                
+                                    65536 bytes
+                                
+                            
                             
                                 
                                     ttl
diff --git a/websites/docs/next-env.d.ts b/websites/docs/next-env.d.ts
index 20e7bcfb..644e5801 100644
--- a/websites/docs/next-env.d.ts
+++ b/websites/docs/next-env.d.ts
@@ -1,6 +1,7 @@
 /// 
 /// 
-import './.next/dev/types/routes.d.ts';
+import './.next/types/routes.d.ts';
+import './.next/types/root-params.d.ts';
 
 // NOTE: This file should not be edited
 // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
diff --git a/websites/docs/package.json b/websites/docs/package.json
index d9f05403..add66155 100644
--- a/websites/docs/package.json
+++ b/websites/docs/package.json
@@ -8,8 +8,8 @@
         "start": "next start"
     },
     "dependencies": {
-        "@cleverbrush/website-shared": "file:../shared",
-        "next": "^16.2.1",
+        "@cleverbrush/website-shared": "*",
+        "next": "^16.3.6",
         "performative-ui": "^0.3.0",
         "react": "^19.1.0",
         "react-dom": "^19.1.0"
diff --git a/websites/schema/next-env.d.ts b/websites/schema/next-env.d.ts
index 20e7bcfb..644e5801 100644
--- a/websites/schema/next-env.d.ts
+++ b/websites/schema/next-env.d.ts
@@ -1,6 +1,7 @@
 /// 
 /// 
-import './.next/dev/types/routes.d.ts';
+import './.next/types/routes.d.ts';
+import './.next/types/root-params.d.ts';
 
 // NOTE: This file should not be edited
 // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
diff --git a/websites/schema/package.json b/websites/schema/package.json
index 691964b6..717ec8fc 100644
--- a/websites/schema/package.json
+++ b/websites/schema/package.json
@@ -11,14 +11,14 @@
         "typecheck": "tsc --noEmit"
     },
     "dependencies": {
-        "@cleverbrush/mapper": "file:../../libs/mapper",
-        "@cleverbrush/schema": "file:../../libs/schema",
-        "@cleverbrush/website-shared": "file:../shared",
+        "@cleverbrush/mapper": "*",
+        "@cleverbrush/schema": "*",
+        "@cleverbrush/website-shared": "*",
         "@monaco-editor/react": "^4.7.0",
         "@t3-oss/env-core": "^0.13.11",
         "@t3-oss/env-nextjs": "^0.13.11",
         "@tanstack/react-form": "^1.28.6",
-        "next": "^16.2.1",
+        "next": "^16.3.6",
         "performative-ui": "^0.3.0",
         "react": "^19.2.4",
         "react-dom": "^19.2.4"
diff --git a/websites/shared/package.json b/websites/shared/package.json
index cc08ba96..af63fbf4 100644
--- a/websites/shared/package.json
+++ b/websites/shared/package.json
@@ -17,7 +17,7 @@
         "./lib/bundleSizes": "./lib/bundleSizes.ts"
     },
     "dependencies": {
-        "next": "^16.2.1",
+        "next": "^16.3.6",
         "performative-ui": "^0.3.0",
         "react": "^19.1.0"
     }