From 93d3f9bdd38a4b13d8dd37736eaaca459b8da680 Mon Sep 17 00:00:00 2001 From: Max Holman Date: Wed, 23 Sep 2026 17:06:01 +0800 Subject: [PATCH 1/9] build: swap eslint, prettier and make for oxlint, oxfmt and just Takes the layout and tooling from rest-client: @block65/shared-config 0.5.0 for the oxlint, oxfmt and fallow configs, @block65/tsconfig for the compiler options, a justfile in place of the Makefile, and CI that runs the gate through just. deploy.yml stages the publish for review with pnpm stage. Every dependency moves to its latest release. yaml, type-fest and json-schema-to-typescript are dropped, since nothing imports them. vite 8 is pinned because vitest 5 otherwise resolves vite 7, which fails on the null entries the shared tsconfig uses. Co-Authored-By: LLM --- .eslintrc.cjs | 29 - .fallowrc.jsonc | 10 + .github/workflows/ci.yml | 41 + .github/workflows/deploy.yml | 22 +- .github/workflows/pr.yml | 26 - .node-version | 2 +- .prettierignore | 7 - Makefile | 13 - justfile | 29 + oxfmt.config.ts | 3 + oxlint.config.ts | 5 + package.json | 87 +- pnpm-lock.yaml | 2583 +++++++++++++++++++++++++++------- pnpm-workspace.yaml | 16 + tsconfig.json | 22 +- 15 files changed, 2235 insertions(+), 660 deletions(-) delete mode 100644 .eslintrc.cjs create mode 100644 .fallowrc.jsonc create mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/pr.yml delete mode 100644 .prettierignore delete mode 100644 Makefile create mode 100644 justfile create mode 100644 oxfmt.config.ts create mode 100644 oxlint.config.ts create mode 100644 pnpm-workspace.yaml diff --git a/.eslintrc.cjs b/.eslintrc.cjs deleted file mode 100644 index deb120a..0000000 --- a/.eslintrc.cjs +++ /dev/null @@ -1,29 +0,0 @@ -/** @type {import('eslint').Linter.Config} */ -module.exports = { - root: true, - extends: [ - '@block65/eslint-config/javascript', - '@block65/eslint-config/typescript', - ], - parserOptions: { - project: ['./tsconfig.json'], - }, - - overrides: [ - { - files: ['**/*.test.ts', '**/*.test.tsx', '**/test/*.ts'], - rules: { - '@typescript-eslint/no-import-type-side-effects': 'error', - '@typescript-eslint/consistent-type-imports': 'off', - 'import/no-extraneous-dependencies': 'off', - }, - }, - - { - files: ['*.config.js', '*.config.ts', '*.workspace.ts'], - rules: { - 'import/no-extraneous-dependencies': 'off', - }, - }, - ], -}; diff --git a/.fallowrc.jsonc b/.fallowrc.jsonc new file mode 100644 index 0000000..6d376c7 --- /dev/null +++ b/.fallowrc.jsonc @@ -0,0 +1,10 @@ +{ + "extends": ["npm:@block65/shared-config/lib/fallow/fallow.jsonc"], + + "entry": [ + "lib/index.ts", + "examples/example.ts", + "oxfmt.config.ts", + "oxlint.config.ts" + ] +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..de664ce --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,41 @@ +name: CI + +on: + workflow_dispatch: {} + push: + branches: ["canary", "master"] + pull_request: + branches: ["canary", "master"] + +permissions: + contents: read + +jobs: + check: + strategy: + matrix: + version: [24, 26] + os: [ubuntu-latest] + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 + + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: ${{ matrix.version }} + cache: "pnpm" + + # The package scripts call just, so it is needed before install + - uses: extractions/setup-just@53165ef7e734c5c07cb06b3c8e7b647c5aa16db3 # v4.0.0 + + - run: pnpm install --frozen-lockfile + + - run: just typecheck + - run: just lint-ci + - run: just fmt-check + - run: just test + - run: just dead-code diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index e567803..44f9374 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -13,16 +13,24 @@ jobs: id-token: write attestations: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: pnpm/action-setup@v4 + - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: - node-version: 24 + node-version: 26 registry-url: https://registry.npmjs.org/ - cache: 'pnpm' + cache: "pnpm" - - run: make + # The package scripts call just, so it is needed before install + - uses: extractions/setup-just@53165ef7e734c5c07cb06b3c8e7b647c5aa16db3 # v4.0.0 - - run: pnpm publish --access=public --no-git-checks --provenance + - run: pnpm install --frozen-lockfile + + - run: just typecheck + + # the trusted publisher grants "npm stage publish", so this uploads for + # review rather than going live. approve it with pnpm stage approve. + # provenance is attached automatically when publishing over oidc + - run: pnpm stage publish --access public --no-git-checks diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml deleted file mode 100644 index 19eadf8..0000000 --- a/.github/workflows/pr.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Unit Tests (PR) - -on: - pull_request: - branches: ['canary', 'master'] - -jobs: - test: - strategy: - matrix: - version: [24] - os: [ubuntu-latest] - - runs-on: ${{ matrix.os }} - - steps: - - uses: actions/checkout@v4 - - - uses: pnpm/action-setup@v4 - - - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.version }} - cache: 'pnpm' - - - run: make test diff --git a/.node-version b/.node-version index 209e3ef..6f4247a 100644 --- a/.node-version +++ b/.node-version @@ -1 +1 @@ -20 +26 diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index c31eb7f..0000000 --- a/.prettierignore +++ /dev/null @@ -1,7 +0,0 @@ -dist -build -coverage -tmp -.vscode -.idea -pnpm-lock.yaml diff --git a/Makefile b/Makefile deleted file mode 100644 index edcda9f..0000000 --- a/Makefile +++ /dev/null @@ -1,13 +0,0 @@ - -SRCS = $(wildcard lib/**) - -all: node_modules - pnpm exec tsc - -.PHONY: test -test: node_modules - pnpm exec tsc - pnpm exec vitest - -node_modules: package.json - pnpm install diff --git a/justfile b/justfile new file mode 100644 index 0000000..0d9a582 --- /dev/null +++ b/justfile @@ -0,0 +1,29 @@ +check: typecheck lint fmt-check test dead-code + +typecheck: + pnpm exec oxlint --type-aware --type-check + +lint: + pnpm exec oxlint + +# CI reads the annotations, which only this format writes +lint-ci: + pnpm exec oxlint --format=github + +fmt: + pnpm exec oxfmt + +fmt-check: + pnpm exec oxfmt --check + +test: + pnpm exec vitest run + +dead-code: + pnpm exec fallow + +clean: + rm -rf node_modules + +# no build, so just alias and keep the standard +dist-clean: clean diff --git a/oxfmt.config.ts b/oxfmt.config.ts new file mode 100644 index 0000000..e8f3d52 --- /dev/null +++ b/oxfmt.config.ts @@ -0,0 +1,3 @@ +import { defineConfig } from "@block65/shared-config/oxfmt"; + +export default defineConfig(); diff --git a/oxlint.config.ts b/oxlint.config.ts new file mode 100644 index 0000000..6c0d0dd --- /dev/null +++ b/oxlint.config.ts @@ -0,0 +1,5 @@ +import { defineConfig } from "@block65/shared-config/oxlint"; + +export default defineConfig({ + groups: { vitest: "on" }, +}); diff --git a/package.json b/package.json index 830ec31..7d1f4b9 100644 --- a/package.json +++ b/package.json @@ -1,45 +1,46 @@ { - "name": "@block65/openapi-constructs", - "version": "6.0.0", - "private": false, - "license": "UNLICENSED", - "repository": { - "type": "git", - "url": "https://github.com/block65/openapi-constructs" - }, - "sideEffects": false, - "type": "module", - "exports": { - ".": "./lib/index.ts" - }, - "files": [ - "lib" - ], - "scripts": { - "preversion": "make test" - }, - "dependencies": { - "yaml": "^2.8.2" - }, - "devDependencies": { - "@apidevtools/swagger-parser": "^12.1.0", - "@tsconfig/node24": "^24.0.4", - "@tsconfig/strictest": "^2.0.8", - "@types/json-schema": "^7.0.15", - "@types/node": "^24.0.0", - "ajv": "^8.17.1", - "constructs": "^10.4.5", - "json-schema-to-typescript": "^15.0.4", - "type-fest": "^5.0.0", - "typescript": "^5.8.0", - "vitest": "^3.0.0" - }, - "peerDependencies": { - "constructs": "^10.4.4", - "openapi3-ts": "^4.4.0" - }, - "engines": { - "node": ">=24.0.0" - }, - "packageManager": "pnpm@10.30.3" + "name": "@block65/openapi-constructs", + "version": "6.0.0", + "private": false, + "license": "UNLICENSED", + "repository": { + "type": "git", + "url": "https://github.com/block65/openapi-constructs" + }, + "files": [ + "lib" + ], + "type": "module", + "sideEffects": false, + "exports": { + ".": "./lib/index.ts" + }, + "scripts": { + "preversion": "just check" + }, + "devDependencies": { + "@apidevtools/swagger-parser": "^13.1.0", + "@block65/shared-config": "^0.5.0", + "@block65/tsconfig": "^0.3.1", + "@types/json-schema": "^7.0.15", + "@types/node": "^26.6.2", + "ajv": "^8.20.0", + "constructs": "^10.8.1", + "fallow": "^3.28.0", + "openapi3-ts": "^4.6.1", + "oxfmt": "^0.70.0", + "oxlint": "^1.83.0", + "oxlint-tsgolint": "^7.0.2002", + "typescript": "^7.0.2", + "vite": "^8.3.0", + "vitest": "^5.0.1" + }, + "peerDependencies": { + "constructs": "^10.4.4", + "openapi3-ts": "^4.4.0" + }, + "engines": { + "node": ">=24.0.0" + }, + "packageManager": "pnpm@12.5.1" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 62bd59b..63bee14 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,63 +1,228 @@ +--- +lockfileVersion: '9.0' + +importers: + + .: + configDependencies: {} + packageManagerDependencies: + pnpm: + specifier: 12.5.1 + version: 12.5.1 + +packages: + + '@pnpm/exe.android-arm64@12.5.1': + resolution: {integrity: sha512-6LGBmQuDzUmgpHSuy+haG0jrmkwFNa4EuShDZqK3cIBv4Wfb75CzjJmowkzr+iJg40vnBKMQ62t4Ko6mwsId7A==} + cpu: [arm64] + os: [android] + + '@pnpm/exe.android-x64@12.5.1': + resolution: {integrity: sha512-091oCltP3Rx89yxL1Wh5rXkUXEyFzyxIzSHKRFDyDFlQkb8Ox8FaDcm+M8PNkMyqnLA+qeGOLzY7Rjj9izl1Ww==} + cpu: [x64] + os: [android] + + '@pnpm/exe.darwin-arm64@12.5.1': + resolution: {integrity: sha512-r1vEMQPVjRexZMyL2HonxJoo3+nuB/+G+tg+f2PK6faRdXX1TDwevKXOxJAfqlPInTy6W/pm912+s+m0NcR4BQ==} + cpu: [arm64] + os: [darwin] + + '@pnpm/exe.darwin-x64@12.5.1': + resolution: {integrity: sha512-YHwI1g2B+Y2cAEylOR31r781GWijdJL7Gy7TEn/ZpZ5v/Cuwp5/Pz0EFCgo0M2MKLafAbL/laUMGvOpAdnerAw==} + cpu: [x64] + os: [darwin] + + '@pnpm/exe.freebsd-x64@12.5.1': + resolution: {integrity: sha512-sFUMHiYFs6q5syFwgaVppzhQSmqnXoJIz/YwTXIOXTDE76ndIpTRpDGAnBCBKjsaEBnqjJA/2l6hpJcNDNoMiA==} + cpu: [x64] + os: [freebsd] + + '@pnpm/exe.linux-arm64-musl@12.5.1': + resolution: {integrity: sha512-zupuxYZBuBEGjUz805/4/Asq01UNziFADANt8yp+LdUjU9bbMa3xY78Bx1PsGDqBy7aiad4sdUMK8ZxH4n4gmQ==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@pnpm/exe.linux-arm64@12.5.1': + resolution: {integrity: sha512-6hbMWW2/eQo1b5wvQPe/ndLHVTH88Rav6N+ffD4/h6IuFMjWCgasFYYsFv0GrvpqREAx3SwmZSvli+0NFXqbxA==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-ppc64@12.5.1': + resolution: {integrity: sha512-Gg3maDzaE/ZFMV9HIx/BwMN0Y6QbEs13URfxmiYHtKjjLAs5wEQZRMUP87YNLYO7cNaEfbM8GRbS9vPosyD9Gw==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-riscv64@12.5.1': + resolution: {integrity: sha512-6fgWxXYMBMsWQ8DAiRevscX4QZQjwh+FsyCj8ZOk8pMXvqeq84MqBLCmS6KckvnbdqJ+HlSVQ7fs/kd0ldwfhA==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-s390x@12.5.1': + resolution: {integrity: sha512-nzMZruRvxDhEfYUhzu+7EmM39+82Dgagc/6hPFPqGqzXNGcIG0anuxbUxfeNtI64SHt91KuuYUmCB/inlM+Pag==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-x64-musl@12.5.1': + resolution: {integrity: sha512-kHCbegdTNHpzmPtUD4S5fqkVkqgdQR47spF9p2VPP3dIIkZl6Ytk8w0vhlAsAGgge8FmucfwX+vvPKgm0NtOCQ==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@pnpm/exe.linux-x64@12.5.1': + resolution: {integrity: sha512-3PkUBYo5z4dgtlnTNIFj7QGpcDUAuqXz9WGVigPDCeccEnhGiRkWmA5102TmYJHtwJP3LfmE+ZF9PGeWhn8p9Q==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.win32-arm64@12.5.1': + resolution: {integrity: sha512-49NvjZIXHly56/PLoEDD7VphbO4N8dwMXoApElDFCpVGDNGeKlwlib1tXtnpTF6i2/XIsAj7GgatHB5KHXgBnA==} + cpu: [arm64] + os: [win32] + + '@pnpm/exe.win32-x64@12.5.1': + resolution: {integrity: sha512-ePUmQFPAPJFNMwg1lelDx2PdjHqYRiF0yfg5e6qeDbPNOu97QNo557Ru/SxmCrmt8ydQWNuZXWpdEp019md5vw==} + cpu: [x64] + os: [win32] + + pnpm@12.5.1: + resolution: {integrity: sha512-4/MFvHhKK8ifWtO2E4iJRw+ujSr182thIW7JHCw9ZAiXdfneOKrDMQROpA8kXLDVZmOS399lgk4ZB+9qLGLeXw==} + engines: {node: '>=18.*'} + hasBin: true + +snapshots: + + '@pnpm/exe.android-arm64@12.5.1': + optional: true + + '@pnpm/exe.android-x64@12.5.1': + optional: true + + '@pnpm/exe.darwin-arm64@12.5.1': + optional: true + + '@pnpm/exe.darwin-x64@12.5.1': + optional: true + + '@pnpm/exe.freebsd-x64@12.5.1': + optional: true + + '@pnpm/exe.linux-arm64-musl@12.5.1': + optional: true + + '@pnpm/exe.linux-arm64@12.5.1': + optional: true + + '@pnpm/exe.linux-ppc64@12.5.1': + optional: true + + '@pnpm/exe.linux-riscv64@12.5.1': + optional: true + + '@pnpm/exe.linux-s390x@12.5.1': + optional: true + + '@pnpm/exe.linux-x64-musl@12.5.1': + optional: true + + '@pnpm/exe.linux-x64@12.5.1': + optional: true + + '@pnpm/exe.win32-arm64@12.5.1': + optional: true + + '@pnpm/exe.win32-x64@12.5.1': + optional: true + + pnpm@12.5.1: + optionalDependencies: + '@pnpm/exe.android-arm64': 12.5.1 + '@pnpm/exe.android-x64': 12.5.1 + '@pnpm/exe.darwin-arm64': 12.5.1 + '@pnpm/exe.darwin-x64': 12.5.1 + '@pnpm/exe.freebsd-x64': 12.5.1 + '@pnpm/exe.linux-arm64': 12.5.1 + '@pnpm/exe.linux-arm64-musl': 12.5.1 + '@pnpm/exe.linux-ppc64': 12.5.1 + '@pnpm/exe.linux-riscv64': 12.5.1 + '@pnpm/exe.linux-s390x': 12.5.1 + '@pnpm/exe.linux-x64': 12.5.1 + '@pnpm/exe.linux-x64-musl': 12.5.1 + '@pnpm/exe.win32-arm64': 12.5.1 + '@pnpm/exe.win32-x64': 12.5.1 + +--- lockfileVersion: '9.0' settings: autoInstallPeers: true excludeLinksFromLockfile: false +overrides: + oxlint: npm:@block65/oxlint@>=1.83.0 + oxlint-tsgolint: npm:@block65/oxlint-tsgolint@>=7.0.200200 + importers: .: - dependencies: - openapi3-ts: - specifier: ^4.4.0 - version: 4.5.0 - yaml: - specifier: ^2.8.2 - version: 2.8.2 devDependencies: '@apidevtools/swagger-parser': - specifier: ^12.1.0 - version: 12.1.0(openapi-types@12.1.3) - '@tsconfig/node24': - specifier: ^24.0.4 - version: 24.0.4 - '@tsconfig/strictest': - specifier: ^2.0.8 - version: 2.0.8 + specifier: ^13.1.0 + version: 13.1.0(openapi-types@12.1.3) + '@block65/shared-config': + specifier: ^0.5.0 + version: 0.5.0(@block65/oxlint-tsgolint@7.0.200200)(@block65/oxlint@1.83.0(@block65/oxlint-tsgolint@7.0.200200))(eslint@10.11.0)(fallow@3.28.0)(oxfmt@0.70.0) + '@block65/tsconfig': + specifier: ^0.3.1 + version: 0.3.1 '@types/json-schema': specifier: ^7.0.15 version: 7.0.15 '@types/node': - specifier: ^24.0.0 - version: 24.12.0 + specifier: ^26.6.2 + version: 26.6.2 ajv: - specifier: ^8.17.1 - version: 8.18.0 + specifier: ^8.20.0 + version: 8.20.0 constructs: - specifier: ^10.4.5 - version: 10.5.1 - json-schema-to-typescript: - specifier: ^15.0.4 - version: 15.0.4 - type-fest: - specifier: ^5.0.0 - version: 5.4.4 + specifier: ^10.8.1 + version: 10.8.1 + fallow: + specifier: ^3.28.0 + version: 3.28.0 + openapi3-ts: + specifier: ^4.6.1 + version: 4.6.1 + oxfmt: + specifier: ^0.70.0 + version: 0.70.0 + oxlint: + specifier: npm:@block65/oxlint@>=1.83.0 + version: '@block65/oxlint@1.83.0(@block65/oxlint-tsgolint@7.0.200200)' + oxlint-tsgolint: + specifier: npm:@block65/oxlint-tsgolint@>=7.0.200200 + version: '@block65/oxlint-tsgolint@7.0.200200' typescript: - specifier: ^5.8.0 - version: 5.9.3 + specifier: ^7.0.2 + version: 7.0.2 + vite: + specifier: ^8.3.0 + version: 8.3.0(@types/node@26.6.2)(esbuild@0.27.3)(yaml@2.8.2) vitest: - specifier: ^3.0.0 - version: 3.2.4(@types/node@24.12.0)(yaml@2.8.2) + specifier: ^5.0.1 + version: 5.0.1(@types/node@26.6.2)(vite@8.3.0(@types/node@26.6.2)(esbuild@0.27.3)(yaml@2.8.2)) packages: - '@apidevtools/json-schema-ref-parser@11.9.3': - resolution: {integrity: sha512-60vepv88RwcJtSHrD6MjIL6Ta3SOYbgfnkHb+ppAVK+o9mXprRtulx7VlRl3lN3bbvysAfCS7WMVfhUYemB0IQ==} - engines: {node: '>= 16'} - - '@apidevtools/json-schema-ref-parser@14.0.1': - resolution: {integrity: sha512-Oc96zvmxx1fqoSEdUmfmvvb59/KDOnUoJ7s2t7bISyAn0XEz57LCCw8k2Y4Pf3mwKaZLMciESALORLgfe2frCw==} - engines: {node: '>= 16'} + '@apidevtools/json-schema-ref-parser@16.0.3': + resolution: {integrity: sha512-7lZp4XCTZhU/AgiGpIG+j4uJeWPdGbpQ+8wqXrzuHy+tboKsjSacjGH9/kNThRDIFhMWHF3ba+ejS8J3PeSI1Q==} + engines: {node: '>=22.19.0'} + peerDependencies: + '@types/json-schema': ^7.0.15 '@apidevtools/openapi-schemas@2.1.0': resolution: {integrity: sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==} @@ -66,11 +231,86 @@ packages: '@apidevtools/swagger-methods@3.0.2': resolution: {integrity: sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==} - '@apidevtools/swagger-parser@12.1.0': - resolution: {integrity: sha512-e5mJoswsnAX0jG+J09xHFYQXb/bUc5S3pLpMxUuRUA2H8T2kni3yEoyz2R3Dltw5f4A6j6rPNMpWTK+iVDFlng==} + '@apidevtools/swagger-parser@13.1.0': + resolution: {integrity: sha512-dkCuqCWh5U7u0aurmmc/qvel2KeKDxHTwVtUdYonndIpD7mXV6AVaWNdvaja2HAy628z7FT/Qdz8sCgzwhgwKg==} + engines: {node: '>=22.19.0'} peerDependencies: openapi-types: '>=7' + '@block65/oxlint-binding-linux-arm64-gnu@1.83.0': + resolution: {integrity: sha512-iH3ufhqCxiemK1JIQj8Er/Z//L4fNawO9Mb8CzCou6p84l+HWAWkHVV2vWd+Wk8zaMm2m5vs2ONy0CBzrus3Qw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@block65/oxlint-binding-linux-x64-gnu@1.83.0': + resolution: {integrity: sha512-zQ+L+Jpv2m/1DDqZr56WMirvz483ULuGROq1+ka4iqfG2vJh14KNxgWUXaahmnXGSvBnQCH1y0Q0kfMvtqqc8w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@block65/oxlint-plugin@0.11.0': + resolution: {integrity: sha512-68MuNjQkloeKt4B4rQdW5jYoWK/e+6WB11uOmPC66R/sZdMMgi7yqA+YCG8wJRJPy4/NSK7zAdt1JiRoOBp+kA==} + engines: {node: '>=24'} + peerDependencies: + oxlint: ^1.83.0 + + '@block65/oxlint-tsgolint-linux-arm64@7.0.200200': + resolution: {integrity: sha512-BRTbnw/wRUQePD6sKup3cyPoN5jc7GnAOyyJ6TE5lXp5B8cr1zbt3OVjoksoYnceqZpeO5g36PecgauSMNO+Pg==} + cpu: [arm64] + os: [linux] + + '@block65/oxlint-tsgolint-linux-x64@7.0.200200': + resolution: {integrity: sha512-Jj6CpfCqkioWv1kOtDBOFF1BS2OJ9KLWoowfOwPB635VnR1mLsi+utWh/6C0rkxxM6luunX5cB15g1rQ+OaGOA==} + cpu: [x64] + os: [linux] + + '@block65/oxlint-tsgolint@7.0.200200': + resolution: {integrity: sha512-UZ/b6cEl7ahIygTXKaQKcsb7Abt2a8GW1BvZb4JVNJjwEcDXp/k1iL1u/yeUedLzKRQQvqmLHOzXPd6cl82WyQ==} + hasBin: true + + '@block65/oxlint@1.83.0': + resolution: {integrity: sha512-5TR+kgM82dZXxA9WXa+sF1S4bUy5JZzCxeU83ItD/EifhTO1EHiEZ9Pnhf9YD7fjukDb7z7nji5D3CN9i8utvw==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + oxlint-tsgolint: '>=7.0.2001' + vite-plus: '*' + peerDependenciesMeta: + oxlint-tsgolint: + optional: true + vite-plus: + optional: true + + '@block65/shared-config@0.5.0': + resolution: {integrity: sha512-f7iXlaPc0GozRwUHzGxZ9hMrBGC6Ay07zTLt96A2zJ0YXsfyq0NkSOYe58nupTyu5P4rk/+lxjZPearGL/ORVg==} + engines: {node: '>=24'} + peerDependencies: + fallow: ^3.27.0 + oxfmt: '>=0.68.0 <1.0.0' + oxlint: ^1.83.0 + oxlint-tsgolint: ^7.0.2002 + peerDependenciesMeta: + fallow: + optional: true + oxfmt: + optional: true + oxlint: + optional: true + oxlint-tsgolint: + optional: true + + '@block65/tsconfig@0.3.1': + resolution: {integrity: sha512-eDuYELFtiXrjYo/V6N6UQbBx4MKOJD2FZ/mzy2fuypyoNNPRxIaf5FxTl/xavO5y4+5FN2h5kQs81B+ko8MUWw==} + + '@cacheable/memory@2.2.0': + resolution: {integrity: sha512-CTLKqLItRCEixEAewD3/j9DB3/o96gpTPD4eJ1v+DGOlxZRZncRQkGYqqnAGCscYd6RNeXfGeiuCphsPtqyIfQ==} + + '@cacheable/utils@2.5.0': + resolution: {integrity: sha512-buipgOVDkkPXNR5+xBpDw7Zk2n1EvU7qBJCNUcL7rhQ//kfpOXPAvQ511Os0vpLYJ1pZnvudNytkQt2hst3wqA==} + '@esbuild/aix-ppc64@0.27.3': resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} engines: {node: '>=18'} @@ -227,202 +467,507 @@ packages: cpu: [x64] os: [win32] + '@eslint-community/eslint-utils@4.10.1': + resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.23.5': + resolution: {integrity: sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/config-helpers@0.7.0': + resolution: {integrity: sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/core@1.2.1': + resolution: {integrity: sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/css-tree@4.1.1': + resolution: {integrity: sha512-A+s89eP+yDAPkKjnIOfjlkGifg5J7GPn01nWx14UjBVWktfkxOU4975F9ZZ13xNxkGiblNcBaAqgKhnQrW6SWA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/object-schema@3.0.5': + resolution: {integrity: sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@eslint/plugin-kit@0.7.3': + resolution: {integrity: sha512-IkO+/KEUvwbVpiURZg+P7zF74z5Jxe0UgJxVni+RtoHQ6IZieXaO02kmadomap/q+l6bc/jdPGGqTjhuZnuz1Q==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + '@fallow-cli/darwin-arm64@3.28.0': + resolution: {integrity: sha512-gsNF3u1brFaQDPgUPbIli7EwMNy/mLMwKK2vuZUzY786F06bPlTNNVd75ZcIzRowTtBfSm7beISusXJCPSwP3Q==} + cpu: [arm64] + os: [darwin] + + '@fallow-cli/darwin-x64@3.28.0': + resolution: {integrity: sha512-1lLxI8MIKUa9A3E5eGTY9Za4iovGPDCmsiph3OSjIvXAsey3C4iqygs+mIH307mu1yd5r3PqGHVKAdBOScZW4A==} + cpu: [x64] + os: [darwin] + + '@fallow-cli/linux-arm64-gnu@3.28.0': + resolution: {integrity: sha512-GSKMesFLtA41HqhwApqACd1hCerdUCZ/GTuRoraVBtbl0p6Y5gZZpUWeARy/akAi0twbIBV1k7hCLy/sPv5B3w==} + cpu: [arm64] + os: [linux] + + '@fallow-cli/linux-arm64-musl@3.28.0': + resolution: {integrity: sha512-KyLtUIUjFTa5r6ra/Y7omzVxK4lb3k/zBQ7l6PRakhfNwQKjX4/+ULU2lrXyEuA2Jmp4h0ZyE7hMhbOqp/1vlQ==} + cpu: [arm64] + os: [linux] + + '@fallow-cli/linux-x64-gnu@3.28.0': + resolution: {integrity: sha512-UYDgdjuzk9c8YMRtq6GPIrVAk3k8CccdaA9qFQ2Cin7xakp3JAWSep0aMP2kiCr9QZ/hKxxE8Ev+Q6joqbBbtw==} + cpu: [x64] + os: [linux] + + '@fallow-cli/linux-x64-musl@3.28.0': + resolution: {integrity: sha512-ZaFDgMEVMiujrJHCU7kn83yUeC5JkbB9ucOPG24lPqCRLelyd7fhLaEZ8P3zqP+yTYrAi3/KFgQjlVweFQZv6g==} + cpu: [x64] + os: [linux] + + '@fallow-cli/win32-arm64-msvc@3.28.0': + resolution: {integrity: sha512-ZKEFEp4CUnTkBZRjCMqN5GSJjzbmry9ZAjAawQPlwpzsaMlqBjSuZ1rBS/OsaiSHjZW3zXbtL/mc1+I+o2OEgA==} + cpu: [arm64] + os: [win32] + + '@fallow-cli/win32-x64-msvc@3.28.0': + resolution: {integrity: sha512-w8/QvEHLTbu+4DtHF6gd6bAIGEgGxy6zSOhd0F79Hqg3Ydx4NuyQFE7/sTsE8PEKIuYDbMpNy/Dx65W2lWeGWQ==} + cpu: [x64] + os: [win32] + + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} + engines: {node: '>=18.18.0'} + + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - '@jsdevtools/ono@7.1.3': - resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} + '@jridgewell/sourcemap-codec@1.6.0': + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@keyv/bigmap@1.3.1': + resolution: {integrity: sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==} + engines: {node: '>= 18'} + peerDependencies: + keyv: ^5.6.0 + + '@keyv/serialize@1.1.1': + resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==} - '@rollup/rollup-android-arm-eabi@4.59.0': - resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} + '@oxc-project/types@0.150.0': + resolution: {integrity: sha512-rDS5/31E9HfPl/CIzGrn0DOlvBbXFseQ5URJ9sYMfstbKLD/c6Gm9vmRzRGDdAXyOIL4zmO37lc9RIwYqVruZw==} + + '@oxfmt/binding-android-arm-eabi@0.70.0': + resolution: {integrity: sha512-Xd7YO4/T2axEj6FTLcj4Why3mTBqFMg+x24xtorT4Lb2+1g82090GH0a/4U1m0pABGYiix2bq1pqkYrmV3f0Sw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.59.0': - resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==} + '@oxfmt/binding-android-arm64@0.70.0': + resolution: {integrity: sha512-x9rlMYyKXdgKdYyUJzGsK1ZV8P4di/J32ipzcS6Jet6p9r9UAh28neXIMtdlSaJJycdi61Z4YkcLKLpk8ueFjg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.59.0': - resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==} + '@oxfmt/binding-darwin-arm64@0.70.0': + resolution: {integrity: sha512-IUTUPvrBVYy7POh4stXzRdz4IVC/1QSaviCWoyenSlOhGu0X9j5K07vCTM9biLjAA2Zs31l0Rj5vvRpj9n95wA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.59.0': - resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==} + '@oxfmt/binding-darwin-x64@0.70.0': + resolution: {integrity: sha512-vw745q870oTd6J517O24asoX4/E+eK0nxYIFoedSLqgJ+nI5En7+ZS82iZSHZ69zevQrnOXiyHP01dA+t8xD8w==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.59.0': - resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==} - cpu: [arm64] - os: [freebsd] - - '@rollup/rollup-freebsd-x64@4.59.0': - resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==} + '@oxfmt/binding-freebsd-x64@0.70.0': + resolution: {integrity: sha512-NO14EgSM9dFkcg+MfGPxvsKqXYs9LKaxPrOKXpv1R0rLokGGFDcCq6dBMq18dE4wlpFOovX0UZY2uh1P30O7QA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.59.0': - resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} + '@oxfmt/binding-linux-arm-gnueabihf@0.70.0': + resolution: {integrity: sha512-139OEhHarj9CYoJ/i9gXlPv4KLBGtLj2toseOWYFf09QwlhklaZk+wW3aOvlqoeZtuayvkoSNVWra7WJ21s3VQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.59.0': - resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} + '@oxfmt/binding-linux-arm-musleabihf@0.70.0': + resolution: {integrity: sha512-GEh2PY3IWTE0M24eNhTduountANSbWyDmMnzFSQE/nGg/bjPugbUgiGuFu+xdqcQSd/HKSwH80/F2yVVD48yhA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.59.0': - resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} + '@oxfmt/binding-linux-arm64-gnu@0.70.0': + resolution: {integrity: sha512-En5i+UJmZSPxuSf47F2Hl5YOzKB0bicQLnGQkeTCMQ35cWLtbrSwACJKfiLqRZrk05DwSnsJkhBRaM3OURtIaA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.59.0': - resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} + '@oxfmt/binding-linux-arm64-musl@0.70.0': + resolution: {integrity: sha512-WWOoV5W9Im3flVwOVrWn/2DUlOF8v5vcCip+kcNuaMpulRCh6nzzt1Su2vcL2F908YJIXNV3HvegbBHuyLwKHg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.59.0': - resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} - cpu: [loong64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-loong64-musl@4.59.0': - resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} - cpu: [loong64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-ppc64-gnu@4.59.0': - resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} + '@oxfmt/binding-linux-ppc64-gnu@0.70.0': + resolution: {integrity: sha512-YUouneIqW+5n7aE8xx/zeZ6/utr/KH7oykcGoFyd8Uz8uh591T1oKlnoWA3BsRq/ZR42oY1w4MUYvS/0e/MQOA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.59.0': - resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} - cpu: [ppc64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-riscv64-gnu@4.59.0': - resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} + '@oxfmt/binding-linux-riscv64-gnu@0.70.0': + resolution: {integrity: sha512-iEnMf21S5aGVa4hViDGY8sAQ/AHyCu2JPyrQF8P06wtHhSkD1YJBeT4m/KiGewgf7+a5XCYSCRIPcRQa1xwEoQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.59.0': - resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} + '@oxfmt/binding-linux-riscv64-musl@0.70.0': + resolution: {integrity: sha512-91Sdniaj20fQzyMeCxMDzTP4c9s4RB8dGQ308xHhDR0n6U7+1Xq7N9klE7mfXq8iV3lRmIGSXi5X23Hn/0XX/g==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.59.0': - resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} + '@oxfmt/binding-linux-s390x-gnu@0.70.0': + resolution: {integrity: sha512-uUV30M6E+2TKKGMaKiwfeL4RZrviHXlUxsrYJ/jFBb+1EZy+pnFT+hF73eeWdzh5NqPOAnw0iiMAIqjqiLZPFg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.59.0': - resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} + '@oxfmt/binding-linux-x64-gnu@0.70.0': + resolution: {integrity: sha512-ivMcX6kNDPhqtbOaBt/ItFlLlTlXNHLgRuNmxP6Na6UuYXRT10llpJcAPbGeRgjjb3Qzv4jwPp3fB0hui50WNQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.59.0': - resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} + '@oxfmt/binding-linux-x64-musl@0.70.0': + resolution: {integrity: sha512-w+S+fERxYmlZSyZlJK/U292FjyBoH8cCEj21/tYJX6atX5kNSn+HDkhlFQKT2zcMwUW0uAtUL/bOrlwJZwqRdA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-openbsd-x64@4.59.0': - resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} - cpu: [x64] - os: [openbsd] - - '@rollup/rollup-openharmony-arm64@4.59.0': - resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==} + '@oxfmt/binding-openharmony-arm64@0.70.0': + resolution: {integrity: sha512-Zlom1Xkx257R8bk4ZI4zJsrGno2opknz1+5v5baka3nn4FPyvNSdh8JUL4CdN1S1OWRMvJ9UJQ+RfIqGGCUEfA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.59.0': - resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==} + '@oxfmt/binding-win32-arm64-msvc@0.70.0': + resolution: {integrity: sha512-FQgPW5R17vzt7cgrJ8eG/dqX00o2xHsqFeLfw4xzA9FRHpN/DjFo9YDonvIIXGxiEuS9F/jZPnGO+KHNKuCo4Q==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.59.0': - resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==} + '@oxfmt/binding-win32-ia32-msvc@0.70.0': + resolution: {integrity: sha512-ZfZublNhZ+XBndMiXhkiLlPE+XyGRDa0CweeTL6t1fZypfCh1LTg7e5CvnOeTBunq15MskOcRempumSPGAaCQA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.59.0': - resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==} - cpu: [x64] - os: [win32] - - '@rollup/rollup-win32-x64-msvc@4.59.0': - resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==} + '@oxfmt/binding-win32-x64-msvc@0.70.0': + resolution: {integrity: sha512-HlIZEn+WzLQL0DszNzldiRl/DPRCX5R0Vkt6qeUPR1YHwy52hZZo4x6HoTOVmKRP2wUiwPGtKsihNY/f8KRaBg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@tsconfig/node24@24.0.4': - resolution: {integrity: sha512-2A933l5P5oCbv6qSxHs7ckKwobs8BDAe9SJ/Xr2Hy+nDlwmLE1GhFh/g/vXGRZWgxBg9nX/5piDtHR9Dkw/XuA==} - - '@tsconfig/strictest@2.0.8': - resolution: {integrity: sha512-XnQ7vNz5HRN0r88GYf1J9JJjqtZPiHt2woGJOo2dYqyHGGcd6OLGqSlBB6p1j9mpzja6Oe5BoPqWmeDx6X9rLw==} + '@rolldown/binding-android-arm-eabi@1.2.9': + resolution: {integrity: sha512-tNISae1QEf/vkb3xkRcjV5SEdzPE97We5IVaa2Z8jSszQPZ8U60B/YCYpw4QI7VidYsBtKavczXf+DyDs9WGxw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] - '@types/chai@5.2.3': - resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + '@rolldown/binding-android-arm64@1.2.9': + resolution: {integrity: sha512-YC8YsI30o606GTZi0VyzYlsDKFP8W61i/QzayHDkLbNEz/IShqAmTa+hsJRj13xTHA0H+6fk4b2UmGn+Q/cMlg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] - '@types/deep-eql@4.0.2': - resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + '@rolldown/binding-darwin-arm64@1.2.9': + resolution: {integrity: sha512-IwhlH3qK5urrY8hZiEgGkHKEFN901p/p2bjxCxJlr4GyNnF7wYpUvK+Y43uaRYuC4hpfjzbR3SJC3arX1jGvmw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@rolldown/binding-darwin-x64@1.2.9': + resolution: {integrity: sha512-XxpJfVzFh+jilRxIXUqcfYAYcunIc/XEzIizsOL1fcJee5Sf7H3mH8WlLmfHfluz5amqR88QQo9izKtmMlavAw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] - '@types/json-schema@7.0.15': - resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@rolldown/binding-freebsd-x64@1.2.9': + resolution: {integrity: sha512-kSfvhmgeWyfkbT3p/1s5vSgboogoah2zkm9fX2zjg2hHxSV7T4KhMWRUUaRk4OXNqoD3QAUeRqLcs1aZOK4U1g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] - '@types/lodash@4.17.24': - resolution: {integrity: sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==} + '@rolldown/binding-linux-arm-gnueabihf@1.2.9': + resolution: {integrity: sha512-1RVzG17pxqbTfYLC352JlLt6kKLG+6Hr30n8DlIJqsnV5luUDd2Qdx9Ayw1Cabfyb1K9k0jXEZ7evxkRoT+uiw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] - '@types/node@24.12.0': - resolution: {integrity: sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==} + '@rolldown/binding-linux-arm64-gnu@1.2.9': + resolution: {integrity: sha512-BXqPvZ2drqVD+/Z8UpKwcs4Mp7grM+eGFku4CAEKrEtcbAsUpzREphK1sogCRZGreVPiMkiiBtw0n3TPteuqvw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] - '@vitest/expect@3.2.4': - resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} + '@rolldown/binding-linux-arm64-musl@1.2.9': + resolution: {integrity: sha512-11vWvo8YDwLzukt27J3aYDWU+gg2P7J+ZOmiJ0hkF5BXZDW7pVya7r40MXDy6ya0i9KamoENSVKIugvJNgFXIA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] - '@vitest/mocker@3.2.4': - resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} + '@rolldown/binding-linux-ppc64-gnu@1.2.9': + resolution: {integrity: sha512-a1tijMkdwsIARtc0F39ApURROkf3NwqinI6TOiSSWCTR7dT96dffNvMUtDHnq64wKNTIZOIlzKrFvvFUznJiyw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.2.9': + resolution: {integrity: sha512-x6SQNdAvv4c3hWqTMaWuawzMX9myaCs/yEmlGsxJzkdClnHW7FbrjQuSiRDhuSYzEYoEMhsaJy9qHG/XNemJPQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.2.9': + resolution: {integrity: sha512-9s0AZ8BFK5/n7B/TBoa2yJE3gI3KURrbXcPBlsAsvjU4VeJKgE90y1YtNxyEUIcHPQkg6/yfF3qihUrcM/Kf0Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.2.9': + resolution: {integrity: sha512-P7VWAmV+WdJluH7ovnRGoiv2i8To7GAZ+kGzfGup635cyL7SyYl3lSUaA3Gp5THf0n/Co5EyEqb2zbqq+nMOHQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.2.9': + resolution: {integrity: sha512-1qixtsE4BK8h+yS3BfmZ09UhA7O/N4IACva6YBr7EBvCJraByTuRcgOTaiA62Tm0vey3UcKXLOaoGHtYmNGEVg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-win32-arm64-msvc@1.2.9': + resolution: {integrity: sha512-ok8IQjcEPs1AKZfuEUznVBrJw+gK4soq+bx8b1X2XoMqVClarc1q5JDmVtWXY1xfr6ZuHTAsPXHTgTrqKTZeww==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.2.9': + resolution: {integrity: sha512-Ip2mXoU0hM0boq3Rf+ekuT653OROSo6aSYcPT1VHE4q52KvyxgFkQgrgb/IEsxOuvQ2fZZbs8khJAyCEPM24/g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/esrecurse@4.3.1': + resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/node@26.6.2': + resolution: {integrity: sha512-X1P21scMv4zGKLYqjdGjaKa7COa0RKVYYZZN/NfvLQ1JegxFhdhpZG/Lyn8AXx6CDUavKAd11v6BvfpkDByK8g==} + + '@typescript/typescript-aix-ppc64@7.0.2': + resolution: {integrity: sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==} + engines: {node: '>=16.20.0'} + cpu: [ppc64] + os: [aix] + + '@typescript/typescript-darwin-arm64@7.0.2': + resolution: {integrity: sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [darwin] + + '@typescript/typescript-darwin-x64@7.0.2': + resolution: {integrity: sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [darwin] + + '@typescript/typescript-freebsd-arm64@7.0.2': + resolution: {integrity: sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [freebsd] + + '@typescript/typescript-freebsd-x64@7.0.2': + resolution: {integrity: sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [freebsd] + + '@typescript/typescript-linux-arm64@7.0.2': + resolution: {integrity: sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [linux] + + '@typescript/typescript-linux-arm@7.0.2': + resolution: {integrity: sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==} + engines: {node: '>=16.20.0'} + cpu: [arm] + os: [linux] + + '@typescript/typescript-linux-loong64@7.0.2': + resolution: {integrity: sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==} + engines: {node: '>=16.20.0'} + cpu: [loong64] + os: [linux] + + '@typescript/typescript-linux-mips64el@7.0.2': + resolution: {integrity: sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==} + engines: {node: '>=16.20.0'} + cpu: [mips64el] + os: [linux] + + '@typescript/typescript-linux-ppc64@7.0.2': + resolution: {integrity: sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==} + engines: {node: '>=16.20.0'} + cpu: [ppc64] + os: [linux] + + '@typescript/typescript-linux-riscv64@7.0.2': + resolution: {integrity: sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==} + engines: {node: '>=16.20.0'} + cpu: [riscv64] + os: [linux] + + '@typescript/typescript-linux-s390x@7.0.2': + resolution: {integrity: sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==} + engines: {node: '>=16.20.0'} + cpu: [s390x] + os: [linux] + + '@typescript/typescript-linux-x64@7.0.2': + resolution: {integrity: sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [linux] + + '@typescript/typescript-netbsd-arm64@7.0.2': + resolution: {integrity: sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [netbsd] + + '@typescript/typescript-netbsd-x64@7.0.2': + resolution: {integrity: sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [netbsd] + + '@typescript/typescript-openbsd-arm64@7.0.2': + resolution: {integrity: sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [openbsd] + + '@typescript/typescript-openbsd-x64@7.0.2': + resolution: {integrity: sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [openbsd] + + '@typescript/typescript-sunos-x64@7.0.2': + resolution: {integrity: sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [sunos] + + '@typescript/typescript-win32-arm64@7.0.2': + resolution: {integrity: sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [win32] + + '@typescript/typescript-win32-x64@7.0.2': + resolution: {integrity: sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [win32] + + '@vitest/mocker@5.0.1': + resolution: {integrity: sha512-6K1DoBNAPGvuOcSsGA4D6x+5zEEff/KmOOP3uetT2TrGpVfI+HRHRnJJfKi5ib/g1vx8IYHQD8s0pbJz8WQI7Q==} peerDependencies: msw: ^2.4.9 - vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: msw: optional: true vite: optional: true - '@vitest/pretty-format@3.2.4': - resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} - - '@vitest/runner@3.2.4': - resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} + '@vitest/spy@5.0.1': + resolution: {integrity: sha512-rbto/mF/SGERxEgYOek7Xm6B9b+y+mVoo+f4b2LymYO8zM1b7uB5nHuhVMTP2hxdzgxvGiZYGxGIaMvL5y180Q==} - '@vitest/snapshot@3.2.4': - resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} - - '@vitest/spy@3.2.4': - resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - '@vitest/utils@3.2.4': - resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + acorn@8.18.0: + resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==} + engines: {node: '>=0.4.0'} + hasBin: true ajv-draft-04@1.0.0: resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} @@ -432,8 +977,11 @@ packages: ajv: optional: true - ajv@8.18.0: - resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + + ajv@8.20.0: + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -442,23 +990,66 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} - cac@6.7.14: - resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} - engines: {node: '>=8'} + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + baseline-browser-mapping@2.11.25: + resolution: {integrity: sha512-gMmEShwwq7FJqMwvfRwvCl00v4kN+KOfJqXn+f4nrufak5gNHJOksd/60Dvjuz7sI8Y5WiSFBa8FEYr+zoyqCw==} + engines: {node: '>=6.0.0'} + hasBin: true + + brace-expansion@5.0.12: + resolution: {integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==} + engines: {node: 20 || >=22} + + browserslist@4.29.0: + resolution: {integrity: sha512-3GSvyjvDI4Dur1Meg2BekJquu5uF+9R9a1+5M1Mde192eZoXbeXjzgOsgqPS2V8D5wrrip0gR5Hf/GhWQ9ZzaA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + builtin-modules@5.4.0: + resolution: {integrity: sha512-JCWSCdun+4Ovd9BhM/Vtlmwb7oD6Wl4YiPRXXwhAuwlPhW1un/MKgXn7GZoFm53ginnoNzus69V8JWx0K393jg==} + engines: {node: '>=18.20'} + + cacheable@2.5.0: + resolution: {integrity: sha512-60cyAOytib/OzBw1JNSoSV/boK1AtHryDIjvVBk7XbN4ugfkM3+Sry7fEjNgPMGgOjuaZPAp8ruZ0Cxafwyq9g==} call-me-maybe@1.0.2: resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} - chai@5.3.3: - resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + caniuse-lite@1.0.30001810: + resolution: {integrity: sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==} + + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} - check-error@2.1.3: - resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} - engines: {node: '>= 16'} + change-case@5.4.4: + resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==} + + ci-info@4.4.0: + resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} + engines: {node: '>=8'} + + constructs@10.8.1: + resolution: {integrity: sha512-98yGXYyhePqPYh3cYu8nzBERmAhC0DONe3UD03okK0nehZ7hYP4wgZuf02a04+uOWxnTJ5Rpp5m0GRNpwyLGGA==} + + convert-hrtime@5.0.0: + resolution: {integrity: sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==} + engines: {node: '>=12'} + + core-js-compat@3.50.0: + resolution: {integrity: sha512-XGpFGbMLHwSt74YLTKho7Ib242qi6O8MSX+sRokV4oz7iKXvQWGYZthjIhjRGMxjzVkAubBO512dKGYcefmX3Q==} + engines: {node: '>=6.4.0'} - constructs@10.5.1: - resolution: {integrity: sha512-f/TfFXiS3G/yVIXDjOQn9oTlyu9Wo7Fxyjj7lb8r92iO81jR2uST+9MstxZTmDGx/CgIbxCXkFXgupnLTNxQZg==} + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + css-tree@3.2.1: + resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} @@ -469,28 +1060,114 @@ packages: supports-color: optional: true - deep-eql@5.0.2: - resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} - engines: {node: '>=6'} + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + detect-indent@7.0.2: + resolution: {integrity: sha512-y+8xyqdGLL+6sh0tVeHcfP/QDd8gUgbasolJJpY7NgeQGSZ739bDtSiaiDgtoicy+mtYB81dKLxO9xRhCyIB3A==} + engines: {node: '>=12.20'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + electron-to-chromium@1.5.434: + resolution: {integrity: sha512-7EeFW9OLf1NN9NKQP9xdOE/XKqjxv7JDPP6AEAkHli11+Fae1OaLoNuAf13hQv1PPNtuES0pLQSPk2+fS3s8ww==} + + entities@8.1.0: + resolution: {integrity: sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==} + engines: {node: '>=20.19.0'} - es-module-lexer@1.7.0: - resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + es-module-lexer@2.3.2: + resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} esbuild@0.27.3: resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} engines: {node: '>=18'} hasBin: true + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-plugin-unicorn@74.0.0: + resolution: {integrity: sha512-AGnsGi2SxHg1HEAXxn9nSnZfyjvTWkxm8E8hpd/9tD6dLjBUdcD7+D6ZN64HmmCXTSXlrwVyUqe20Uyb2CaurA==} + engines: {node: '>=22'} + peerDependencies: + eslint: '>=10.4' + + eslint-scope@9.1.2: + resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@10.11.0: + resolution: {integrity: sha512-P7a6UEEqb9G95MYAtqkmsTbVXIYyzIfl6NGOIJk162PaahFxFyeGcrlXYFSiagECg4sEm8IseJdZBKR3rx6MsQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@11.2.0: + resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} - expect-type@1.3.0: - resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} engines: {node: '>=12.0.0'} + fallow-type-aware@3.28.0: + resolution: {integrity: sha512-QazEIGu/D2aicBobtzbVt9IvK/YMzbQWBhs3rcDFfXKzWjBMiiOHmn70Fluod3N7BjE4zh5QM65mqGvBHjylAw==} + engines: {node: '>=20'} + hasBin: true + + fallow@3.28.0: + resolution: {integrity: sha512-8rUgXb+lep5zi/Ss0C/GajxKldsG5zyRm1EenQRdDDhIuMGeMeV/HKi6TyaAFezBeEt9aIob0YO5EvxvNC3emg==} + engines: {node: '>=22'} + hasBin: true + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-uri@3.1.0: resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} @@ -503,11 +1180,70 @@ packages: picomatch: optional: true + file-entry-cache@11.1.5: + resolution: {integrity: sha512-+PFTHITI08JIGhnNpGNI8T8inUpgZfk3GNEqfT9R2zZV2iFXg3CvqzSl/uEhs7TSGujYRELEANyDvS8Fj7+S7Q==} + + find-up-simple@1.0.1: + resolution: {integrity: sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==} + engines: {node: '>=18'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@6.1.23: + resolution: {integrity: sha512-f++BY9pTk+983xK1FLzlLpmM0i0z+jHmx3QESGkURMXujQZz1k5wzwX6hjnQ8goaD0B+sYnDK1yZ6MTyZfUaqA==} + + flatted@3.4.4: + resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + function-timeout@1.0.2: + resolution: {integrity: sha512-939eZS4gJ3htTHAldmyyuzlrD58P03fHG49v2JfFXbV6OhvZKRC9j2yAtdHw/zrp2zXHuv05zMIy40F0ge7spA==} + engines: {node: '>=18'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globals@17.12.0: + resolution: {integrity: sha512-cezEd/DTyyht9cvSSURyygXPfy04GtWO/5e6ZPvH7fCtjKz9PYOmuawphw1Ctd1f6C+5JypXfGD7ahNMXvevBA==} + engines: {node: '>=18'} + + hashery@1.5.1: + resolution: {integrity: sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ==} + engines: {node: '>=20'} + + hookified@1.15.1: + resolution: {integrity: sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==} + + hookified@2.2.0: + resolution: {integrity: sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA==} + + identifier-regex@1.1.0: + resolution: {integrity: sha512-SLX4H/vtcYlYnL7XqnuJKHU7Z8517TgsW9nmQiGOgMCjQ8V/deLYu6bEmbGoXe7WMMhc9+EUGyFFneHja8KabA==} + engines: {node: '>=18'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + indent-string@5.0.0: + resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} + engines: {node: '>=12'} + + is-builtin-module@5.0.0: + resolution: {integrity: sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA==} + engines: {node: '>=18.20'} + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -516,79 +1252,260 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} - js-tokens@9.0.1: - resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + is-identifier@1.1.0: + resolution: {integrity: sha512-NhOds0mDx9lJu+1lBRO0xbwFo5nobA7GCk/0e5xjr6+6XugX985+0OyGX35BNrTkPAsdLcIKg02HUQJOK8D8kw==} + engines: {node: '>=18'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - js-yaml@4.1.1: - resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + js-yaml@5.4.2: + resolution: {integrity: sha512-m+aqu+LwO1O6sIopafj8HUVl5aawITwZQe/yHpMCKjaWBaA/d07B/QdMb3529REftiU+RMMHL3Vlsw3hON7vWg==} hasBin: true - json-schema-to-typescript@15.0.4: - resolution: {integrity: sha512-Su9oK8DR4xCmDsLlyvadkXzX6+GGXJpbhwoLtOGArAG61dvbW4YQmSEno2y66ahpIdmLMg6YUf/QHLgiwvkrHQ==} - engines: {node: '>=16.0.0'} + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} hasBin: true + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - lodash@4.17.23: - resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + keyv@5.6.0: + resolution: {integrity: sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lightningcss-android-arm64@1.33.0: + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.33.0: + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.33.0: + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.33.0: + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.33.0: + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.33.0: + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.33.0: + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.33.0: + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.33.0: + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.33.0: + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.33.0: + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.33.0: + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + engines: {node: '>= 12.0.0'} - loupe@3.2.1: - resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + magic-string@1.4.1: + resolution: {integrity: sha512-8lyCu36ErXR0J9uaGKlKQoiLZKmtI63YGLE8G2o9jyRPdr4X47LusSOwgOJOzcVtp81fTAAjxR7BwKz682Jhow==} + + make-asynchronous@1.1.0: + resolution: {integrity: sha512-ayF7iT+44LXdxJLTrTd3TLQpFDDvPCBxXxbv+pMUSuHA5Q8zyAfwkRP6aHHwNVFBUFWtxAHqwNJxF8vMZLAbVg==} + engines: {node: '>=18'} - magic-string@0.30.21: - resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + mdn-data@2.27.1: + resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} - minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + mdn-data@2.35.0: + resolution: {integrity: sha512-k+1+dEIm2z/BEfdvYzT/fIyAGeDBo/1AJNJI4ilFXJRYoxP/3Ds+GP8shAOIq8jYXY9N5nGB3Ag104NsagmLAA==} + + minimatch@10.2.6: + resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} + engines: {node: 18 || 20 || >=22} ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + nanoid@3.3.19: + resolution: {integrity: sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + node-releases@2.0.56: + resolution: {integrity: sha512-x0InOIyzgdk+eyaWaRJFH5snEtiImgBgblZ2CyPrLmqqcuMQkEvcDPHbzqbD8eDsSeJbVOjn+crzyzHaM4D+/A==} + engines: {node: '>=18'} + + obug@2.2.1: + resolution: {integrity: sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==} + engines: {node: '>=12.20.0'} + openapi-types@12.1.3: resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} - openapi3-ts@4.5.0: - resolution: {integrity: sha512-jaL+HgTq2Gj5jRcfdutgRGLosCy/hT8sQf6VOy+P+g36cZOjI1iukdPnijC+4CmeRzg/jEllJUboEic2FhxhtQ==} + openapi3-ts@4.6.1: + resolution: {integrity: sha512-XW9MOldkhoICNeXVzzmXzmOW5G73ppOEGmh7fLCqHjgfdEYCGGN+00MlVCeUZgovjjfC56j9tvtDt1zGabNjjA==} - pathe@2.0.3: - resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} - pathval@2.0.1: - resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} - engines: {node: '>= 14.16'} + oxfmt@0.70.0: + resolution: {integrity: sha512-IsHxZ4y0wQLLMhnrJblBJgZsLDzfULrJnAw5j/QqsTlMa/m3AqsbToi+W71uhBGaqlqq/PbbjvHc09TJwdv3Tw==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + svelte: ^5.0.0 + vite-plus: '*' + peerDependenciesMeta: + svelte: + optional: true + vite-plus: + optional: true + + p-event@6.0.1: + resolution: {integrity: sha512-Q6Bekk5wpzW5qIyUP4gdMEujObYstZl6DMMOSenwBvV0BlE5LkDwkjs5yHbZmdCEq2o4RJx4tE1vwxFVf2FG1w==} + engines: {node: '>=16.17'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + p-timeout@6.1.4: + resolution: {integrity: sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==} + engines: {node: '>=14.16'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@4.0.3: - resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} engines: {node: '>=12'} - postcss@8.5.8: - resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} + pluralize@8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + + postcss@8.5.28: + resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==} engines: {node: ^10 || ^12 || >=14} - prettier@3.8.1: - resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} - engines: {node: '>=14'} + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + qified@0.10.1: + resolution: {integrity: sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA==} + engines: {node: '>=20'} + + quote-js-string@0.1.0: + resolution: {integrity: sha512-Y3NoRtprEEZQD8RfxMCfS0ZTqc4e+i18OrXEXAvpM6TfC/3y+0L5rNbZiSnbBBEkDfFzbpd8o+cE8q3/anjMGA==} + engines: {node: '>=22'} + + regjsparser@0.13.3: + resolution: {integrity: sha512-ycwFAS14Jw4mppvmK4GR/J6u3WpWpjkEApehuHtLc/8VpPNpDMbQ4WjqwplXifGeyKOzHSFLmSPqzksDQE2Sfg==} hasBin: true require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} - rollup@4.59.0: - resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} + reserved-identifiers@1.2.0: + resolution: {integrity: sha512-yE7KUfFvaBFzGPs5H3Ops1RevfUEsDc5Iz65rOwWg4lE8HJSYtle77uul3+573457oHvBKuHYDl/xqUkKpEEdw==} + engines: {node: '>=18'} + + rolldown@1.2.9: + resolution: {integrity: sha512-hx/Pv0N1haXRb11qkfnK5MXB/iqr7i0yjWQqmO9uHqZpBgQSqzc8UsSnEpalsh+j1I8qQ2CkXAkJC8Br3dKSlg==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} @@ -599,64 +1516,76 @@ packages: stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - std-env@3.10.0: - resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + std-env@4.2.0: + resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} - strip-literal@3.1.0: - resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} + strip-indent@4.1.1: + resolution: {integrity: sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==} + engines: {node: '>=12'} - tagged-tag@1.0.0: - resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} - engines: {node: '>=20'} + super-regex@1.1.0: + resolution: {integrity: sha512-WHkws2ZflZe41zj6AolvvmaTrWds/VuyeYr9iPVv/oQeaIoVxMKaushfFWpOGDT+GuBrM/sVqF8KUCYQlSSTdQ==} + engines: {node: '>=18'} + + time-span@5.1.0: + resolution: {integrity: sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==} + engines: {node: '>=12'} - tinybench@2.9.0: - resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + tinybench@6.1.4: + resolution: {integrity: sha512-9APumHG7r4yOk4X4WlkmE71aZcv1gvin1czO3OQ1U9iJcFA5Ja/ygyb0vPOVHTthFozUYs8CLoLUlM8grb2lTQ==} + engines: {node: '>=20.0.0'} - tinyexec@0.3.2: - resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + tinyexec@1.3.0: + resolution: {integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==} + engines: {node: '>=18'} - tinyglobby@0.2.15: - resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} - tinypool@1.1.1: - resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} - engines: {node: ^18.0.0 || >=20.0.0} + tinypool@2.1.2: + resolution: {integrity: sha512-9YodfrxS9g9IbFr/KOjE5bAeJ0p61n3bW6mqvy0jtoeKd1kTW1Cxm0oulm6KX2lyM9Gl6WIe8nEbY7LWv5ZJww==} + engines: {node: ^20.0.0 || >=22.0.0} - tinyrainbow@2.0.0: - resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} - engines: {node: '>=14.0.0'} + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} - tinyspy@4.0.4: - resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} - engines: {node: '>=14.0.0'} - - type-fest@5.4.4: - resolution: {integrity: sha512-JnTrzGu+zPV3aXIUhnyWJj4z/wigMsdYajGLIYakqyOW1nPllzXEJee0QQbHj+CTIQtXGlAjuK0UY+2xTyjVAw==} - engines: {node: '>=20'} + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} - typescript@5.9.3: - resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} - engines: {node: '>=14.17'} + typescript@7.0.2: + resolution: {integrity: sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==} + engines: {node: '>=16.20.0'} hasBin: true - undici-types@7.16.0: - resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + undici-types@8.9.0: + resolution: {integrity: sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==} - vite-node@3.2.4: - resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + undici@8.11.0: + resolution: {integrity: sha512-AdzuGcAkzhbha3GgCSoUBx2P0quwym6qdFNCQz/fDGZc8w9XJ741wHUbt2kTmfPz+ZIOMkySFkiY7RXmiDDC8w==} + engines: {node: '>=22.19.0'} + + update-browserslist-db@1.3.3: + resolution: {integrity: sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ==} hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - vite@7.3.1: - resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} + vite@8.3.0: + resolution: {integrity: sha512-lhZBVvEHefgE+HQZC9O7EBJgCU/nVzFNl7vkS4RE0APtWLP02/8QVIkQtzBxPquh7lq5/78NHipTj7ODQ6XuyQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.7.1 + esbuild: ^0.27.0 || ^0.28.0 jiti: '>=1.21.0' less: ^4.0.0 - lightningcss: ^1.21.0 sass: ^1.70.0 sass-embedded: ^1.70.0 stylus: '>=0.54.8' @@ -667,12 +1596,14 @@ packages: peerDependenciesMeta: '@types/node': optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true jiti: optional: true less: optional: true - lightningcss: - optional: true sass: optional: true sass-embedded: @@ -688,26 +1619,39 @@ packages: yaml: optional: true - vitest@3.2.4: - resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + vitest@5.0.1: + resolution: {integrity: sha512-iA95lQbKEkvrtTkdAgnWbXfbipWiiWe/hDl2P5tMi6WFwD76G0NxXAGp/M9EOcYupeGJRr6wppMc7CoA41TQjg==} + engines: {node: ^22.12.0 || ^24.0.0 || >=26.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' - '@types/debug': ^4.1.12 - '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 - '@vitest/browser': 3.2.4 - '@vitest/ui': 3.2.4 + '@opentelemetry/api': ^1.9.0 + '@types/node': ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 5.0.1 + '@vitest/browser-preview': 5.0.1 + '@vitest/browser-webdriverio': ^5.0.0-beta.5 || >=5.0.0 + '@vitest/coverage-istanbul': 5.0.1 + '@vitest/coverage-v8': 5.0.1 + '@vitest/ui': 5.0.1 happy-dom: '*' jsdom: '*' + vite: ^6.4.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: '@edge-runtime/vm': optional: true - '@types/debug': + '@opentelemetry/api': optional: true '@types/node': optional: true - '@vitest/browser': + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': optional: true '@vitest/ui': optional: true @@ -716,301 +1660,580 @@ packages: jsdom: optional: true + web-worker@1.5.0: + resolution: {integrity: sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw==} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + why-is-node-running@2.3.0: resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} engines: {node: '>=8'} hasBin: true + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + yaml@2.8.2: resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} engines: {node: '>= 14.6'} hasBin: true -snapshots: + yaml@2.9.1: + resolution: {integrity: sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==} + engines: {node: '>= 14.6'} + hasBin: true + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + +snapshots: + + '@apidevtools/json-schema-ref-parser@16.0.3(@types/json-schema@7.0.15)': + dependencies: + '@types/json-schema': 7.0.15 + js-yaml: 5.4.2 + undici: 8.11.0 + + '@apidevtools/openapi-schemas@2.1.0': {} + + '@apidevtools/swagger-methods@3.0.2': {} + + '@apidevtools/swagger-parser@13.1.0(openapi-types@12.1.3)': + dependencies: + '@apidevtools/json-schema-ref-parser': 16.0.3(@types/json-schema@7.0.15) + '@apidevtools/openapi-schemas': 2.1.0 + '@apidevtools/swagger-methods': 3.0.2 + '@types/json-schema': 7.0.15 + ajv: 8.20.0 + ajv-draft-04: 1.0.0(ajv@8.20.0) + call-me-maybe: 1.0.2 + openapi-types: 12.1.3 + + '@block65/oxlint-binding-linux-arm64-gnu@1.83.0': + optional: true + + '@block65/oxlint-binding-linux-x64-gnu@1.83.0': + optional: true + + '@block65/oxlint-plugin@0.11.0(@block65/oxlint@1.83.0(@block65/oxlint-tsgolint@7.0.200200))(eslint@10.11.0)': + dependencies: + css-tree: 3.2.1 + eslint-plugin-unicorn: 74.0.0(eslint@10.11.0) + oxlint: '@block65/oxlint@1.83.0(@block65/oxlint-tsgolint@7.0.200200)' + transitivePeerDependencies: + - eslint + + '@block65/oxlint-tsgolint-linux-arm64@7.0.200200': + optional: true + + '@block65/oxlint-tsgolint-linux-x64@7.0.200200': + optional: true + + '@block65/oxlint-tsgolint@7.0.200200': + optionalDependencies: + '@block65/oxlint-tsgolint-linux-arm64': 7.0.200200 + '@block65/oxlint-tsgolint-linux-x64': 7.0.200200 + + '@block65/oxlint@1.83.0(@block65/oxlint-tsgolint@7.0.200200)': + optionalDependencies: + '@block65/oxlint-binding-linux-arm64-gnu': 1.83.0 + '@block65/oxlint-binding-linux-x64-gnu': 1.83.0 + oxlint-tsgolint: '@block65/oxlint-tsgolint@7.0.200200' + + '@block65/shared-config@0.5.0(@block65/oxlint-tsgolint@7.0.200200)(@block65/oxlint@1.83.0(@block65/oxlint-tsgolint@7.0.200200))(eslint@10.11.0)(fallow@3.28.0)(oxfmt@0.70.0)': + dependencies: + '@block65/oxlint-plugin': 0.11.0(@block65/oxlint@1.83.0(@block65/oxlint-tsgolint@7.0.200200))(eslint@10.11.0) + optionalDependencies: + fallow: 3.28.0 + oxfmt: 0.70.0 + oxlint: '@block65/oxlint@1.83.0(@block65/oxlint-tsgolint@7.0.200200)' + oxlint-tsgolint: '@block65/oxlint-tsgolint@7.0.200200' + transitivePeerDependencies: + - eslint + + '@block65/tsconfig@0.3.1': {} + + '@cacheable/memory@2.2.0': + dependencies: + '@cacheable/utils': 2.5.0 + '@keyv/bigmap': 1.3.1(keyv@5.6.0) + hookified: 1.15.1 + keyv: 5.6.0 + + '@cacheable/utils@2.5.0': + dependencies: + hashery: 1.5.1 + keyv: 5.6.0 + + '@esbuild/aix-ppc64@0.27.3': + optional: true + + '@esbuild/android-arm64@0.27.3': + optional: true + + '@esbuild/android-arm@0.27.3': + optional: true + + '@esbuild/android-x64@0.27.3': + optional: true + + '@esbuild/darwin-arm64@0.27.3': + optional: true + + '@esbuild/darwin-x64@0.27.3': + optional: true + + '@esbuild/freebsd-arm64@0.27.3': + optional: true + + '@esbuild/freebsd-x64@0.27.3': + optional: true + + '@esbuild/linux-arm64@0.27.3': + optional: true + + '@esbuild/linux-arm@0.27.3': + optional: true + + '@esbuild/linux-ia32@0.27.3': + optional: true + + '@esbuild/linux-loong64@0.27.3': + optional: true + + '@esbuild/linux-mips64el@0.27.3': + optional: true + + '@esbuild/linux-ppc64@0.27.3': + optional: true + + '@esbuild/linux-riscv64@0.27.3': + optional: true + + '@esbuild/linux-s390x@0.27.3': + optional: true + + '@esbuild/linux-x64@0.27.3': + optional: true + + '@esbuild/netbsd-arm64@0.27.3': + optional: true + + '@esbuild/netbsd-x64@0.27.3': + optional: true + + '@esbuild/openbsd-arm64@0.27.3': + optional: true + + '@esbuild/openbsd-x64@0.27.3': + optional: true + + '@esbuild/openharmony-arm64@0.27.3': + optional: true + + '@esbuild/sunos-x64@0.27.3': + optional: true + + '@esbuild/win32-arm64@0.27.3': + optional: true + + '@esbuild/win32-ia32@0.27.3': + optional: true + + '@esbuild/win32-x64@0.27.3': + optional: true + + '@eslint-community/eslint-utils@4.10.1(eslint@10.11.0)': + dependencies: + eslint: 10.11.0 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.23.5': + dependencies: + '@eslint/object-schema': 3.0.5 + debug: 4.4.3 + minimatch: 10.2.6 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.7.0': + dependencies: + '@eslint/core': 1.2.1 + + '@eslint/core@1.2.1': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/css-tree@4.1.1': + dependencies: + mdn-data: 2.35.0 + source-map-js: 1.2.1 + + '@eslint/object-schema@3.0.5': {} + + '@eslint/plugin-kit@0.7.3': + dependencies: + '@eslint/core': 1.2.1 + levn: 0.4.1 + + '@fallow-cli/darwin-arm64@3.28.0': + optional: true + + '@fallow-cli/darwin-x64@3.28.0': + optional: true + + '@fallow-cli/linux-arm64-gnu@3.28.0': + optional: true + + '@fallow-cli/linux-arm64-musl@3.28.0': + optional: true + + '@fallow-cli/linux-x64-gnu@3.28.0': + optional: true + + '@fallow-cli/linux-x64-musl@3.28.0': + optional: true + + '@fallow-cli/win32-arm64-msvc@3.28.0': + optional: true + + '@fallow-cli/win32-x64-msvc@3.28.0': + optional: true - '@apidevtools/json-schema-ref-parser@11.9.3': + '@humanfs/core@0.19.2': dependencies: - '@jsdevtools/ono': 7.1.3 - '@types/json-schema': 7.0.15 - js-yaml: 4.1.1 + '@humanfs/types': 0.15.0 - '@apidevtools/json-schema-ref-parser@14.0.1': + '@humanfs/node@0.16.8': dependencies: - '@types/json-schema': 7.0.15 - js-yaml: 4.1.1 + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 + '@humanwhocodes/retry': 0.4.3 - '@apidevtools/openapi-schemas@2.1.0': {} + '@humanfs/types@0.15.0': {} - '@apidevtools/swagger-methods@3.0.2': {} + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@jridgewell/resolve-uri@3.1.2': {} - '@apidevtools/swagger-parser@12.1.0(openapi-types@12.1.3)': + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/sourcemap-codec@1.6.0': {} + + '@jridgewell/trace-mapping@0.3.31': dependencies: - '@apidevtools/json-schema-ref-parser': 14.0.1 - '@apidevtools/openapi-schemas': 2.1.0 - '@apidevtools/swagger-methods': 3.0.2 - ajv: 8.18.0 - ajv-draft-04: 1.0.0(ajv@8.18.0) - call-me-maybe: 1.0.2 - openapi-types: 12.1.3 + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 - '@esbuild/aix-ppc64@0.27.3': - optional: true + '@keyv/bigmap@1.3.1(keyv@5.6.0)': + dependencies: + hashery: 1.5.1 + hookified: 1.15.1 + keyv: 5.6.0 - '@esbuild/android-arm64@0.27.3': + '@keyv/serialize@1.1.1': {} + + '@oxc-project/types@0.150.0': {} + + '@oxfmt/binding-android-arm-eabi@0.70.0': optional: true - '@esbuild/android-arm@0.27.3': + '@oxfmt/binding-android-arm64@0.70.0': optional: true - '@esbuild/android-x64@0.27.3': + '@oxfmt/binding-darwin-arm64@0.70.0': optional: true - '@esbuild/darwin-arm64@0.27.3': + '@oxfmt/binding-darwin-x64@0.70.0': optional: true - '@esbuild/darwin-x64@0.27.3': + '@oxfmt/binding-freebsd-x64@0.70.0': optional: true - '@esbuild/freebsd-arm64@0.27.3': + '@oxfmt/binding-linux-arm-gnueabihf@0.70.0': optional: true - '@esbuild/freebsd-x64@0.27.3': + '@oxfmt/binding-linux-arm-musleabihf@0.70.0': optional: true - '@esbuild/linux-arm64@0.27.3': + '@oxfmt/binding-linux-arm64-gnu@0.70.0': optional: true - '@esbuild/linux-arm@0.27.3': + '@oxfmt/binding-linux-arm64-musl@0.70.0': optional: true - '@esbuild/linux-ia32@0.27.3': + '@oxfmt/binding-linux-ppc64-gnu@0.70.0': optional: true - '@esbuild/linux-loong64@0.27.3': + '@oxfmt/binding-linux-riscv64-gnu@0.70.0': optional: true - '@esbuild/linux-mips64el@0.27.3': + '@oxfmt/binding-linux-riscv64-musl@0.70.0': optional: true - '@esbuild/linux-ppc64@0.27.3': + '@oxfmt/binding-linux-s390x-gnu@0.70.0': optional: true - '@esbuild/linux-riscv64@0.27.3': + '@oxfmt/binding-linux-x64-gnu@0.70.0': optional: true - '@esbuild/linux-s390x@0.27.3': + '@oxfmt/binding-linux-x64-musl@0.70.0': optional: true - '@esbuild/linux-x64@0.27.3': + '@oxfmt/binding-openharmony-arm64@0.70.0': optional: true - '@esbuild/netbsd-arm64@0.27.3': + '@oxfmt/binding-win32-arm64-msvc@0.70.0': optional: true - '@esbuild/netbsd-x64@0.27.3': + '@oxfmt/binding-win32-ia32-msvc@0.70.0': optional: true - '@esbuild/openbsd-arm64@0.27.3': + '@oxfmt/binding-win32-x64-msvc@0.70.0': optional: true - '@esbuild/openbsd-x64@0.27.3': + '@rolldown/binding-android-arm-eabi@1.2.9': optional: true - '@esbuild/openharmony-arm64@0.27.3': + '@rolldown/binding-android-arm64@1.2.9': optional: true - '@esbuild/sunos-x64@0.27.3': + '@rolldown/binding-darwin-arm64@1.2.9': optional: true - '@esbuild/win32-arm64@0.27.3': + '@rolldown/binding-darwin-x64@1.2.9': optional: true - '@esbuild/win32-ia32@0.27.3': + '@rolldown/binding-freebsd-x64@1.2.9': optional: true - '@esbuild/win32-x64@0.27.3': + '@rolldown/binding-linux-arm-gnueabihf@1.2.9': optional: true - '@jridgewell/sourcemap-codec@1.5.5': {} + '@rolldown/binding-linux-arm64-gnu@1.2.9': + optional: true - '@jsdevtools/ono@7.1.3': {} + '@rolldown/binding-linux-arm64-musl@1.2.9': + optional: true - '@rollup/rollup-android-arm-eabi@4.59.0': + '@rolldown/binding-linux-ppc64-gnu@1.2.9': optional: true - '@rollup/rollup-android-arm64@4.59.0': + '@rolldown/binding-linux-s390x-gnu@1.2.9': optional: true - '@rollup/rollup-darwin-arm64@4.59.0': + '@rolldown/binding-linux-x64-gnu@1.2.9': optional: true - '@rollup/rollup-darwin-x64@4.59.0': + '@rolldown/binding-linux-x64-musl@1.2.9': optional: true - '@rollup/rollup-freebsd-arm64@4.59.0': + '@rolldown/binding-openharmony-arm64@1.2.9': optional: true - '@rollup/rollup-freebsd-x64@4.59.0': + '@rolldown/binding-win32-arm64-msvc@1.2.9': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.59.0': + '@rolldown/binding-win32-x64-msvc@1.2.9': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.59.0': + '@rolldown/pluginutils@1.0.1': {} + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/deep-eql@4.0.2': {} + + '@types/esrecurse@4.3.1': {} + + '@types/estree@1.0.8': {} + + '@types/json-schema@7.0.15': {} + + '@types/node@26.6.2': + dependencies: + undici-types: 8.9.0 + + '@typescript/typescript-aix-ppc64@7.0.2': optional: true - '@rollup/rollup-linux-arm64-gnu@4.59.0': + '@typescript/typescript-darwin-arm64@7.0.2': optional: true - '@rollup/rollup-linux-arm64-musl@4.59.0': + '@typescript/typescript-darwin-x64@7.0.2': optional: true - '@rollup/rollup-linux-loong64-gnu@4.59.0': + '@typescript/typescript-freebsd-arm64@7.0.2': optional: true - '@rollup/rollup-linux-loong64-musl@4.59.0': + '@typescript/typescript-freebsd-x64@7.0.2': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.59.0': + '@typescript/typescript-linux-arm64@7.0.2': optional: true - '@rollup/rollup-linux-ppc64-musl@4.59.0': + '@typescript/typescript-linux-arm@7.0.2': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.59.0': + '@typescript/typescript-linux-loong64@7.0.2': optional: true - '@rollup/rollup-linux-riscv64-musl@4.59.0': + '@typescript/typescript-linux-mips64el@7.0.2': optional: true - '@rollup/rollup-linux-s390x-gnu@4.59.0': + '@typescript/typescript-linux-ppc64@7.0.2': optional: true - '@rollup/rollup-linux-x64-gnu@4.59.0': + '@typescript/typescript-linux-riscv64@7.0.2': optional: true - '@rollup/rollup-linux-x64-musl@4.59.0': + '@typescript/typescript-linux-s390x@7.0.2': optional: true - '@rollup/rollup-openbsd-x64@4.59.0': + '@typescript/typescript-linux-x64@7.0.2': optional: true - '@rollup/rollup-openharmony-arm64@4.59.0': + '@typescript/typescript-netbsd-arm64@7.0.2': optional: true - '@rollup/rollup-win32-arm64-msvc@4.59.0': + '@typescript/typescript-netbsd-x64@7.0.2': optional: true - '@rollup/rollup-win32-ia32-msvc@4.59.0': + '@typescript/typescript-openbsd-arm64@7.0.2': optional: true - '@rollup/rollup-win32-x64-gnu@4.59.0': + '@typescript/typescript-openbsd-x64@7.0.2': optional: true - '@rollup/rollup-win32-x64-msvc@4.59.0': + '@typescript/typescript-sunos-x64@7.0.2': optional: true - '@tsconfig/node24@24.0.4': {} + '@typescript/typescript-win32-arm64@7.0.2': + optional: true - '@tsconfig/strictest@2.0.8': {} + '@typescript/typescript-win32-x64@7.0.2': + optional: true - '@types/chai@5.2.3': + '@vitest/mocker@5.0.1(vite@8.3.0(@types/node@26.6.2)(esbuild@0.27.3)(yaml@2.8.2))': dependencies: - '@types/deep-eql': 4.0.2 - assertion-error: 2.0.1 + '@jridgewell/trace-mapping': 0.3.31 + '@vitest/spy': 5.0.1 + estree-walker: 3.0.3 + magic-string: 1.4.1 + optionalDependencies: + vite: 8.3.0(@types/node@26.6.2)(esbuild@0.27.3)(yaml@2.8.2) - '@types/deep-eql@4.0.2': {} + '@vitest/spy@5.0.1': {} - '@types/estree@1.0.8': {} + acorn-jsx@5.3.2(acorn@8.18.0): + dependencies: + acorn: 8.18.0 - '@types/json-schema@7.0.15': {} + acorn@8.18.0: {} - '@types/lodash@4.17.24': {} + ajv-draft-04@1.0.0(ajv@8.20.0): + optionalDependencies: + ajv: 8.20.0 - '@types/node@24.12.0': + ajv@6.15.0: dependencies: - undici-types: 7.16.0 + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 - '@vitest/expect@3.2.4': + ajv@8.20.0: dependencies: - '@types/chai': 5.2.3 - '@vitest/spy': 3.2.4 - '@vitest/utils': 3.2.4 - chai: 5.3.3 - tinyrainbow: 2.0.0 + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 - '@vitest/mocker@3.2.4(vite@7.3.1(@types/node@24.12.0)(yaml@2.8.2))': - dependencies: - '@vitest/spy': 3.2.4 - estree-walker: 3.0.3 - magic-string: 0.30.21 - optionalDependencies: - vite: 7.3.1(@types/node@24.12.0)(yaml@2.8.2) + argparse@2.0.1: {} - '@vitest/pretty-format@3.2.4': - dependencies: - tinyrainbow: 2.0.0 + assertion-error@2.0.1: {} - '@vitest/runner@3.2.4': - dependencies: - '@vitest/utils': 3.2.4 - pathe: 2.0.3 - strip-literal: 3.1.0 + balanced-match@4.0.4: {} - '@vitest/snapshot@3.2.4': - dependencies: - '@vitest/pretty-format': 3.2.4 - magic-string: 0.30.21 - pathe: 2.0.3 + baseline-browser-mapping@2.11.25: {} - '@vitest/spy@3.2.4': + brace-expansion@5.0.12: dependencies: - tinyspy: 4.0.4 + balanced-match: 4.0.4 - '@vitest/utils@3.2.4': + browserslist@4.29.0: dependencies: - '@vitest/pretty-format': 3.2.4 - loupe: 3.2.1 - tinyrainbow: 2.0.0 + baseline-browser-mapping: 2.11.25 + caniuse-lite: 1.0.30001810 + electron-to-chromium: 1.5.434 + node-releases: 2.0.56 + update-browserslist-db: 1.3.3(browserslist@4.29.0) - ajv-draft-04@1.0.0(ajv@8.18.0): - optionalDependencies: - ajv: 8.18.0 + builtin-modules@5.4.0: {} - ajv@8.18.0: + cacheable@2.5.0: dependencies: - fast-deep-equal: 3.1.3 - fast-uri: 3.1.0 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 + '@cacheable/memory': 2.2.0 + '@cacheable/utils': 2.5.0 + hookified: 1.15.1 + keyv: 5.6.0 + qified: 0.10.1 - argparse@2.0.1: {} + call-me-maybe@1.0.2: {} - assertion-error@2.0.1: {} + caniuse-lite@1.0.30001810: {} - cac@6.7.14: {} + chai@6.2.2: {} - call-me-maybe@1.0.2: {} + change-case@5.4.4: {} + + ci-info@4.4.0: {} - chai@5.3.3: + constructs@10.8.1: {} + + convert-hrtime@5.0.0: {} + + core-js-compat@3.50.0: dependencies: - assertion-error: 2.0.1 - check-error: 2.1.3 - deep-eql: 5.0.2 - loupe: 3.2.1 - pathval: 2.0.1 + browserslist: 4.29.0 - check-error@2.1.3: {} + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 - constructs@10.5.1: {} + css-tree@3.2.1: + dependencies: + mdn-data: 2.27.1 + source-map-js: 1.2.1 debug@4.4.3: dependencies: ms: 2.1.3 - deep-eql@5.0.2: {} + deep-is@0.1.4: {} + + detect-indent@7.0.2: {} + + detect-libc@2.1.2: {} - es-module-lexer@1.7.0: {} + electron-to-chromium@1.5.434: {} + + entities@8.1.0: {} + + es-module-lexer@2.3.2: {} esbuild@0.27.3: optionalDependencies: @@ -1040,118 +2263,419 @@ snapshots: '@esbuild/win32-arm64': 0.27.3 '@esbuild/win32-ia32': 0.27.3 '@esbuild/win32-x64': 0.27.3 + optional: true + + escalade@3.2.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-plugin-unicorn@74.0.0(eslint@10.11.0): + dependencies: + '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0) + '@eslint/css-tree': 4.1.1 + browserslist: 4.29.0 + change-case: 5.4.4 + ci-info: 4.4.0 + core-js-compat: 3.50.0 + detect-indent: 7.0.2 + entities: 8.1.0 + eslint: 10.11.0 + find-up-simple: 1.0.1 + globals: 17.12.0 + indent-string: 5.0.0 + is-builtin-module: 5.0.0 + is-identifier: 1.1.0 + pluralize: 8.0.0 + quote-js-string: 0.1.0 + regjsparser: 0.13.3 + reserved-identifiers: 1.2.0 + semver: 7.8.5 + strip-indent: 4.1.1 + yaml: 2.9.1 + + eslint-scope@9.1.2: + dependencies: + '@types/esrecurse': 4.3.1 + '@types/estree': 1.0.8 + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@10.11.0: + dependencies: + '@eslint-community/eslint-utils': 4.10.1(eslint@10.11.0) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.23.5 + '@eslint/config-helpers': 0.7.0 + '@eslint/core': 1.2.1 + '@eslint/plugin-kit': 0.7.3 + '@humanfs/node': 0.16.8 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + ajv: 6.15.0 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 9.1.2 + eslint-visitor-keys: 5.0.1 + espree: 11.2.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 11.1.5 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + minimatch: 10.2.6 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@11.2.0: + dependencies: + acorn: 8.18.0 + acorn-jsx: 5.3.2(acorn@8.18.0) + eslint-visitor-keys: 5.0.1 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} estree-walker@3.0.3: dependencies: '@types/estree': 1.0.8 - expect-type@1.3.0: {} + esutils@2.0.3: {} + + expect-type@1.4.0: {} + + fallow-type-aware@3.28.0: + dependencies: + typescript: 7.0.2 + optional: true + + fallow@3.28.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + '@fallow-cli/darwin-arm64': 3.28.0 + '@fallow-cli/darwin-x64': 3.28.0 + '@fallow-cli/linux-arm64-gnu': 3.28.0 + '@fallow-cli/linux-arm64-musl': 3.28.0 + '@fallow-cli/linux-x64-gnu': 3.28.0 + '@fallow-cli/linux-x64-musl': 3.28.0 + '@fallow-cli/win32-arm64-msvc': 3.28.0 + '@fallow-cli/win32-x64-msvc': 3.28.0 + fallow-type-aware: 3.28.0 fast-deep-equal@3.1.3: {} + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + fast-uri@3.1.0: {} - fdir@6.5.0(picomatch@4.0.3): + fdir@6.5.0(picomatch@4.0.7): optionalDependencies: - picomatch: 4.0.3 + picomatch: 4.0.7 + + file-entry-cache@11.1.5: + dependencies: + flat-cache: 6.1.23 + + find-up-simple@1.0.1: {} + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@6.1.23: + dependencies: + cacheable: 2.5.0 + flatted: 3.4.4 + hookified: 1.15.1 + + flatted@3.4.4: {} fsevents@2.3.3: optional: true + function-timeout@1.0.2: {} + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@17.12.0: {} + + hashery@1.5.1: + dependencies: + hookified: 1.15.1 + + hookified@1.15.1: {} + + hookified@2.2.0: {} + + identifier-regex@1.1.0: + dependencies: + reserved-identifiers: 1.2.0 + + ignore@5.3.2: {} + + imurmurhash@0.1.4: {} + + indent-string@5.0.0: {} + + is-builtin-module@5.0.0: + dependencies: + builtin-modules: 5.4.0 + is-extglob@2.1.1: {} is-glob@4.0.3: dependencies: is-extglob: 2.1.1 - js-tokens@9.0.1: {} + is-identifier@1.1.0: + dependencies: + identifier-regex: 1.1.0 + super-regex: 1.1.0 + + isexe@2.0.0: {} - js-yaml@4.1.1: + js-yaml@5.4.2: dependencies: argparse: 2.0.1 - json-schema-to-typescript@15.0.4: - dependencies: - '@apidevtools/json-schema-ref-parser': 11.9.3 - '@types/json-schema': 7.0.15 - '@types/lodash': 4.17.24 - is-glob: 4.0.3 - js-yaml: 4.1.1 - lodash: 4.17.23 - minimist: 1.2.8 - prettier: 3.8.1 - tinyglobby: 0.2.15 + jsesc@3.1.0: {} + + json-schema-traverse@0.4.1: {} json-schema-traverse@1.0.0: {} - lodash@4.17.23: {} + json-stable-stringify-without-jsonify@1.0.1: {} - loupe@3.2.1: {} + keyv@5.6.0: + dependencies: + '@keyv/serialize': 1.1.1 - magic-string@0.30.21: + levn@0.4.1: dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lightningcss-android-arm64@1.33.0: + optional: true + + lightningcss-darwin-arm64@1.33.0: + optional: true + + lightningcss-darwin-x64@1.33.0: + optional: true + + lightningcss-freebsd-x64@1.33.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.33.0: + optional: true + + lightningcss-linux-arm64-gnu@1.33.0: + optional: true + + lightningcss-linux-arm64-musl@1.33.0: + optional: true + + lightningcss-linux-x64-gnu@1.33.0: + optional: true + + lightningcss-linux-x64-musl@1.33.0: + optional: true + + lightningcss-win32-arm64-msvc@1.33.0: + optional: true + + lightningcss-win32-x64-msvc@1.33.0: + optional: true + + lightningcss@1.33.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.33.0 + lightningcss-darwin-arm64: 1.33.0 + lightningcss-darwin-x64: 1.33.0 + lightningcss-freebsd-x64: 1.33.0 + lightningcss-linux-arm-gnueabihf: 1.33.0 + lightningcss-linux-arm64-gnu: 1.33.0 + lightningcss-linux-arm64-musl: 1.33.0 + lightningcss-linux-x64-gnu: 1.33.0 + lightningcss-linux-x64-musl: 1.33.0 + lightningcss-win32-arm64-msvc: 1.33.0 + lightningcss-win32-x64-msvc: 1.33.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + magic-string@1.4.1: + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + + make-asynchronous@1.1.0: + dependencies: + p-event: 6.0.1 + type-fest: 4.41.0 + web-worker: 1.5.0 + + mdn-data@2.27.1: {} - minimist@1.2.8: {} + mdn-data@2.35.0: {} + + minimatch@10.2.6: + dependencies: + brace-expansion: 5.0.12 ms@2.1.3: {} - nanoid@3.3.11: {} + nanoid@3.3.19: {} + + natural-compare@1.4.0: {} + + node-releases@2.0.56: {} + + obug@2.2.1: {} openapi-types@12.1.3: {} - openapi3-ts@4.5.0: + openapi3-ts@4.6.1: dependencies: - yaml: 2.8.2 + yaml: 2.9.1 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + oxfmt@0.70.0: + dependencies: + tinypool: 2.1.2 + optionalDependencies: + '@oxfmt/binding-android-arm-eabi': 0.70.0 + '@oxfmt/binding-android-arm64': 0.70.0 + '@oxfmt/binding-darwin-arm64': 0.70.0 + '@oxfmt/binding-darwin-x64': 0.70.0 + '@oxfmt/binding-freebsd-x64': 0.70.0 + '@oxfmt/binding-linux-arm-gnueabihf': 0.70.0 + '@oxfmt/binding-linux-arm-musleabihf': 0.70.0 + '@oxfmt/binding-linux-arm64-gnu': 0.70.0 + '@oxfmt/binding-linux-arm64-musl': 0.70.0 + '@oxfmt/binding-linux-ppc64-gnu': 0.70.0 + '@oxfmt/binding-linux-riscv64-gnu': 0.70.0 + '@oxfmt/binding-linux-riscv64-musl': 0.70.0 + '@oxfmt/binding-linux-s390x-gnu': 0.70.0 + '@oxfmt/binding-linux-x64-gnu': 0.70.0 + '@oxfmt/binding-linux-x64-musl': 0.70.0 + '@oxfmt/binding-openharmony-arm64': 0.70.0 + '@oxfmt/binding-win32-arm64-msvc': 0.70.0 + '@oxfmt/binding-win32-ia32-msvc': 0.70.0 + '@oxfmt/binding-win32-x64-msvc': 0.70.0 + + p-event@6.0.1: + dependencies: + p-timeout: 6.1.4 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + p-timeout@6.1.4: {} - pathe@2.0.3: {} + path-exists@4.0.0: {} - pathval@2.0.1: {} + path-key@3.1.1: {} picocolors@1.1.1: {} - picomatch@4.0.3: {} + picomatch@4.0.7: {} - postcss@8.5.8: + pluralize@8.0.0: {} + + postcss@8.5.28: dependencies: - nanoid: 3.3.11 + nanoid: 3.3.19 picocolors: 1.1.1 source-map-js: 1.2.1 - prettier@3.8.1: {} + prelude-ls@1.2.1: {} + + punycode@2.3.1: {} + + qified@0.10.1: + dependencies: + hookified: 2.2.0 + + quote-js-string@0.1.0: {} + + regjsparser@0.13.3: + dependencies: + jsesc: 3.1.0 require-from-string@2.0.2: {} - rollup@4.59.0: + reserved-identifiers@1.2.0: {} + + rolldown@1.2.9: dependencies: - '@types/estree': 1.0.8 + '@oxc-project/types': 0.150.0 + '@rolldown/pluginutils': 1.0.1 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.59.0 - '@rollup/rollup-android-arm64': 4.59.0 - '@rollup/rollup-darwin-arm64': 4.59.0 - '@rollup/rollup-darwin-x64': 4.59.0 - '@rollup/rollup-freebsd-arm64': 4.59.0 - '@rollup/rollup-freebsd-x64': 4.59.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.59.0 - '@rollup/rollup-linux-arm-musleabihf': 4.59.0 - '@rollup/rollup-linux-arm64-gnu': 4.59.0 - '@rollup/rollup-linux-arm64-musl': 4.59.0 - '@rollup/rollup-linux-loong64-gnu': 4.59.0 - '@rollup/rollup-linux-loong64-musl': 4.59.0 - '@rollup/rollup-linux-ppc64-gnu': 4.59.0 - '@rollup/rollup-linux-ppc64-musl': 4.59.0 - '@rollup/rollup-linux-riscv64-gnu': 4.59.0 - '@rollup/rollup-linux-riscv64-musl': 4.59.0 - '@rollup/rollup-linux-s390x-gnu': 4.59.0 - '@rollup/rollup-linux-x64-gnu': 4.59.0 - '@rollup/rollup-linux-x64-musl': 4.59.0 - '@rollup/rollup-openbsd-x64': 4.59.0 - '@rollup/rollup-openharmony-arm64': 4.59.0 - '@rollup/rollup-win32-arm64-msvc': 4.59.0 - '@rollup/rollup-win32-ia32-msvc': 4.59.0 - '@rollup/rollup-win32-x64-gnu': 4.59.0 - '@rollup/rollup-win32-x64-msvc': 4.59.0 - fsevents: 2.3.3 + '@rolldown/binding-android-arm-eabi': 1.2.9 + '@rolldown/binding-android-arm64': 1.2.9 + '@rolldown/binding-darwin-arm64': 1.2.9 + '@rolldown/binding-darwin-x64': 1.2.9 + '@rolldown/binding-freebsd-x64': 1.2.9 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.9 + '@rolldown/binding-linux-arm64-gnu': 1.2.9 + '@rolldown/binding-linux-arm64-musl': 1.2.9 + '@rolldown/binding-linux-ppc64-gnu': 1.2.9 + '@rolldown/binding-linux-s390x-gnu': 1.2.9 + '@rolldown/binding-linux-x64-gnu': 1.2.9 + '@rolldown/binding-linux-x64-musl': 1.2.9 + '@rolldown/binding-openharmony-arm64': 1.2.9 + '@rolldown/binding-win32-arm64-msvc': 1.2.9 + '@rolldown/binding-win32-x64-msvc': 1.2.9 + + semver@7.8.5: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} siginfo@2.0.0: {} @@ -1159,115 +2683,124 @@ snapshots: stackback@0.0.2: {} - std-env@3.10.0: {} + std-env@4.2.0: {} - strip-literal@3.1.0: + strip-indent@4.1.1: {} + + super-regex@1.1.0: dependencies: - js-tokens: 9.0.1 + function-timeout: 1.0.2 + make-asynchronous: 1.1.0 + time-span: 5.1.0 - tagged-tag@1.0.0: {} + time-span@5.1.0: + dependencies: + convert-hrtime: 5.0.0 - tinybench@2.9.0: {} + tinybench@6.1.4: {} - tinyexec@0.3.2: {} + tinyexec@1.3.0: {} - tinyglobby@0.2.15: + tinyglobby@0.2.17: dependencies: - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 - tinypool@1.1.1: {} + tinypool@2.1.2: {} - tinyrainbow@2.0.0: {} - - tinyspy@4.0.4: {} - - type-fest@5.4.4: + type-check@0.4.0: dependencies: - tagged-tag: 1.0.0 + prelude-ls: 1.2.1 - typescript@5.9.3: {} + type-fest@4.41.0: {} - undici-types@7.16.0: {} + typescript@7.0.2: + optionalDependencies: + '@typescript/typescript-aix-ppc64': 7.0.2 + '@typescript/typescript-darwin-arm64': 7.0.2 + '@typescript/typescript-darwin-x64': 7.0.2 + '@typescript/typescript-freebsd-arm64': 7.0.2 + '@typescript/typescript-freebsd-x64': 7.0.2 + '@typescript/typescript-linux-arm': 7.0.2 + '@typescript/typescript-linux-arm64': 7.0.2 + '@typescript/typescript-linux-loong64': 7.0.2 + '@typescript/typescript-linux-mips64el': 7.0.2 + '@typescript/typescript-linux-ppc64': 7.0.2 + '@typescript/typescript-linux-riscv64': 7.0.2 + '@typescript/typescript-linux-s390x': 7.0.2 + '@typescript/typescript-linux-x64': 7.0.2 + '@typescript/typescript-netbsd-arm64': 7.0.2 + '@typescript/typescript-netbsd-x64': 7.0.2 + '@typescript/typescript-openbsd-arm64': 7.0.2 + '@typescript/typescript-openbsd-x64': 7.0.2 + '@typescript/typescript-sunos-x64': 7.0.2 + '@typescript/typescript-win32-arm64': 7.0.2 + '@typescript/typescript-win32-x64': 7.0.2 + + undici-types@8.9.0: {} + + undici@8.11.0: {} + + update-browserslist-db@1.3.3(browserslist@4.29.0): + dependencies: + browserslist: 4.29.0 + escalade: 3.2.0 + picocolors: 1.1.1 - vite-node@3.2.4(@types/node@24.12.0)(yaml@2.8.2): + uri-js@4.4.1: dependencies: - cac: 6.7.14 - debug: 4.4.3 - es-module-lexer: 1.7.0 - pathe: 2.0.3 - vite: 7.3.1(@types/node@24.12.0)(yaml@2.8.2) - transitivePeerDependencies: - - '@types/node' - - jiti - - less - - lightningcss - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml + punycode: 2.3.1 - vite@7.3.1(@types/node@24.12.0)(yaml@2.8.2): + vite@8.3.0(@types/node@26.6.2)(esbuild@0.27.3)(yaml@2.8.2): dependencies: - esbuild: 0.27.3 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - postcss: 8.5.8 - rollup: 4.59.0 - tinyglobby: 0.2.15 + lightningcss: 1.33.0 + picomatch: 4.0.7 + postcss: 8.5.28 + rolldown: 1.2.9 + tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 24.12.0 + '@types/node': 26.6.2 + esbuild: 0.27.3 fsevents: 2.3.3 yaml: 2.8.2 - vitest@3.2.4(@types/node@24.12.0)(yaml@2.8.2): + vitest@5.0.1(@types/node@26.6.2)(vite@8.3.0(@types/node@26.6.2)(esbuild@0.27.3)(yaml@2.8.2)): dependencies: '@types/chai': 5.2.3 - '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.3.1(@types/node@24.12.0)(yaml@2.8.2)) - '@vitest/pretty-format': 3.2.4 - '@vitest/runner': 3.2.4 - '@vitest/snapshot': 3.2.4 - '@vitest/spy': 3.2.4 - '@vitest/utils': 3.2.4 - chai: 5.3.3 - debug: 4.4.3 - expect-type: 1.3.0 - magic-string: 0.30.21 - pathe: 2.0.3 - picomatch: 4.0.3 - std-env: 3.10.0 - tinybench: 2.9.0 - tinyexec: 0.3.2 - tinyglobby: 0.2.15 - tinypool: 1.1.1 - tinyrainbow: 2.0.0 - vite: 7.3.1(@types/node@24.12.0)(yaml@2.8.2) - vite-node: 3.2.4(@types/node@24.12.0)(yaml@2.8.2) + '@vitest/mocker': 5.0.1(vite@8.3.0(@types/node@26.6.2)(esbuild@0.27.3)(yaml@2.8.2)) + chai: 6.2.2 + es-module-lexer: 2.3.2 + expect-type: 1.4.0 + magic-string: 1.4.1 + obug: 2.2.1 + picomatch: 4.0.7 + std-env: 4.2.0 + tinybench: 6.1.4 + tinyexec: 1.3.0 + tinyglobby: 0.2.17 + vite: 8.3.0(@types/node@26.6.2)(esbuild@0.27.3)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 24.12.0 + '@types/node': 26.6.2 transitivePeerDependencies: - - jiti - - less - - lightningcss - msw - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml + + web-worker@1.5.0: {} + + which@2.0.2: + dependencies: + isexe: 2.0.0 why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 stackback: 0.0.2 - yaml@2.8.2: {} + word-wrap@1.2.5: {} + + yaml@2.8.2: + optional: true + + yaml@2.9.1: {} + + yocto-queue@0.1.0: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..922a3ac --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,16 @@ +allowBuilds: + esbuild: false + +minimumReleaseAgeExclude: + - "@block65/oxlint-binding-linux-arm64-gnu@1.83.0" + - "@block65/oxlint-binding-linux-x64-gnu@1.83.0" + - "@block65/oxlint-tsgolint-linux-arm64@7.0.200200" + - "@block65/oxlint-tsgolint-linux-x64@7.0.200200" + - "@block65/oxlint-tsgolint@7.0.200200" + - "@block65/oxlint@1.83.0" + - "@block65/oxlint-plugin@0.11.0" + - "@block65/shared-config@0.5.0" + +overrides: + oxlint: npm:@block65/oxlint@>=1.83.0 + oxlint-tsgolint: npm:@block65/oxlint-tsgolint@>=7.0.200200 diff --git a/tsconfig.json b/tsconfig.json index aaa9712..ac66423 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,11 +1,15 @@ { - "extends": ["@tsconfig/node24/tsconfig.json", "@tsconfig/strictest/tsconfig.json"], - "compilerOptions": { - "allowImportingTsExtensions": true, - "verbatimModuleSyntax": true, - "noEmit": true, - "skipLibCheck": true - }, - "include": ["./lib", "./examples", "./__tests__"], - "exclude": ["./dist", "node_modules"] + "extends": ["@block65/tsconfig/vanilla", "@block65/tsconfig/noprojref"], + + "compilerOptions": { + "types": ["node"] + }, + + "include": [ + "lib", + "examples", + "__tests__", + "oxlint.config.ts", + "oxfmt.config.ts" + ] } From 7a481d1e5f3b38916ea2f1a8e19e51ae13865c0b Mon Sep 17 00:00:00 2001 From: Max Holman Date: Wed, 23 Sep 2026 17:06:14 +0800 Subject: [PATCH 2/9] style: reformat with oxfmt, drop .editorconfig Co-Authored-By: LLM --- .editorconfig | 14 - README.md | 336 ++-- __tests__/fixtures/apis/example.ts | 494 ++--- __tests__/fixtures/apis/note-taking.ts | 632 +++--- __tests__/fixtures/oas31.json | 2550 ++++++++++++------------ __tests__/json-schema.test.ts | 44 +- __tests__/openapi-schema.test.ts | 56 +- __tests__/parameter-types.test-d.ts | 40 +- __tests__/regression.test.ts | 94 +- examples/example.ts | 338 ++-- lib/ApiLowLevel.ts | 20 +- lib/api.ts | 164 +- lib/header.ts | 114 +- lib/http-method.ts | 10 +- lib/index.ts | 24 +- lib/media-type.ts | 58 +- lib/operation.ts | 178 +- lib/parameter.ts | 140 +- lib/path.ts | 114 +- lib/reference.ts | 40 +- lib/request-body.ts | 67 +- lib/response.ts | 81 +- lib/schema.ts | 132 +- lib/security-requirement.ts | 44 +- lib/security-scheme.ts | 34 +- lib/server.ts | 36 +- lib/tag.ts | 28 +- lib/types.ts | 22 +- lib/utils.ts | 10 +- 29 files changed, 2959 insertions(+), 2955 deletions(-) delete mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 1db4a53..0000000 --- a/.editorconfig +++ /dev/null @@ -1,14 +0,0 @@ -# top-most EditorConfig file -root = true - -[*] -end_of_line = lf -charset = utf-8 -insert_final_newline = true -indent_style = space -indent_size = 2 - -[Makefile] -indent_style = tab -indent_size = 4 -insert_final_newline = false diff --git a/README.md b/README.md index 63ab7ae..715955f 100644 --- a/README.md +++ b/README.md @@ -6,206 +6,206 @@ An experimental AWS Constructs based Open API schema builder ```typescript import { - Api, - OpenApiVersion, - Parameter, - Path, - Reference, - Response, - Schema, - SecurityRequirement, - SecurityScheme, - Server, - Tag, -} from '@block65/openapi-constructs'; + Api, + OpenApiVersion, + Parameter, + Path, + Reference, + Response, + Schema, + SecurityRequirement, + SecurityScheme, + Server, + Tag, +} from "@block65/openapi-constructs"; const api = new Api({ - openapi: OpenApiVersion.V3_1, - info: { - title: 'Example REST API', - version: '1.0.0', - }, + openapi: OpenApiVersion.V3_1, + info: { + title: "Example REST API", + version: "1.0.0", + }, }); -new Server(api, 'ExampleServer', { - url: new URL('https://api.example.com'), +new Server(api, "ExampleServer", { + url: new URL("https://api.example.com"), }); -const httpBearerJwtScheme = new SecurityScheme(api, 'HttpBearerJwtScheme', { - type: 'http', - scheme: 'bearer', - bearerFormat: 'JWT', +const httpBearerJwtScheme = new SecurityScheme(api, "HttpBearerJwtScheme", { + type: "http", + scheme: "bearer", + bearerFormat: "JWT", }); -new SecurityRequirement(api, 'AllScopes', { - securityScheme: httpBearerJwtScheme, - scopes: [], +new SecurityRequirement(api, "AllScopes", { + securityScheme: httpBearerJwtScheme, + scopes: [], }); -const userTag = new Tag(api, 'UserTag', { - name: 'user', +const userTag = new Tag(api, "UserTag", { + name: "user", }); -const userDeleteScopeReq = new SecurityRequirement(api, 'UserDeleteScope', { - securityScheme: httpBearerJwtScheme, - scopes: ['users.delete'], +const userDeleteScopeReq = new SecurityRequirement(api, "UserDeleteScope", { + securityScheme: httpBearerJwtScheme, + scopes: ["users.delete"], }); -const noSecurityRequirement = new SecurityRequirement(api, 'NoSecurity'); - -const addressSchema = new Schema(api, 'Address', { - schema: { - type: 'object', - required: ['name'], - additionalProperties: false, - properties: { - postcode: { - type: 'integer', - format: 'int32', - minimum: 1000, - maximum: 9999, - }, - }, - }, +const noSecurityRequirement = new SecurityRequirement(api, "NoSecurity"); + +const addressSchema = new Schema(api, "Address", { + schema: { + type: "object", + required: ["name"], + additionalProperties: false, + properties: { + postcode: { + type: "integer", + format: "int32", + minimum: 1000, + maximum: 9999, + }, + }, + }, }); -const idSchema = new Schema(api, 'Id', { - schema: { - type: 'string', - minLength: 6, - maxLength: 6, - }, +const idSchema = new Schema(api, "Id", { + schema: { + type: "string", + minLength: 6, + maxLength: 6, + }, }); -const user = new Schema(api, 'User', { - schema: { - type: 'object', - required: ['name'], - additionalProperties: false, - properties: { - userId: idSchema.referenceObject(), - name: { - type: 'string', - }, - address: addressSchema.referenceObject(), - age: { - type: 'integer', - format: 'int32', - minimum: 0, - }, - }, - }, +const user = new Schema(api, "User", { + schema: { + type: "object", + required: ["name"], + additionalProperties: false, + properties: { + userId: idSchema.referenceObject(), + name: { + type: "string", + }, + address: addressSchema.referenceObject(), + age: { + type: "integer", + format: "int32", + minimum: 0, + }, + }, + }, }); -const updateUserRequest = new Schema(api, 'UpdateUserRequest', { - schema: { - type: 'object', - minProperties: 1, - additionalProperties: false, - properties: { - address: addressSchema.referenceObject(), - age: { - type: 'integer', - format: 'int32', - minimum: 0, - }, - }, - }, +const updateUserRequest = new Schema(api, "UpdateUserRequest", { + schema: { + type: "object", + minProperties: 1, + additionalProperties: false, + properties: { + address: addressSchema.referenceObject(), + age: { + type: "integer", + format: "int32", + minimum: 0, + }, + }, + }, }); -const createUserRequest = new Reference(user, 'CreateUserRequest'); +const createUserRequest = new Reference(user, "CreateUserRequest"); -const users = new Schema(api, 'Users', { - schema: { - type: 'array', - uniqueItems: true, - items: user.referenceObject(), - }, +const users = new Schema(api, "Users", { + schema: { + type: "array", + uniqueItems: true, + items: user.referenceObject(), + }, }); -const userIdParameter = new Parameter(api, 'UserId', { - name: 'userId', - in: 'path', - required: true, - schema: idSchema, +const userIdParameter = new Parameter(api, "UserId", { + name: "userId", + in: "path", + required: true, + schema: idSchema, }); new Path(api, { - path: '/users', - tags: new Set([userTag]), + path: "/users", + tags: new Set([userTag]), }) - .addOperation("GET", { - operationId: 'listUsersCommand', - responses: { - 200: new Response(api, 'ListUsersResponse', { - description: 'User 200 response', - content: { - contentType: 'application/json', - schema: users, - }, - }), - }, - }) - .addOperation("POST", { - operationId: 'createUserCommand', - requestBody: { - content: { - contentType: 'application/json', - schema: createUserRequest, - }, - }, - responses: { - 200: new Response(api, 'CreateUserResponse', { - description: 'User 200 response', - content: { - contentType: 'application/json', - schema: users, - }, - }), - }, - }); + .addOperation("GET", { + operationId: "listUsersCommand", + responses: { + 200: new Response(api, "ListUsersResponse", { + description: "User 200 response", + content: { + contentType: "application/json", + schema: users, + }, + }), + }, + }) + .addOperation("POST", { + operationId: "createUserCommand", + requestBody: { + content: { + contentType: "application/json", + schema: createUserRequest, + }, + }, + responses: { + 200: new Response(api, "CreateUserResponse", { + description: "User 200 response", + content: { + contentType: "application/json", + schema: users, + }, + }), + }, + }); new Path(api, { - path: '/users/{userId}', - parameters: [userIdParameter], + path: "/users/{userId}", + parameters: [userIdParameter], }) - .addOperation("GET", { - operationId: 'getUserByIdCommand', - responses: { - 200: new Response(api, 'GetUserById', { - description: 'User 200 response', - content: { - contentType: 'application/json', - schema: user, - }, - }), - }, - }) - .addOperation("DELETE", { - operationId: 'deleteUserByIdCommand', - security: userDeleteScopeReq, - }) - .addOperation("HEAD", { - operationId: 'checkUserIdAvailableCommand', - security: noSecurityRequirement, - }) - .addOperation("POST", { - operationId: 'updateUserCommand', - requestBody: { - content: { - contentType: 'application/json', - schema: updateUserRequest, - }, - }, - responses: { - 200: new Response(api, 'UpdateUserResponse', { - content: { - contentType: 'application/json', - schema: user, - }, - }), - }, - }); + .addOperation("GET", { + operationId: "getUserByIdCommand", + responses: { + 200: new Response(api, "GetUserById", { + description: "User 200 response", + content: { + contentType: "application/json", + schema: user, + }, + }), + }, + }) + .addOperation("DELETE", { + operationId: "deleteUserByIdCommand", + security: userDeleteScopeReq, + }) + .addOperation("HEAD", { + operationId: "checkUserIdAvailableCommand", + security: noSecurityRequirement, + }) + .addOperation("POST", { + operationId: "updateUserCommand", + requestBody: { + content: { + contentType: "application/json", + schema: updateUserRequest, + }, + }, + responses: { + 200: new Response(api, "UpdateUserResponse", { + content: { + contentType: "application/json", + schema: user, + }, + }), + }, + }); process.stdout.write(JSON.stringify(api.synth(), null, 2)); ``` diff --git a/__tests__/fixtures/apis/example.ts b/__tests__/fixtures/apis/example.ts index 5208c14..0dd1d8a 100644 --- a/__tests__/fixtures/apis/example.ts +++ b/__tests__/fixtures/apis/example.ts @@ -1,138 +1,138 @@ /* eslint-disable no-new */ import { - Api, - Header, - Schema, - Parameter, - Path, - Reference, - Response, - SecurityRequirement, - SecurityScheme, - Server, - Tag, -} from '@block65/openapi-constructs'; + Api, + Header, + Schema, + Parameter, + Path, + Reference, + Response, + SecurityRequirement, + SecurityScheme, + Server, + Tag, +} from "@block65/openapi-constructs"; export const exampleApi = new Api({ - openapi: "3.1.0", - info: { - title: 'Example REST API', - version: '1.0.0', - }, + openapi: "3.1.0", + info: { + title: "Example REST API", + version: "1.0.0", + }, }); -new Server(exampleApi, 'ExampleServer', { - url: new URL('https://api.example.com'), +new Server(exampleApi, "ExampleServer", { + url: new URL("https://api.example.com"), }); const httpBearerJwtScheme = new SecurityScheme( - exampleApi, - 'HttpBearerJwtScheme', - { - type: 'http', - scheme: 'bearer', - bearerFormat: 'JWT', - }, + exampleApi, + "HttpBearerJwtScheme", + { + type: "http", + scheme: "bearer", + bearerFormat: "JWT", + }, ); -new SecurityRequirement(exampleApi, 'AllScopes', { - securityScheme: httpBearerJwtScheme, - scopes: [], +new SecurityRequirement(exampleApi, "AllScopes", { + securityScheme: httpBearerJwtScheme, + scopes: [], }); -const userTag = new Tag(exampleApi, 'UserTag', { - name: 'user', +const userTag = new Tag(exampleApi, "UserTag", { + name: "user", }); -const randomTag = new Tag(exampleApi, 'RandomTag', { - name: 'random', +const randomTag = new Tag(exampleApi, "RandomTag", { + name: "random", }); const userDeleteScopeReq = new SecurityRequirement( - exampleApi, - 'UserReadScope', - { - securityScheme: httpBearerJwtScheme, - scopes: ['users.delete'], - }, + exampleApi, + "UserReadScope", + { + securityScheme: httpBearerJwtScheme, + scopes: ["users.delete"], + }, ); -const noSecurityRequirement = new SecurityRequirement(exampleApi, 'NoSecurity'); +const noSecurityRequirement = new SecurityRequirement(exampleApi, "NoSecurity"); -const addressSchema = new Schema(exampleApi, 'Address', { - schema: { - type: 'object', - required: ['postcode'], - additionalProperties: false, - properties: { - postcode: { - type: 'integer', - format: 'int32', - minimum: 1000, - maximum: 9999, - }, - }, - }, +const addressSchema = new Schema(exampleApi, "Address", { + schema: { + type: "object", + required: ["postcode"], + additionalProperties: false, + properties: { + postcode: { + type: "integer", + format: "int32", + minimum: 1000, + maximum: 9999, + }, + }, + }, }); -const idSchema = new Schema(exampleApi, 'Id', { - schema: { - type: 'string', - }, +const idSchema = new Schema(exampleApi, "Id", { + schema: { + type: "string", + }, }); -const user = new Schema(exampleApi, 'User', { - schema: { - type: 'object', - required: ['name'], - additionalProperties: false, - properties: { - userId: idSchema.referenceObject(), - name: { - type: 'string', - }, - address: addressSchema.referenceObject(), - age: { - anyOf: [ - { - type: 'integer', - format: 'int32', - minimum: 0, - }, - { - type: 'null', - }, - ], - }, - }, - }, +const user = new Schema(exampleApi, "User", { + schema: { + type: "object", + required: ["name"], + additionalProperties: false, + properties: { + userId: idSchema.referenceObject(), + name: { + type: "string", + }, + address: addressSchema.referenceObject(), + age: { + anyOf: [ + { + type: "integer", + format: "int32", + minimum: 0, + }, + { + type: "null", + }, + ], + }, + }, + }, }); -const updateUserRequest = new Schema(exampleApi, 'UpdateUserRequest', { - schema: { - type: 'object', - minProperties: 1, - additionalProperties: false, - properties: { - address: addressSchema.referenceObject(), - age: { - type: 'integer', - format: 'int32', - minimum: 0, - }, - }, - }, +const updateUserRequest = new Schema(exampleApi, "UpdateUserRequest", { + schema: { + type: "object", + minProperties: 1, + additionalProperties: false, + properties: { + address: addressSchema.referenceObject(), + age: { + type: "integer", + format: "int32", + minimum: 0, + }, + }, + }, }); -const createUserRequest = new Reference(user, 'CreateUserRequest'); +const createUserRequest = new Reference(user, "CreateUserRequest"); -const users = new Schema(exampleApi, 'Users', { - schema: { - type: 'array', - // additionalItems: false, - uniqueItems: true, - items: user.referenceObject(), - }, +const users = new Schema(exampleApi, "Users", { + schema: { + type: "array", + // additionalItems: false, + uniqueItems: true, + items: user.referenceObject(), + }, }); /* const errorSchema = new Schema(api, 'ErrorSchema', { @@ -155,37 +155,41 @@ const users = new Schema(exampleApi, 'Users', { schema: idSchema, }); */ -const rateLimitSchema = new Schema(exampleApi, 'RateLimit', { - schema: { - type: 'integer', - format: 'int32', - minimum: 0, - }, +const rateLimitSchema = new Schema(exampleApi, "RateLimit", { + schema: { + type: "integer", + format: "int32", + minimum: 0, + }, }); -const rateLimitHeader = new Header(exampleApi, 'x-rate-limit', { - description: 'Number of requests allowed per hour', - required: true, - schema: rateLimitSchema, +const rateLimitHeader = new Header(exampleApi, "x-rate-limit", { + description: "Number of requests allowed per hour", + required: true, + schema: rateLimitSchema, }); -const rateLimitRemainingHeader = new Header(exampleApi, 'x-rate-limit-remaining', { - description: 'Number of requests remaining in the current window', - schema: rateLimitSchema, -}); +const rateLimitRemainingHeader = new Header( + exampleApi, + "x-rate-limit-remaining", + { + description: "Number of requests remaining in the current window", + schema: rateLimitSchema, + }, +); -const binarySchema = new Schema(exampleApi, 'Binary', { - schema: { - type: 'string', - format: 'binary', - }, +const binarySchema = new Schema(exampleApi, "Binary", { + schema: { + type: "string", + format: "binary", + }, }); -const userIdParameter = new Parameter(exampleApi, 'UserId', { - name: 'userId', - in: 'path', - required: true, - schema: idSchema, +const userIdParameter = new Parameter(exampleApi, "UserId", { + name: "userId", + in: "path", + required: true, + schema: idSchema, }); /* const userIdentifiersSchema = new Schema(api, 'UserIdentifiers', { @@ -208,126 +212,126 @@ const userIdParameter = new Parameter(exampleApi, 'UserId', { }); */ new Path(exampleApi, { - path: '/users', - tags: new Set([userTag]), + path: "/users", + tags: new Set([userTag]), }) - .addOperation("get", { - operationId: 'listUsersCommand', - responses: { - 200: new Response(exampleApi, 'ListUsers200Response', { - description: 'User 200 response', - content: { - contentType: 'application/json', - schema: users, - }, - headers: { - 'x-rate-limit': rateLimitHeader, - 'x-rate-limit-remaining': rateLimitRemainingHeader, - }, - }), - }, - }) - .addOperation("post", { - operationId: 'createUserCommand', - tags: new Set([randomTag]), - requestBody: { - content: { - contentType: 'application/json', - schema: createUserRequest, - }, - }, - responses: { - 200: new Response(exampleApi, 'CreateUser200Response', { - description: 'User 200 response', - content: { - contentType: 'application/json', - schema: users, - }, - }), - }, - }); + .addOperation("get", { + operationId: "listUsersCommand", + responses: { + 200: new Response(exampleApi, "ListUsers200Response", { + description: "User 200 response", + content: { + contentType: "application/json", + schema: users, + }, + headers: { + "x-rate-limit": rateLimitHeader, + "x-rate-limit-remaining": rateLimitRemainingHeader, + }, + }), + }, + }) + .addOperation("post", { + operationId: "createUserCommand", + tags: new Set([randomTag]), + requestBody: { + content: { + contentType: "application/json", + schema: createUserRequest, + }, + }, + responses: { + 200: new Response(exampleApi, "CreateUser200Response", { + description: "User 200 response", + content: { + contentType: "application/json", + schema: users, + }, + }), + }, + }); new Path(exampleApi, { - path: '/users/{userId}', - parameters: [userIdParameter], + path: "/users/{userId}", + parameters: [userIdParameter], }) - .addOperation("get", { - operationId: 'getUserByIdCommand', - responses: { - 200: new Response(exampleApi, 'GetUserById', { - description: 'User 200 response', - content: { - contentType: 'application/json', - schema: user, - }, - }), - }, - }) - .addOperation("delete", { - operationId: 'deleteUserByIdCommand', - security: userDeleteScopeReq, - tags: new Set([userTag, randomTag]), - }) - .addOperation("head", { - operationId: 'checkUserIdAvailableCommand', - security: noSecurityRequirement, - }) - .addOperation("post", { - operationId: 'updateUserCommand', - requestBody: { - content: { - contentType: 'application/json', - schema: updateUserRequest, - }, - }, - responses: { - 200: new Response(exampleApi, 'UpdateUserResponse200', { - content: { - contentType: 'application/json', - schema: user, - }, - }), - }, - }); + .addOperation("get", { + operationId: "getUserByIdCommand", + responses: { + 200: new Response(exampleApi, "GetUserById", { + description: "User 200 response", + content: { + contentType: "application/json", + schema: user, + }, + }), + }, + }) + .addOperation("delete", { + operationId: "deleteUserByIdCommand", + security: userDeleteScopeReq, + tags: new Set([userTag, randomTag]), + }) + .addOperation("head", { + operationId: "checkUserIdAvailableCommand", + security: noSecurityRequirement, + }) + .addOperation("post", { + operationId: "updateUserCommand", + requestBody: { + content: { + contentType: "application/json", + schema: updateUserRequest, + }, + }, + responses: { + 200: new Response(exampleApi, "UpdateUserResponse200", { + content: { + contentType: "application/json", + schema: user, + }, + }), + }, + }); new Path(exampleApi, { - path: '/users/{userId}/avatar', - parameters: [userIdParameter], + path: "/users/{userId}/avatar", + parameters: [userIdParameter], }) - .addOperation("get", { - operationId: 'getUserAvatarCommand', - description: 'Download user avatar as JSON metadata or raw binary', - responses: { - 200: new Response(exampleApi, 'GetUserAvatar200Response', { - description: 'Avatar response', - content: [ - { - contentType: 'application/json', - schema: user, - }, - { - contentType: 'application/octet-stream', - schema: binarySchema, - }, - ], - }), - }, - }) - .addOperation("put", { - operationId: 'uploadUserAvatarCommand', - description: 'Upload user avatar as binary', - requestBody: { - content: { - contentType: 'application/octet-stream', - schema: binarySchema, - }, - }, - responses: { - 200: new Response(exampleApi, 'UploadUserAvatar200Response', { - content: { - contentType: 'application/json', - schema: user, - }, - }), - }, - }); + .addOperation("get", { + operationId: "getUserAvatarCommand", + description: "Download user avatar as JSON metadata or raw binary", + responses: { + 200: new Response(exampleApi, "GetUserAvatar200Response", { + description: "Avatar response", + content: [ + { + contentType: "application/json", + schema: user, + }, + { + contentType: "application/octet-stream", + schema: binarySchema, + }, + ], + }), + }, + }) + .addOperation("put", { + operationId: "uploadUserAvatarCommand", + description: "Upload user avatar as binary", + requestBody: { + content: { + contentType: "application/octet-stream", + schema: binarySchema, + }, + }, + responses: { + 200: new Response(exampleApi, "UploadUserAvatar200Response", { + content: { + contentType: "application/json", + schema: user, + }, + }), + }, + }); diff --git a/__tests__/fixtures/apis/note-taking.ts b/__tests__/fixtures/apis/note-taking.ts index bc38c8c..d1abdec 100644 --- a/__tests__/fixtures/apis/note-taking.ts +++ b/__tests__/fixtures/apis/note-taking.ts @@ -1,375 +1,375 @@ /* eslint-disable no-new */ import { - Api, - Parameter, - Path, - Reference, - Response, - Schema, - SecurityRequirement, - SecurityScheme, - Server, - Tag, -} from '@block65/openapi-constructs'; + Api, + Parameter, + Path, + Reference, + Response, + Schema, + SecurityRequirement, + SecurityScheme, + Server, + Tag, +} from "@block65/openapi-constructs"; export const noteTakingApi = new Api({ - openapi: "3.1.0", - info: { - title: 'Example REST API', - version: '1.0.0', - }, + openapi: "3.1.0", + info: { + title: "Example REST API", + version: "1.0.0", + }, }); -new Server(noteTakingApi, 'ExampleServer', { - url: new URL('https://api.example.com'), +new Server(noteTakingApi, "ExampleServer", { + url: new URL("https://api.example.com"), }); const httpBearerJwtScheme = new SecurityScheme( - noteTakingApi, - 'HttpBearerJwtScheme', - { - type: 'http', - scheme: 'bearer', - bearerFormat: 'JWT', - }, + noteTakingApi, + "HttpBearerJwtScheme", + { + type: "http", + scheme: "bearer", + bearerFormat: "JWT", + }, ); -new SecurityRequirement(noteTakingApi, 'AllScopes', { - securityScheme: httpBearerJwtScheme, - scopes: [], +new SecurityRequirement(noteTakingApi, "AllScopes", { + securityScheme: httpBearerJwtScheme, + scopes: [], }); -const userApiTag = new Tag(noteTakingApi, 'UserApiTag', { - name: 'user', +const userApiTag = new Tag(noteTakingApi, "UserApiTag", { + name: "user", }); const userDeleteScopeReq = new SecurityRequirement( - noteTakingApi, - 'UserDeleteScope', - { - securityScheme: httpBearerJwtScheme, - scopes: ['users.delete'], - }, + noteTakingApi, + "UserDeleteScope", + { + securityScheme: httpBearerJwtScheme, + scopes: ["users.delete"], + }, ); const noSecurityRequirement = new SecurityRequirement( - noteTakingApi, - 'NoSecurity', + noteTakingApi, + "NoSecurity", ); -const addressSchema = new Schema(noteTakingApi, 'Address', { - schema: { - type: 'object', - required: ['postcode'], - additionalProperties: false, - properties: { - postcode: { - type: 'integer', - format: 'int32', - minimum: 1000, - maximum: 9999, - }, - }, - }, +const addressSchema = new Schema(noteTakingApi, "Address", { + schema: { + type: "object", + required: ["postcode"], + additionalProperties: false, + properties: { + postcode: { + type: "integer", + format: "int32", + minimum: 1000, + maximum: 9999, + }, + }, + }, }); -const idSchema = new Schema(noteTakingApi, 'Id', { - schema: { - type: 'string', - minLength: 6, - maxLength: 6, - }, +const idSchema = new Schema(noteTakingApi, "Id", { + schema: { + type: "string", + minLength: 6, + maxLength: 6, + }, }); -const user = new Schema(noteTakingApi, 'User', { - schema: { - type: 'object', - required: ['name'], - additionalProperties: false, - properties: { - userId: idSchema.referenceObject(), - name: { - type: 'string', - }, - address: addressSchema.referenceObject(), - age: { - type: 'integer', - format: 'int32', - minimum: 0, - }, - }, - }, +const user = new Schema(noteTakingApi, "User", { + schema: { + type: "object", + required: ["name"], + additionalProperties: false, + properties: { + userId: idSchema.referenceObject(), + name: { + type: "string", + }, + address: addressSchema.referenceObject(), + age: { + type: "integer", + format: "int32", + minimum: 0, + }, + }, + }, }); -const updateUserRequest = new Schema(noteTakingApi, 'UpdateUserRequest', { - schema: { - type: 'object', - minProperties: 1, - additionalProperties: false, - properties: { - address: addressSchema.referenceObject(), - age: { - type: 'integer', - format: 'int32', - minimum: 0, - }, - }, - }, +const updateUserRequest = new Schema(noteTakingApi, "UpdateUserRequest", { + schema: { + type: "object", + minProperties: 1, + additionalProperties: false, + properties: { + address: addressSchema.referenceObject(), + age: { + type: "integer", + format: "int32", + minimum: 0, + }, + }, + }, }); -const createUserRequest = new Reference(user, 'CreateUserRequest'); +const createUserRequest = new Reference(user, "CreateUserRequest"); -const users = new Schema(noteTakingApi, 'Users', { - schema: { - type: 'array', - uniqueItems: true, - items: user.referenceObject(), - }, +const users = new Schema(noteTakingApi, "Users", { + schema: { + type: "array", + uniqueItems: true, + items: user.referenceObject(), + }, }); -const userIdParameter = new Parameter(noteTakingApi, 'UserId', { - name: 'userId', - in: 'path', - required: true, - schema: idSchema, +const userIdParameter = new Parameter(noteTakingApi, "UserId", { + name: "userId", + in: "path", + required: true, + schema: idSchema, }); new Path(noteTakingApi, { - path: '/users', - tags: new Set([userApiTag]), + path: "/users", + tags: new Set([userApiTag]), }) - .addOperation("get", { - operationId: 'listUsersCommand', - responses: { - 200: new Response(noteTakingApi, 'ListUsersResponse', { - description: 'User 200 response', - content: { - contentType: 'application/json', - schema: users, - }, - }), - }, - }) - .addOperation("post", { - operationId: 'createUserCommand', - requestBody: { - content: { - contentType: 'application/json', - schema: createUserRequest, - }, - }, - responses: { - 200: new Response(noteTakingApi, 'CreateUserResponse', { - description: 'User 200 response', - content: { - contentType: 'application/json', - schema: users, - }, - }), - }, - }); + .addOperation("get", { + operationId: "listUsersCommand", + responses: { + 200: new Response(noteTakingApi, "ListUsersResponse", { + description: "User 200 response", + content: { + contentType: "application/json", + schema: users, + }, + }), + }, + }) + .addOperation("post", { + operationId: "createUserCommand", + requestBody: { + content: { + contentType: "application/json", + schema: createUserRequest, + }, + }, + responses: { + 200: new Response(noteTakingApi, "CreateUserResponse", { + description: "User 200 response", + content: { + contentType: "application/json", + schema: users, + }, + }), + }, + }); new Path(noteTakingApi, { - path: '/users/{userId}', - parameters: [userIdParameter], + path: "/users/{userId}", + parameters: [userIdParameter], }) - .addOperation("get", { - operationId: 'getUserByIdCommand', - responses: { - 200: new Response(noteTakingApi, 'GetUserById', { - description: 'User 200 response', - content: { - contentType: 'application/json', - schema: user, - }, - }), - }, - }) - .addOperation("delete", { - operationId: 'deleteUserByIdCommand', - security: userDeleteScopeReq, - }) - .addOperation("head", { - operationId: 'checkUserIdAvailableCommand', - security: noSecurityRequirement, - responses: { - 204: new Response(noteTakingApi, 'HeadUserResponseFound'), - 404: new Response(noteTakingApi, 'HeadUserResponseNotFound'), - }, - }) - .addOperation("post", { - operationId: 'updateUserCommand', - requestBody: { - content: { - contentType: 'application/json', - schema: updateUserRequest, - }, - }, - responses: { - 200: new Response(noteTakingApi, 'UpdateUserResponse', { - content: { - contentType: 'application/json', - schema: user, - }, - }), - }, - }); + .addOperation("get", { + operationId: "getUserByIdCommand", + responses: { + 200: new Response(noteTakingApi, "GetUserById", { + description: "User 200 response", + content: { + contentType: "application/json", + schema: user, + }, + }), + }, + }) + .addOperation("delete", { + operationId: "deleteUserByIdCommand", + security: userDeleteScopeReq, + }) + .addOperation("head", { + operationId: "checkUserIdAvailableCommand", + security: noSecurityRequirement, + responses: { + 204: new Response(noteTakingApi, "HeadUserResponseFound"), + 404: new Response(noteTakingApi, "HeadUserResponseNotFound"), + }, + }) + .addOperation("post", { + operationId: "updateUserCommand", + requestBody: { + content: { + contentType: "application/json", + schema: updateUserRequest, + }, + }, + responses: { + 200: new Response(noteTakingApi, "UpdateUserResponse", { + content: { + contentType: "application/json", + schema: user, + }, + }), + }, + }); -const noteSchema = new Schema(noteTakingApi, 'Note', { - schema: { - type: 'object', - required: ['title', 'content'], - additionalProperties: false, - properties: { - title: { - type: 'string', - }, - content: { - type: 'string', - }, - labels: { - type: 'array', - items: { - type: 'string', - minLength: 1, - maxLength: 256, - }, - }, - }, - }, +const noteSchema = new Schema(noteTakingApi, "Note", { + schema: { + type: "object", + required: ["title", "content"], + additionalProperties: false, + properties: { + title: { + type: "string", + }, + content: { + type: "string", + }, + labels: { + type: "array", + items: { + type: "string", + minLength: 1, + maxLength: 256, + }, + }, + }, + }, }); -const createNoteRequest = new Reference(noteSchema, 'CreateNoteRequest'); +const createNoteRequest = new Reference(noteSchema, "CreateNoteRequest"); -const notes = new Schema(noteTakingApi, 'Notes', { - schema: { - type: 'array', - uniqueItems: true, - items: noteSchema.referenceObject(), - }, +const notes = new Schema(noteTakingApi, "Notes", { + schema: { + type: "array", + uniqueItems: true, + items: noteSchema.referenceObject(), + }, }); -const noteUserIdParameter = new Parameter(noteTakingApi, 'NoteUserId', { - name: 'userId', - in: 'path', - required: true, - schema: idSchema, +const noteUserIdParameter = new Parameter(noteTakingApi, "NoteUserId", { + name: "userId", + in: "path", + required: true, + schema: idSchema, }); -const noteIdParameter = new Parameter(noteTakingApi, 'NoteId', { - name: 'noteId', - in: 'path', - required: true, - schema: idSchema, +const noteIdParameter = new Parameter(noteTakingApi, "NoteId", { + name: "noteId", + in: "path", + required: true, + schema: idSchema, }); new Path(noteTakingApi, { - path: '/users/{userId}/notes', - tags: new Set([userApiTag]), - parameters: [noteUserIdParameter], + path: "/users/{userId}/notes", + tags: new Set([userApiTag]), + parameters: [noteUserIdParameter], }) - .addOperation("get", { - operationId: 'listNotesCommand', - description: 'List all of the notes', - summary: 'List notes', - responses: { - 200: new Response(noteTakingApi, 'ListNotesResponse', { - description: 'Notes 200 response', - content: { - contentType: 'application/json', - schema: notes, - }, - }), - }, - }) - .addOperation("post", { - operationId: 'createNoteCommand', - deprecated: true, - requestBody: { - required: true, - content: { - contentType: 'application/json', - schema: createNoteRequest, - }, - }, - responses: { - 200: new Response(noteTakingApi, 'CreateNoteResponse', { - description: 'Note created 200 response', - content: { - contentType: 'application/json', - schema: noteSchema, - }, - }), - }, - }); + .addOperation("get", { + operationId: "listNotesCommand", + description: "List all of the notes", + summary: "List notes", + responses: { + 200: new Response(noteTakingApi, "ListNotesResponse", { + description: "Notes 200 response", + content: { + contentType: "application/json", + schema: notes, + }, + }), + }, + }) + .addOperation("post", { + operationId: "createNoteCommand", + deprecated: true, + requestBody: { + required: true, + content: { + contentType: "application/json", + schema: createNoteRequest, + }, + }, + responses: { + 200: new Response(noteTakingApi, "CreateNoteResponse", { + description: "Note created 200 response", + content: { + contentType: "application/json", + schema: noteSchema, + }, + }), + }, + }); -const updateNoteRequest = new Schema(noteTakingApi, 'UpdateNoteRequest', { - schema: { - type: 'object', - minProperties: 1, - additionalProperties: false, - properties: { - title: { - type: 'string', - }, - content: { - type: 'string', - }, - labels: { - type: 'array', - items: { - type: 'string', - minLength: 1, - maxLength: 256, - }, - }, - }, - }, +const updateNoteRequest = new Schema(noteTakingApi, "UpdateNoteRequest", { + schema: { + type: "object", + minProperties: 1, + additionalProperties: false, + properties: { + title: { + type: "string", + }, + content: { + type: "string", + }, + labels: { + type: "array", + items: { + type: "string", + minLength: 1, + maxLength: 256, + }, + }, + }, + }, }); const noteDeleteScopeReq = new SecurityRequirement( - noteTakingApi, - 'NoteDeleteScope', - { - securityScheme: httpBearerJwtScheme, - scopes: ['notes.delete'], - }, + noteTakingApi, + "NoteDeleteScope", + { + securityScheme: httpBearerJwtScheme, + scopes: ["notes.delete"], + }, ); new Path(noteTakingApi, { - path: '/users/{userId}/notes/{noteId}', - parameters: [noteUserIdParameter, noteIdParameter], + path: "/users/{userId}/notes/{noteId}", + parameters: [noteUserIdParameter, noteIdParameter], }) - .addOperation("get", { - operationId: 'getNoteCommand', - responses: { - 200: new Response(noteTakingApi, 'GetNote', { - description: 'Note 200 response', - content: { - contentType: 'application/json', - schema: noteSchema, - }, - }), - }, - }) - .addOperation("delete", { - operationId: 'deleteNoteCommand', - security: noteDeleteScopeReq, - }) - .addOperation("post", { - operationId: 'updateNoteCommand', - requestBody: { - content: { - contentType: 'application/json', - schema: updateNoteRequest, - }, - }, - responses: { - 200: new Response(noteTakingApi, 'UpdateNoteResponse', { - content: { - contentType: 'application/json', - schema: noteSchema, - }, - }), - }, - }); + .addOperation("get", { + operationId: "getNoteCommand", + responses: { + 200: new Response(noteTakingApi, "GetNote", { + description: "Note 200 response", + content: { + contentType: "application/json", + schema: noteSchema, + }, + }), + }, + }) + .addOperation("delete", { + operationId: "deleteNoteCommand", + security: noteDeleteScopeReq, + }) + .addOperation("post", { + operationId: "updateNoteCommand", + requestBody: { + content: { + contentType: "application/json", + schema: updateNoteRequest, + }, + }, + responses: { + 200: new Response(noteTakingApi, "UpdateNoteResponse", { + content: { + contentType: "application/json", + schema: noteSchema, + }, + }), + }, + }); diff --git a/__tests__/fixtures/oas31.json b/__tests__/fixtures/oas31.json index 9e14a91..4493410 100644 --- a/__tests__/fixtures/oas31.json +++ b/__tests__/fixtures/oas31.json @@ -1,1277 +1,1277 @@ { - "$id": "https://spec.openapis.org/oas/3.1/schema/2022-10-07", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "description": "The description of OpenAPI v3.1.x documents without schema validation, as defined by https://spec.openapis.org/oas/v3.1.0", - "type": "object", - "properties": { - "openapi": { - "type": "string", - "pattern": "^3\\.1\\.\\d+(-.+)?$" - }, - "info": { - "$ref": "#/$defs/info" - }, - "jsonSchemaDialect": { - "type": "string", - "format": "uri", - "default": "https://spec.openapis.org/oas/3.1/dialect/base" - }, - "servers": { - "type": "array", - "items": { - "$ref": "#/$defs/server" - }, - "default": [ - { - "url": "/" - } - ] - }, - "paths": { - "$ref": "#/$defs/paths" - }, - "webhooks": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/path-item-or-reference" - } - }, - "components": { - "$ref": "#/$defs/components" - }, - "security": { - "type": "array", - "items": { - "$ref": "#/$defs/security-requirement" - } - }, - "tags": { - "type": "array", - "items": { - "$ref": "#/$defs/tag" - } - }, - "externalDocs": { - "$ref": "#/$defs/external-documentation" - } - }, - "required": ["openapi", "info"], - "anyOf": [ - { - "required": ["paths"] - }, - { - "required": ["components"] - }, - { - "required": ["webhooks"] - } - ], - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false, - "$defs": { - "info": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#info-object", - "type": "object", - "properties": { - "title": { - "type": "string" - }, - "summary": { - "type": "string" - }, - "description": { - "type": "string" - }, - "termsOfService": { - "type": "string", - "format": "uri" - }, - "contact": { - "$ref": "#/$defs/contact" - }, - "license": { - "$ref": "#/$defs/license" - }, - "version": { - "type": "string" - } - }, - "required": ["title", "version"], - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "contact": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#contact-object", - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "url": { - "type": "string", - "format": "uri" - }, - "email": { - "type": "string", - "format": "email" - } - }, - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "license": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#license-object", - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "identifier": { - "type": "string" - }, - "url": { - "type": "string", - "format": "uri" - } - }, - "required": ["name"], - "dependentSchemas": { - "identifier": { - "not": { - "required": ["url"] - } - } - }, - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "server": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#server-object", - "type": "object", - "properties": { - "url": { - "type": "string", - "format": "uri-reference" - }, - "description": { - "type": "string" - }, - "variables": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/server-variable" - } - } - }, - "required": ["url"], - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "server-variable": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#server-variable-object", - "type": "object", - "properties": { - "enum": { - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1 - }, - "default": { - "type": "string" - }, - "description": { - "type": "string" - } - }, - "required": ["default"], - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "components": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#components-object", - "type": "object", - "properties": { - "schemas": { - "type": "object", - "additionalProperties": { - "$dynamicRef": "#meta" - } - }, - "responses": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/response-or-reference" - } - }, - "parameters": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/parameter-or-reference" - } - }, - "examples": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/example-or-reference" - } - }, - "requestBodies": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/request-body-or-reference" - } - }, - "headers": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/header-or-reference" - } - }, - "securitySchemes": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/security-scheme-or-reference" - } - }, - "links": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/link-or-reference" - } - }, - "callbacks": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/callbacks-or-reference" - } - }, - "pathItems": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/path-item-or-reference" - } - } - }, - "patternProperties": { - "^(schemas|responses|parameters|examples|requestBodies|headers|securitySchemes|links|callbacks|pathItems)$": { - "$comment": "Enumerating all of the property names in the regex above is necessary for unevaluatedProperties to work as expected", - "propertyNames": { - "pattern": "^[a-zA-Z0-9._-]+$" - } - } - }, - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "paths": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#paths-object", - "type": "object", - "patternProperties": { - "^/": { - "$ref": "#/$defs/path-item" - } - }, - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "path-item": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#path-item-object", - "type": "object", - "properties": { - "summary": { - "type": "string" - }, - "description": { - "type": "string" - }, - "servers": { - "type": "array", - "items": { - "$ref": "#/$defs/server" - } - }, - "parameters": { - "type": "array", - "items": { - "$ref": "#/$defs/parameter-or-reference" - } - }, - "get": { - "$ref": "#/$defs/operation" - }, - "put": { - "$ref": "#/$defs/operation" - }, - "post": { - "$ref": "#/$defs/operation" - }, - "delete": { - "$ref": "#/$defs/operation" - }, - "options": { - "$ref": "#/$defs/operation" - }, - "head": { - "$ref": "#/$defs/operation" - }, - "patch": { - "$ref": "#/$defs/operation" - }, - "trace": { - "$ref": "#/$defs/operation" - } - }, - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "path-item-or-reference": { - "if": { - "type": "object", - "required": ["$ref"] - }, - "then": { - "$ref": "#/$defs/reference" - }, - "else": { - "$ref": "#/$defs/path-item" - } - }, - "operation": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#operation-object", - "type": "object", - "properties": { - "tags": { - "type": "array", - "items": { - "type": "string" - } - }, - "summary": { - "type": "string" - }, - "description": { - "type": "string" - }, - "externalDocs": { - "$ref": "#/$defs/external-documentation" - }, - "operationId": { - "type": "string" - }, - "parameters": { - "type": "array", - "items": { - "$ref": "#/$defs/parameter-or-reference" - } - }, - "requestBody": { - "$ref": "#/$defs/request-body-or-reference" - }, - "responses": { - "$ref": "#/$defs/responses" - }, - "callbacks": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/callbacks-or-reference" - } - }, - "deprecated": { - "default": false, - "type": "boolean" - }, - "security": { - "type": "array", - "items": { - "$ref": "#/$defs/security-requirement" - } - }, - "servers": { - "type": "array", - "items": { - "$ref": "#/$defs/server" - } - } - }, - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "external-documentation": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#external-documentation-object", - "type": "object", - "properties": { - "description": { - "type": "string" - }, - "url": { - "type": "string", - "format": "uri" - } - }, - "required": ["url"], - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "parameter": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#parameter-object", - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "in": { - "enum": ["query", "header", "path", "cookie"] - }, - "description": { - "type": "string" - }, - "required": { - "default": false, - "type": "boolean" - }, - "deprecated": { - "default": false, - "type": "boolean" - }, - "schema": { - "$dynamicRef": "#meta" - }, - "content": { - "$ref": "#/$defs/content", - "minProperties": 1, - "maxProperties": 1 - } - }, - "required": ["name", "in"], - "oneOf": [ - { - "required": ["schema"] - }, - { - "required": ["content"] - } - ], - "if": { - "properties": { - "in": { - "const": "query" - } - }, - "required": ["in"] - }, - "then": { - "properties": { - "allowEmptyValue": { - "default": false, - "type": "boolean" - } - } - }, - "dependentSchemas": { - "schema": { - "properties": { - "style": { - "type": "string" - }, - "explode": { - "type": "boolean" - } - }, - "allOf": [ - { - "$ref": "#/$defs/examples" - }, - { - "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-path" - }, - { - "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-header" - }, - { - "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-query" - }, - { - "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-cookie" - }, - { - "$ref": "#/$defs/styles-for-form" - } - ], - "$defs": { - "styles-for-path": { - "if": { - "properties": { - "in": { - "const": "path" - } - }, - "required": ["in"] - }, - "then": { - "properties": { - "style": { - "default": "simple", - "enum": ["matrix", "label", "simple"] - }, - "required": { - "const": true - } - }, - "required": ["required"] - } - }, - "styles-for-header": { - "if": { - "properties": { - "in": { - "const": "header" - } - }, - "required": ["in"] - }, - "then": { - "properties": { - "style": { - "default": "simple", - "const": "simple" - } - } - } - }, - "styles-for-query": { - "if": { - "properties": { - "in": { - "const": "query" - } - }, - "required": ["in"] - }, - "then": { - "properties": { - "style": { - "default": "form", - "enum": [ - "form", - "spaceDelimited", - "pipeDelimited", - "deepObject" - ] - }, - "allowReserved": { - "default": false, - "type": "boolean" - } - } - } - }, - "styles-for-cookie": { - "if": { - "properties": { - "in": { - "const": "cookie" - } - }, - "required": ["in"] - }, - "then": { - "properties": { - "style": { - "default": "form", - "const": "form" - } - } - } - } - } - } - }, - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "parameter-or-reference": { - "if": { - "type": "object", - "required": ["$ref"] - }, - "then": { - "$ref": "#/$defs/reference" - }, - "else": { - "$ref": "#/$defs/parameter" - } - }, - "request-body": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#request-body-object", - "type": "object", - "properties": { - "description": { - "type": "string" - }, - "content": { - "$ref": "#/$defs/content" - }, - "required": { - "default": false, - "type": "boolean" - } - }, - "required": ["content"], - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "request-body-or-reference": { - "if": { - "type": "object", - "required": ["$ref"] - }, - "then": { - "$ref": "#/$defs/reference" - }, - "else": { - "$ref": "#/$defs/request-body" - } - }, - "content": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#fixed-fields-10", - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/media-type" - }, - "propertyNames": { - "format": "media-range" - } - }, - "media-type": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#media-type-object", - "type": "object", - "properties": { - "schema": { - "$dynamicRef": "#meta" - }, - "encoding": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/encoding" - } - } - }, - "allOf": [ - { - "$ref": "#/$defs/specification-extensions" - }, - { - "$ref": "#/$defs/examples" - } - ], - "unevaluatedProperties": false - }, - "encoding": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#encoding-object", - "type": "object", - "properties": { - "contentType": { - "type": "string", - "format": "media-range" - }, - "headers": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/header-or-reference" - } - }, - "style": { - "default": "form", - "enum": ["form", "spaceDelimited", "pipeDelimited", "deepObject"] - }, - "explode": { - "type": "boolean" - }, - "allowReserved": { - "default": false, - "type": "boolean" - } - }, - "allOf": [ - { - "$ref": "#/$defs/specification-extensions" - }, - { - "$ref": "#/$defs/styles-for-form" - } - ], - "unevaluatedProperties": false - }, - "responses": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#responses-object", - "type": "object", - "properties": { - "default": { - "$ref": "#/$defs/response-or-reference" - } - }, - "patternProperties": { - "^[1-5](?:[0-9]{2}|XX)$": { - "$ref": "#/$defs/response-or-reference" - } - }, - "minProperties": 1, - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false, - "if": { - "$comment": "either default, or at least one response code property must exist", - "patternProperties": { - "^[1-5](?:[0-9]{2}|XX)$": false - } - }, - "then": { - "required": ["default"] - } - }, - "response": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#response-object", - "type": "object", - "properties": { - "description": { - "type": "string" - }, - "headers": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/header-or-reference" - } - }, - "content": { - "$ref": "#/$defs/content" - }, - "links": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/link-or-reference" - } - } - }, - "required": ["description"], - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "response-or-reference": { - "if": { - "type": "object", - "required": ["$ref"] - }, - "then": { - "$ref": "#/$defs/reference" - }, - "else": { - "$ref": "#/$defs/response" - } - }, - "callbacks": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#callback-object", - "type": "object", - "$ref": "#/$defs/specification-extensions", - "additionalProperties": { - "$ref": "#/$defs/path-item-or-reference" - } - }, - "callbacks-or-reference": { - "if": { - "type": "object", - "required": ["$ref"] - }, - "then": { - "$ref": "#/$defs/reference" - }, - "else": { - "$ref": "#/$defs/callbacks" - } - }, - "example": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#example-object", - "type": "object", - "properties": { - "summary": { - "type": "string" - }, - "description": { - "type": "string" - }, - "value": true, - "externalValue": { - "type": "string", - "format": "uri" - } - }, - "not": { - "required": ["value", "externalValue"] - }, - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "example-or-reference": { - "if": { - "type": "object", - "required": ["$ref"] - }, - "then": { - "$ref": "#/$defs/reference" - }, - "else": { - "$ref": "#/$defs/example" - } - }, - "link": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#link-object", - "type": "object", - "properties": { - "operationRef": { - "type": "string", - "format": "uri-reference" - }, - "operationId": { - "type": "string" - }, - "parameters": { - "$ref": "#/$defs/map-of-strings" - }, - "requestBody": true, - "description": { - "type": "string" - }, - "body": { - "$ref": "#/$defs/server" - } - }, - "oneOf": [ - { - "required": ["operationRef"] - }, - { - "required": ["operationId"] - } - ], - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "link-or-reference": { - "if": { - "type": "object", - "required": ["$ref"] - }, - "then": { - "$ref": "#/$defs/reference" - }, - "else": { - "$ref": "#/$defs/link" - } - }, - "header": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#header-object", - "type": "object", - "properties": { - "description": { - "type": "string" - }, - "required": { - "default": false, - "type": "boolean" - }, - "deprecated": { - "default": false, - "type": "boolean" - }, - "schema": { - "$dynamicRef": "#meta" - }, - "content": { - "$ref": "#/$defs/content", - "minProperties": 1, - "maxProperties": 1 - } - }, - "oneOf": [ - { - "required": ["schema"] - }, - { - "required": ["content"] - } - ], - "dependentSchemas": { - "schema": { - "properties": { - "style": { - "default": "simple", - "const": "simple" - }, - "explode": { - "default": false, - "type": "boolean" - } - }, - "$ref": "#/$defs/examples" - } - }, - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "header-or-reference": { - "if": { - "type": "object", - "required": ["$ref"] - }, - "then": { - "$ref": "#/$defs/reference" - }, - "else": { - "$ref": "#/$defs/header" - } - }, - "tag": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#tag-object", - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "externalDocs": { - "$ref": "#/$defs/external-documentation" - } - }, - "required": ["name"], - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "reference": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#reference-object", - "type": "object", - "properties": { - "$ref": { - "type": "string", - "format": "uri-reference" - }, - "summary": { - "type": "string" - }, - "description": { - "type": "string" - } - } - }, - "schema": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#schema-object", - "$dynamicAnchor": "meta", - "type": ["object", "boolean"] - }, - "security-scheme": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#security-scheme-object", - "type": "object", - "properties": { - "type": { - "enum": ["apiKey", "http", "mutualTLS", "oauth2", "openIdConnect"] - }, - "description": { - "type": "string" - } - }, - "required": ["type"], - "allOf": [ - { - "$ref": "#/$defs/specification-extensions" - }, - { - "$ref": "#/$defs/security-scheme/$defs/type-apikey" - }, - { - "$ref": "#/$defs/security-scheme/$defs/type-http" - }, - { - "$ref": "#/$defs/security-scheme/$defs/type-http-bearer" - }, - { - "$ref": "#/$defs/security-scheme/$defs/type-oauth2" - }, - { - "$ref": "#/$defs/security-scheme/$defs/type-oidc" - } - ], - "unevaluatedProperties": false, - "$defs": { - "type-apikey": { - "if": { - "properties": { - "type": { - "const": "apiKey" - } - }, - "required": ["type"] - }, - "then": { - "properties": { - "name": { - "type": "string" - }, - "in": { - "enum": ["query", "header", "cookie"] - } - }, - "required": ["name", "in"] - } - }, - "type-http": { - "if": { - "properties": { - "type": { - "const": "http" - } - }, - "required": ["type"] - }, - "then": { - "properties": { - "scheme": { - "type": "string" - } - }, - "required": ["scheme"] - } - }, - "type-http-bearer": { - "if": { - "properties": { - "type": { - "const": "http" - }, - "scheme": { - "type": "string", - "pattern": "^[Bb][Ee][Aa][Rr][Ee][Rr]$" - } - }, - "required": ["type", "scheme"] - }, - "then": { - "properties": { - "bearerFormat": { - "type": "string" - } - } - } - }, - "type-oauth2": { - "if": { - "properties": { - "type": { - "const": "oauth2" - } - }, - "required": ["type"] - }, - "then": { - "properties": { - "flows": { - "$ref": "#/$defs/oauth-flows" - } - }, - "required": ["flows"] - } - }, - "type-oidc": { - "if": { - "properties": { - "type": { - "const": "openIdConnect" - } - }, - "required": ["type"] - }, - "then": { - "properties": { - "openIdConnectUrl": { - "type": "string", - "format": "uri" - } - }, - "required": ["openIdConnectUrl"] - } - } - } - }, - "security-scheme-or-reference": { - "if": { - "type": "object", - "required": ["$ref"] - }, - "then": { - "$ref": "#/$defs/reference" - }, - "else": { - "$ref": "#/$defs/security-scheme" - } - }, - "oauth-flows": { - "type": "object", - "properties": { - "implicit": { - "$ref": "#/$defs/oauth-flows/$defs/implicit" - }, - "password": { - "$ref": "#/$defs/oauth-flows/$defs/password" - }, - "clientCredentials": { - "$ref": "#/$defs/oauth-flows/$defs/client-credentials" - }, - "authorizationCode": { - "$ref": "#/$defs/oauth-flows/$defs/authorization-code" - } - }, - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false, - "$defs": { - "implicit": { - "type": "object", - "properties": { - "authorizationUrl": { - "type": "string", - "format": "uri" - }, - "refreshUrl": { - "type": "string", - "format": "uri" - }, - "scopes": { - "$ref": "#/$defs/map-of-strings" - } - }, - "required": ["authorizationUrl", "scopes"], - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "password": { - "type": "object", - "properties": { - "tokenUrl": { - "type": "string", - "format": "uri" - }, - "refreshUrl": { - "type": "string", - "format": "uri" - }, - "scopes": { - "$ref": "#/$defs/map-of-strings" - } - }, - "required": ["tokenUrl", "scopes"], - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "client-credentials": { - "type": "object", - "properties": { - "tokenUrl": { - "type": "string", - "format": "uri" - }, - "refreshUrl": { - "type": "string", - "format": "uri" - }, - "scopes": { - "$ref": "#/$defs/map-of-strings" - } - }, - "required": ["tokenUrl", "scopes"], - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "authorization-code": { - "type": "object", - "properties": { - "authorizationUrl": { - "type": "string", - "format": "uri" - }, - "tokenUrl": { - "type": "string", - "format": "uri" - }, - "refreshUrl": { - "type": "string", - "format": "uri" - }, - "scopes": { - "$ref": "#/$defs/map-of-strings" - } - }, - "required": ["authorizationUrl", "tokenUrl", "scopes"], - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - } - } - }, - "security-requirement": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#security-requirement-object", - "type": "object", - "additionalProperties": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "specification-extensions": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#specification-extensions", - "patternProperties": { - "^x-": true - } - }, - "examples": { - "properties": { - "example": true, - "examples": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/example-or-reference" - } - } - } - }, - "map-of-strings": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "styles-for-form": { - "if": { - "properties": { - "style": { - "const": "form" - } - }, - "required": ["style"] - }, - "then": { - "properties": { - "explode": { - "default": true - } - } - }, - "else": { - "properties": { - "explode": { - "default": false - } - } - } - } - } + "$id": "https://spec.openapis.org/oas/3.1/schema/2022-10-07", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "The description of OpenAPI v3.1.x documents without schema validation, as defined by https://spec.openapis.org/oas/v3.1.0", + "type": "object", + "properties": { + "openapi": { + "type": "string", + "pattern": "^3\\.1\\.\\d+(-.+)?$" + }, + "info": { + "$ref": "#/$defs/info" + }, + "jsonSchemaDialect": { + "type": "string", + "format": "uri", + "default": "https://spec.openapis.org/oas/3.1/dialect/base" + }, + "servers": { + "type": "array", + "items": { + "$ref": "#/$defs/server" + }, + "default": [ + { + "url": "/" + } + ] + }, + "paths": { + "$ref": "#/$defs/paths" + }, + "webhooks": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/path-item-or-reference" + } + }, + "components": { + "$ref": "#/$defs/components" + }, + "security": { + "type": "array", + "items": { + "$ref": "#/$defs/security-requirement" + } + }, + "tags": { + "type": "array", + "items": { + "$ref": "#/$defs/tag" + } + }, + "externalDocs": { + "$ref": "#/$defs/external-documentation" + } + }, + "required": ["openapi", "info"], + "anyOf": [ + { + "required": ["paths"] + }, + { + "required": ["components"] + }, + { + "required": ["webhooks"] + } + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false, + "$defs": { + "info": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#info-object", + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "termsOfService": { + "type": "string", + "format": "uri" + }, + "contact": { + "$ref": "#/$defs/contact" + }, + "license": { + "$ref": "#/$defs/license" + }, + "version": { + "type": "string" + } + }, + "required": ["title", "version"], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "contact": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#contact-object", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + }, + "email": { + "type": "string", + "format": "email" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "license": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#license-object", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "identifier": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + } + }, + "required": ["name"], + "dependentSchemas": { + "identifier": { + "not": { + "required": ["url"] + } + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "server": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#server-object", + "type": "object", + "properties": { + "url": { + "type": "string", + "format": "uri-reference" + }, + "description": { + "type": "string" + }, + "variables": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/server-variable" + } + } + }, + "required": ["url"], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "server-variable": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#server-variable-object", + "type": "object", + "properties": { + "enum": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + }, + "default": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "required": ["default"], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "components": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#components-object", + "type": "object", + "properties": { + "schemas": { + "type": "object", + "additionalProperties": { + "$dynamicRef": "#meta" + } + }, + "responses": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/response-or-reference" + } + }, + "parameters": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/parameter-or-reference" + } + }, + "examples": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/example-or-reference" + } + }, + "requestBodies": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/request-body-or-reference" + } + }, + "headers": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/header-or-reference" + } + }, + "securitySchemes": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/security-scheme-or-reference" + } + }, + "links": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/link-or-reference" + } + }, + "callbacks": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/callbacks-or-reference" + } + }, + "pathItems": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/path-item-or-reference" + } + } + }, + "patternProperties": { + "^(schemas|responses|parameters|examples|requestBodies|headers|securitySchemes|links|callbacks|pathItems)$": { + "$comment": "Enumerating all of the property names in the regex above is necessary for unevaluatedProperties to work as expected", + "propertyNames": { + "pattern": "^[a-zA-Z0-9._-]+$" + } + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "paths": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#paths-object", + "type": "object", + "patternProperties": { + "^/": { + "$ref": "#/$defs/path-item" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "path-item": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#path-item-object", + "type": "object", + "properties": { + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "servers": { + "type": "array", + "items": { + "$ref": "#/$defs/server" + } + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/$defs/parameter-or-reference" + } + }, + "get": { + "$ref": "#/$defs/operation" + }, + "put": { + "$ref": "#/$defs/operation" + }, + "post": { + "$ref": "#/$defs/operation" + }, + "delete": { + "$ref": "#/$defs/operation" + }, + "options": { + "$ref": "#/$defs/operation" + }, + "head": { + "$ref": "#/$defs/operation" + }, + "patch": { + "$ref": "#/$defs/operation" + }, + "trace": { + "$ref": "#/$defs/operation" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "path-item-or-reference": { + "if": { + "type": "object", + "required": ["$ref"] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/path-item" + } + }, + "operation": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#operation-object", + "type": "object", + "properties": { + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "externalDocs": { + "$ref": "#/$defs/external-documentation" + }, + "operationId": { + "type": "string" + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/$defs/parameter-or-reference" + } + }, + "requestBody": { + "$ref": "#/$defs/request-body-or-reference" + }, + "responses": { + "$ref": "#/$defs/responses" + }, + "callbacks": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/callbacks-or-reference" + } + }, + "deprecated": { + "default": false, + "type": "boolean" + }, + "security": { + "type": "array", + "items": { + "$ref": "#/$defs/security-requirement" + } + }, + "servers": { + "type": "array", + "items": { + "$ref": "#/$defs/server" + } + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "external-documentation": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#external-documentation-object", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + } + }, + "required": ["url"], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "parameter": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#parameter-object", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "in": { + "enum": ["query", "header", "path", "cookie"] + }, + "description": { + "type": "string" + }, + "required": { + "default": false, + "type": "boolean" + }, + "deprecated": { + "default": false, + "type": "boolean" + }, + "schema": { + "$dynamicRef": "#meta" + }, + "content": { + "$ref": "#/$defs/content", + "minProperties": 1, + "maxProperties": 1 + } + }, + "required": ["name", "in"], + "oneOf": [ + { + "required": ["schema"] + }, + { + "required": ["content"] + } + ], + "if": { + "properties": { + "in": { + "const": "query" + } + }, + "required": ["in"] + }, + "then": { + "properties": { + "allowEmptyValue": { + "default": false, + "type": "boolean" + } + } + }, + "dependentSchemas": { + "schema": { + "properties": { + "style": { + "type": "string" + }, + "explode": { + "type": "boolean" + } + }, + "allOf": [ + { + "$ref": "#/$defs/examples" + }, + { + "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-path" + }, + { + "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-header" + }, + { + "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-query" + }, + { + "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-cookie" + }, + { + "$ref": "#/$defs/styles-for-form" + } + ], + "$defs": { + "styles-for-path": { + "if": { + "properties": { + "in": { + "const": "path" + } + }, + "required": ["in"] + }, + "then": { + "properties": { + "style": { + "default": "simple", + "enum": ["matrix", "label", "simple"] + }, + "required": { + "const": true + } + }, + "required": ["required"] + } + }, + "styles-for-header": { + "if": { + "properties": { + "in": { + "const": "header" + } + }, + "required": ["in"] + }, + "then": { + "properties": { + "style": { + "default": "simple", + "const": "simple" + } + } + } + }, + "styles-for-query": { + "if": { + "properties": { + "in": { + "const": "query" + } + }, + "required": ["in"] + }, + "then": { + "properties": { + "style": { + "default": "form", + "enum": [ + "form", + "spaceDelimited", + "pipeDelimited", + "deepObject" + ] + }, + "allowReserved": { + "default": false, + "type": "boolean" + } + } + } + }, + "styles-for-cookie": { + "if": { + "properties": { + "in": { + "const": "cookie" + } + }, + "required": ["in"] + }, + "then": { + "properties": { + "style": { + "default": "form", + "const": "form" + } + } + } + } + } + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "parameter-or-reference": { + "if": { + "type": "object", + "required": ["$ref"] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/parameter" + } + }, + "request-body": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#request-body-object", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "content": { + "$ref": "#/$defs/content" + }, + "required": { + "default": false, + "type": "boolean" + } + }, + "required": ["content"], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "request-body-or-reference": { + "if": { + "type": "object", + "required": ["$ref"] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/request-body" + } + }, + "content": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#fixed-fields-10", + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/media-type" + }, + "propertyNames": { + "format": "media-range" + } + }, + "media-type": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#media-type-object", + "type": "object", + "properties": { + "schema": { + "$dynamicRef": "#meta" + }, + "encoding": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/encoding" + } + } + }, + "allOf": [ + { + "$ref": "#/$defs/specification-extensions" + }, + { + "$ref": "#/$defs/examples" + } + ], + "unevaluatedProperties": false + }, + "encoding": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#encoding-object", + "type": "object", + "properties": { + "contentType": { + "type": "string", + "format": "media-range" + }, + "headers": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/header-or-reference" + } + }, + "style": { + "default": "form", + "enum": ["form", "spaceDelimited", "pipeDelimited", "deepObject"] + }, + "explode": { + "type": "boolean" + }, + "allowReserved": { + "default": false, + "type": "boolean" + } + }, + "allOf": [ + { + "$ref": "#/$defs/specification-extensions" + }, + { + "$ref": "#/$defs/styles-for-form" + } + ], + "unevaluatedProperties": false + }, + "responses": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#responses-object", + "type": "object", + "properties": { + "default": { + "$ref": "#/$defs/response-or-reference" + } + }, + "patternProperties": { + "^[1-5](?:[0-9]{2}|XX)$": { + "$ref": "#/$defs/response-or-reference" + } + }, + "minProperties": 1, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false, + "if": { + "$comment": "either default, or at least one response code property must exist", + "patternProperties": { + "^[1-5](?:[0-9]{2}|XX)$": false + } + }, + "then": { + "required": ["default"] + } + }, + "response": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#response-object", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "headers": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/header-or-reference" + } + }, + "content": { + "$ref": "#/$defs/content" + }, + "links": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/link-or-reference" + } + } + }, + "required": ["description"], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "response-or-reference": { + "if": { + "type": "object", + "required": ["$ref"] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/response" + } + }, + "callbacks": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#callback-object", + "type": "object", + "$ref": "#/$defs/specification-extensions", + "additionalProperties": { + "$ref": "#/$defs/path-item-or-reference" + } + }, + "callbacks-or-reference": { + "if": { + "type": "object", + "required": ["$ref"] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/callbacks" + } + }, + "example": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#example-object", + "type": "object", + "properties": { + "summary": { + "type": "string" + }, + "description": { + "type": "string" + }, + "value": true, + "externalValue": { + "type": "string", + "format": "uri" + } + }, + "not": { + "required": ["value", "externalValue"] + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "example-or-reference": { + "if": { + "type": "object", + "required": ["$ref"] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/example" + } + }, + "link": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#link-object", + "type": "object", + "properties": { + "operationRef": { + "type": "string", + "format": "uri-reference" + }, + "operationId": { + "type": "string" + }, + "parameters": { + "$ref": "#/$defs/map-of-strings" + }, + "requestBody": true, + "description": { + "type": "string" + }, + "body": { + "$ref": "#/$defs/server" + } + }, + "oneOf": [ + { + "required": ["operationRef"] + }, + { + "required": ["operationId"] + } + ], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "link-or-reference": { + "if": { + "type": "object", + "required": ["$ref"] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/link" + } + }, + "header": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#header-object", + "type": "object", + "properties": { + "description": { + "type": "string" + }, + "required": { + "default": false, + "type": "boolean" + }, + "deprecated": { + "default": false, + "type": "boolean" + }, + "schema": { + "$dynamicRef": "#meta" + }, + "content": { + "$ref": "#/$defs/content", + "minProperties": 1, + "maxProperties": 1 + } + }, + "oneOf": [ + { + "required": ["schema"] + }, + { + "required": ["content"] + } + ], + "dependentSchemas": { + "schema": { + "properties": { + "style": { + "default": "simple", + "const": "simple" + }, + "explode": { + "default": false, + "type": "boolean" + } + }, + "$ref": "#/$defs/examples" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "header-or-reference": { + "if": { + "type": "object", + "required": ["$ref"] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/header" + } + }, + "tag": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#tag-object", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "externalDocs": { + "$ref": "#/$defs/external-documentation" + } + }, + "required": ["name"], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "reference": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#reference-object", + "type": "object", + "properties": { + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "summary": { + "type": "string" + }, + "description": { + "type": "string" + } + } + }, + "schema": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#schema-object", + "$dynamicAnchor": "meta", + "type": ["object", "boolean"] + }, + "security-scheme": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#security-scheme-object", + "type": "object", + "properties": { + "type": { + "enum": ["apiKey", "http", "mutualTLS", "oauth2", "openIdConnect"] + }, + "description": { + "type": "string" + } + }, + "required": ["type"], + "allOf": [ + { + "$ref": "#/$defs/specification-extensions" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-apikey" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-http" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-http-bearer" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-oauth2" + }, + { + "$ref": "#/$defs/security-scheme/$defs/type-oidc" + } + ], + "unevaluatedProperties": false, + "$defs": { + "type-apikey": { + "if": { + "properties": { + "type": { + "const": "apiKey" + } + }, + "required": ["type"] + }, + "then": { + "properties": { + "name": { + "type": "string" + }, + "in": { + "enum": ["query", "header", "cookie"] + } + }, + "required": ["name", "in"] + } + }, + "type-http": { + "if": { + "properties": { + "type": { + "const": "http" + } + }, + "required": ["type"] + }, + "then": { + "properties": { + "scheme": { + "type": "string" + } + }, + "required": ["scheme"] + } + }, + "type-http-bearer": { + "if": { + "properties": { + "type": { + "const": "http" + }, + "scheme": { + "type": "string", + "pattern": "^[Bb][Ee][Aa][Rr][Ee][Rr]$" + } + }, + "required": ["type", "scheme"] + }, + "then": { + "properties": { + "bearerFormat": { + "type": "string" + } + } + } + }, + "type-oauth2": { + "if": { + "properties": { + "type": { + "const": "oauth2" + } + }, + "required": ["type"] + }, + "then": { + "properties": { + "flows": { + "$ref": "#/$defs/oauth-flows" + } + }, + "required": ["flows"] + } + }, + "type-oidc": { + "if": { + "properties": { + "type": { + "const": "openIdConnect" + } + }, + "required": ["type"] + }, + "then": { + "properties": { + "openIdConnectUrl": { + "type": "string", + "format": "uri" + } + }, + "required": ["openIdConnectUrl"] + } + } + } + }, + "security-scheme-or-reference": { + "if": { + "type": "object", + "required": ["$ref"] + }, + "then": { + "$ref": "#/$defs/reference" + }, + "else": { + "$ref": "#/$defs/security-scheme" + } + }, + "oauth-flows": { + "type": "object", + "properties": { + "implicit": { + "$ref": "#/$defs/oauth-flows/$defs/implicit" + }, + "password": { + "$ref": "#/$defs/oauth-flows/$defs/password" + }, + "clientCredentials": { + "$ref": "#/$defs/oauth-flows/$defs/client-credentials" + }, + "authorizationCode": { + "$ref": "#/$defs/oauth-flows/$defs/authorization-code" + } + }, + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false, + "$defs": { + "implicit": { + "type": "object", + "properties": { + "authorizationUrl": { + "type": "string", + "format": "uri" + }, + "refreshUrl": { + "type": "string", + "format": "uri" + }, + "scopes": { + "$ref": "#/$defs/map-of-strings" + } + }, + "required": ["authorizationUrl", "scopes"], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "password": { + "type": "object", + "properties": { + "tokenUrl": { + "type": "string", + "format": "uri" + }, + "refreshUrl": { + "type": "string", + "format": "uri" + }, + "scopes": { + "$ref": "#/$defs/map-of-strings" + } + }, + "required": ["tokenUrl", "scopes"], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "client-credentials": { + "type": "object", + "properties": { + "tokenUrl": { + "type": "string", + "format": "uri" + }, + "refreshUrl": { + "type": "string", + "format": "uri" + }, + "scopes": { + "$ref": "#/$defs/map-of-strings" + } + }, + "required": ["tokenUrl", "scopes"], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + }, + "authorization-code": { + "type": "object", + "properties": { + "authorizationUrl": { + "type": "string", + "format": "uri" + }, + "tokenUrl": { + "type": "string", + "format": "uri" + }, + "refreshUrl": { + "type": "string", + "format": "uri" + }, + "scopes": { + "$ref": "#/$defs/map-of-strings" + } + }, + "required": ["authorizationUrl", "tokenUrl", "scopes"], + "$ref": "#/$defs/specification-extensions", + "unevaluatedProperties": false + } + } + }, + "security-requirement": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#security-requirement-object", + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "specification-extensions": { + "$comment": "https://spec.openapis.org/oas/v3.1.0#specification-extensions", + "patternProperties": { + "^x-": true + } + }, + "examples": { + "properties": { + "example": true, + "examples": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/example-or-reference" + } + } + } + }, + "map-of-strings": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "styles-for-form": { + "if": { + "properties": { + "style": { + "const": "form" + } + }, + "required": ["style"] + }, + "then": { + "properties": { + "explode": { + "default": true + } + } + }, + "else": { + "properties": { + "explode": { + "default": false + } + } + } + } + } } diff --git a/__tests__/json-schema.test.ts b/__tests__/json-schema.test.ts index 230dc0f..1ad163b 100644 --- a/__tests__/json-schema.test.ts +++ b/__tests__/json-schema.test.ts @@ -1,31 +1,31 @@ /* eslint-disable no-new */ -import { Ajv } from 'ajv'; -import { test, expect, describe } from 'vitest'; -import { exampleApi } from './fixtures/apis/example.ts'; -import { noteTakingApi } from './fixtures/apis/note-taking.ts'; +import { Ajv } from "ajv"; +import { test, expect, describe } from "vitest"; +import { exampleApi } from "./fixtures/apis/example.ts"; +import { noteTakingApi } from "./fixtures/apis/note-taking.ts"; -describe('Example', () => { - const jsonSchema = exampleApi.synthJsonSchema(); +describe("Example", () => { + const jsonSchema = exampleApi.synthJsonSchema(); - test('JSON Schema snapshot', async () => { - expect(jsonSchema).toMatchSnapshot(); - }); + test("JSON Schema snapshot", async () => { + expect(jsonSchema).toMatchSnapshot(); + }); - test('JSON Schema AJV validate', async () => { - const ajv = new Ajv(); - expect(ajv.validateSchema(jsonSchema)).toBeTruthy(); - }); + test("JSON Schema AJV validate", async () => { + const ajv = new Ajv(); + expect(ajv.validateSchema(jsonSchema)).toBeTruthy(); + }); }); -describe('Note Taking', () => { - const jsonSchema = noteTakingApi.synthJsonSchema(); +describe("Note Taking", () => { + const jsonSchema = noteTakingApi.synthJsonSchema(); - test('JSON Schema snapshot', async () => { - expect(jsonSchema).toMatchSnapshot(); - }); + test("JSON Schema snapshot", async () => { + expect(jsonSchema).toMatchSnapshot(); + }); - test('JSON Schema AJV validate', async () => { - const ajv = new Ajv(); - expect(ajv.validateSchema(jsonSchema)).toBeTruthy(); - }); + test("JSON Schema AJV validate", async () => { + const ajv = new Ajv(); + expect(ajv.validateSchema(jsonSchema)).toBeTruthy(); + }); }); diff --git a/__tests__/openapi-schema.test.ts b/__tests__/openapi-schema.test.ts index 62294c8..c3cd2cd 100644 --- a/__tests__/openapi-schema.test.ts +++ b/__tests__/openapi-schema.test.ts @@ -1,34 +1,34 @@ -import SwaggerParser from '@apidevtools/swagger-parser'; -import { test, expect, describe } from 'vitest'; -import { exampleApi } from './fixtures/apis/example.ts'; -import { noteTakingApi } from './fixtures/apis/note-taking.ts'; +import SwaggerParser from "@apidevtools/swagger-parser"; +import { test, expect, describe } from "vitest"; +import { exampleApi } from "./fixtures/apis/example.ts"; +import { noteTakingApi } from "./fixtures/apis/note-taking.ts"; -describe('Example', () => { - test('OpenAPI', async () => { - const document = exampleApi.synth(); - expect(document).toMatchSnapshot(); - }); +describe("Example", () => { + test("OpenAPI", async () => { + const document = exampleApi.synth(); + expect(document).toMatchSnapshot(); + }); - test('Swagger Parser validate', async () => { - const document = exampleApi.synth(); - // WARN: this function mutates the input - // @ts-expect-error openapi3-ts and openapi-types have incompatible types under exactOptionalPropertyTypes - const result = await SwaggerParser.validate(structuredClone(document)); - expect(result).toMatchSnapshot(); - }); + test("Swagger Parser validate", async () => { + const document = exampleApi.synth(); + // WARN: this function mutates the input + // @ts-expect-error openapi3-ts and openapi-types have incompatible types under exactOptionalPropertyTypes + const result = await SwaggerParser.validate(structuredClone(document)); + expect(result).toMatchSnapshot(); + }); }); -describe('Note Taking', () => { - test('OpenAPI', async () => { - const document = noteTakingApi.synth(); - expect(document).toMatchSnapshot(); - }); +describe("Note Taking", () => { + test("OpenAPI", async () => { + const document = noteTakingApi.synth(); + expect(document).toMatchSnapshot(); + }); - test('Swagger Parser validate', async () => { - const document = noteTakingApi.synth(); - // WARN: this function mutates the input - // @ts-expect-error openapi3-ts and openapi-types have incompatible types under exactOptionalPropertyTypes - const result = await SwaggerParser.validate(structuredClone(document)); - expect(result).toMatchSnapshot(); - }); + test("Swagger Parser validate", async () => { + const document = noteTakingApi.synth(); + // WARN: this function mutates the input + // @ts-expect-error openapi3-ts and openapi-types have incompatible types under exactOptionalPropertyTypes + const result = await SwaggerParser.validate(structuredClone(document)); + expect(result).toMatchSnapshot(); + }); }); diff --git a/__tests__/parameter-types.test-d.ts b/__tests__/parameter-types.test-d.ts index fea21b0..8ce9bce 100644 --- a/__tests__/parameter-types.test-d.ts +++ b/__tests__/parameter-types.test-d.ts @@ -1,42 +1,42 @@ -import { expectTypeOf } from 'vitest'; -import type { Parameter } from '../lib/parameter.ts'; -import type { ValidParameter } from '../lib/types.ts'; +import { expectTypeOf } from "vitest"; +import type { Parameter } from "../lib/parameter.ts"; +import type { ValidParameter } from "../lib/types.ts"; // Test that only matching path param names are allowed -type PathParams = ValidParameter<'/users/{userId}'>; -expectTypeOf>().toExtend(); +type PathParams = ValidParameter<"/users/{userId}">; +expectTypeOf>().toExtend(); // @ts-expect-error path parameter with wrong name is not allowed -expectTypeOf>().toExtend(); +expectTypeOf>().toExtend(); // Test that query parameters can be any string -expectTypeOf>().toExtend< - ValidParameter<'/users/{userId}'> +expectTypeOf>().toExtend< + ValidParameter<"/users/{userId}"> >(); // Test that lowercase header parameters are allowed -expectTypeOf>().toExtend< - ValidParameter<'/users/{userId}'> +expectTypeOf>().toExtend< + ValidParameter<"/users/{userId}"> >(); // @ts-expect-error uppercase header parameter names are not allowed (HTTP/2) -expectTypeOf>().toExtend< - ValidParameter<'/users/{userId}'> +expectTypeOf>().toExtend< + ValidParameter<"/users/{userId}"> >(); // Test that cookie parameters can be any string -expectTypeOf>().toExtend< - ValidParameter<'/users/{userId}'> +expectTypeOf>().toExtend< + ValidParameter<"/users/{userId}"> >(); // Test multi-param routes -type MultiParamRoute = ValidParameter<'/users/{userId}/notes/{noteId}'>; -expectTypeOf>().toExtend(); -expectTypeOf>().toExtend(); +type MultiParamRoute = ValidParameter<"/users/{userId}/notes/{noteId}">; +expectTypeOf>().toExtend(); +expectTypeOf>().toExtend(); // @ts-expect-error path parameter not in route -expectTypeOf>().toExtend(); +expectTypeOf>().toExtend(); // Test that non-path parameters work for any route -expectTypeOf>().toExtend(); -expectTypeOf>().toExtend(); +expectTypeOf>().toExtend(); +expectTypeOf>().toExtend(); diff --git a/__tests__/regression.test.ts b/__tests__/regression.test.ts index d2d6ed9..4fea802 100644 --- a/__tests__/regression.test.ts +++ b/__tests__/regression.test.ts @@ -1,52 +1,52 @@ +import { Api, Schema } from "@block65/openapi-constructs"; /* eslint-disable no-new */ -import { test } from 'vitest'; -import { Api, Schema } from '@block65/openapi-constructs'; +import { test } from "vitest"; -test('Regression', async () => { - const api = new Api({ - openapi: '3.1.0', - info: { - title: 'Example', - version: '1.0.0', - }, - }); +test("Regression", async () => { + const api = new Api({ + openapi: "3.1.0", + info: { + title: "Example", + version: "1.0.0", + }, + }); - const schema1 = new Schema(api, 'Test1', { - schema: { - type: 'object', - additionalProperties: false, - minProperties: 1, - properties: { - description: { - oneOf: [ - { - type: ['string', 'null'], - minLength: 1, - maxLength: 1024, - }, - { - type: 'null', - }, - ], - }, - }, - }, - }); + const schema1 = new Schema(api, "Test1", { + schema: { + type: "object", + additionalProperties: false, + minProperties: 1, + properties: { + description: { + oneOf: [ + { + type: ["string", "null"], + minLength: 1, + maxLength: 1024, + }, + { + type: "null", + }, + ], + }, + }, + }, + }); - new Schema(api, 'Test2', { - schema: { - allOf: [ - schema1.schema, - { - type: 'object', - required: ['name'], - properties: { - name: { - type: 'string', - }, - }, - }, - ], - }, - }); + new Schema(api, "Test2", { + schema: { + allOf: [ + schema1.schema, + { + type: "object", + required: ["name"], + properties: { + name: { + type: "string", + }, + }, + }, + ], + }, + }); }); diff --git a/examples/example.ts b/examples/example.ts index abf7a40..0b83a03 100644 --- a/examples/example.ts +++ b/examples/example.ts @@ -1,205 +1,205 @@ /* eslint-disable no-new */ import { - Api, - Parameter, - Path, - Reference, - Response, - Schema, - SecurityRequirement, - SecurityScheme, - Server, - Tag, -} from '@block65/openapi-constructs'; + Api, + Parameter, + Path, + Reference, + Response, + Schema, + SecurityRequirement, + SecurityScheme, + Server, + Tag, +} from "@block65/openapi-constructs"; const api = new Api({ - openapi: "3.1.0", - info: { - title: 'Example REST API', - version: '1.0.0', - }, + openapi: "3.1.0", + info: { + title: "Example REST API", + version: "1.0.0", + }, }); -new Server(api, 'ExampleServer', { - url: new URL('https://api.example.com'), +new Server(api, "ExampleServer", { + url: new URL("https://api.example.com"), }); -const httpBearerJwtScheme = new SecurityScheme(api, 'HttpBearerJwtScheme', { - type: 'http', - scheme: 'bearer', - bearerFormat: 'JWT', +const httpBearerJwtScheme = new SecurityScheme(api, "HttpBearerJwtScheme", { + type: "http", + scheme: "bearer", + bearerFormat: "JWT", }); -new SecurityRequirement(api, 'AllScopes', { - securityScheme: httpBearerJwtScheme, - scopes: [], +new SecurityRequirement(api, "AllScopes", { + securityScheme: httpBearerJwtScheme, + scopes: [], }); -const userTag = new Tag(api, 'UserTag', { - name: 'user', +const userTag = new Tag(api, "UserTag", { + name: "user", }); -const userDeleteScopeReq = new SecurityRequirement(api, 'UserDeleteScope', { - securityScheme: httpBearerJwtScheme, - scopes: ['users.delete'], +const userDeleteScopeReq = new SecurityRequirement(api, "UserDeleteScope", { + securityScheme: httpBearerJwtScheme, + scopes: ["users.delete"], }); -const noSecurityRequirement = new SecurityRequirement(api, 'NoSecurity'); - -const addressSchema = new Schema(api, 'Address', { - schema: { - type: 'object', - required: ['postcode'], - additionalProperties: false, - properties: { - postcode: { - type: 'integer', - format: 'int32', - minimum: 1000, - maximum: 9999, - }, - }, - }, +const noSecurityRequirement = new SecurityRequirement(api, "NoSecurity"); + +const addressSchema = new Schema(api, "Address", { + schema: { + type: "object", + required: ["postcode"], + additionalProperties: false, + properties: { + postcode: { + type: "integer", + format: "int32", + minimum: 1000, + maximum: 9999, + }, + }, + }, }); -const idSchema = new Schema(api, 'Id', { - schema: { - type: 'string', - minLength: 6, - maxLength: 6, - }, +const idSchema = new Schema(api, "Id", { + schema: { + type: "string", + minLength: 6, + maxLength: 6, + }, }); -const user = new Schema(api, 'User', { - schema: { - type: 'object', - required: ['name'], - additionalProperties: false, - properties: { - userId: idSchema.referenceObject(), - name: { - type: 'string', - }, - address: addressSchema.referenceObject(), - age: { - type: 'integer', - format: 'int32', - minimum: 0, - }, - }, - }, +const user = new Schema(api, "User", { + schema: { + type: "object", + required: ["name"], + additionalProperties: false, + properties: { + userId: idSchema.referenceObject(), + name: { + type: "string", + }, + address: addressSchema.referenceObject(), + age: { + type: "integer", + format: "int32", + minimum: 0, + }, + }, + }, }); -const updateUserRequest = new Schema(api, 'UpdateUserRequest', { - schema: { - type: 'object', - minProperties: 1, - additionalProperties: false, - properties: { - address: addressSchema.referenceObject(), - age: { - type: 'integer', - format: 'int32', - minimum: 0, - }, - }, - examples: [{ age: 10, address: { postcode: 2000 } }], - }, +const updateUserRequest = new Schema(api, "UpdateUserRequest", { + schema: { + type: "object", + minProperties: 1, + additionalProperties: false, + properties: { + address: addressSchema.referenceObject(), + age: { + type: "integer", + format: "int32", + minimum: 0, + }, + }, + examples: [{ age: 10, address: { postcode: 2000 } }], + }, }); -const createUserRequest = new Reference(user, 'CreateUserRequest'); +const createUserRequest = new Reference(user, "CreateUserRequest"); -const users = new Schema(api, 'Users', { - schema: { - type: 'array', - uniqueItems: true, - items: user.referenceObject(), - examples: [[1, 2, 3]], - }, +const users = new Schema(api, "Users", { + schema: { + type: "array", + uniqueItems: true, + items: user.referenceObject(), + examples: [[1, 2, 3]], + }, }); -const userIdParameter = new Parameter(api, 'UserId', { - name: 'userId', - in: 'path', - required: true, - schema: idSchema, +const userIdParameter = new Parameter(api, "UserId", { + name: "userId", + in: "path", + required: true, + schema: idSchema, }); new Path(api, { - path: '/users', - tags: new Set([userTag]), + path: "/users", + tags: new Set([userTag]), }) - .addOperation("get", { - operationId: 'listUsersCommand', - responses: { - 200: new Response(api, 'ListUsersResponse', { - description: 'User 200 response', - content: { - contentType: 'application/json', - schema: users, - }, - }), - }, - }) - .addOperation("post", { - operationId: 'createUserCommand', - requestBody: { - content: { - contentType: 'application/json', - schema: createUserRequest, - }, - }, - responses: { - 200: new Response(api, 'CreateUserResponse', { - description: 'User 200 response', - content: { - contentType: 'application/json', - schema: users, - }, - }), - }, - }); + .addOperation("get", { + operationId: "listUsersCommand", + responses: { + 200: new Response(api, "ListUsersResponse", { + description: "User 200 response", + content: { + contentType: "application/json", + schema: users, + }, + }), + }, + }) + .addOperation("post", { + operationId: "createUserCommand", + requestBody: { + content: { + contentType: "application/json", + schema: createUserRequest, + }, + }, + responses: { + 200: new Response(api, "CreateUserResponse", { + description: "User 200 response", + content: { + contentType: "application/json", + schema: users, + }, + }), + }, + }); new Path(api, { - path: '/users/{userId}', - parameters: [userIdParameter], + path: "/users/{userId}", + parameters: [userIdParameter], }) - .addOperation("get", { - operationId: 'getUserByIdCommand', - responses: { - 200: new Response(api, 'GetUserById', { - description: 'User 200 response', - content: { - contentType: 'application/json', - schema: user, - }, - }), - }, - }) - .addOperation("delete", { - operationId: 'deleteUserByIdCommand', - security: userDeleteScopeReq, - }) - .addOperation("head", { - operationId: 'checkUserIdAvailableCommand', - security: noSecurityRequirement, - }) - .addOperation("post", { - operationId: 'updateUserCommand', - requestBody: { - content: { - contentType: 'application/json', - schema: updateUserRequest, - }, - }, - responses: { - 200: new Response(api, 'UpdateUserResponse', { - content: { - contentType: 'application/json', - schema: user, - }, - }), - }, - }); + .addOperation("get", { + operationId: "getUserByIdCommand", + responses: { + 200: new Response(api, "GetUserById", { + description: "User 200 response", + content: { + contentType: "application/json", + schema: user, + }, + }), + }, + }) + .addOperation("delete", { + operationId: "deleteUserByIdCommand", + security: userDeleteScopeReq, + }) + .addOperation("head", { + operationId: "checkUserIdAvailableCommand", + security: noSecurityRequirement, + }) + .addOperation("post", { + operationId: "updateUserCommand", + requestBody: { + content: { + contentType: "application/json", + schema: updateUserRequest, + }, + }, + responses: { + 200: new Response(api, "UpdateUserResponse", { + content: { + contentType: "application/json", + schema: user, + }, + }), + }, + }); process.stdout.write(JSON.stringify(api.synth(), null, 2)); diff --git a/lib/ApiLowLevel.ts b/lib/ApiLowLevel.ts index 2ab0e77..ab5dff9 100644 --- a/lib/ApiLowLevel.ts +++ b/lib/ApiLowLevel.ts @@ -1,15 +1,15 @@ -import { Construct, type IConstruct } from 'constructs'; -import type { Api } from './api.ts'; +import { Construct, type IConstruct } from "constructs"; +import type { Api } from "./api.ts"; export class ApiLowLevel extends Construct { - public static of(c: IConstruct): Api { - const { scope } = c.node; + public static of(c: IConstruct): Api { + const { scope } = c.node; - if (!scope) { - // Api is the only construct without a scope. - return c as Api; - } + if (!scope) { + // Api is the only construct without a scope. + return c as Api; + } - return ApiLowLevel.of(scope); - } + return ApiLowLevel.of(scope); + } } diff --git a/lib/api.ts b/lib/api.ts index 2cae57a..2849ec0 100644 --- a/lib/api.ts +++ b/lib/api.ts @@ -1,93 +1,93 @@ -import type { JSONSchema7 } from 'json-schema'; -import type { oas31 } from 'openapi3-ts'; -import { ApiLowLevel } from './ApiLowLevel.ts'; -import { Header } from './header.ts'; -import { Parameter } from './parameter.ts'; -import { Path } from './path.ts'; -import { Reference } from './reference.ts'; -import { Schema } from './schema.ts'; -import { SecurityRequirement } from './security-requirement.ts'; -import { SecurityScheme } from './security-scheme.ts'; -import { Server } from './server.ts'; -import { Tag } from './tag.ts'; +import type { JSONSchema7 } from "json-schema"; +import type { oas31 } from "openapi3-ts"; +import { ApiLowLevel } from "./ApiLowLevel.ts"; +import { Header } from "./header.ts"; +import { Parameter } from "./parameter.ts"; +import { Path } from "./path.ts"; +import { Reference } from "./reference.ts"; +import { Schema } from "./schema.ts"; +import { SecurityRequirement } from "./security-requirement.ts"; +import { SecurityScheme } from "./security-scheme.ts"; +import { Server } from "./server.ts"; +import { Tag } from "./tag.ts"; -export type OpenApiVersion = '3.0' | '3.1.0'; +export type OpenApiVersion = "3.0" | "3.1.0"; export interface ApiOptions { - openapi: OpenApiVersion; - info: oas31.InfoObject; + openapi: OpenApiVersion; + info: oas31.InfoObject; } export class Api extends ApiLowLevel { - private options: ApiOptions; + private options: ApiOptions; - constructor(options: ApiOptions) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any - super(undefined as any, ''); + constructor(options: ApiOptions) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any + super(undefined as any, ""); - this.options = options; - } + this.options = options; + } - public synth() { - return { - openapi: this.options.openapi, - info: this.options.info, - servers: this.node.children - .filter((child): child is Server => child instanceof Server) - .map((child) => child.synth()), - tags: this.node.children - .filter((child): child is Tag => child instanceof Tag) - .map((child) => child.synth()), - components: { - schemas: Object.fromEntries( - this.node.children - .filter( - (child): child is Schema | Reference => - child instanceof Schema || child instanceof Reference, - ) - .map((child) => [child.schemaKey, child.synth()]), - ), - parameters: Object.fromEntries( - this.node.children - .filter((child): child is Parameter => child instanceof Parameter) - .map((child) => [child.schemaKey, child.synth()]), - ), - securitySchemes: Object.fromEntries( - this.node.children - .filter( - (child): child is SecurityScheme => - child instanceof SecurityScheme, - ) - .map((child) => [child.schemaKey, child.synth()]), - ), - headers: Object.fromEntries( - this.node.children - .filter((child): child is Header => child instanceof Header) - .map((child) => [child.schemaKey, child.synth()]), - ), - }, - paths: Object.fromEntries( - this.node.children - .filter((child): child is Path => child instanceof Path) - .map((child) => [child.schemaKey, child.synth()]), - ), - security: this.node.children - .filter( - (child): child is SecurityRequirement => - child instanceof SecurityRequirement, - ) - .map((child) => child.synth()), - } satisfies oas31.OpenAPIObject; - } + public synth() { + return { + openapi: this.options.openapi, + info: this.options.info, + servers: this.node.children + .filter((child): child is Server => child instanceof Server) + .map((child) => child.synth()), + tags: this.node.children + .filter((child): child is Tag => child instanceof Tag) + .map((child) => child.synth()), + components: { + schemas: Object.fromEntries( + this.node.children + .filter( + (child): child is Schema | Reference => + child instanceof Schema || child instanceof Reference, + ) + .map((child) => [child.schemaKey, child.synth()]), + ), + parameters: Object.fromEntries( + this.node.children + .filter((child): child is Parameter => child instanceof Parameter) + .map((child) => [child.schemaKey, child.synth()]), + ), + securitySchemes: Object.fromEntries( + this.node.children + .filter( + (child): child is SecurityScheme => + child instanceof SecurityScheme, + ) + .map((child) => [child.schemaKey, child.synth()]), + ), + headers: Object.fromEntries( + this.node.children + .filter((child): child is Header => child instanceof Header) + .map((child) => [child.schemaKey, child.synth()]), + ), + }, + paths: Object.fromEntries( + this.node.children + .filter((child): child is Path => child instanceof Path) + .map((child) => [child.schemaKey, child.synth()]), + ), + security: this.node.children + .filter( + (child): child is SecurityRequirement => + child instanceof SecurityRequirement, + ) + .map((child) => child.synth()), + } satisfies oas31.OpenAPIObject; + } - public synthJsonSchema() { - return { - $schema: 'http://json-schema.org/draft-07/schema#', - definitions: Object.fromEntries( - this.node.children - .filter((child): child is Schema => child instanceof Schema) - .map((child) => [child.schemaKey, child.synth()]), - ), - } satisfies JSONSchema7; - } + public synthJsonSchema() { + return { + $schema: "http://json-schema.org/draft-07/schema#", + definitions: Object.fromEntries( + this.node.children + .filter((child): child is Schema => child instanceof Schema) + .map((child) => [child.schemaKey, child.synth()]), + ), + } satisfies JSONSchema7; + } } diff --git a/lib/header.ts b/lib/header.ts index 3ae9b80..b81e711 100644 --- a/lib/header.ts +++ b/lib/header.ts @@ -1,66 +1,70 @@ -import { Construct } from 'constructs'; -import type { oas31 } from 'openapi3-ts'; -import type { Schema } from './schema.ts'; +import { Construct } from "constructs"; +import type { oas31 } from "openapi3-ts"; +import type { Schema } from "./schema.ts"; interface HeaderOptions { - description?: string; - required?: boolean; - deprecated?: boolean; - allowEmptyValue?: boolean; - style?: 'simple'; - explode?: boolean; - allowReserved?: boolean; - schema: Schema; + description?: string; + required?: boolean; + deprecated?: boolean; + allowEmptyValue?: boolean; + style?: "simple"; + explode?: boolean; + allowReserved?: boolean; + schema: Schema; } export class Header extends Construct { - private options: HeaderOptions; + private options: HeaderOptions; - constructor(scope: Construct, id: TName & Lowercase, options: HeaderOptions) { - super(scope, id); - this.options = options; - } + constructor( + scope: Construct, + id: TName & Lowercase, + options: HeaderOptions, + ) { + super(scope, id); + this.options = options; + } - public referenceObject(): oas31.ReferenceObject { - return { - $ref: this.jsonPointer(), - }; - } + public referenceObject(): oas31.ReferenceObject { + return { + $ref: this.jsonPointer(), + }; + } - public get schemaKey() { - return this.node.id; - } + public get schemaKey() { + return this.node.id; + } - public jsonPointer(): string { - return `#/components/headers/${this.schemaKey}`; - } + public jsonPointer(): string { + return `#/components/headers/${this.schemaKey}`; + } - public synth() { - return { - ...(this.options.description && { - description: this.options.description, - }), - ...(this.options.required && { - required: this.options.required, - }), - ...(this.options.deprecated && { - deprecated: this.options.deprecated, - }), - ...(this.options.allowEmptyValue && { - allowEmptyValue: this.options.allowEmptyValue, - }), - ...(this.options.style && { - style: this.options.style, - }), - ...(this.options.explode && { - explode: this.options.explode, - }), - ...(this.options.allowReserved && { - allowReserved: this.options.allowReserved, - }), - ...(this.options.schema && { - schema: this.options.schema.referenceObject(), - }), - } satisfies oas31.HeaderObject; - } + public synth() { + return { + ...(this.options.description && { + description: this.options.description, + }), + ...(this.options.required && { + required: this.options.required, + }), + ...(this.options.deprecated && { + deprecated: this.options.deprecated, + }), + ...(this.options.allowEmptyValue && { + allowEmptyValue: this.options.allowEmptyValue, + }), + ...(this.options.style && { + style: this.options.style, + }), + ...(this.options.explode && { + explode: this.options.explode, + }), + ...(this.options.allowReserved && { + allowReserved: this.options.allowReserved, + }), + ...(this.options.schema && { + schema: this.options.schema.referenceObject(), + }), + } satisfies oas31.HeaderObject; + } } diff --git a/lib/http-method.ts b/lib/http-method.ts index c8664c2..377e928 100644 --- a/lib/http-method.ts +++ b/lib/http-method.ts @@ -1 +1,9 @@ -export type HttpMethod = "get" | "put" | "post" | "delete" | "options" | "head" | "patch" | "trace"; \ No newline at end of file +export type HttpMethod = + | "get" + | "put" + | "post" + | "delete" + | "options" + | "head" + | "patch" + | "trace"; diff --git a/lib/index.ts b/lib/index.ts index a2e1133..8b37e7a 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -1,21 +1,21 @@ /* eslint-disable max-classes-per-file */ -import { Construct } from 'constructs'; +import { Construct } from "constructs"; // export the version of Constructs we are using to ensure we're // everyone is using the same version export { Construct }; -export { Api } from './api.ts'; -export { Header } from './header.ts'; -export { Parameter } from './parameter.ts'; -export { Path } from './path.ts'; -export { Reference } from './reference.ts'; -export { Response } from './response.ts'; -export { Schema, type SchemaOptions } from './schema.ts'; -export { SecurityRequirement } from './security-requirement.ts'; -export { SecurityScheme } from './security-scheme.ts'; -export { Server } from './server.ts'; -export { Tag } from './tag.ts'; +export { Api } from "./api.ts"; +export { Header } from "./header.ts"; +export { Parameter } from "./parameter.ts"; +export { Path } from "./path.ts"; +export { Reference } from "./reference.ts"; +export { Response } from "./response.ts"; +export { Schema, type SchemaOptions } from "./schema.ts"; +export { SecurityRequirement } from "./security-requirement.ts"; +export { SecurityScheme } from "./security-scheme.ts"; +export { Server } from "./server.ts"; +export { Tag } from "./tag.ts"; export class ServerVariable extends Construct {} diff --git a/lib/media-type.ts b/lib/media-type.ts index 063f0fc..2cec5bd 100644 --- a/lib/media-type.ts +++ b/lib/media-type.ts @@ -1,40 +1,40 @@ -import { Construct } from 'constructs'; -import type { oas31 } from 'openapi3-ts'; -import { Reference } from './reference.ts'; -import type { Schema } from './schema.ts'; +import { Construct } from "constructs"; +import type { oas31 } from "openapi3-ts"; +import { Reference } from "./reference.ts"; +import type { Schema } from "./schema.ts"; type ContentType = - | 'application/json' - | 'application/octet-stream' - | 'application/x-www-form-urlencoded' - | 'multipart/form-data' - | 'text/plain' - | 'image/*' - | (string & {}); + | "application/json" + | "application/octet-stream" + | "application/x-www-form-urlencoded" + | "multipart/form-data" + | "text/plain" + | "image/*" + | (string & {}); export interface MediaTypeOptions { - contentType: ContentType; - schema: Schema | Reference; + contentType: ContentType; + schema: Schema | Reference; } export class MediaType extends Construct { - private options: MediaTypeOptions; + private options: MediaTypeOptions; - public get contentType() { - return this.options.contentType; - } + public get contentType() { + return this.options.contentType; + } - constructor(scope: Construct, id: string, options: MediaTypeOptions) { - super(scope, id); - this.options = options; - } + constructor(scope: Construct, id: string, options: MediaTypeOptions) { + super(scope, id); + this.options = options; + } - public synth(): oas31.MediaTypeObject { - return { - schema: - this.options.schema instanceof Reference - ? this.options.schema.synth() - : this.options.schema.referenceObject(), - }; - } + public synth(): oas31.MediaTypeObject { + return { + schema: + this.options.schema instanceof Reference + ? this.options.schema.synth() + : this.options.schema.referenceObject(), + }; + } } diff --git a/lib/operation.ts b/lib/operation.ts index 596be4d..c4fbf72 100644 --- a/lib/operation.ts +++ b/lib/operation.ts @@ -1,105 +1,105 @@ -import { Construct } from 'constructs'; -import type { oas31 } from 'openapi3-ts'; -import { RequestBody, type RequestBodyOptions } from './request-body.ts'; -import type { Response } from './response.ts'; -import type { SecurityRequirement } from './security-requirement.ts'; -import type { Tag } from './tag.ts'; -import type { ValidParameter } from './types.ts'; -import { stripUndefined } from './utils.ts'; -import type { HttpMethod } from './http-method.ts'; +import { Construct } from "constructs"; +import type { oas31 } from "openapi3-ts"; +import type { HttpMethod } from "./http-method.ts"; +import { RequestBody, type RequestBodyOptions } from "./request-body.ts"; +import type { Response } from "./response.ts"; +import type { SecurityRequirement } from "./security-requirement.ts"; +import type { Tag } from "./tag.ts"; +import type { ValidParameter } from "./types.ts"; +import { stripUndefined } from "./utils.ts"; -export interface OperationOptions { - operationId: string; - summary?: string; - description?: string; - tags?: Set; - deprecated?: boolean; - parameters?: ValidParameter[]; - security?: SecurityRequirement; - responses?: { - [statusCode: string | number]: Response; - }; - requestBody?: RequestBody | RequestBodyOptions; - order?: number; +export interface OperationOptions { + operationId: string; + summary?: string; + description?: string; + tags?: Set; + deprecated?: boolean; + parameters?: ValidParameter[]; + security?: SecurityRequirement; + responses?: { + [statusCode: string | number]: Response; + }; + requestBody?: RequestBody | RequestBodyOptions; + order?: number; } -export class Operation extends Construct { - private readonly options: OperationOptions; +export class Operation extends Construct { + private readonly options: OperationOptions; - public readonly method: HttpMethod; + public readonly method: HttpMethod; - public readonly order: number; + public readonly order: number; - private requestBody?: RequestBody; + private requestBody?: RequestBody; - public hasOperationId(operationId: OperationOptions['operationId']): boolean { - return this.options.operationId === operationId; - } + public hasOperationId(operationId: OperationOptions["operationId"]): boolean { + return this.options.operationId === operationId; + } - constructor( - scope: Construct, - method: HttpMethod, - options: OperationOptions, - ) { - super(scope, method); - this.method = method; - this.options = options; + constructor( + scope: Construct, + method: HttpMethod, + options: OperationOptions, + ) { + super(scope, method); + this.method = method; + this.options = options; - this.order = options.order || 0; + this.order = options.order || 0; - if (options.requestBody) { - this.requestBody = - options.requestBody instanceof RequestBody - ? options.requestBody - : new RequestBody(this, method, options.requestBody); - } - } + if (options.requestBody) { + this.requestBody = + options.requestBody instanceof RequestBody + ? options.requestBody + : new RequestBody(this, method, options.requestBody); + } + } - public validate() { - // const api = Api.of(this).node.findChild; + public validate() { + // const api = Api.of(this).node.findChild; - const duplicateOperation = this.node.scope?.node.children - .filter( - (child): child is Operation => - child instanceof Operation && child !== this, - ) - .find((child) => child.hasOperationId(this.options.operationId)); + const duplicateOperation = this.node.scope?.node.children + .filter( + (child): child is Operation => + child instanceof Operation && child !== this, + ) + .find((child) => child.hasOperationId(this.options.operationId)); - if (duplicateOperation) { - return [`Duplicate operationId ${this.options.operationId}`]; - } + if (duplicateOperation) { + return [`Duplicate operationId ${this.options.operationId}`]; + } - return []; - } + return []; + } - public synth() { - return stripUndefined({ - operationId: this.options.operationId, - description: this.options.description || undefined, - summary: this.options.summary || undefined, - tags: - this.options.tags && [...this.options.tags].map((child) => child.name), - deprecated: this.options.deprecated, - ...(this.options.parameters && { - parameters: this.options.parameters.map((child) => child.synth()), - }), - ...(this.options.security && { - security: [this.options.security.synth()], - }), - ...(this.options.tags && { - tags: Array.from(this.options.tags).map((child) => child.name), - }), - ...(this.requestBody && { - requestBody: this.requestBody.synth(), - }), - ...(this.options.responses && { - responses: Object.fromEntries( - Object.entries(this.options.responses).map(([statusCode, child]) => [ - statusCode.toString(), - child.synth(), - ]), - ), - }), - }) satisfies oas31.OperationObject; - } + public synth() { + return stripUndefined({ + operationId: this.options.operationId, + description: this.options.description || undefined, + summary: this.options.summary || undefined, + tags: + this.options.tags && [...this.options.tags].map((child) => child.name), + deprecated: this.options.deprecated, + ...(this.options.parameters && { + parameters: this.options.parameters.map((child) => child.synth()), + }), + ...(this.options.security && { + security: [this.options.security.synth()], + }), + ...(this.options.tags && { + tags: Array.from(this.options.tags).map((child) => child.name), + }), + ...(this.requestBody && { + requestBody: this.requestBody.synth(), + }), + ...(this.options.responses && { + responses: Object.fromEntries( + Object.entries(this.options.responses).map(([statusCode, child]) => [ + statusCode.toString(), + child.synth(), + ]), + ), + }), + }) satisfies oas31.OperationObject; + } } diff --git a/lib/parameter.ts b/lib/parameter.ts index 6387c4f..ab527da 100644 --- a/lib/parameter.ts +++ b/lib/parameter.ts @@ -1,90 +1,90 @@ -import { Construct } from 'constructs'; -import type { oas31 } from 'openapi3-ts'; -import type { Api } from './api.ts'; -import type { Schema } from './schema.ts'; +import { Construct } from "constructs"; +import type { oas31 } from "openapi3-ts"; +import type { Api } from "./api.ts"; +import type { Schema } from "./schema.ts"; -type StyleForIn = - TIn extends 'path' - ? 'matrix' | 'label' | 'simple' - : TIn extends 'query' - ? 'form' | 'spaceDelimited' | 'pipeDelimited' | 'deepObject' - : TIn extends 'header' - ? 'simple' - : TIn extends 'cookie' - ? 'form' - : never; +type StyleForIn = + TIn extends "path" + ? "matrix" | "label" | "simple" + : TIn extends "query" + ? "form" | "spaceDelimited" | "pipeDelimited" | "deepObject" + : TIn extends "header" + ? "simple" + : TIn extends "cookie" + ? "form" + : never; type ParameterName< - TName extends string | number | symbol, - TIn extends 'query' | 'header' | 'path' | 'cookie', -> = TIn extends 'header' ? TName & Lowercase : TName; + TName extends string | number | symbol, + TIn extends "query" | "header" | "path" | "cookie", +> = TIn extends "header" ? TName & Lowercase : TName; interface ParameterOptionsBase< - TName extends string | number | symbol, - TIn extends 'query' | 'header' | 'path' | 'cookie', + TName extends string | number | symbol, + TIn extends "query" | "header" | "path" | "cookie", > { - name: ParameterName; - in: TIn; - required: boolean; - description?: string; - deprecated?: boolean; - allowEmptyValue?: boolean; - allowReserved?: boolean; - style?: StyleForIn; - explode?: boolean; + name: ParameterName; + in: TIn; + required: boolean; + description?: string; + deprecated?: boolean; + allowEmptyValue?: boolean; + allowReserved?: boolean; + style?: StyleForIn; + explode?: boolean; } interface ParameterOptions< - TName extends string | number | symbol, - TIn extends 'query' | 'header' | 'path' | 'cookie', + TName extends string | number | symbol, + TIn extends "query" | "header" | "path" | "cookie", > extends ParameterOptionsBase { - schema: Schema; + schema: Schema; } export class Parameter< - TName extends string | number | symbol = '', - TIn extends 'query' | 'header' | 'path' | 'cookie' = 'query', + TName extends string | number | symbol = "", + TIn extends "query" | "header" | "path" | "cookie" = "query", > extends Construct { - private options: ParameterOptions; + private options: ParameterOptions; - constructor(scope: Api, id: string, options: ParameterOptions) { - super(scope, id); - this.options = options; - } + constructor(scope: Api, id: string, options: ParameterOptions) { + super(scope, id); + this.options = options; + } - public referenceObject(): oas31.ReferenceObject { - return { - $ref: this.jsonPointer(), - }; - } + public referenceObject(): oas31.ReferenceObject { + return { + $ref: this.jsonPointer(), + }; + } - public get schemaKey() { - return this.node.id; - } + public get schemaKey() { + return this.node.id; + } - public jsonPointer(): string { - return `#/components/parameters/${this.schemaKey}`; - } + public jsonPointer(): string { + return `#/components/parameters/${this.schemaKey}`; + } - public synth() { - return { - name: this.options.name.toString(), - in: this.options.in, - ...(this.options.description && { - description: this.options.description, - }), - ...(this.options.allowReserved && { - allowReserved: this.options.allowReserved, - }), - required: this.options.required, - ...(this.options.allowEmptyValue && { - allowEmptyValue: this.options.allowEmptyValue, - }), - ...(this.options.style && { style: this.options.style }), - ...(this.options.explode != null && { explode: this.options.explode }), - ...(this.options.schema && { - schema: this.options.schema.referenceObject(), - }), - } satisfies oas31.ParameterObject | oas31.ReferenceObject; - } + public synth() { + return { + name: this.options.name.toString(), + in: this.options.in, + ...(this.options.description && { + description: this.options.description, + }), + ...(this.options.allowReserved && { + allowReserved: this.options.allowReserved, + }), + required: this.options.required, + ...(this.options.allowEmptyValue && { + allowEmptyValue: this.options.allowEmptyValue, + }), + ...(this.options.style && { style: this.options.style }), + ...(this.options.explode != null && { explode: this.options.explode }), + ...(this.options.schema && { + schema: this.options.schema.referenceObject(), + }), + } satisfies oas31.ParameterObject | oas31.ReferenceObject; + } } diff --git a/lib/path.ts b/lib/path.ts index c465ea0..df8863b 100644 --- a/lib/path.ts +++ b/lib/path.ts @@ -1,67 +1,67 @@ -import { Construct } from 'constructs'; -import type { oas31 } from 'openapi3-ts'; -import type { Api } from './api.ts'; -import type { HttpMethod } from './http-method.ts'; -import { Operation, type OperationOptions } from './operation.ts'; -import type { Server } from './server.ts'; -import type { Tag } from './tag.ts'; -import type { ValidParameter } from './types.ts'; +import { Construct } from "constructs"; +import type { oas31 } from "openapi3-ts"; +import type { Api } from "./api.ts"; +import type { HttpMethod } from "./http-method.ts"; +import { Operation, type OperationOptions } from "./operation.ts"; +import type { Server } from "./server.ts"; +import type { Tag } from "./tag.ts"; +import type { ValidParameter } from "./types.ts"; interface PathOptions { - path: TPath; - summary?: string; - servers?: Server[]; - parameters?: ValidParameter[]; - tags?: Set; + path: TPath; + summary?: string; + servers?: Server[]; + parameters?: ValidParameter[]; + tags?: Set; } -export class Path extends Construct { - public options: PathOptions; +export class Path extends Construct { + public options: PathOptions; - constructor(scope: Api, options: PathOptions) { - super(scope, options.path); - this.options = options; - } + constructor(scope: Api, options: PathOptions) { + super(scope, options.path); + this.options = options; + } - public addOperation( - method: HttpMethod, - options: OperationOptions, - ): this { - // make sure we are not duplicating tags - // options.tags?.forEach((tag) => strict(!this.options.tags?.has(tag))); + public addOperation( + method: HttpMethod, + options: OperationOptions, + ): this { + // make sure we are not duplicating tags + // options.tags?.forEach((tag) => strict(!this.options.tags?.has(tag))); - // eslint-disable-next-line no-new - new Operation(this, method, { - ...options, - tags: new Set([...(this.options.tags || []), ...(options.tags || [])]), - }); - return this; - } + // eslint-disable-next-line no-new + new Operation(this, method, { + ...options, + tags: new Set([...(this.options.tags || []), ...(options.tags || [])]), + }); + return this; + } - public get schemaKey() { - return this.options.path; - } + public get schemaKey() { + return this.options.path; + } - public synth() { - return { - ...Object.fromEntries( - this.node - .findAll() - .filter( - (child): child is Operation => child instanceof Operation, - ) - .sort((a, b) => a.order - b.order) - .map((child) => [child.method, child.synth()]), - ), - ...(this.options.servers && { - servers: this.options.servers.map((child) => child.synth()), - }), - ...(this.options.parameters && { - parameters: this.options.parameters.map((child) => - child.referenceObject(), - ), - }), - ...(this.options.summary && { summary: this.options.summary }), - } satisfies oas31.PathItemObject; - } + public synth() { + return { + ...Object.fromEntries( + this.node + .findAll() + .filter( + (child): child is Operation => child instanceof Operation, + ) + .sort((a, b) => a.order - b.order) + .map((child) => [child.method, child.synth()]), + ), + ...(this.options.servers && { + servers: this.options.servers.map((child) => child.synth()), + }), + ...(this.options.parameters && { + parameters: this.options.parameters.map((child) => + child.referenceObject(), + ), + }), + ...(this.options.summary && { summary: this.options.summary }), + } satisfies oas31.PathItemObject; + } } diff --git a/lib/reference.ts b/lib/reference.ts index dfe3e35..d9a5748 100644 --- a/lib/reference.ts +++ b/lib/reference.ts @@ -1,28 +1,28 @@ -import { Construct } from 'constructs'; -import type { oas31 } from 'openapi3-ts'; -import { ApiLowLevel } from './ApiLowLevel.ts'; -import { Schema } from './schema.ts'; +import { Construct } from "constructs"; +import type { oas31 } from "openapi3-ts"; +import { ApiLowLevel } from "./ApiLowLevel.ts"; +import { Schema } from "./schema.ts"; type Target = Schema; // | Parameter; export class Reference extends Construct { - private schema: Schema; + private schema: Schema; - constructor(target: T, id: string) { - super(target, id); - // NOTE: Api.of causes a circular dependency - this.schema = new Schema(ApiLowLevel.of(this), id, { - // NOTE: synth because refs are not valid as #/component/schemas - // if it did, we could use referenceObject here instead - schema: target.synth(), - }); - } + constructor(target: T, id: string) { + super(target, id); + // NOTE: Api.of causes a circular dependency + this.schema = new Schema(ApiLowLevel.of(this), id, { + // NOTE: synth because refs are not valid as #/component/schemas + // if it did, we could use referenceObject here instead + schema: target.synth(), + }); + } - public get schemaKey() { - return this.schema.schemaKey; - } + public get schemaKey() { + return this.schema.schemaKey; + } - public synth(): oas31.ReferenceObject { - return this.schema.referenceObject(); - } + public synth(): oas31.ReferenceObject { + return this.schema.referenceObject(); + } } diff --git a/lib/request-body.ts b/lib/request-body.ts index fdcfd5a..f510998 100644 --- a/lib/request-body.ts +++ b/lib/request-body.ts @@ -1,46 +1,43 @@ -import { Construct } from 'constructs'; -import type { oas31 } from 'openapi3-ts'; -import { MediaType, type MediaTypeOptions } from './media-type.ts'; +import { Construct } from "constructs"; +import type { oas31 } from "openapi3-ts"; +import { MediaType, type MediaTypeOptions } from "./media-type.ts"; export interface RequestBodyOptions { - content: MediaType | MediaTypeOptions | (MediaType | MediaTypeOptions)[]; - description?: string; - required?: boolean; + content: MediaType | MediaTypeOptions | (MediaType | MediaTypeOptions)[]; + description?: string; + required?: boolean; } export class RequestBody extends Construct { - private options: RequestBodyOptions; + private options: RequestBodyOptions; - private contentEntries: MediaType[]; + private contentEntries: MediaType[]; - constructor(scope: Construct, id: string, options: RequestBodyOptions) { - super(scope, id); - this.options = { - required: true, - ...options, - }; + constructor(scope: Construct, id: string, options: RequestBodyOptions) { + super(scope, id); + this.options = { + required: true, + ...options, + }; - const items = Array.isArray(options.content) - ? options.content - : [options.content]; + const items = Array.isArray(options.content) + ? options.content + : [options.content]; - this.contentEntries = items.map((item, index) => - item instanceof MediaType - ? item - : new MediaType(this, `${id}${index}`, item), - ); - } + this.contentEntries = items.map((item, index) => + item instanceof MediaType + ? item + : new MediaType(this, `${id}${index}`, item), + ); + } - public synth(): oas31.RequestBodyObject { - return { - description: this.options.description || '', - content: Object.fromEntries( - this.contentEntries.map((entry) => [ - entry.contentType, - entry.synth(), - ]), - ), - ...(this.options.required && { required: this.options.required }), - }; - } + public synth(): oas31.RequestBodyObject { + return { + description: this.options.description || "", + content: Object.fromEntries( + this.contentEntries.map((entry) => [entry.contentType, entry.synth()]), + ), + ...(this.options.required && { required: this.options.required }), + }; + } } diff --git a/lib/response.ts b/lib/response.ts index d604b19..71dd2e3 100644 --- a/lib/response.ts +++ b/lib/response.ts @@ -1,53 +1,50 @@ -import { Construct } from 'constructs'; -import type { oas31 } from 'openapi3-ts'; -import type { Header } from './header.ts'; -import { MediaType, type MediaTypeOptions } from './media-type.ts'; +import { Construct } from "constructs"; +import type { oas31 } from "openapi3-ts"; +import type { Header } from "./header.ts"; +import { MediaType, type MediaTypeOptions } from "./media-type.ts"; interface ResponseOptions { - content?: MediaType | MediaTypeOptions | (MediaType | MediaTypeOptions)[]; - description?: string; - headers?: Record, Header>; + content?: MediaType | MediaTypeOptions | (MediaType | MediaTypeOptions)[]; + description?: string; + headers?: Record, Header>; } export class Response extends Construct { - private options: ResponseOptions; + private options: ResponseOptions; - private contentEntries: MediaType[]; + private contentEntries: MediaType[]; - constructor(scope: Construct, id: string, options: ResponseOptions = {}) { - super(scope, id); - this.options = options; + constructor(scope: Construct, id: string, options: ResponseOptions = {}) { + super(scope, id); + this.options = options; - const items = options.content - ? Array.isArray(options.content) - ? options.content - : [options.content] - : []; + const items = options.content + ? Array.isArray(options.content) + ? options.content + : [options.content] + : []; - this.contentEntries = items.map((item, index) => - item instanceof MediaType - ? item - : new MediaType(this, `${id}MediaType${index}`, item), - ); - } + this.contentEntries = items.map((item, index) => + item instanceof MediaType + ? item + : new MediaType(this, `${id}MediaType${index}`, item), + ); + } - public synth() { - return { - description: this.options.description || 'Successful response', - content: Object.fromEntries( - this.contentEntries.map((entry) => [ - entry.contentType, - entry.synth(), - ]), - ), - ...(this.options.headers && { - headers: Object.fromEntries( - Object.entries(this.options.headers).map(([name, header]) => [ - name, - header.synth(), - ]), - ), - }), - } satisfies oas31.ResponseObject; - } + public synth() { + return { + description: this.options.description || "Successful response", + content: Object.fromEntries( + this.contentEntries.map((entry) => [entry.contentType, entry.synth()]), + ), + ...(this.options.headers && { + headers: Object.fromEntries( + Object.entries(this.options.headers).map(([name, header]) => [ + name, + header.synth(), + ]), + ), + }), + } satisfies oas31.ResponseObject; + } } diff --git a/lib/schema.ts b/lib/schema.ts index c124f72..524a4d2 100644 --- a/lib/schema.ts +++ b/lib/schema.ts @@ -1,76 +1,84 @@ -import { Construct } from 'constructs'; -import type { oas31 } from 'openapi3-ts'; +import { Construct } from "constructs"; +import type { oas31 } from "openapi3-ts"; -type InferExample = TSchema extends oas31.ReferenceObject - ? any - : TSchema extends { - type: 'string'; - } - ? string - : TSchema extends { - type: 'number' | 'integer'; - } - ? number - : TSchema extends { - type: 'boolean'; - } - ? boolean - : TSchema extends oas31.SchemaObject & { type: 'object' } - ? { - [K in keyof TSchema['properties']]: TStopRecurse extends true ? unknown : InferExample; - } - : TSchema extends oas31.SchemaObject & { type: 'array' } - ? TStopRecurse extends true ? unknown : InferExample[] - : any; +type InferExample< + TSchema, + TStopRecurse extends boolean = false, +> = TSchema extends oas31.ReferenceObject + ? any + : TSchema extends { + type: "string"; + } + ? string + : TSchema extends { + type: "number" | "integer"; + } + ? number + : TSchema extends { + type: "boolean"; + } + ? boolean + : TSchema extends oas31.SchemaObject & { type: "object" } + ? { + [K in keyof TSchema["properties"]]: TStopRecurse extends true + ? unknown + : InferExample; + } + : TSchema extends oas31.SchemaObject & { type: "array" } + ? TStopRecurse extends true + ? unknown + : InferExample[] + : any; - -export type SchemaOptions> = { - schema: T & { - examples?: InferExample[]; - required?: T extends { type: 'object', properties: infer P } ? (keyof P)[] : string[]; - }; +export type SchemaOptions> = { + schema: T & { + examples?: InferExample[]; + required?: T extends { type: "object"; properties: infer P } + ? (keyof P)[] + : string[]; + }; }; export class Schema< - const T extends oas31.SchemaObject = oas31.SchemaObject, + const T extends oas31.SchemaObject = oas31.SchemaObject, > extends Construct { - private options: SchemaOptions; + private options: SchemaOptions; - constructor(scope: Construct, id: string, options: SchemaOptions) { - super(scope, id); + constructor(scope: Construct, id: string, options: SchemaOptions) { + super(scope, id); - this.options = options; - } + this.options = options; + } - public get schema() { - return this.options.schema; - } + public get schema() { + return this.options.schema; + } - public get schemaKey() { - return this.node.id; - } + public get schemaKey() { + return this.node.id; + } - public jsonPointer() { - return `#/components/schemas/${this.schemaKey}`; - } + public jsonPointer() { + return `#/components/schemas/${this.schemaKey}`; + } - public referenceObject(): oas31.ReferenceObject { - return { - $ref: this.jsonPointer(), - }; - } + public referenceObject(): oas31.ReferenceObject { + return { + $ref: this.jsonPointer(), + }; + } - // eslint-disable-next-line class-methods-use-this - public validate() { - return []; - } + // eslint-disable-next-line class-methods-use-this + public validate() { + return []; + } - public synth() { - return { - // default to disallow additional properties on objects - ...(this.options.schema.type === 'object' && { - additionalProperties: false, - }), - ...this.options.schema, - }; - } + public synth() { + return { + // default to disallow additional properties on objects + ...(this.options.schema.type === "object" && { + additionalProperties: false, + }), + ...this.options.schema, + }; + } } diff --git a/lib/security-requirement.ts b/lib/security-requirement.ts index 72a3c92..81a2fb6 100644 --- a/lib/security-requirement.ts +++ b/lib/security-requirement.ts @@ -1,30 +1,30 @@ -import { Construct } from 'constructs'; -import type { oas31 } from 'openapi3-ts'; -import type { Api } from './api.ts'; -import type { SecurityScheme } from './security-scheme.ts'; +import { Construct } from "constructs"; +import type { oas31 } from "openapi3-ts"; +import type { Api } from "./api.ts"; +import type { SecurityScheme } from "./security-scheme.ts"; interface SecurityRequirementOptions { - securityScheme?: SecurityScheme; - scopes?: string[]; + securityScheme?: SecurityScheme; + scopes?: string[]; } export class SecurityRequirement extends Construct { - private options: SecurityRequirementOptions; + private options: SecurityRequirementOptions; - constructor( - scope: Api, - id: string, - options: SecurityRequirementOptions = {}, - ) { - super(scope, id); - this.options = options; - } + constructor( + scope: Api, + id: string, + options: SecurityRequirementOptions = {}, + ) { + super(scope, id); + this.options = options; + } - public synth(): oas31.SecurityRequirementObject { - return this.options.securityScheme - ? { - [this.options.securityScheme.node.id]: this.options.scopes || [], - } - : {}; - } + public synth(): oas31.SecurityRequirementObject { + return this.options.securityScheme + ? { + [this.options.securityScheme.node.id]: this.options.scopes || [], + } + : {}; + } } diff --git a/lib/security-scheme.ts b/lib/security-scheme.ts index fd56904..eb3b793 100644 --- a/lib/security-scheme.ts +++ b/lib/security-scheme.ts @@ -1,23 +1,23 @@ -import { Construct } from 'constructs'; -import type { oas31 } from 'openapi3-ts'; +import { Construct } from "constructs"; +import type { oas31 } from "openapi3-ts"; export class SecurityScheme extends Construct { - private options: oas31.SecuritySchemeObject; + private options: oas31.SecuritySchemeObject; - constructor( - scope: Construct, - id: string, - options: oas31.SecuritySchemeObject, - ) { - super(scope, id); - this.options = options; - } + constructor( + scope: Construct, + id: string, + options: oas31.SecuritySchemeObject, + ) { + super(scope, id); + this.options = options; + } - public get schemaKey() { - return this.node.id; - } + public get schemaKey() { + return this.node.id; + } - public synth(): oas31.SecuritySchemeObject { - return this.options; - } + public synth(): oas31.SecuritySchemeObject { + return this.options; + } } diff --git a/lib/server.ts b/lib/server.ts index 99bf162..f99fd2d 100644 --- a/lib/server.ts +++ b/lib/server.ts @@ -1,26 +1,26 @@ -import { Construct } from 'constructs'; -import type { oas31 } from 'openapi3-ts'; -import type { Api } from './api.ts'; +import { Construct } from "constructs"; +import type { oas31 } from "openapi3-ts"; +import type { Api } from "./api.ts"; interface ServerOptions { - url: URL; - description?: string; + url: URL; + description?: string; } export class Server extends Construct { - private options: ServerOptions; + private options: ServerOptions; - constructor(scope: Api, id: string, options: ServerOptions) { - super(scope, id); - this.options = options; - } + constructor(scope: Api, id: string, options: ServerOptions) { + super(scope, id); + this.options = options; + } - public synth(): oas31.ServerObject { - return { - url: this.options.url.toString(), - ...(this.options.description && { - description: this.options.description, - }), - }; - } + public synth(): oas31.ServerObject { + return { + url: this.options.url.toString(), + ...(this.options.description && { + description: this.options.description, + }), + }; + } } diff --git a/lib/tag.ts b/lib/tag.ts index 631bdd6..0574475 100644 --- a/lib/tag.ts +++ b/lib/tag.ts @@ -1,21 +1,21 @@ -import { Construct } from 'constructs'; -import type { oas31 } from 'openapi3-ts'; +import { Construct } from "constructs"; +import type { oas31 } from "openapi3-ts"; export class Tag< - T extends oas31.TagObject = oas31.TagObject, + T extends oas31.TagObject = oas31.TagObject, > extends Construct { - private options: T; + private options: T; - public get name() { - return this.options.name; - } + public get name() { + return this.options.name; + } - constructor(scope: Construct, id: string, options: T) { - super(scope, id); - this.options = options; - } + constructor(scope: Construct, id: string, options: T) { + super(scope, id); + this.options = options; + } - public synth() { - return this.options; - } + public synth() { + return this.options; + } } diff --git a/lib/types.ts b/lib/types.ts index 79c55ea..3a45086 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -1,16 +1,16 @@ -import type { Parameter } from './parameter.ts'; +import type { Parameter } from "./parameter.ts"; /* eslint-disable @typescript-eslint/no-unused-vars */ export type ExtractRouteParams = string extends T - ? Record - : T extends `${infer _Start}{${infer Param}}/${infer Rest}` - ? { [k in Param | keyof ExtractRouteParams]: string } - : T extends `${infer _Start}{${infer Param}}` - ? { [k in Param]: string } - : Record; + ? Record + : T extends `${infer _Start}{${infer Param}}/${infer Rest}` + ? { [k in Param | keyof ExtractRouteParams]: string } + : T extends `${infer _Start}{${infer Param}}` + ? { [k in Param]: string } + : Record; export type ValidParameter = - | Parameter, 'path'> - | Parameter - | Parameter, 'header'> - | Parameter; + | Parameter, "path"> + | Parameter + | Parameter, "header"> + | Parameter; diff --git a/lib/utils.ts b/lib/utils.ts index b1efeb5..fc85d7d 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -1,14 +1,14 @@ export type WithoutUndefinedProperties = { - [P in keyof T]: Exclude; + [P in keyof T]: Exclude; }; export type OptionalToUndefined = { - [P in keyof T]: undefined extends T[P] ? T[P] | undefined : T[P]; + [P in keyof T]: undefined extends T[P] ? T[P] | undefined : T[P]; }; // this is a better version that uses Object.fromEntries export function stripUndefined(obj: OptionalToUndefined) { - return Object.fromEntries( - Object.entries(obj).filter(([, v]) => typeof v !== 'undefined'), - ) as WithoutUndefinedProperties; + return Object.fromEntries( + Object.entries(obj).filter(([, v]) => typeof v !== "undefined"), + ) as WithoutUndefinedProperties; } From 0ccb63022cd9dc9c7e53939e37f808d6e31658e6 Mon Sep 17 00:00:00 2001 From: Max Holman Date: Wed, 23 Sep 2026 17:06:14 +0800 Subject: [PATCH 3/9] build: skip the oxfmt reformat in git blame Co-Authored-By: LLM --- .git-blame-ignore-revs | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .git-blame-ignore-revs diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000..063bd6f --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,6 @@ +# Revisions listed here are skipped by `git blame`. +# Enable locally with: +# git config blame.ignoreRevsFile .git-blame-ignore-revs + +# style: reformat with oxfmt, drop .editorconfig +7a481d1e5f3b38916ea2f1a8e19e51ae13865c0b From deb18e4428304b981009a064908fd5d6ada1ec87 Mon Sep 17 00:00:00 2001 From: Max Holman Date: Wed, 23 Sep 2026 17:09:57 +0800 Subject: [PATCH 4/9] style: clear the lint warnings in lib No behaviour change: the snapshots of both fixture APIs are untouched. ApiLowLevel.ts becomes api-low-level.ts for filename-case, data-only interfaces become type aliases, and the eslint disables either go, being unused, or become oxlint disables with a reason. explode is compared with undefined rather than loosely with null, which its boolean type already limits it to. Co-Authored-By: LLM --- lib/{ApiLowLevel.ts => api-low-level.ts} | 4 +++- lib/api.ts | 9 +++++---- lib/header.ts | 4 ++-- lib/index.ts | 7 +++---- lib/media-type.ts | 4 ++-- lib/operation.ts | 10 ++++------ lib/parameter.ts | 10 ++++++---- lib/path.ts | 11 ++++------- lib/reference.ts | 5 +++-- lib/request-body.ts | 4 ++-- lib/response.ts | 11 ++++------- lib/schema.ts | 1 - lib/security-requirement.ts | 4 ++-- lib/server.ts | 4 ++-- lib/types.ts | 3 +-- lib/utils.ts | 11 ++++++----- 16 files changed, 49 insertions(+), 53 deletions(-) rename lib/{ApiLowLevel.ts => api-low-level.ts} (56%) diff --git a/lib/ApiLowLevel.ts b/lib/api-low-level.ts similarity index 56% rename from lib/ApiLowLevel.ts rename to lib/api-low-level.ts index ab5dff9..b5b3888 100644 --- a/lib/ApiLowLevel.ts +++ b/lib/api-low-level.ts @@ -6,7 +6,9 @@ export class ApiLowLevel extends Construct { const { scope } = c.node; if (!scope) { - // Api is the only construct without a scope. + // the root of the tree, which the API constructor creates. That class + // imports this module, so instanceof would be a circular import + // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- the root return c as Api; } diff --git a/lib/api.ts b/lib/api.ts index 2849ec0..eab5812 100644 --- a/lib/api.ts +++ b/lib/api.ts @@ -1,6 +1,6 @@ import type { JSONSchema7 } from "json-schema"; import type { oas31 } from "openapi3-ts"; -import { ApiLowLevel } from "./ApiLowLevel.ts"; +import { ApiLowLevel } from "./api-low-level.ts"; import { Header } from "./header.ts"; import { Parameter } from "./parameter.ts"; import { Path } from "./path.ts"; @@ -13,16 +13,17 @@ import { Tag } from "./tag.ts"; export type OpenApiVersion = "3.0" | "3.1.0"; -export interface ApiOptions { +export type ApiOptions = { openapi: OpenApiVersion; info: oas31.InfoObject; -} +}; export class Api extends ApiLowLevel { private options: ApiOptions; constructor(options: ApiOptions) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any + // the root's scope is undefined, which the Construct constructor types deny + // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- the root super(undefined as any, ""); this.options = options; diff --git a/lib/header.ts b/lib/header.ts index b81e711..d636eaa 100644 --- a/lib/header.ts +++ b/lib/header.ts @@ -2,7 +2,7 @@ import { Construct } from "constructs"; import type { oas31 } from "openapi3-ts"; import type { Schema } from "./schema.ts"; -interface HeaderOptions { +type HeaderOptions = { description?: string; required?: boolean; deprecated?: boolean; @@ -11,7 +11,7 @@ interface HeaderOptions { explode?: boolean; allowReserved?: boolean; schema: Schema; -} +}; export class Header extends Construct { private options: HeaderOptions; diff --git a/lib/index.ts b/lib/index.ts index 8b37e7a..386aa28 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -1,9 +1,8 @@ -/* eslint-disable max-classes-per-file */ import { Construct } from "constructs"; -// export the version of Constructs we are using to ensure we're -// everyone is using the same version -export { Construct }; +// consumers build on this copy of constructs, so their constructs and ours +// share one class +export { Construct } from "constructs"; export { Api } from "./api.ts"; export { Header } from "./header.ts"; diff --git a/lib/media-type.ts b/lib/media-type.ts index 2cec5bd..ed21847 100644 --- a/lib/media-type.ts +++ b/lib/media-type.ts @@ -12,10 +12,10 @@ type ContentType = | "image/*" | (string & {}); -export interface MediaTypeOptions { +export type MediaTypeOptions = { contentType: ContentType; schema: Schema | Reference; -} +}; export class MediaType extends Construct { private options: MediaTypeOptions; diff --git a/lib/operation.ts b/lib/operation.ts index c4fbf72..10d484e 100644 --- a/lib/operation.ts +++ b/lib/operation.ts @@ -8,7 +8,7 @@ import type { Tag } from "./tag.ts"; import type { ValidParameter } from "./types.ts"; import { stripUndefined } from "./utils.ts"; -export interface OperationOptions { +export type OperationOptions = { operationId: string; summary?: string; description?: string; @@ -21,7 +21,7 @@ export interface OperationOptions { }; requestBody?: RequestBody | RequestBodyOptions; order?: number; -} +}; export class Operation extends Construct { private readonly options: OperationOptions; @@ -56,8 +56,6 @@ export class Operation extends Construct { } public validate() { - // const api = Api.of(this).node.findChild; - const duplicateOperation = this.node.scope?.node.children .filter( (child): child is Operation => @@ -87,7 +85,7 @@ export class Operation extends Construct { security: [this.options.security.synth()], }), ...(this.options.tags && { - tags: Array.from(this.options.tags).map((child) => child.name), + tags: [...this.options.tags].map((child) => child.name), }), ...(this.requestBody && { requestBody: this.requestBody.synth(), @@ -95,7 +93,7 @@ export class Operation extends Construct { ...(this.options.responses && { responses: Object.fromEntries( Object.entries(this.options.responses).map(([statusCode, child]) => [ - statusCode.toString(), + statusCode, child.synth(), ]), ), diff --git a/lib/parameter.ts b/lib/parameter.ts index ab527da..5ead005 100644 --- a/lib/parameter.ts +++ b/lib/parameter.ts @@ -19,10 +19,10 @@ type ParameterName< TIn extends "query" | "header" | "path" | "cookie", > = TIn extends "header" ? TName & Lowercase : TName; -interface ParameterOptionsBase< +type ParameterOptionsBase< TName extends string | number | symbol, TIn extends "query" | "header" | "path" | "cookie", -> { +> = { name: ParameterName; in: TIn; required: boolean; @@ -32,7 +32,7 @@ interface ParameterOptionsBase< allowReserved?: boolean; style?: StyleForIn; explode?: boolean; -} +}; interface ParameterOptions< TName extends string | number | symbol, @@ -81,7 +81,9 @@ export class Parameter< allowEmptyValue: this.options.allowEmptyValue, }), ...(this.options.style && { style: this.options.style }), - ...(this.options.explode != null && { explode: this.options.explode }), + ...(this.options.explode !== undefined && { + explode: this.options.explode, + }), ...(this.options.schema && { schema: this.options.schema.referenceObject(), }), diff --git a/lib/path.ts b/lib/path.ts index df8863b..7bbd331 100644 --- a/lib/path.ts +++ b/lib/path.ts @@ -7,13 +7,13 @@ import type { Server } from "./server.ts"; import type { Tag } from "./tag.ts"; import type { ValidParameter } from "./types.ts"; -interface PathOptions { +type PathOptions = { path: TPath; summary?: string; servers?: Server[]; parameters?: ValidParameter[]; tags?: Set; -} +}; export class Path extends Construct { public options: PathOptions; @@ -27,10 +27,7 @@ export class Path extends Construct { method: HttpMethod, options: OperationOptions, ): this { - // make sure we are not duplicating tags - // options.tags?.forEach((tag) => strict(!this.options.tags?.has(tag))); - - // eslint-disable-next-line no-new + // oxlint-disable-next-line no-new -- it attaches itself to this path new Operation(this, method, { ...options, tags: new Set([...(this.options.tags || []), ...(options.tags || [])]), @@ -50,7 +47,7 @@ export class Path extends Construct { .filter( (child): child is Operation => child instanceof Operation, ) - .sort((a, b) => a.order - b.order) + .toSorted((a, b) => a.order - b.order) .map((child) => [child.method, child.synth()]), ), ...(this.options.servers && { diff --git a/lib/reference.ts b/lib/reference.ts index d9a5748..48c895a 100644 --- a/lib/reference.ts +++ b/lib/reference.ts @@ -1,15 +1,16 @@ import { Construct } from "constructs"; import type { oas31 } from "openapi3-ts"; -import { ApiLowLevel } from "./ApiLowLevel.ts"; +import { ApiLowLevel } from "./api-low-level.ts"; import { Schema } from "./schema.ts"; -type Target = Schema; // | Parameter; +type Target = Schema; export class Reference extends Construct { private schema: Schema; constructor(target: T, id: string) { super(target, id); + // NOTE: Api.of causes a circular dependency this.schema = new Schema(ApiLowLevel.of(this), id, { // NOTE: synth because refs are not valid as #/component/schemas diff --git a/lib/request-body.ts b/lib/request-body.ts index f510998..f15f833 100644 --- a/lib/request-body.ts +++ b/lib/request-body.ts @@ -2,11 +2,11 @@ import { Construct } from "constructs"; import type { oas31 } from "openapi3-ts"; import { MediaType, type MediaTypeOptions } from "./media-type.ts"; -export interface RequestBodyOptions { +export type RequestBodyOptions = { content: MediaType | MediaTypeOptions | (MediaType | MediaTypeOptions)[]; description?: string; required?: boolean; -} +}; export class RequestBody extends Construct { private options: RequestBodyOptions; diff --git a/lib/response.ts b/lib/response.ts index 71dd2e3..0dd91b3 100644 --- a/lib/response.ts +++ b/lib/response.ts @@ -3,11 +3,11 @@ import type { oas31 } from "openapi3-ts"; import type { Header } from "./header.ts"; import { MediaType, type MediaTypeOptions } from "./media-type.ts"; -interface ResponseOptions { +type ResponseOptions = { content?: MediaType | MediaTypeOptions | (MediaType | MediaTypeOptions)[]; description?: string; headers?: Record, Header>; -} +}; export class Response extends Construct { private options: ResponseOptions; @@ -18,11 +18,8 @@ export class Response extends Construct { super(scope, id); this.options = options; - const items = options.content - ? Array.isArray(options.content) - ? options.content - : [options.content] - : []; + const { content = [] } = options; + const items = Array.isArray(content) ? content : [content]; this.contentEntries = items.map((item, index) => item instanceof MediaType diff --git a/lib/schema.ts b/lib/schema.ts index 524a4d2..72f376d 100644 --- a/lib/schema.ts +++ b/lib/schema.ts @@ -67,7 +67,6 @@ export class Schema< }; } - // eslint-disable-next-line class-methods-use-this public validate() { return []; } diff --git a/lib/security-requirement.ts b/lib/security-requirement.ts index 81a2fb6..d7fb22f 100644 --- a/lib/security-requirement.ts +++ b/lib/security-requirement.ts @@ -3,10 +3,10 @@ import type { oas31 } from "openapi3-ts"; import type { Api } from "./api.ts"; import type { SecurityScheme } from "./security-scheme.ts"; -interface SecurityRequirementOptions { +type SecurityRequirementOptions = { securityScheme?: SecurityScheme; scopes?: string[]; -} +}; export class SecurityRequirement extends Construct { private options: SecurityRequirementOptions; diff --git a/lib/server.ts b/lib/server.ts index f99fd2d..2154baa 100644 --- a/lib/server.ts +++ b/lib/server.ts @@ -2,10 +2,10 @@ import { Construct } from "constructs"; import type { oas31 } from "openapi3-ts"; import type { Api } from "./api.ts"; -interface ServerOptions { +type ServerOptions = { url: URL; description?: string; -} +}; export class Server extends Construct { private options: ServerOptions; diff --git a/lib/types.ts b/lib/types.ts index 3a45086..9697183 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -1,6 +1,5 @@ import type { Parameter } from "./parameter.ts"; -/* eslint-disable @typescript-eslint/no-unused-vars */ export type ExtractRouteParams = string extends T ? Record : T extends `${infer _Start}{${infer Param}}/${infer Rest}` @@ -11,6 +10,6 @@ export type ExtractRouteParams = string extends T export type ValidParameter = | Parameter, "path"> - | Parameter + | Parameter | Parameter, "header"> | Parameter; diff --git a/lib/utils.ts b/lib/utils.ts index fc85d7d..0eef625 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -1,4 +1,4 @@ -export type WithoutUndefinedProperties = { +type WithoutUndefinedProperties = { [P in keyof T]: Exclude; }; @@ -6,9 +6,10 @@ export type OptionalToUndefined = { [P in keyof T]: undefined extends T[P] ? T[P] | undefined : T[P]; }; -// this is a better version that uses Object.fromEntries export function stripUndefined(obj: OptionalToUndefined) { - return Object.fromEntries( - Object.entries(obj).filter(([, v]) => typeof v !== "undefined"), - ) as WithoutUndefinedProperties; + const kept = Object.entries(obj).filter(([, v]) => v !== undefined); + + // fromEntries returns an index signature, never the mapped type + // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- by design + return Object.fromEntries(kept) as WithoutUndefinedProperties; } From 5a36251da628fb0b539b68f08a779936c778cff7 Mon Sep 17 00:00:00 2001 From: Max Holman Date: Wed, 23 Sep 2026 17:09:57 +0800 Subject: [PATCH 5/9] style: clear the lint warnings in tests and examples The commented-out fixtures go, and the regression test asserts that the allOf schema constructs without throwing, which it only implied before. Co-Authored-By: LLM --- __tests__/fixtures/apis/example.ts | 42 +------------------------- __tests__/fixtures/apis/note-taking.ts | 2 +- __tests__/json-schema.test.ts | 1 - __tests__/openapi-schema.test.ts | 2 ++ __tests__/parameter-types.test-d.ts | 6 ++-- __tests__/regression.test.ts | 34 +++++++++++---------- examples/example.ts | 4 +-- 7 files changed, 27 insertions(+), 64 deletions(-) diff --git a/__tests__/fixtures/apis/example.ts b/__tests__/fixtures/apis/example.ts index 0dd1d8a..f8407ef 100644 --- a/__tests__/fixtures/apis/example.ts +++ b/__tests__/fixtures/apis/example.ts @@ -1,4 +1,4 @@ -/* eslint-disable no-new */ +// oxlint-disable no-new -- constructs attach to their scope when built import { Api, Header, @@ -129,32 +129,11 @@ const createUserRequest = new Reference(user, "CreateUserRequest"); const users = new Schema(exampleApi, "Users", { schema: { type: "array", - // additionalItems: false, uniqueItems: true, items: user.referenceObject(), }, }); -/* const errorSchema = new Schema(api, 'ErrorSchema', { - schema: { - type: 'object', - required: ['name'], - additionalProperties: false, - properties: { - name: { - type: 'string', - }, - }, - }, -}); */ - -/* const idParameter = new Parameter(api, 'IdParam', { - name: 'userId', - in: 'path', - required: true, - schema: idSchema, -}); */ - const rateLimitSchema = new Schema(exampleApi, "RateLimit", { schema: { type: "integer", @@ -192,25 +171,6 @@ const userIdParameter = new Parameter(exampleApi, "UserId", { schema: idSchema, }); -/* const userIdentifiersSchema = new Schema(api, 'UserIdentifiers', { - schema: { - type: 'object', - additionalProperties: false, - required: ['userId'], - properties: { - userId: userIdParameter.referenceObject(), - }, - }, -}); */ - -/* const errorResponse = new Response(api, 'ErrorResponse', { - description: 'Error response', - content: new MediaType(api, 'Error', { - contentType: 'application/json', - schema: errorSchema, - }), -}); */ - new Path(exampleApi, { path: "/users", tags: new Set([userTag]), diff --git a/__tests__/fixtures/apis/note-taking.ts b/__tests__/fixtures/apis/note-taking.ts index d1abdec..7c15a7d 100644 --- a/__tests__/fixtures/apis/note-taking.ts +++ b/__tests__/fixtures/apis/note-taking.ts @@ -1,4 +1,4 @@ -/* eslint-disable no-new */ +// oxlint-disable no-new -- constructs attach to their scope when built import { Api, Parameter, diff --git a/__tests__/json-schema.test.ts b/__tests__/json-schema.test.ts index 1ad163b..ab51242 100644 --- a/__tests__/json-schema.test.ts +++ b/__tests__/json-schema.test.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-new */ import { Ajv } from "ajv"; import { test, expect, describe } from "vitest"; import { exampleApi } from "./fixtures/apis/example.ts"; diff --git a/__tests__/openapi-schema.test.ts b/__tests__/openapi-schema.test.ts index c3cd2cd..5e9dd3d 100644 --- a/__tests__/openapi-schema.test.ts +++ b/__tests__/openapi-schema.test.ts @@ -11,6 +11,7 @@ describe("Example", () => { test("Swagger Parser validate", async () => { const document = exampleApi.synth(); + // WARN: this function mutates the input // @ts-expect-error openapi3-ts and openapi-types have incompatible types under exactOptionalPropertyTypes const result = await SwaggerParser.validate(structuredClone(document)); @@ -26,6 +27,7 @@ describe("Note Taking", () => { test("Swagger Parser validate", async () => { const document = noteTakingApi.synth(); + // WARN: this function mutates the input // @ts-expect-error openapi3-ts and openapi-types have incompatible types under exactOptionalPropertyTypes const result = await SwaggerParser.validate(structuredClone(document)); diff --git a/__tests__/parameter-types.test-d.ts b/__tests__/parameter-types.test-d.ts index 8ce9bce..86bf585 100644 --- a/__tests__/parameter-types.test-d.ts +++ b/__tests__/parameter-types.test-d.ts @@ -10,7 +10,7 @@ expectTypeOf>().toExtend(); expectTypeOf>().toExtend(); // Test that query parameters can be any string -expectTypeOf>().toExtend< +expectTypeOf>().toExtend< ValidParameter<"/users/{userId}"> >(); @@ -38,5 +38,5 @@ expectTypeOf>().toExtend(); expectTypeOf>().toExtend(); // Test that non-path parameters work for any route -expectTypeOf>().toExtend(); -expectTypeOf>().toExtend(); +expectTypeOf>().toExtend(); +expectTypeOf>().toExtend(); diff --git a/__tests__/regression.test.ts b/__tests__/regression.test.ts index 4fea802..9cf0919 100644 --- a/__tests__/regression.test.ts +++ b/__tests__/regression.test.ts @@ -1,6 +1,5 @@ import { Api, Schema } from "@block65/openapi-constructs"; -/* eslint-disable no-new */ -import { test } from "vitest"; +import { expect, test } from "vitest"; test("Regression", async () => { const api = new Api({ @@ -33,20 +32,23 @@ test("Regression", async () => { }, }); - new Schema(api, "Test2", { - schema: { - allOf: [ - schema1.schema, - { - type: "object", - required: ["name"], - properties: { - name: { - type: "string", + expect( + () => + new Schema(api, "Test2", { + schema: { + allOf: [ + schema1.schema, + { + type: "object", + required: ["name"], + properties: { + name: { + type: "string", + }, + }, }, - }, + ], }, - ], - }, - }); + }), + ).not.toThrow(); }); diff --git a/examples/example.ts b/examples/example.ts index 0b83a03..e6c495c 100644 --- a/examples/example.ts +++ b/examples/example.ts @@ -1,4 +1,4 @@ -/* eslint-disable no-new */ +// oxlint-disable no-new -- constructs attach to their scope when built import { Api, Parameter, @@ -202,4 +202,4 @@ new Path(api, { }, }); -process.stdout.write(JSON.stringify(api.synth(), null, 2)); +process.stdout.write(JSON.stringify(api.synth(), undefined, 2)); From 3ff894ace4403fb1f9f74049c4ffbb51c4968223 Mon Sep 17 00:00:00 2001 From: Max Holman Date: Wed, 23 Sep 2026 17:09:57 +0800 Subject: [PATCH 6/9] build: tell fallow about the members it cannot trace Constructs are found through node.children and a type guard, so synth and friends read as unused. The Header and Parameter synth() clone stays, since the two emit explode and required differently. Co-Authored-By: LLM --- .fallowrc.jsonc | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/.fallowrc.jsonc b/.fallowrc.jsonc index 6d376c7..fe5dadf 100644 --- a/.fallowrc.jsonc +++ b/.fallowrc.jsonc @@ -6,5 +6,20 @@ "examples/example.ts", "oxfmt.config.ts", "oxlint.config.ts" - ] + ], + + // Constructs are reached through node.children and a type guard, which + // fallow does not follow, so these members read as unused + "usedClassMembers": [ + { + "extends": "Construct", + "members": ["synth", "contentType", "hasOperationId"] + } + ], + + "duplicates": { + // Header and Parameter synth() differ in how they emit explode and + // required, so sharing one body would change the output + "ignoredClones": ["dup:6f87acd9:2"] + } } From b095428e7f402f51b1740f107c8c594847b7cb3e Mon Sep 17 00:00:00 2001 From: Max Holman Date: Wed, 23 Sep 2026 17:18:02 +0800 Subject: [PATCH 7/9] refactor: replace the type assertions and any with real types Api builds on RootConstruct, which types the scopeless root, so the undefined as any goes. ApiLowLevel.of finds the root through node.root and narrows it with instanceof against the class it is called on, so Api.of still returns an Api and throws a TypeError where the root is something else. Operation.synth spreads each optional field in like the other constructs, which retires stripUndefined and its cast. InferExample falls back to unknown. In the tests, the negative parameter cases use expectTypeOf().not, and swagger-parser validates the emitted JSON from a file, since the openapi3-ts and openapi-types document types disagree on server variable defaults. Co-Authored-By: LLM --- __tests__/openapi-schema.test.ts | 24 ++++++++++++++++++------ __tests__/parameter-types.test-d.ts | 12 ++++++------ lib/api-low-level.ts | 21 ++++++++++----------- lib/api.ts | 4 +--- lib/operation.ts | 23 ++++++++++++----------- lib/schema.ts | 4 ++-- lib/utils.ts | 15 --------------- 7 files changed, 49 insertions(+), 54 deletions(-) delete mode 100644 lib/utils.ts diff --git a/__tests__/openapi-schema.test.ts b/__tests__/openapi-schema.test.ts index 5e9dd3d..5999240 100644 --- a/__tests__/openapi-schema.test.ts +++ b/__tests__/openapi-schema.test.ts @@ -1,8 +1,24 @@ +import { mkdtemp, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import path from "node:path"; import SwaggerParser from "@apidevtools/swagger-parser"; import { test, expect, describe } from "vitest"; import { exampleApi } from "./fixtures/apis/example.ts"; import { noteTakingApi } from "./fixtures/apis/note-taking.ts"; +// the JSON is validated, since openapi3-ts and openapi-types types disagree +async function validate(document: object) { + const dir = await mkdtemp(path.join(tmpdir(), "openapi-constructs-")); + const file = path.join(dir, "openapi.json"); + + try { + await writeFile(file, JSON.stringify(document)); + return await SwaggerParser.validate(file); + } finally { + await rm(dir, { recursive: true }); + } +} + describe("Example", () => { test("OpenAPI", async () => { const document = exampleApi.synth(); @@ -12,9 +28,7 @@ describe("Example", () => { test("Swagger Parser validate", async () => { const document = exampleApi.synth(); - // WARN: this function mutates the input - // @ts-expect-error openapi3-ts and openapi-types have incompatible types under exactOptionalPropertyTypes - const result = await SwaggerParser.validate(structuredClone(document)); + const result = await validate(document); expect(result).toMatchSnapshot(); }); }); @@ -28,9 +42,7 @@ describe("Note Taking", () => { test("Swagger Parser validate", async () => { const document = noteTakingApi.synth(); - // WARN: this function mutates the input - // @ts-expect-error openapi3-ts and openapi-types have incompatible types under exactOptionalPropertyTypes - const result = await SwaggerParser.validate(structuredClone(document)); + const result = await validate(document); expect(result).toMatchSnapshot(); }); }); diff --git a/__tests__/parameter-types.test-d.ts b/__tests__/parameter-types.test-d.ts index 86bf585..aead901 100644 --- a/__tests__/parameter-types.test-d.ts +++ b/__tests__/parameter-types.test-d.ts @@ -6,8 +6,8 @@ import type { ValidParameter } from "../lib/types.ts"; type PathParams = ValidParameter<"/users/{userId}">; expectTypeOf>().toExtend(); -// @ts-expect-error path parameter with wrong name is not allowed -expectTypeOf>().toExtend(); +// path parameter with wrong name is not allowed +expectTypeOf>().not.toExtend(); // Test that query parameters can be any string expectTypeOf>().toExtend< @@ -19,8 +19,8 @@ expectTypeOf>().toExtend< ValidParameter<"/users/{userId}"> >(); -// @ts-expect-error uppercase header parameter names are not allowed (HTTP/2) -expectTypeOf>().toExtend< +// uppercase header parameter names are not allowed (HTTP/2) +expectTypeOf>().not.toExtend< ValidParameter<"/users/{userId}"> >(); @@ -34,8 +34,8 @@ type MultiParamRoute = ValidParameter<"/users/{userId}/notes/{noteId}">; expectTypeOf>().toExtend(); expectTypeOf>().toExtend(); -// @ts-expect-error path parameter not in route -expectTypeOf>().toExtend(); +// path parameter not in route +expectTypeOf>().not.toExtend(); // Test that non-path parameters work for any route expectTypeOf>().toExtend(); diff --git a/lib/api-low-level.ts b/lib/api-low-level.ts index b5b3888..51377dd 100644 --- a/lib/api-low-level.ts +++ b/lib/api-low-level.ts @@ -1,17 +1,16 @@ -import { Construct, type IConstruct } from "constructs"; -import type { Api } from "./api.ts"; +import { RootConstruct, type IConstruct } from "constructs"; -export class ApiLowLevel extends Construct { - public static of(c: IConstruct): Api { - const { scope } = c.node; +export class ApiLowLevel extends RootConstruct { + public static of( + this: abstract new (...args: never[]) => T, + c: IConstruct, + ): T { + const { root } = c.node; - if (!scope) { - // the root of the tree, which the API constructor creates. That class - // imports this module, so instanceof would be a circular import - // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- the root - return c as Api; + if (root instanceof this) { + return root; } - return ApiLowLevel.of(scope); + throw new TypeError(`${c.node.path} is not in a ${this.name} tree`); } } diff --git a/lib/api.ts b/lib/api.ts index eab5812..8c12545 100644 --- a/lib/api.ts +++ b/lib/api.ts @@ -22,9 +22,7 @@ export class Api extends ApiLowLevel { private options: ApiOptions; constructor(options: ApiOptions) { - // the root's scope is undefined, which the Construct constructor types deny - // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- the root - super(undefined as any, ""); + super(""); this.options = options; } diff --git a/lib/operation.ts b/lib/operation.ts index 10d484e..c1b1c23 100644 --- a/lib/operation.ts +++ b/lib/operation.ts @@ -6,7 +6,6 @@ import type { Response } from "./response.ts"; import type { SecurityRequirement } from "./security-requirement.ts"; import type { Tag } from "./tag.ts"; import type { ValidParameter } from "./types.ts"; -import { stripUndefined } from "./utils.ts"; export type OperationOptions = { operationId: string; @@ -71,22 +70,24 @@ export class Operation extends Construct { } public synth() { - return stripUndefined({ + return { operationId: this.options.operationId, - description: this.options.description || undefined, - summary: this.options.summary || undefined, - tags: - this.options.tags && [...this.options.tags].map((child) => child.name), - deprecated: this.options.deprecated, + ...(this.options.description && { + description: this.options.description, + }), + ...(this.options.summary && { summary: this.options.summary }), + ...(this.options.tags && { + tags: [...this.options.tags].map((child) => child.name), + }), + ...(this.options.deprecated !== undefined && { + deprecated: this.options.deprecated, + }), ...(this.options.parameters && { parameters: this.options.parameters.map((child) => child.synth()), }), ...(this.options.security && { security: [this.options.security.synth()], }), - ...(this.options.tags && { - tags: [...this.options.tags].map((child) => child.name), - }), ...(this.requestBody && { requestBody: this.requestBody.synth(), }), @@ -98,6 +99,6 @@ export class Operation extends Construct { ]), ), }), - }) satisfies oas31.OperationObject; + } satisfies oas31.OperationObject; } } diff --git a/lib/schema.ts b/lib/schema.ts index 72f376d..7ad81ef 100644 --- a/lib/schema.ts +++ b/lib/schema.ts @@ -5,7 +5,7 @@ type InferExample< TSchema, TStopRecurse extends boolean = false, > = TSchema extends oas31.ReferenceObject - ? any + ? unknown : TSchema extends { type: "string"; } @@ -28,7 +28,7 @@ type InferExample< ? TStopRecurse extends true ? unknown : InferExample[] - : any; + : unknown; export type SchemaOptions> = { schema: T & { diff --git a/lib/utils.ts b/lib/utils.ts deleted file mode 100644 index 0eef625..0000000 --- a/lib/utils.ts +++ /dev/null @@ -1,15 +0,0 @@ -type WithoutUndefinedProperties = { - [P in keyof T]: Exclude; -}; - -export type OptionalToUndefined = { - [P in keyof T]: undefined extends T[P] ? T[P] | undefined : T[P]; -}; - -export function stripUndefined(obj: OptionalToUndefined) { - const kept = Object.entries(obj).filter(([, v]) => v !== undefined); - - // fromEntries returns an index signature, never the mapped type - // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- by design - return Object.fromEntries(kept) as WithoutUndefinedProperties; -} From 01bb385f9b5bffb45ebfd3ee8de910ea2c924cd5 Mon Sep 17 00:00:00 2001 From: Max Holman Date: Wed, 23 Sep 2026 17:25:03 +0800 Subject: [PATCH 8/9] test: validate against the OpenAPI 3.1 schema with ajv, not swagger-parser @apidevtools/swagger-parser goes. ajv's 2020 validator checks each synthesised document against the vendored OpenAPI 3.1 schema, and a document with an unknown top-level key must fail it. ajv mis-resolves the schema's $dynamicRef to #meta, so the four of them are patched to a $ref to $defs/schema, which holds the only meta anchor. The $comment records it. Co-Authored-By: LLM --- .../__snapshots__/openapi-schema.test.ts.snap | 1774 +---------------- __tests__/fixtures/oas31.json | 9 +- __tests__/openapi-schema.test.ts | 60 +- package.json | 1 - pnpm-lock.yaml | 84 - 5 files changed, 56 insertions(+), 1872 deletions(-) diff --git a/__tests__/__snapshots__/openapi-schema.test.ts.snap b/__tests__/__snapshots__/openapi-schema.test.ts.snap index d91ac6d..752660f 100644 --- a/__tests__/__snapshots__/openapi-schema.test.ts.snap +++ b/__tests__/__snapshots__/openapi-schema.test.ts.snap @@ -374,1269 +374,7 @@ exports[`Example > OpenAPI 1`] = ` } `; -exports[`Example > Swagger Parser validate 1`] = ` -{ - "components": { - "headers": { - "x-rate-limit": { - "description": "Number of requests allowed per hour", - "required": true, - "schema": { - "format": "int32", - "minimum": 0, - "type": "integer", - }, - }, - "x-rate-limit-remaining": { - "description": "Number of requests remaining in the current window", - "schema": { - "format": "int32", - "minimum": 0, - "type": "integer", - }, - }, - }, - "parameters": { - "UserId": { - "in": "path", - "name": "userId", - "required": true, - "schema": { - "type": "string", - }, - }, - }, - "schemas": { - "Address": { - "additionalProperties": false, - "properties": { - "postcode": { - "format": "int32", - "maximum": 9999, - "minimum": 1000, - "type": "integer", - }, - }, - "required": [ - "postcode", - ], - "type": "object", - }, - "Binary": { - "format": "binary", - "type": "string", - }, - "CreateUserRequest": { - "additionalProperties": false, - "properties": { - "address": { - "additionalProperties": false, - "properties": { - "postcode": { - "format": "int32", - "maximum": 9999, - "minimum": 1000, - "type": "integer", - }, - }, - "required": [ - "postcode", - ], - "type": "object", - }, - "age": { - "anyOf": [ - { - "format": "int32", - "minimum": 0, - "type": "integer", - }, - { - "type": "null", - }, - ], - }, - "name": { - "type": "string", - }, - "userId": { - "type": "string", - }, - }, - "required": [ - "name", - ], - "type": "object", - }, - "Id": { - "type": "string", - }, - "RateLimit": { - "format": "int32", - "minimum": 0, - "type": "integer", - }, - "UpdateUserRequest": { - "additionalProperties": false, - "minProperties": 1, - "properties": { - "address": { - "additionalProperties": false, - "properties": { - "postcode": { - "format": "int32", - "maximum": 9999, - "minimum": 1000, - "type": "integer", - }, - }, - "required": [ - "postcode", - ], - "type": "object", - }, - "age": { - "format": "int32", - "minimum": 0, - "type": "integer", - }, - }, - "type": "object", - }, - "User": { - "additionalProperties": false, - "properties": { - "address": { - "additionalProperties": false, - "properties": { - "postcode": { - "format": "int32", - "maximum": 9999, - "minimum": 1000, - "type": "integer", - }, - }, - "required": [ - "postcode", - ], - "type": "object", - }, - "age": { - "anyOf": [ - { - "format": "int32", - "minimum": 0, - "type": "integer", - }, - { - "type": "null", - }, - ], - }, - "name": { - "type": "string", - }, - "userId": { - "type": "string", - }, - }, - "required": [ - "name", - ], - "type": "object", - }, - "Users": { - "items": { - "additionalProperties": false, - "properties": { - "address": { - "additionalProperties": false, - "properties": { - "postcode": { - "format": "int32", - "maximum": 9999, - "minimum": 1000, - "type": "integer", - }, - }, - "required": [ - "postcode", - ], - "type": "object", - }, - "age": { - "anyOf": [ - { - "format": "int32", - "minimum": 0, - "type": "integer", - }, - { - "type": "null", - }, - ], - }, - "name": { - "type": "string", - }, - "userId": { - "type": "string", - }, - }, - "required": [ - "name", - ], - "type": "object", - }, - "type": "array", - "uniqueItems": true, - }, - }, - "securitySchemes": { - "HttpBearerJwtScheme": { - "bearerFormat": "JWT", - "scheme": "bearer", - "type": "http", - }, - }, - }, - "info": { - "title": "Example REST API", - "version": "1.0.0", - }, - "openapi": "3.1.0", - "paths": { - "/users": { - "get": { - "operationId": "listUsersCommand", - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "items": { - "additionalProperties": false, - "properties": { - "address": { - "additionalProperties": false, - "properties": { - "postcode": { - "format": "int32", - "maximum": 9999, - "minimum": 1000, - "type": "integer", - }, - }, - "required": [ - "postcode", - ], - "type": "object", - }, - "age": { - "anyOf": [ - { - "format": "int32", - "minimum": 0, - "type": "integer", - }, - { - "type": "null", - }, - ], - }, - "name": { - "type": "string", - }, - "userId": { - "type": "string", - }, - }, - "required": [ - "name", - ], - "type": "object", - }, - "type": "array", - "uniqueItems": true, - }, - }, - }, - "description": "User 200 response", - "headers": { - "x-rate-limit": { - "description": "Number of requests allowed per hour", - "required": true, - "schema": { - "format": "int32", - "minimum": 0, - "type": "integer", - }, - }, - "x-rate-limit-remaining": { - "description": "Number of requests remaining in the current window", - "schema": { - "format": "int32", - "minimum": 0, - "type": "integer", - }, - }, - }, - }, - }, - "tags": [ - "user", - ], - }, - "post": { - "operationId": "createUserCommand", - "requestBody": { - "content": { - "application/json": { - "schema": { - "additionalProperties": false, - "properties": { - "address": { - "additionalProperties": false, - "properties": { - "postcode": { - "format": "int32", - "maximum": 9999, - "minimum": 1000, - "type": "integer", - }, - }, - "required": [ - "postcode", - ], - "type": "object", - }, - "age": { - "anyOf": [ - { - "format": "int32", - "minimum": 0, - "type": "integer", - }, - { - "type": "null", - }, - ], - }, - "name": { - "type": "string", - }, - "userId": { - "type": "string", - }, - }, - "required": [ - "name", - ], - "type": "object", - }, - }, - }, - "description": "", - "required": true, - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "items": { - "additionalProperties": false, - "properties": { - "address": { - "additionalProperties": false, - "properties": { - "postcode": { - "format": "int32", - "maximum": 9999, - "minimum": 1000, - "type": "integer", - }, - }, - "required": [ - "postcode", - ], - "type": "object", - }, - "age": { - "anyOf": [ - { - "format": "int32", - "minimum": 0, - "type": "integer", - }, - { - "type": "null", - }, - ], - }, - "name": { - "type": "string", - }, - "userId": { - "type": "string", - }, - }, - "required": [ - "name", - ], - "type": "object", - }, - "type": "array", - "uniqueItems": true, - }, - }, - }, - "description": "User 200 response", - }, - }, - "tags": [ - "user", - "random", - ], - }, - }, - "/users/{userId}": { - "delete": { - "operationId": "deleteUserByIdCommand", - "security": [ - { - "HttpBearerJwtScheme": [ - "users.delete", - ], - }, - ], - "tags": [ - "user", - "random", - ], - }, - "get": { - "operationId": "getUserByIdCommand", - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "additionalProperties": false, - "properties": { - "address": { - "additionalProperties": false, - "properties": { - "postcode": { - "format": "int32", - "maximum": 9999, - "minimum": 1000, - "type": "integer", - }, - }, - "required": [ - "postcode", - ], - "type": "object", - }, - "age": { - "anyOf": [ - { - "format": "int32", - "minimum": 0, - "type": "integer", - }, - { - "type": "null", - }, - ], - }, - "name": { - "type": "string", - }, - "userId": { - "type": "string", - }, - }, - "required": [ - "name", - ], - "type": "object", - }, - }, - }, - "description": "User 200 response", - }, - }, - "tags": [], - }, - "head": { - "operationId": "checkUserIdAvailableCommand", - "security": [ - {}, - ], - "tags": [], - }, - "parameters": [ - { - "in": "path", - "name": "userId", - "required": true, - "schema": { - "type": "string", - }, - }, - ], - "post": { - "operationId": "updateUserCommand", - "requestBody": { - "content": { - "application/json": { - "schema": { - "additionalProperties": false, - "minProperties": 1, - "properties": { - "address": { - "additionalProperties": false, - "properties": { - "postcode": { - "format": "int32", - "maximum": 9999, - "minimum": 1000, - "type": "integer", - }, - }, - "required": [ - "postcode", - ], - "type": "object", - }, - "age": { - "format": "int32", - "minimum": 0, - "type": "integer", - }, - }, - "type": "object", - }, - }, - }, - "description": "", - "required": true, - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "additionalProperties": false, - "properties": { - "address": { - "additionalProperties": false, - "properties": { - "postcode": { - "format": "int32", - "maximum": 9999, - "minimum": 1000, - "type": "integer", - }, - }, - "required": [ - "postcode", - ], - "type": "object", - }, - "age": { - "anyOf": [ - { - "format": "int32", - "minimum": 0, - "type": "integer", - }, - { - "type": "null", - }, - ], - }, - "name": { - "type": "string", - }, - "userId": { - "type": "string", - }, - }, - "required": [ - "name", - ], - "type": "object", - }, - }, - }, - "description": "Successful response", - }, - }, - "tags": [], - }, - }, - "/users/{userId}/avatar": { - "get": { - "description": "Download user avatar as JSON metadata or raw binary", - "operationId": "getUserAvatarCommand", - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "additionalProperties": false, - "properties": { - "address": { - "additionalProperties": false, - "properties": { - "postcode": { - "format": "int32", - "maximum": 9999, - "minimum": 1000, - "type": "integer", - }, - }, - "required": [ - "postcode", - ], - "type": "object", - }, - "age": { - "anyOf": [ - { - "format": "int32", - "minimum": 0, - "type": "integer", - }, - { - "type": "null", - }, - ], - }, - "name": { - "type": "string", - }, - "userId": { - "type": "string", - }, - }, - "required": [ - "name", - ], - "type": "object", - }, - }, - "application/octet-stream": { - "schema": { - "format": "binary", - "type": "string", - }, - }, - }, - "description": "Avatar response", - }, - }, - "tags": [], - }, - "parameters": [ - { - "in": "path", - "name": "userId", - "required": true, - "schema": { - "type": "string", - }, - }, - ], - "put": { - "description": "Upload user avatar as binary", - "operationId": "uploadUserAvatarCommand", - "requestBody": { - "content": { - "application/octet-stream": { - "schema": { - "format": "binary", - "type": "string", - }, - }, - }, - "description": "", - "required": true, - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "additionalProperties": false, - "properties": { - "address": { - "additionalProperties": false, - "properties": { - "postcode": { - "format": "int32", - "maximum": 9999, - "minimum": 1000, - "type": "integer", - }, - }, - "required": [ - "postcode", - ], - "type": "object", - }, - "age": { - "anyOf": [ - { - "format": "int32", - "minimum": 0, - "type": "integer", - }, - { - "type": "null", - }, - ], - }, - "name": { - "type": "string", - }, - "userId": { - "type": "string", - }, - }, - "required": [ - "name", - ], - "type": "object", - }, - }, - }, - "description": "Successful response", - }, - }, - "tags": [], - }, - }, - }, - "security": [ - { - "HttpBearerJwtScheme": [], - }, - { - "HttpBearerJwtScheme": [ - "users.delete", - ], - }, - {}, - ], - "servers": [ - { - "url": "https://api.example.com/", - }, - ], - "tags": [ - { - "name": "user", - }, - { - "name": "random", - }, - ], -} -`; - -exports[`Note Taking > OpenAPI 1`] = ` -{ - "components": { - "headers": {}, - "parameters": { - "NoteId": { - "in": "path", - "name": "noteId", - "required": true, - "schema": { - "$ref": "#/components/schemas/Id", - }, - }, - "NoteUserId": { - "in": "path", - "name": "userId", - "required": true, - "schema": { - "$ref": "#/components/schemas/Id", - }, - }, - "UserId": { - "in": "path", - "name": "userId", - "required": true, - "schema": { - "$ref": "#/components/schemas/Id", - }, - }, - }, - "schemas": { - "Address": { - "additionalProperties": false, - "properties": { - "postcode": { - "format": "int32", - "maximum": 9999, - "minimum": 1000, - "type": "integer", - }, - }, - "required": [ - "postcode", - ], - "type": "object", - }, - "CreateNoteRequest": { - "additionalProperties": false, - "properties": { - "content": { - "type": "string", - }, - "labels": { - "items": { - "maxLength": 256, - "minLength": 1, - "type": "string", - }, - "type": "array", - }, - "title": { - "type": "string", - }, - }, - "required": [ - "title", - "content", - ], - "type": "object", - }, - "CreateUserRequest": { - "additionalProperties": false, - "properties": { - "address": { - "$ref": "#/components/schemas/Address", - }, - "age": { - "format": "int32", - "minimum": 0, - "type": "integer", - }, - "name": { - "type": "string", - }, - "userId": { - "$ref": "#/components/schemas/Id", - }, - }, - "required": [ - "name", - ], - "type": "object", - }, - "Id": { - "maxLength": 6, - "minLength": 6, - "type": "string", - }, - "Note": { - "additionalProperties": false, - "properties": { - "content": { - "type": "string", - }, - "labels": { - "items": { - "maxLength": 256, - "minLength": 1, - "type": "string", - }, - "type": "array", - }, - "title": { - "type": "string", - }, - }, - "required": [ - "title", - "content", - ], - "type": "object", - }, - "Notes": { - "items": { - "$ref": "#/components/schemas/Note", - }, - "type": "array", - "uniqueItems": true, - }, - "UpdateNoteRequest": { - "additionalProperties": false, - "minProperties": 1, - "properties": { - "content": { - "type": "string", - }, - "labels": { - "items": { - "maxLength": 256, - "minLength": 1, - "type": "string", - }, - "type": "array", - }, - "title": { - "type": "string", - }, - }, - "type": "object", - }, - "UpdateUserRequest": { - "additionalProperties": false, - "minProperties": 1, - "properties": { - "address": { - "$ref": "#/components/schemas/Address", - }, - "age": { - "format": "int32", - "minimum": 0, - "type": "integer", - }, - }, - "type": "object", - }, - "User": { - "additionalProperties": false, - "properties": { - "address": { - "$ref": "#/components/schemas/Address", - }, - "age": { - "format": "int32", - "minimum": 0, - "type": "integer", - }, - "name": { - "type": "string", - }, - "userId": { - "$ref": "#/components/schemas/Id", - }, - }, - "required": [ - "name", - ], - "type": "object", - }, - "Users": { - "items": { - "$ref": "#/components/schemas/User", - }, - "type": "array", - "uniqueItems": true, - }, - }, - "securitySchemes": { - "HttpBearerJwtScheme": { - "bearerFormat": "JWT", - "scheme": "bearer", - "type": "http", - }, - }, - }, - "info": { - "title": "Example REST API", - "version": "1.0.0", - }, - "openapi": "3.1.0", - "paths": { - "/users": { - "get": { - "operationId": "listUsersCommand", - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Users", - }, - }, - }, - "description": "User 200 response", - }, - }, - "tags": [ - "user", - ], - }, - "post": { - "operationId": "createUserCommand", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateUserRequest", - }, - }, - }, - "description": "", - "required": true, - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Users", - }, - }, - }, - "description": "User 200 response", - }, - }, - "tags": [ - "user", - ], - }, - }, - "/users/{userId}": { - "delete": { - "operationId": "deleteUserByIdCommand", - "security": [ - { - "HttpBearerJwtScheme": [ - "users.delete", - ], - }, - ], - "tags": [], - }, - "get": { - "operationId": "getUserByIdCommand", - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/User", - }, - }, - }, - "description": "User 200 response", - }, - }, - "tags": [], - }, - "head": { - "operationId": "checkUserIdAvailableCommand", - "responses": { - "204": { - "content": {}, - "description": "Successful response", - }, - "404": { - "content": {}, - "description": "Successful response", - }, - }, - "security": [ - {}, - ], - "tags": [], - }, - "parameters": [ - { - "$ref": "#/components/parameters/UserId", - }, - ], - "post": { - "operationId": "updateUserCommand", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateUserRequest", - }, - }, - }, - "description": "", - "required": true, - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/User", - }, - }, - }, - "description": "Successful response", - }, - }, - "tags": [], - }, - }, - "/users/{userId}/notes": { - "get": { - "description": "List all of the notes", - "operationId": "listNotesCommand", - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Notes", - }, - }, - }, - "description": "Notes 200 response", - }, - }, - "summary": "List notes", - "tags": [ - "user", - ], - }, - "parameters": [ - { - "$ref": "#/components/parameters/NoteUserId", - }, - ], - "post": { - "deprecated": true, - "operationId": "createNoteCommand", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateNoteRequest", - }, - }, - }, - "description": "", - "required": true, - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Note", - }, - }, - }, - "description": "Note created 200 response", - }, - }, - "tags": [ - "user", - ], - }, - }, - "/users/{userId}/notes/{noteId}": { - "delete": { - "operationId": "deleteNoteCommand", - "security": [ - { - "HttpBearerJwtScheme": [ - "notes.delete", - ], - }, - ], - "tags": [], - }, - "get": { - "operationId": "getNoteCommand", - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Note", - }, - }, - }, - "description": "Note 200 response", - }, - }, - "tags": [], - }, - "parameters": [ - { - "$ref": "#/components/parameters/NoteUserId", - }, - { - "$ref": "#/components/parameters/NoteId", - }, - ], - "post": { - "operationId": "updateNoteCommand", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateNoteRequest", - }, - }, - }, - "description": "", - "required": true, - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Note", - }, - }, - }, - "description": "Successful response", - }, - }, - "tags": [], - }, - }, - }, - "security": [ - { - "HttpBearerJwtScheme": [], - }, - { - "HttpBearerJwtScheme": [ - "users.delete", - ], - }, - {}, - { - "HttpBearerJwtScheme": [ - "notes.delete", - ], - }, - ], - "servers": [ - { - "url": "https://api.example.com/", - }, - ], - "tags": [ - { - "name": "user", - }, - ], -} -`; - -exports[`Note Taking > Swagger Parser validate 1`] = ` +exports[`Note Taking > OpenAPI 1`] = ` { "components": { "headers": {}, @@ -1646,9 +384,7 @@ exports[`Note Taking > Swagger Parser validate 1`] = ` "name": "noteId", "required": true, "schema": { - "maxLength": 6, - "minLength": 6, - "type": "string", + "$ref": "#/components/schemas/Id", }, }, "NoteUserId": { @@ -1656,9 +392,7 @@ exports[`Note Taking > Swagger Parser validate 1`] = ` "name": "userId", "required": true, "schema": { - "maxLength": 6, - "minLength": 6, - "type": "string", + "$ref": "#/components/schemas/Id", }, }, "UserId": { @@ -1666,9 +400,7 @@ exports[`Note Taking > Swagger Parser validate 1`] = ` "name": "userId", "required": true, "schema": { - "maxLength": 6, - "minLength": 6, - "type": "string", + "$ref": "#/components/schemas/Id", }, }, }, @@ -1716,19 +448,7 @@ exports[`Note Taking > Swagger Parser validate 1`] = ` "additionalProperties": false, "properties": { "address": { - "additionalProperties": false, - "properties": { - "postcode": { - "format": "int32", - "maximum": 9999, - "minimum": 1000, - "type": "integer", - }, - }, - "required": [ - "postcode", - ], - "type": "object", + "$ref": "#/components/schemas/Address", }, "age": { "format": "int32", @@ -1739,9 +459,7 @@ exports[`Note Taking > Swagger Parser validate 1`] = ` "type": "string", }, "userId": { - "maxLength": 6, - "minLength": 6, - "type": "string", + "$ref": "#/components/schemas/Id", }, }, "required": [ @@ -1780,28 +498,7 @@ exports[`Note Taking > Swagger Parser validate 1`] = ` }, "Notes": { "items": { - "additionalProperties": false, - "properties": { - "content": { - "type": "string", - }, - "labels": { - "items": { - "maxLength": 256, - "minLength": 1, - "type": "string", - }, - "type": "array", - }, - "title": { - "type": "string", - }, - }, - "required": [ - "title", - "content", - ], - "type": "object", + "$ref": "#/components/schemas/Note", }, "type": "array", "uniqueItems": true, @@ -1832,19 +529,7 @@ exports[`Note Taking > Swagger Parser validate 1`] = ` "minProperties": 1, "properties": { "address": { - "additionalProperties": false, - "properties": { - "postcode": { - "format": "int32", - "maximum": 9999, - "minimum": 1000, - "type": "integer", - }, - }, - "required": [ - "postcode", - ], - "type": "object", + "$ref": "#/components/schemas/Address", }, "age": { "format": "int32", @@ -1858,19 +543,7 @@ exports[`Note Taking > Swagger Parser validate 1`] = ` "additionalProperties": false, "properties": { "address": { - "additionalProperties": false, - "properties": { - "postcode": { - "format": "int32", - "maximum": 9999, - "minimum": 1000, - "type": "integer", - }, - }, - "required": [ - "postcode", - ], - "type": "object", + "$ref": "#/components/schemas/Address", }, "age": { "format": "int32", @@ -1881,9 +554,7 @@ exports[`Note Taking > Swagger Parser validate 1`] = ` "type": "string", }, "userId": { - "maxLength": 6, - "minLength": 6, - "type": "string", + "$ref": "#/components/schemas/Id", }, }, "required": [ @@ -1893,41 +564,7 @@ exports[`Note Taking > Swagger Parser validate 1`] = ` }, "Users": { "items": { - "additionalProperties": false, - "properties": { - "address": { - "additionalProperties": false, - "properties": { - "postcode": { - "format": "int32", - "maximum": 9999, - "minimum": 1000, - "type": "integer", - }, - }, - "required": [ - "postcode", - ], - "type": "object", - }, - "age": { - "format": "int32", - "minimum": 0, - "type": "integer", - }, - "name": { - "type": "string", - }, - "userId": { - "maxLength": 6, - "minLength": 6, - "type": "string", - }, - }, - "required": [ - "name", - ], - "type": "object", + "$ref": "#/components/schemas/User", }, "type": "array", "uniqueItems": true, @@ -1955,45 +592,7 @@ exports[`Note Taking > Swagger Parser validate 1`] = ` "content": { "application/json": { "schema": { - "items": { - "additionalProperties": false, - "properties": { - "address": { - "additionalProperties": false, - "properties": { - "postcode": { - "format": "int32", - "maximum": 9999, - "minimum": 1000, - "type": "integer", - }, - }, - "required": [ - "postcode", - ], - "type": "object", - }, - "age": { - "format": "int32", - "minimum": 0, - "type": "integer", - }, - "name": { - "type": "string", - }, - "userId": { - "maxLength": 6, - "minLength": 6, - "type": "string", - }, - }, - "required": [ - "name", - ], - "type": "object", - }, - "type": "array", - "uniqueItems": true, + "$ref": "#/components/schemas/Users", }, }, }, @@ -2010,41 +609,7 @@ exports[`Note Taking > Swagger Parser validate 1`] = ` "content": { "application/json": { "schema": { - "additionalProperties": false, - "properties": { - "address": { - "additionalProperties": false, - "properties": { - "postcode": { - "format": "int32", - "maximum": 9999, - "minimum": 1000, - "type": "integer", - }, - }, - "required": [ - "postcode", - ], - "type": "object", - }, - "age": { - "format": "int32", - "minimum": 0, - "type": "integer", - }, - "name": { - "type": "string", - }, - "userId": { - "maxLength": 6, - "minLength": 6, - "type": "string", - }, - }, - "required": [ - "name", - ], - "type": "object", + "$ref": "#/components/schemas/CreateUserRequest", }, }, }, @@ -2056,45 +621,7 @@ exports[`Note Taking > Swagger Parser validate 1`] = ` "content": { "application/json": { "schema": { - "items": { - "additionalProperties": false, - "properties": { - "address": { - "additionalProperties": false, - "properties": { - "postcode": { - "format": "int32", - "maximum": 9999, - "minimum": 1000, - "type": "integer", - }, - }, - "required": [ - "postcode", - ], - "type": "object", - }, - "age": { - "format": "int32", - "minimum": 0, - "type": "integer", - }, - "name": { - "type": "string", - }, - "userId": { - "maxLength": 6, - "minLength": 6, - "type": "string", - }, - }, - "required": [ - "name", - ], - "type": "object", - }, - "type": "array", - "uniqueItems": true, + "$ref": "#/components/schemas/Users", }, }, }, @@ -2125,41 +652,7 @@ exports[`Note Taking > Swagger Parser validate 1`] = ` "content": { "application/json": { "schema": { - "additionalProperties": false, - "properties": { - "address": { - "additionalProperties": false, - "properties": { - "postcode": { - "format": "int32", - "maximum": 9999, - "minimum": 1000, - "type": "integer", - }, - }, - "required": [ - "postcode", - ], - "type": "object", - }, - "age": { - "format": "int32", - "minimum": 0, - "type": "integer", - }, - "name": { - "type": "string", - }, - "userId": { - "maxLength": 6, - "minLength": 6, - "type": "string", - }, - }, - "required": [ - "name", - ], - "type": "object", + "$ref": "#/components/schemas/User", }, }, }, @@ -2187,14 +680,7 @@ exports[`Note Taking > Swagger Parser validate 1`] = ` }, "parameters": [ { - "in": "path", - "name": "userId", - "required": true, - "schema": { - "maxLength": 6, - "minLength": 6, - "type": "string", - }, + "$ref": "#/components/parameters/UserId", }, ], "post": { @@ -2203,31 +689,7 @@ exports[`Note Taking > Swagger Parser validate 1`] = ` "content": { "application/json": { "schema": { - "additionalProperties": false, - "minProperties": 1, - "properties": { - "address": { - "additionalProperties": false, - "properties": { - "postcode": { - "format": "int32", - "maximum": 9999, - "minimum": 1000, - "type": "integer", - }, - }, - "required": [ - "postcode", - ], - "type": "object", - }, - "age": { - "format": "int32", - "minimum": 0, - "type": "integer", - }, - }, - "type": "object", + "$ref": "#/components/schemas/UpdateUserRequest", }, }, }, @@ -2239,41 +701,7 @@ exports[`Note Taking > Swagger Parser validate 1`] = ` "content": { "application/json": { "schema": { - "additionalProperties": false, - "properties": { - "address": { - "additionalProperties": false, - "properties": { - "postcode": { - "format": "int32", - "maximum": 9999, - "minimum": 1000, - "type": "integer", - }, - }, - "required": [ - "postcode", - ], - "type": "object", - }, - "age": { - "format": "int32", - "minimum": 0, - "type": "integer", - }, - "name": { - "type": "string", - }, - "userId": { - "maxLength": 6, - "minLength": 6, - "type": "string", - }, - }, - "required": [ - "name", - ], - "type": "object", + "$ref": "#/components/schemas/User", }, }, }, @@ -2292,32 +720,7 @@ exports[`Note Taking > Swagger Parser validate 1`] = ` "content": { "application/json": { "schema": { - "items": { - "additionalProperties": false, - "properties": { - "content": { - "type": "string", - }, - "labels": { - "items": { - "maxLength": 256, - "minLength": 1, - "type": "string", - }, - "type": "array", - }, - "title": { - "type": "string", - }, - }, - "required": [ - "title", - "content", - ], - "type": "object", - }, - "type": "array", - "uniqueItems": true, + "$ref": "#/components/schemas/Notes", }, }, }, @@ -2331,14 +734,7 @@ exports[`Note Taking > Swagger Parser validate 1`] = ` }, "parameters": [ { - "in": "path", - "name": "userId", - "required": true, - "schema": { - "maxLength": 6, - "minLength": 6, - "type": "string", - }, + "$ref": "#/components/parameters/NoteUserId", }, ], "post": { @@ -2348,28 +744,7 @@ exports[`Note Taking > Swagger Parser validate 1`] = ` "content": { "application/json": { "schema": { - "additionalProperties": false, - "properties": { - "content": { - "type": "string", - }, - "labels": { - "items": { - "maxLength": 256, - "minLength": 1, - "type": "string", - }, - "type": "array", - }, - "title": { - "type": "string", - }, - }, - "required": [ - "title", - "content", - ], - "type": "object", + "$ref": "#/components/schemas/CreateNoteRequest", }, }, }, @@ -2381,28 +756,7 @@ exports[`Note Taking > Swagger Parser validate 1`] = ` "content": { "application/json": { "schema": { - "additionalProperties": false, - "properties": { - "content": { - "type": "string", - }, - "labels": { - "items": { - "maxLength": 256, - "minLength": 1, - "type": "string", - }, - "type": "array", - }, - "title": { - "type": "string", - }, - }, - "required": [ - "title", - "content", - ], - "type": "object", + "$ref": "#/components/schemas/Note", }, }, }, @@ -2433,28 +787,7 @@ exports[`Note Taking > Swagger Parser validate 1`] = ` "content": { "application/json": { "schema": { - "additionalProperties": false, - "properties": { - "content": { - "type": "string", - }, - "labels": { - "items": { - "maxLength": 256, - "minLength": 1, - "type": "string", - }, - "type": "array", - }, - "title": { - "type": "string", - }, - }, - "required": [ - "title", - "content", - ], - "type": "object", + "$ref": "#/components/schemas/Note", }, }, }, @@ -2465,24 +798,10 @@ exports[`Note Taking > Swagger Parser validate 1`] = ` }, "parameters": [ { - "in": "path", - "name": "userId", - "required": true, - "schema": { - "maxLength": 6, - "minLength": 6, - "type": "string", - }, + "$ref": "#/components/parameters/NoteUserId", }, { - "in": "path", - "name": "noteId", - "required": true, - "schema": { - "maxLength": 6, - "minLength": 6, - "type": "string", - }, + "$ref": "#/components/parameters/NoteId", }, ], "post": { @@ -2491,25 +810,7 @@ exports[`Note Taking > Swagger Parser validate 1`] = ` "content": { "application/json": { "schema": { - "additionalProperties": false, - "minProperties": 1, - "properties": { - "content": { - "type": "string", - }, - "labels": { - "items": { - "maxLength": 256, - "minLength": 1, - "type": "string", - }, - "type": "array", - }, - "title": { - "type": "string", - }, - }, - "type": "object", + "$ref": "#/components/schemas/UpdateNoteRequest", }, }, }, @@ -2521,28 +822,7 @@ exports[`Note Taking > Swagger Parser validate 1`] = ` "content": { "application/json": { "schema": { - "additionalProperties": false, - "properties": { - "content": { - "type": "string", - }, - "labels": { - "items": { - "maxLength": 256, - "minLength": 1, - "type": "string", - }, - "type": "array", - }, - "title": { - "type": "string", - }, - }, - "required": [ - "title", - "content", - ], - "type": "object", + "$ref": "#/components/schemas/Note", }, }, }, diff --git a/__tests__/fixtures/oas31.json b/__tests__/fixtures/oas31.json index 4493410..ab23f8a 100644 --- a/__tests__/fixtures/oas31.json +++ b/__tests__/fixtures/oas31.json @@ -1,6 +1,7 @@ { "$id": "https://spec.openapis.org/oas/3.1/schema/2022-10-07", "$schema": "https://json-schema.org/draft/2020-12/schema", + "$comment": "Vendored 2024-10-12 from https://spec.openapis.org/oas/3.1/schema/2022-10-07 and patched: each \"$dynamicRef\": \"#meta\" is \"$ref\": \"#/$defs/schema\", because ajv mis-resolves $dynamicRef. The two are equivalent here, since $defs/schema holds the only meta anchor.", "description": "The description of OpenAPI v3.1.x documents without schema validation, as defined by https://spec.openapis.org/oas/v3.1.0", "type": "object", "properties": { @@ -197,7 +198,7 @@ "schemas": { "type": "object", "additionalProperties": { - "$dynamicRef": "#meta" + "$ref": "#/$defs/schema" } }, "responses": { @@ -437,7 +438,7 @@ "type": "boolean" }, "schema": { - "$dynamicRef": "#meta" + "$ref": "#/$defs/schema" }, "content": { "$ref": "#/$defs/content", @@ -650,7 +651,7 @@ "type": "object", "properties": { "schema": { - "$dynamicRef": "#meta" + "$ref": "#/$defs/schema" }, "encoding": { "type": "object", @@ -885,7 +886,7 @@ "type": "boolean" }, "schema": { - "$dynamicRef": "#meta" + "$ref": "#/$defs/schema" }, "content": { "$ref": "#/$defs/content", diff --git a/__tests__/openapi-schema.test.ts b/__tests__/openapi-schema.test.ts index 5999240..332c375 100644 --- a/__tests__/openapi-schema.test.ts +++ b/__tests__/openapi-schema.test.ts @@ -1,48 +1,36 @@ -import { mkdtemp, rm, writeFile } from "node:fs/promises"; -import { tmpdir } from "node:os"; -import path from "node:path"; -import SwaggerParser from "@apidevtools/swagger-parser"; -import { test, expect, describe } from "vitest"; +import { Ajv2020 } from "ajv/dist/2020.js"; +import { describe, expect, test } from "vitest"; import { exampleApi } from "./fixtures/apis/example.ts"; import { noteTakingApi } from "./fixtures/apis/note-taking.ts"; +import oas31Schema from "./fixtures/oas31.json" with { type: "json" }; + +const ajv = new Ajv2020({ + // the official schema leaves the object type implicit beside object keywords + strictTypes: false, + // and lists components both by name and by a pattern that matches them + allowMatchingProperties: true, + // its formats are checked only with ajv-formats, which is not installed + validateFormats: false, +}); +const validateOas31 = ajv.compile(oas31Schema); -// the JSON is validated, since openapi3-ts and openapi-types types disagree -async function validate(document: object) { - const dir = await mkdtemp(path.join(tmpdir(), "openapi-constructs-")); - const file = path.join(dir, "openapi.json"); - - try { - await writeFile(file, JSON.stringify(document)); - return await SwaggerParser.validate(file); - } finally { - await rm(dir, { recursive: true }); - } -} +describe.each([ + ["Example", exampleApi], + ["Note Taking", noteTakingApi], +])("%s", (_, api) => { + const document = api.synth(); -describe("Example", () => { - test("OpenAPI", async () => { - const document = exampleApi.synth(); + test("OpenAPI", () => { expect(document).toMatchSnapshot(); }); - test("Swagger Parser validate", async () => { - const document = exampleApi.synth(); + test("OpenAPI 3.1 schema validate", () => { + validateOas31(document); - const result = await validate(document); - expect(result).toMatchSnapshot(); + expect(validateOas31.errors ?? []).toStrictEqual([]); }); -}); - -describe("Note Taking", () => { - test("OpenAPI", async () => { - const document = noteTakingApi.synth(); - expect(document).toMatchSnapshot(); - }); - - test("Swagger Parser validate", async () => { - const document = noteTakingApi.synth(); - const result = await validate(document); - expect(result).toMatchSnapshot(); + test("OpenAPI 3.1 schema rejects an unknown top-level key", () => { + expect(validateOas31({ ...document, bogus: true })).toBe(false); }); }); diff --git a/package.json b/package.json index 7d1f4b9..5164bc5 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,6 @@ "preversion": "just check" }, "devDependencies": { - "@apidevtools/swagger-parser": "^13.1.0", "@block65/shared-config": "^0.5.0", "@block65/tsconfig": "^0.3.1", "@types/json-schema": "^7.0.15", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 63bee14..e2b36f4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -170,9 +170,6 @@ importers: .: devDependencies: - '@apidevtools/swagger-parser': - specifier: ^13.1.0 - version: 13.1.0(openapi-types@12.1.3) '@block65/shared-config': specifier: ^0.5.0 version: 0.5.0(@block65/oxlint-tsgolint@7.0.200200)(@block65/oxlint@1.83.0(@block65/oxlint-tsgolint@7.0.200200))(eslint@10.11.0)(fallow@3.28.0)(oxfmt@0.70.0) @@ -218,25 +215,6 @@ importers: packages: - '@apidevtools/json-schema-ref-parser@16.0.3': - resolution: {integrity: sha512-7lZp4XCTZhU/AgiGpIG+j4uJeWPdGbpQ+8wqXrzuHy+tboKsjSacjGH9/kNThRDIFhMWHF3ba+ejS8J3PeSI1Q==} - engines: {node: '>=22.19.0'} - peerDependencies: - '@types/json-schema': ^7.0.15 - - '@apidevtools/openapi-schemas@2.1.0': - resolution: {integrity: sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==} - engines: {node: '>=10'} - - '@apidevtools/swagger-methods@3.0.2': - resolution: {integrity: sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==} - - '@apidevtools/swagger-parser@13.1.0': - resolution: {integrity: sha512-dkCuqCWh5U7u0aurmmc/qvel2KeKDxHTwVtUdYonndIpD7mXV6AVaWNdvaja2HAy628z7FT/Qdz8sCgzwhgwKg==} - engines: {node: '>=22.19.0'} - peerDependencies: - openapi-types: '>=7' - '@block65/oxlint-binding-linux-arm64-gnu@1.83.0': resolution: {integrity: sha512-iH3ufhqCxiemK1JIQj8Er/Z//L4fNawO9Mb8CzCou6p84l+HWAWkHVV2vWd+Wk8zaMm2m5vs2ONy0CBzrus3Qw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -969,23 +947,12 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - ajv-draft-04@1.0.0: - resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} - peerDependencies: - ajv: ^8.5.0 - peerDependenciesMeta: - ajv: - optional: true - ajv@6.15.0: resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} ajv@8.20.0: resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} - argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -1015,9 +982,6 @@ packages: cacheable@2.5.0: resolution: {integrity: sha512-60cyAOytib/OzBw1JNSoSV/boK1AtHryDIjvVBk7XbN4ugfkM3+Sry7fEjNgPMGgOjuaZPAp8ruZ0Cxafwyq9g==} - call-me-maybe@1.0.2: - resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} - caniuse-lite@1.0.30001810: resolution: {integrity: sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==} @@ -1259,10 +1223,6 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - js-yaml@5.4.2: - resolution: {integrity: sha512-m+aqu+LwO1O6sIopafj8HUVl5aawITwZQe/yHpMCKjaWBaA/d07B/QdMb3529REftiU+RMMHL3Vlsw3hON7vWg==} - hasBin: true - jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} @@ -1398,9 +1358,6 @@ packages: resolution: {integrity: sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==} engines: {node: '>=12.20.0'} - openapi-types@12.1.3: - resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} - openapi3-ts@4.6.1: resolution: {integrity: sha512-XW9MOldkhoICNeXVzzmXzmOW5G73ppOEGmh7fLCqHjgfdEYCGGN+00MlVCeUZgovjjfC56j9tvtDt1zGabNjjA==} @@ -1563,10 +1520,6 @@ packages: undici-types@8.9.0: resolution: {integrity: sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==} - undici@8.11.0: - resolution: {integrity: sha512-AdzuGcAkzhbha3GgCSoUBx2P0quwym6qdFNCQz/fDGZc8w9XJ741wHUbt2kTmfPz+ZIOMkySFkiY7RXmiDDC8w==} - engines: {node: '>=22.19.0'} - update-browserslist-db@1.3.3: resolution: {integrity: sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ==} hasBin: true @@ -1693,27 +1646,6 @@ packages: snapshots: - '@apidevtools/json-schema-ref-parser@16.0.3(@types/json-schema@7.0.15)': - dependencies: - '@types/json-schema': 7.0.15 - js-yaml: 5.4.2 - undici: 8.11.0 - - '@apidevtools/openapi-schemas@2.1.0': {} - - '@apidevtools/swagger-methods@3.0.2': {} - - '@apidevtools/swagger-parser@13.1.0(openapi-types@12.1.3)': - dependencies: - '@apidevtools/json-schema-ref-parser': 16.0.3(@types/json-schema@7.0.15) - '@apidevtools/openapi-schemas': 2.1.0 - '@apidevtools/swagger-methods': 3.0.2 - '@types/json-schema': 7.0.15 - ajv: 8.20.0 - ajv-draft-04: 1.0.0(ajv@8.20.0) - call-me-maybe: 1.0.2 - openapi-types: 12.1.3 - '@block65/oxlint-binding-linux-arm64-gnu@1.83.0': optional: true @@ -2142,10 +2074,6 @@ snapshots: acorn@8.18.0: {} - ajv-draft-04@1.0.0(ajv@8.20.0): - optionalDependencies: - ajv: 8.20.0 - ajv@6.15.0: dependencies: fast-deep-equal: 3.1.3 @@ -2160,8 +2088,6 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - argparse@2.0.1: {} - assertion-error@2.0.1: {} balanced-match@4.0.4: {} @@ -2190,8 +2116,6 @@ snapshots: keyv: 5.6.0 qified: 0.10.1 - call-me-maybe@1.0.2: {} - caniuse-lite@1.0.30001810: {} chai@6.2.2: {} @@ -2459,10 +2383,6 @@ snapshots: isexe@2.0.0: {} - js-yaml@5.4.2: - dependencies: - argparse: 2.0.1 - jsesc@3.1.0: {} json-schema-traverse@0.4.1: {} @@ -2561,8 +2481,6 @@ snapshots: obug@2.2.1: {} - openapi-types@12.1.3: {} - openapi3-ts@4.6.1: dependencies: yaml: 2.9.1 @@ -2739,8 +2657,6 @@ snapshots: undici-types@8.9.0: {} - undici@8.11.0: {} - update-browserslist-db@1.3.3(browserslist@4.29.0): dependencies: browserslist: 4.29.0 From 565a9769ba83ed2c11ff6fb96c1cdd7ede6ee336 Mon Sep 17 00:00:00 2001 From: Max Holman Date: Wed, 23 Sep 2026 17:27:52 +0800 Subject: [PATCH 9/9] test: validate with @hyperjump/json-schema in place of ajv The OpenAPI documents are validated against the official 3.1 schema-base, which also checks every Schema Object against the OpenAPI dialect, so the vendored schema and its $dynamicRef patch go. The JSON Schemas are validated against the dialect their $schema declares. Each suite also checks that an invalid document is rejected. ajv is no longer a devDependency. Co-Authored-By: LLM --- __tests__/fixtures/oas31.json | 1278 ------------------------------ __tests__/json-schema.test.ts | 42 +- __tests__/json.ts | 31 + __tests__/openapi-schema.test.ts | 31 +- package.json | 2 +- pnpm-lock.yaml | 114 ++- 6 files changed, 156 insertions(+), 1342 deletions(-) delete mode 100644 __tests__/fixtures/oas31.json create mode 100644 __tests__/json.ts diff --git a/__tests__/fixtures/oas31.json b/__tests__/fixtures/oas31.json deleted file mode 100644 index ab23f8a..0000000 --- a/__tests__/fixtures/oas31.json +++ /dev/null @@ -1,1278 +0,0 @@ -{ - "$id": "https://spec.openapis.org/oas/3.1/schema/2022-10-07", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$comment": "Vendored 2024-10-12 from https://spec.openapis.org/oas/3.1/schema/2022-10-07 and patched: each \"$dynamicRef\": \"#meta\" is \"$ref\": \"#/$defs/schema\", because ajv mis-resolves $dynamicRef. The two are equivalent here, since $defs/schema holds the only meta anchor.", - "description": "The description of OpenAPI v3.1.x documents without schema validation, as defined by https://spec.openapis.org/oas/v3.1.0", - "type": "object", - "properties": { - "openapi": { - "type": "string", - "pattern": "^3\\.1\\.\\d+(-.+)?$" - }, - "info": { - "$ref": "#/$defs/info" - }, - "jsonSchemaDialect": { - "type": "string", - "format": "uri", - "default": "https://spec.openapis.org/oas/3.1/dialect/base" - }, - "servers": { - "type": "array", - "items": { - "$ref": "#/$defs/server" - }, - "default": [ - { - "url": "/" - } - ] - }, - "paths": { - "$ref": "#/$defs/paths" - }, - "webhooks": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/path-item-or-reference" - } - }, - "components": { - "$ref": "#/$defs/components" - }, - "security": { - "type": "array", - "items": { - "$ref": "#/$defs/security-requirement" - } - }, - "tags": { - "type": "array", - "items": { - "$ref": "#/$defs/tag" - } - }, - "externalDocs": { - "$ref": "#/$defs/external-documentation" - } - }, - "required": ["openapi", "info"], - "anyOf": [ - { - "required": ["paths"] - }, - { - "required": ["components"] - }, - { - "required": ["webhooks"] - } - ], - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false, - "$defs": { - "info": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#info-object", - "type": "object", - "properties": { - "title": { - "type": "string" - }, - "summary": { - "type": "string" - }, - "description": { - "type": "string" - }, - "termsOfService": { - "type": "string", - "format": "uri" - }, - "contact": { - "$ref": "#/$defs/contact" - }, - "license": { - "$ref": "#/$defs/license" - }, - "version": { - "type": "string" - } - }, - "required": ["title", "version"], - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "contact": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#contact-object", - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "url": { - "type": "string", - "format": "uri" - }, - "email": { - "type": "string", - "format": "email" - } - }, - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "license": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#license-object", - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "identifier": { - "type": "string" - }, - "url": { - "type": "string", - "format": "uri" - } - }, - "required": ["name"], - "dependentSchemas": { - "identifier": { - "not": { - "required": ["url"] - } - } - }, - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "server": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#server-object", - "type": "object", - "properties": { - "url": { - "type": "string", - "format": "uri-reference" - }, - "description": { - "type": "string" - }, - "variables": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/server-variable" - } - } - }, - "required": ["url"], - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "server-variable": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#server-variable-object", - "type": "object", - "properties": { - "enum": { - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1 - }, - "default": { - "type": "string" - }, - "description": { - "type": "string" - } - }, - "required": ["default"], - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "components": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#components-object", - "type": "object", - "properties": { - "schemas": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/schema" - } - }, - "responses": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/response-or-reference" - } - }, - "parameters": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/parameter-or-reference" - } - }, - "examples": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/example-or-reference" - } - }, - "requestBodies": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/request-body-or-reference" - } - }, - "headers": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/header-or-reference" - } - }, - "securitySchemes": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/security-scheme-or-reference" - } - }, - "links": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/link-or-reference" - } - }, - "callbacks": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/callbacks-or-reference" - } - }, - "pathItems": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/path-item-or-reference" - } - } - }, - "patternProperties": { - "^(schemas|responses|parameters|examples|requestBodies|headers|securitySchemes|links|callbacks|pathItems)$": { - "$comment": "Enumerating all of the property names in the regex above is necessary for unevaluatedProperties to work as expected", - "propertyNames": { - "pattern": "^[a-zA-Z0-9._-]+$" - } - } - }, - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "paths": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#paths-object", - "type": "object", - "patternProperties": { - "^/": { - "$ref": "#/$defs/path-item" - } - }, - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "path-item": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#path-item-object", - "type": "object", - "properties": { - "summary": { - "type": "string" - }, - "description": { - "type": "string" - }, - "servers": { - "type": "array", - "items": { - "$ref": "#/$defs/server" - } - }, - "parameters": { - "type": "array", - "items": { - "$ref": "#/$defs/parameter-or-reference" - } - }, - "get": { - "$ref": "#/$defs/operation" - }, - "put": { - "$ref": "#/$defs/operation" - }, - "post": { - "$ref": "#/$defs/operation" - }, - "delete": { - "$ref": "#/$defs/operation" - }, - "options": { - "$ref": "#/$defs/operation" - }, - "head": { - "$ref": "#/$defs/operation" - }, - "patch": { - "$ref": "#/$defs/operation" - }, - "trace": { - "$ref": "#/$defs/operation" - } - }, - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "path-item-or-reference": { - "if": { - "type": "object", - "required": ["$ref"] - }, - "then": { - "$ref": "#/$defs/reference" - }, - "else": { - "$ref": "#/$defs/path-item" - } - }, - "operation": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#operation-object", - "type": "object", - "properties": { - "tags": { - "type": "array", - "items": { - "type": "string" - } - }, - "summary": { - "type": "string" - }, - "description": { - "type": "string" - }, - "externalDocs": { - "$ref": "#/$defs/external-documentation" - }, - "operationId": { - "type": "string" - }, - "parameters": { - "type": "array", - "items": { - "$ref": "#/$defs/parameter-or-reference" - } - }, - "requestBody": { - "$ref": "#/$defs/request-body-or-reference" - }, - "responses": { - "$ref": "#/$defs/responses" - }, - "callbacks": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/callbacks-or-reference" - } - }, - "deprecated": { - "default": false, - "type": "boolean" - }, - "security": { - "type": "array", - "items": { - "$ref": "#/$defs/security-requirement" - } - }, - "servers": { - "type": "array", - "items": { - "$ref": "#/$defs/server" - } - } - }, - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "external-documentation": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#external-documentation-object", - "type": "object", - "properties": { - "description": { - "type": "string" - }, - "url": { - "type": "string", - "format": "uri" - } - }, - "required": ["url"], - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "parameter": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#parameter-object", - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "in": { - "enum": ["query", "header", "path", "cookie"] - }, - "description": { - "type": "string" - }, - "required": { - "default": false, - "type": "boolean" - }, - "deprecated": { - "default": false, - "type": "boolean" - }, - "schema": { - "$ref": "#/$defs/schema" - }, - "content": { - "$ref": "#/$defs/content", - "minProperties": 1, - "maxProperties": 1 - } - }, - "required": ["name", "in"], - "oneOf": [ - { - "required": ["schema"] - }, - { - "required": ["content"] - } - ], - "if": { - "properties": { - "in": { - "const": "query" - } - }, - "required": ["in"] - }, - "then": { - "properties": { - "allowEmptyValue": { - "default": false, - "type": "boolean" - } - } - }, - "dependentSchemas": { - "schema": { - "properties": { - "style": { - "type": "string" - }, - "explode": { - "type": "boolean" - } - }, - "allOf": [ - { - "$ref": "#/$defs/examples" - }, - { - "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-path" - }, - { - "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-header" - }, - { - "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-query" - }, - { - "$ref": "#/$defs/parameter/dependentSchemas/schema/$defs/styles-for-cookie" - }, - { - "$ref": "#/$defs/styles-for-form" - } - ], - "$defs": { - "styles-for-path": { - "if": { - "properties": { - "in": { - "const": "path" - } - }, - "required": ["in"] - }, - "then": { - "properties": { - "style": { - "default": "simple", - "enum": ["matrix", "label", "simple"] - }, - "required": { - "const": true - } - }, - "required": ["required"] - } - }, - "styles-for-header": { - "if": { - "properties": { - "in": { - "const": "header" - } - }, - "required": ["in"] - }, - "then": { - "properties": { - "style": { - "default": "simple", - "const": "simple" - } - } - } - }, - "styles-for-query": { - "if": { - "properties": { - "in": { - "const": "query" - } - }, - "required": ["in"] - }, - "then": { - "properties": { - "style": { - "default": "form", - "enum": [ - "form", - "spaceDelimited", - "pipeDelimited", - "deepObject" - ] - }, - "allowReserved": { - "default": false, - "type": "boolean" - } - } - } - }, - "styles-for-cookie": { - "if": { - "properties": { - "in": { - "const": "cookie" - } - }, - "required": ["in"] - }, - "then": { - "properties": { - "style": { - "default": "form", - "const": "form" - } - } - } - } - } - } - }, - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "parameter-or-reference": { - "if": { - "type": "object", - "required": ["$ref"] - }, - "then": { - "$ref": "#/$defs/reference" - }, - "else": { - "$ref": "#/$defs/parameter" - } - }, - "request-body": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#request-body-object", - "type": "object", - "properties": { - "description": { - "type": "string" - }, - "content": { - "$ref": "#/$defs/content" - }, - "required": { - "default": false, - "type": "boolean" - } - }, - "required": ["content"], - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "request-body-or-reference": { - "if": { - "type": "object", - "required": ["$ref"] - }, - "then": { - "$ref": "#/$defs/reference" - }, - "else": { - "$ref": "#/$defs/request-body" - } - }, - "content": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#fixed-fields-10", - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/media-type" - }, - "propertyNames": { - "format": "media-range" - } - }, - "media-type": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#media-type-object", - "type": "object", - "properties": { - "schema": { - "$ref": "#/$defs/schema" - }, - "encoding": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/encoding" - } - } - }, - "allOf": [ - { - "$ref": "#/$defs/specification-extensions" - }, - { - "$ref": "#/$defs/examples" - } - ], - "unevaluatedProperties": false - }, - "encoding": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#encoding-object", - "type": "object", - "properties": { - "contentType": { - "type": "string", - "format": "media-range" - }, - "headers": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/header-or-reference" - } - }, - "style": { - "default": "form", - "enum": ["form", "spaceDelimited", "pipeDelimited", "deepObject"] - }, - "explode": { - "type": "boolean" - }, - "allowReserved": { - "default": false, - "type": "boolean" - } - }, - "allOf": [ - { - "$ref": "#/$defs/specification-extensions" - }, - { - "$ref": "#/$defs/styles-for-form" - } - ], - "unevaluatedProperties": false - }, - "responses": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#responses-object", - "type": "object", - "properties": { - "default": { - "$ref": "#/$defs/response-or-reference" - } - }, - "patternProperties": { - "^[1-5](?:[0-9]{2}|XX)$": { - "$ref": "#/$defs/response-or-reference" - } - }, - "minProperties": 1, - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false, - "if": { - "$comment": "either default, or at least one response code property must exist", - "patternProperties": { - "^[1-5](?:[0-9]{2}|XX)$": false - } - }, - "then": { - "required": ["default"] - } - }, - "response": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#response-object", - "type": "object", - "properties": { - "description": { - "type": "string" - }, - "headers": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/header-or-reference" - } - }, - "content": { - "$ref": "#/$defs/content" - }, - "links": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/link-or-reference" - } - } - }, - "required": ["description"], - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "response-or-reference": { - "if": { - "type": "object", - "required": ["$ref"] - }, - "then": { - "$ref": "#/$defs/reference" - }, - "else": { - "$ref": "#/$defs/response" - } - }, - "callbacks": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#callback-object", - "type": "object", - "$ref": "#/$defs/specification-extensions", - "additionalProperties": { - "$ref": "#/$defs/path-item-or-reference" - } - }, - "callbacks-or-reference": { - "if": { - "type": "object", - "required": ["$ref"] - }, - "then": { - "$ref": "#/$defs/reference" - }, - "else": { - "$ref": "#/$defs/callbacks" - } - }, - "example": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#example-object", - "type": "object", - "properties": { - "summary": { - "type": "string" - }, - "description": { - "type": "string" - }, - "value": true, - "externalValue": { - "type": "string", - "format": "uri" - } - }, - "not": { - "required": ["value", "externalValue"] - }, - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "example-or-reference": { - "if": { - "type": "object", - "required": ["$ref"] - }, - "then": { - "$ref": "#/$defs/reference" - }, - "else": { - "$ref": "#/$defs/example" - } - }, - "link": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#link-object", - "type": "object", - "properties": { - "operationRef": { - "type": "string", - "format": "uri-reference" - }, - "operationId": { - "type": "string" - }, - "parameters": { - "$ref": "#/$defs/map-of-strings" - }, - "requestBody": true, - "description": { - "type": "string" - }, - "body": { - "$ref": "#/$defs/server" - } - }, - "oneOf": [ - { - "required": ["operationRef"] - }, - { - "required": ["operationId"] - } - ], - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "link-or-reference": { - "if": { - "type": "object", - "required": ["$ref"] - }, - "then": { - "$ref": "#/$defs/reference" - }, - "else": { - "$ref": "#/$defs/link" - } - }, - "header": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#header-object", - "type": "object", - "properties": { - "description": { - "type": "string" - }, - "required": { - "default": false, - "type": "boolean" - }, - "deprecated": { - "default": false, - "type": "boolean" - }, - "schema": { - "$ref": "#/$defs/schema" - }, - "content": { - "$ref": "#/$defs/content", - "minProperties": 1, - "maxProperties": 1 - } - }, - "oneOf": [ - { - "required": ["schema"] - }, - { - "required": ["content"] - } - ], - "dependentSchemas": { - "schema": { - "properties": { - "style": { - "default": "simple", - "const": "simple" - }, - "explode": { - "default": false, - "type": "boolean" - } - }, - "$ref": "#/$defs/examples" - } - }, - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "header-or-reference": { - "if": { - "type": "object", - "required": ["$ref"] - }, - "then": { - "$ref": "#/$defs/reference" - }, - "else": { - "$ref": "#/$defs/header" - } - }, - "tag": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#tag-object", - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "description": { - "type": "string" - }, - "externalDocs": { - "$ref": "#/$defs/external-documentation" - } - }, - "required": ["name"], - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "reference": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#reference-object", - "type": "object", - "properties": { - "$ref": { - "type": "string", - "format": "uri-reference" - }, - "summary": { - "type": "string" - }, - "description": { - "type": "string" - } - } - }, - "schema": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#schema-object", - "$dynamicAnchor": "meta", - "type": ["object", "boolean"] - }, - "security-scheme": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#security-scheme-object", - "type": "object", - "properties": { - "type": { - "enum": ["apiKey", "http", "mutualTLS", "oauth2", "openIdConnect"] - }, - "description": { - "type": "string" - } - }, - "required": ["type"], - "allOf": [ - { - "$ref": "#/$defs/specification-extensions" - }, - { - "$ref": "#/$defs/security-scheme/$defs/type-apikey" - }, - { - "$ref": "#/$defs/security-scheme/$defs/type-http" - }, - { - "$ref": "#/$defs/security-scheme/$defs/type-http-bearer" - }, - { - "$ref": "#/$defs/security-scheme/$defs/type-oauth2" - }, - { - "$ref": "#/$defs/security-scheme/$defs/type-oidc" - } - ], - "unevaluatedProperties": false, - "$defs": { - "type-apikey": { - "if": { - "properties": { - "type": { - "const": "apiKey" - } - }, - "required": ["type"] - }, - "then": { - "properties": { - "name": { - "type": "string" - }, - "in": { - "enum": ["query", "header", "cookie"] - } - }, - "required": ["name", "in"] - } - }, - "type-http": { - "if": { - "properties": { - "type": { - "const": "http" - } - }, - "required": ["type"] - }, - "then": { - "properties": { - "scheme": { - "type": "string" - } - }, - "required": ["scheme"] - } - }, - "type-http-bearer": { - "if": { - "properties": { - "type": { - "const": "http" - }, - "scheme": { - "type": "string", - "pattern": "^[Bb][Ee][Aa][Rr][Ee][Rr]$" - } - }, - "required": ["type", "scheme"] - }, - "then": { - "properties": { - "bearerFormat": { - "type": "string" - } - } - } - }, - "type-oauth2": { - "if": { - "properties": { - "type": { - "const": "oauth2" - } - }, - "required": ["type"] - }, - "then": { - "properties": { - "flows": { - "$ref": "#/$defs/oauth-flows" - } - }, - "required": ["flows"] - } - }, - "type-oidc": { - "if": { - "properties": { - "type": { - "const": "openIdConnect" - } - }, - "required": ["type"] - }, - "then": { - "properties": { - "openIdConnectUrl": { - "type": "string", - "format": "uri" - } - }, - "required": ["openIdConnectUrl"] - } - } - } - }, - "security-scheme-or-reference": { - "if": { - "type": "object", - "required": ["$ref"] - }, - "then": { - "$ref": "#/$defs/reference" - }, - "else": { - "$ref": "#/$defs/security-scheme" - } - }, - "oauth-flows": { - "type": "object", - "properties": { - "implicit": { - "$ref": "#/$defs/oauth-flows/$defs/implicit" - }, - "password": { - "$ref": "#/$defs/oauth-flows/$defs/password" - }, - "clientCredentials": { - "$ref": "#/$defs/oauth-flows/$defs/client-credentials" - }, - "authorizationCode": { - "$ref": "#/$defs/oauth-flows/$defs/authorization-code" - } - }, - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false, - "$defs": { - "implicit": { - "type": "object", - "properties": { - "authorizationUrl": { - "type": "string", - "format": "uri" - }, - "refreshUrl": { - "type": "string", - "format": "uri" - }, - "scopes": { - "$ref": "#/$defs/map-of-strings" - } - }, - "required": ["authorizationUrl", "scopes"], - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "password": { - "type": "object", - "properties": { - "tokenUrl": { - "type": "string", - "format": "uri" - }, - "refreshUrl": { - "type": "string", - "format": "uri" - }, - "scopes": { - "$ref": "#/$defs/map-of-strings" - } - }, - "required": ["tokenUrl", "scopes"], - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "client-credentials": { - "type": "object", - "properties": { - "tokenUrl": { - "type": "string", - "format": "uri" - }, - "refreshUrl": { - "type": "string", - "format": "uri" - }, - "scopes": { - "$ref": "#/$defs/map-of-strings" - } - }, - "required": ["tokenUrl", "scopes"], - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - }, - "authorization-code": { - "type": "object", - "properties": { - "authorizationUrl": { - "type": "string", - "format": "uri" - }, - "tokenUrl": { - "type": "string", - "format": "uri" - }, - "refreshUrl": { - "type": "string", - "format": "uri" - }, - "scopes": { - "$ref": "#/$defs/map-of-strings" - } - }, - "required": ["authorizationUrl", "tokenUrl", "scopes"], - "$ref": "#/$defs/specification-extensions", - "unevaluatedProperties": false - } - } - }, - "security-requirement": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#security-requirement-object", - "type": "object", - "additionalProperties": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "specification-extensions": { - "$comment": "https://spec.openapis.org/oas/v3.1.0#specification-extensions", - "patternProperties": { - "^x-": true - } - }, - "examples": { - "properties": { - "example": true, - "examples": { - "type": "object", - "additionalProperties": { - "$ref": "#/$defs/example-or-reference" - } - } - } - }, - "map-of-strings": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "styles-for-form": { - "if": { - "properties": { - "style": { - "const": "form" - } - }, - "required": ["style"] - }, - "then": { - "properties": { - "explode": { - "default": true - } - } - }, - "else": { - "properties": { - "explode": { - "default": false - } - } - } - } - } -} diff --git a/__tests__/json-schema.test.ts b/__tests__/json-schema.test.ts index ab51242..12be246 100644 --- a/__tests__/json-schema.test.ts +++ b/__tests__/json-schema.test.ts @@ -1,30 +1,36 @@ -import { Ajv } from "ajv"; -import { test, expect, describe } from "vitest"; +import { validate } from "@hyperjump/json-schema/draft-07"; +import { describe, expect, test } from "vitest"; import { exampleApi } from "./fixtures/apis/example.ts"; import { noteTakingApi } from "./fixtures/apis/note-taking.ts"; +import { toJson } from "./json.ts"; -describe("Example", () => { - const jsonSchema = exampleApi.synthJsonSchema(); +describe.each([ + ["Example", exampleApi], + ["Note Taking", noteTakingApi], +])("%s", (_, api) => { + const jsonSchema = api.synthJsonSchema(); - test("JSON Schema snapshot", async () => { + test("JSON Schema snapshot", () => { expect(jsonSchema).toMatchSnapshot(); }); - test("JSON Schema AJV validate", async () => { - const ajv = new Ajv(); - expect(ajv.validateSchema(jsonSchema)).toBeTruthy(); - }); -}); - -describe("Note Taking", () => { - const jsonSchema = noteTakingApi.synthJsonSchema(); + test("JSON Schema validates against the dialect it declares", async () => { + const output = await validate( + jsonSchema.$schema, + toJson(jsonSchema), + "BASIC", + ); - test("JSON Schema snapshot", async () => { - expect(jsonSchema).toMatchSnapshot(); + expect(output).toStrictEqual({ valid: true }); }); - test("JSON Schema AJV validate", async () => { - const ajv = new Ajv(); - expect(ajv.validateSchema(jsonSchema)).toBeTruthy(); + test("JSON Schema dialect rejects an invalid type", async () => { + const output = await validate( + jsonSchema.$schema, + toJson({ ...jsonSchema, definitions: { Invalid: { type: "nope" } } }), + "BASIC", + ); + + expect(output.valid).toBe(false); }); }); diff --git a/__tests__/json.ts b/__tests__/json.ts new file mode 100644 index 0000000..fc8ac53 --- /dev/null +++ b/__tests__/json.ts @@ -0,0 +1,31 @@ +import type { Validator } from "@hyperjump/json-schema/openapi-3-1"; + +type Json = Parameters[0]; + +/** + * Checks each value, since the construct types admit values JSON cannot hold + */ +export function toJson(value: unknown): Json { + if ( + value === null || + typeof value === "string" || + typeof value === "number" || + typeof value === "boolean" + ) { + return value; + } + + if (Array.isArray(value)) { + return value.map((item: unknown) => toJson(item)); + } + + if (typeof value === "object") { + return Object.fromEntries( + Object.entries(value) + .filter(([, item]) => item !== undefined) + .map(([key, item]) => [key, toJson(item)]), + ); + } + + throw new TypeError(`A ${typeof value} is not JSON`); +} diff --git a/__tests__/openapi-schema.test.ts b/__tests__/openapi-schema.test.ts index 332c375..5dcdbee 100644 --- a/__tests__/openapi-schema.test.ts +++ b/__tests__/openapi-schema.test.ts @@ -1,18 +1,11 @@ -import { Ajv2020 } from "ajv/dist/2020.js"; +import { validate } from "@hyperjump/json-schema/openapi-3-1"; import { describe, expect, test } from "vitest"; import { exampleApi } from "./fixtures/apis/example.ts"; import { noteTakingApi } from "./fixtures/apis/note-taking.ts"; -import oas31Schema from "./fixtures/oas31.json" with { type: "json" }; +import { toJson } from "./json.ts"; -const ajv = new Ajv2020({ - // the official schema leaves the object type implicit beside object keywords - strictTypes: false, - // and lists components both by name and by a pattern that matches them - allowMatchingProperties: true, - // its formats are checked only with ajv-formats, which is not installed - validateFormats: false, -}); -const validateOas31 = ajv.compile(oas31Schema); +// schema-base also checks each Schema Object against the OpenAPI 3.1 dialect +const oas31SchemaBase = "https://spec.openapis.org/oas/3.1/schema-base"; describe.each([ ["Example", exampleApi], @@ -24,13 +17,19 @@ describe.each([ expect(document).toMatchSnapshot(); }); - test("OpenAPI 3.1 schema validate", () => { - validateOas31(document); + test("OpenAPI 3.1 schema validate", async () => { + const output = await validate(oas31SchemaBase, toJson(document), "BASIC"); - expect(validateOas31.errors ?? []).toStrictEqual([]); + expect(output).toStrictEqual({ valid: true }); }); - test("OpenAPI 3.1 schema rejects an unknown top-level key", () => { - expect(validateOas31({ ...document, bogus: true })).toBe(false); + test("OpenAPI 3.1 schema rejects an unknown top-level key", async () => { + const output = await validate( + oas31SchemaBase, + toJson({ ...document, bogus: true }), + "BASIC", + ); + + expect(output.valid).toBe(false); }); }); diff --git a/package.json b/package.json index 5164bc5..5ecb773 100644 --- a/package.json +++ b/package.json @@ -21,9 +21,9 @@ "devDependencies": { "@block65/shared-config": "^0.5.0", "@block65/tsconfig": "^0.3.1", + "@hyperjump/json-schema": "^1.17.8", "@types/json-schema": "^7.0.15", "@types/node": "^26.6.2", - "ajv": "^8.20.0", "constructs": "^10.8.1", "fallow": "^3.28.0", "openapi3-ts": "^4.6.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e2b36f4..6279ed0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -176,15 +176,15 @@ importers: '@block65/tsconfig': specifier: ^0.3.1 version: 0.3.1 + '@hyperjump/json-schema': + specifier: ^1.17.8 + version: 1.17.8(@hyperjump/browser@1.5.0) '@types/json-schema': specifier: ^7.0.15 version: 7.0.15 '@types/node': specifier: ^26.6.2 version: 26.6.2 - ajv: - specifier: ^8.20.0 - version: 8.20.0 constructs: specifier: ^10.8.1 version: 10.8.1 @@ -539,6 +539,27 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} + '@hyperjump/browser@1.5.0': + resolution: {integrity: sha512-h+fGXfmMOAxYjd2aZ4Y5jpP2WRi0TAkBmKNtoNJKcJLOahNrurwHEUgd1/jO2Hs2j/b/G1EhX0edX8ntXkH2MA==} + engines: {node: '>=18.0.0'} + + '@hyperjump/json-pointer@1.1.2': + resolution: {integrity: sha512-zPNgu1zdhtjQHFNLGzvEsLDsLOEvhRj6u6ktIQmlz7YPESv5uF8SnAe3Dq0oL6gZ6OGWSLq2n7pphRNF6Hpg6w==} + + '@hyperjump/json-schema-formats@1.0.7': + resolution: {integrity: sha512-5KmK9p/KbHYcDYA+Fqbfgk0IEwiv72MCMm5mzXPf4oQBDXRiy21v8iZPckeqb/KHO+lk8fKKcHL+3cftmHei6g==} + + '@hyperjump/json-schema@1.17.8': + resolution: {integrity: sha512-XOqbR9GRNHaH4JEXHdbsm7xfYwudZG7HVDq3qPZUb1gi+ZQPklgNvhMi6zf0Plf433qR61MK+xeeprUwUUvGPg==} + peerDependencies: + '@hyperjump/browser': ^1.1.0 + + '@hyperjump/pact@1.4.0': + resolution: {integrity: sha512-01Q7VY6BcAkp9W31Fv+ciiZycxZHGlR2N6ba9BifgyclHYHdbaZgITo0U6QMhYRlem4k8pf8J31/tApxvqAz8A==} + + '@hyperjump/uri@1.3.6': + resolution: {integrity: sha512-9a2/wOIp666Veq1a8QctYPRtyFHH0p1yw8Nt/DEf06M+eDpwBpeuc2rGqqjBthENTOy1NyJ/xhSiDtkjU5LfKA==} + '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} @@ -950,9 +971,6 @@ packages: ajv@6.15.0: resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} - ajv@8.20.0: - resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} - assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -999,6 +1017,10 @@ packages: constructs@10.8.1: resolution: {integrity: sha512-98yGXYyhePqPYh3cYu8nzBERmAhC0DONe3UD03okK0nehZ7hYP4wgZuf02a04+uOWxnTJ5Rpp5m0GRNpwyLGGA==} + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + convert-hrtime@5.0.0: resolution: {integrity: sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==} engines: {node: '>=12'} @@ -1132,9 +1154,6 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fast-uri@3.1.0: - resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} - fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -1192,6 +1211,10 @@ packages: resolution: {integrity: sha512-SLX4H/vtcYlYnL7XqnuJKHU7Z8517TgsW9nmQiGOgMCjQ8V/deLYu6bEmbGoXe7WMMhc9+EUGyFFneHja8KabA==} engines: {node: '>=18'} + idn-hostname@15.1.19: + resolution: {integrity: sha512-pRGRbFpQWCptOBWw9Gn5DXtPdern5/XtdkcXdoEavjd+sh72jNm2eVI1eeysp64UisXPfaqoQrnLUKpSN4hJWw==} + engines: {node: '>=20.12.0 <21 || >=22.0.0'} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -1231,12 +1254,16 @@ packages: json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + json-stringify-deterministic@1.0.16: + resolution: {integrity: sha512-barjhF/wL0ot1i7AVA3gDDfuBIS1Iz7dlYmO6fZnVDUXtB/EJKjqREA/FUndo+kCGYLI67jH3hXKQMwuTFhbpQ==} + engines: {node: '>= 4'} + + just-curry-it@5.3.0: + resolution: {integrity: sha512-silMIRiFjUWlfaDhkgSzpuAyQ6EX/o09Eu8ZBfmFwQMbax7+LQzeIU2CBrICT6Ne4l86ITCGvUCBpCubWYy0Yw==} + keyv@5.6.0: resolution: {integrity: sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==} @@ -1437,10 +1464,6 @@ packages: resolution: {integrity: sha512-ycwFAS14Jw4mppvmK4GR/J6u3WpWpjkEApehuHtLc/8VpPNpDMbQ4WjqwplXifGeyKOzHSFLmSPqzksDQE2Sfg==} hasBin: true - require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} - reserved-identifiers@1.2.0: resolution: {integrity: sha512-yE7KUfFvaBFzGPs5H3Ops1RevfUEsDc5Iz65rOwWg4lE8HJSYtle77uul3+573457oHvBKuHYDl/xqUkKpEEdw==} engines: {node: '>=18'} @@ -1529,6 +1552,10 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + uuid@14.0.2: + resolution: {integrity: sha512-xZe/16rV4aa+HGSOCiY2YeLT1OybRLrrkL/Rqaq7p7GMVXjFh+6wN4oMYgjFmnSnhY8t6Xpdl2l9qmnHYuMHwQ==} + hasBin: true + vite@8.3.0: resolution: {integrity: sha512-lhZBVvEHefgE+HQZC9O7EBJgCU/nVzFNl7vkS4RE0APtWLP02/8QVIkQtzBxPquh7lq5/78NHipTj7ODQ6XuyQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1855,6 +1882,36 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} + '@hyperjump/browser@1.5.0': + dependencies: + '@hyperjump/json-pointer': 1.1.2 + '@hyperjump/uri': 1.3.6 + content-type: 1.0.5 + just-curry-it: 5.3.0 + + '@hyperjump/json-pointer@1.1.2': {} + + '@hyperjump/json-schema-formats@1.0.7': + dependencies: + '@hyperjump/uri': 1.3.6 + idn-hostname: 15.1.19 + + '@hyperjump/json-schema@1.17.8(@hyperjump/browser@1.5.0)': + dependencies: + '@hyperjump/browser': 1.5.0 + '@hyperjump/json-pointer': 1.1.2 + '@hyperjump/json-schema-formats': 1.0.7 + '@hyperjump/pact': 1.4.0 + '@hyperjump/uri': 1.3.6 + content-type: 1.0.5 + json-stringify-deterministic: 1.0.16 + just-curry-it: 5.3.0 + uuid: 14.0.2 + + '@hyperjump/pact@1.4.0': {} + + '@hyperjump/uri@1.3.6': {} + '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/sourcemap-codec@1.5.5': {} @@ -2081,13 +2138,6 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 - ajv@8.20.0: - dependencies: - fast-deep-equal: 3.1.3 - fast-uri: 3.1.0 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - assertion-error@2.0.1: {} balanced-match@4.0.4: {} @@ -2126,6 +2176,8 @@ snapshots: constructs@10.8.1: {} + content-type@1.0.5: {} + convert-hrtime@5.0.0: {} core-js-compat@3.50.0: @@ -2312,8 +2364,6 @@ snapshots: fast-levenshtein@2.0.6: {} - fast-uri@3.1.0: {} - fdir@6.5.0(picomatch@4.0.7): optionalDependencies: picomatch: 4.0.7 @@ -2360,6 +2410,10 @@ snapshots: dependencies: reserved-identifiers: 1.2.0 + idn-hostname@15.1.19: + dependencies: + punycode: 2.3.1 + ignore@5.3.2: {} imurmurhash@0.1.4: {} @@ -2387,10 +2441,12 @@ snapshots: json-schema-traverse@0.4.1: {} - json-schema-traverse@1.0.0: {} - json-stable-stringify-without-jsonify@1.0.1: {} + json-stringify-deterministic@1.0.16: {} + + just-curry-it@5.3.0: {} + keyv@5.6.0: dependencies: '@keyv/serialize': 1.1.1 @@ -2562,8 +2618,6 @@ snapshots: dependencies: jsesc: 3.1.0 - require-from-string@2.0.2: {} - reserved-identifiers@1.2.0: {} rolldown@1.2.9: @@ -2667,6 +2721,8 @@ snapshots: dependencies: punycode: 2.3.1 + uuid@14.0.2: {} + vite@8.3.0(@types/node@26.6.2)(esbuild@0.27.3)(yaml@2.8.2): dependencies: lightningcss: 1.33.0