From c8b11ff17c858fedb95726437d2ab271b95ee473 Mon Sep 17 00:00:00 2001 From: Maxim Date: Thu, 8 Oct 2026 23:06:49 +0700 Subject: [PATCH 1/3] feat!: support Payload 4 --- oxfmt.config.ts | 15 +- oxlint.config.ts | 17 +- package.json | 102 +- pnpm-lock.yaml | 3350 +++++++++++------ pnpm-workspace.yaml | 20 +- rslib.config.ts | 17 - src/bin/generateSpec.ts | 80 - src/cli/generateSpec.ts | 48 + src/constants.ts | 1 - src/index.ts | 81 +- src/options.ts | 45 - src/{ => server}/endpoints/interactiveAuth.ts | 2 +- src/{ => server}/endpoints/spec.ts | 24 +- src/server/options/resolveOptions.ts | 69 + src/{ => server}/spec/build.ts | 20 +- src/{ => server}/spec/buildDocument.ts | 153 +- src/{ => server}/spec/components.ts | 103 +- src/{ => server}/spec/downconvert.ts | 2 + src/{ => server}/spec/entitySchemas.ts | 125 +- src/{ => server}/spec/fields.ts | 0 src/{ => server}/spec/filters.ts | 39 +- src/server/spec/hierarchy.ts | 50 + src/{ => server}/spec/names.ts | 25 +- src/{ => server}/spec/params.ts | 21 +- src/{ => server}/spec/paths/auth.ts | 68 +- src/{ => server}/spec/paths/collections.ts | 88 +- src/{ => server}/spec/paths/custom.ts | 9 +- src/{ => server}/spec/paths/globals.ts | 52 +- src/{ => server}/spec/paths/jobs.ts | 20 +- src/server/spec/paths/system.ts | 113 + src/{ => server}/spec/paths/versions.ts | 18 +- src/server/spec/security.ts | 155 + src/{ => server}/spec/tags.ts | 15 +- src/server/telemetry.ts | 70 + src/server/ui/createUiPlugin.ts | 53 + src/{ => server}/ui/html.ts | 3 + src/server/ui/scalar.ts | 22 + src/server/ui/swagger.ts | 23 + src/shared/constants.ts | 3 + src/shared/translations/index.ts | 103 + src/{ => shared}/translations/locales/ar.ts | 27 +- src/{ => shared}/translations/locales/az.ts | 30 +- src/{ => shared}/translations/locales/bg.ts | 29 +- src/{ => shared}/translations/locales/bnBd.ts | 28 +- src/{ => shared}/translations/locales/bnIn.ts | 27 +- src/{ => shared}/translations/locales/ca.ts | 30 +- src/{ => shared}/translations/locales/cs.ts | 28 +- src/{ => shared}/translations/locales/da.ts | 30 +- src/{ => shared}/translations/locales/de.ts | 30 +- src/{ => shared}/translations/locales/en.ts | 30 +- src/{ => shared}/translations/locales/es.ts | 30 +- src/{ => shared}/translations/locales/et.ts | 30 +- src/{ => shared}/translations/locales/fa.ts | 29 +- src/{ => shared}/translations/locales/fr.ts | 30 +- src/{ => shared}/translations/locales/he.ts | 27 +- src/{ => shared}/translations/locales/hr.ts | 30 +- src/{ => shared}/translations/locales/hu.ts | 30 +- src/{ => shared}/translations/locales/hy.ts | 30 +- src/{ => shared}/translations/locales/id.ts | 29 +- src/{ => shared}/translations/locales/is.ts | 30 +- src/{ => shared}/translations/locales/it.ts | 30 +- src/{ => shared}/translations/locales/ja.ts | 29 +- src/{ => shared}/translations/locales/ko.ts | 28 +- src/{ => shared}/translations/locales/lt.ts | 29 +- src/{ => shared}/translations/locales/lv.ts | 29 +- src/{ => shared}/translations/locales/my.ts | 27 +- src/{ => shared}/translations/locales/nb.ts | 30 +- src/{ => shared}/translations/locales/nl.ts | 30 +- src/{ => shared}/translations/locales/pl.ts | 30 +- src/{ => shared}/translations/locales/pt.ts | 30 +- src/{ => shared}/translations/locales/ro.ts | 30 +- src/{ => shared}/translations/locales/rs.ts | 30 +- .../translations/locales/rsLatin.ts | 30 +- src/{ => shared}/translations/locales/ru.ts | 29 +- src/{ => shared}/translations/locales/sk.ts | 29 +- src/{ => shared}/translations/locales/sl.ts | 29 +- src/{ => shared}/translations/locales/sv.ts | 30 +- src/{ => shared}/translations/locales/ta.ts | 29 +- src/{ => shared}/translations/locales/th.ts | 26 +- src/{ => shared}/translations/locales/tr.ts | 29 +- src/{ => shared}/translations/locales/uk.ts | 29 +- src/{ => shared}/translations/locales/vi.ts | 29 +- src/{ => shared}/translations/locales/zh.ts | 26 +- src/{ => shared}/translations/locales/zhTw.ts | 26 +- src/{ => shared}/translations/types.ts | 20 +- src/shared/types/index.ts | 432 +++ src/{ => shared}/utils.ts | 0 src/spec/security.ts | 124 - src/translations/index.ts | 103 - src/types.ts | 212 -- src/ui/createUiPlugin.ts | 44 - src/ui/scalar.ts | 17 - src/ui/swagger.ts | 17 - tests/app/(payload)/admin/importMap.js | 14 +- tests/collections/Folders.ts | 8 + tests/collections/Media.ts | 4 +- tests/collections/Posts.ts | 7 +- tests/collections/Tags.ts | 1 + tests/collections/Users.ts | 1 + tests/collections/index.ts | 1 + tests/{helpers.ts => helpers/context.ts} | 15 +- .../spec.int.spec.ts} | 256 +- tests/next-env.d.ts | 1 + tests/next.config.mjs | 2 +- tests/payload.config.ts | 34 +- tests/tsconfig.json | 3 +- tests/unit/hierarchy.spec.ts | 61 + tests/{unit.spec.ts => unit/index.spec.ts} | 1123 ++++-- tests/unit/telemetry.spec.ts | 149 + tests/vitest.config.ts | 25 + tsconfig.build.json | 11 + tsconfig.json | 21 +- tsdown.config.ts | 4 + vitest.config.ts | 11 - 114 files changed, 6664 insertions(+), 2558 deletions(-) delete mode 100644 rslib.config.ts delete mode 100644 src/bin/generateSpec.ts create mode 100644 src/cli/generateSpec.ts delete mode 100644 src/constants.ts delete mode 100644 src/options.ts rename src/{ => server}/endpoints/interactiveAuth.ts (87%) rename src/{ => server}/endpoints/spec.ts (51%) create mode 100644 src/server/options/resolveOptions.ts rename src/{ => server}/spec/build.ts (72%) rename src/{ => server}/spec/buildDocument.ts (67%) rename src/{ => server}/spec/components.ts (67%) rename src/{ => server}/spec/downconvert.ts (96%) rename src/{ => server}/spec/entitySchemas.ts (72%) rename src/{ => server}/spec/fields.ts (100%) rename src/{ => server}/spec/filters.ts (75%) create mode 100644 src/server/spec/hierarchy.ts rename src/{ => server}/spec/names.ts (67%) rename src/{ => server}/spec/params.ts (94%) rename src/{ => server}/spec/paths/auth.ts (78%) rename src/{ => server}/spec/paths/collections.ts (71%) rename src/{ => server}/spec/paths/custom.ts (89%) rename src/{ => server}/spec/paths/globals.ts (53%) rename src/{ => server}/spec/paths/jobs.ts (86%) create mode 100644 src/server/spec/paths/system.ts rename src/{ => server}/spec/paths/versions.ts (90%) create mode 100644 src/server/spec/security.ts rename src/{ => server}/spec/tags.ts (79%) create mode 100644 src/server/telemetry.ts create mode 100644 src/server/ui/createUiPlugin.ts rename src/{ => server}/ui/html.ts (85%) create mode 100644 src/server/ui/scalar.ts create mode 100644 src/server/ui/swagger.ts create mode 100644 src/shared/constants.ts create mode 100644 src/shared/translations/index.ts rename src/{ => shared}/translations/locales/ar.ts (75%) rename src/{ => shared}/translations/locales/az.ts (75%) rename src/{ => shared}/translations/locales/bg.ts (74%) rename src/{ => shared}/translations/locales/bnBd.ts (75%) rename src/{ => shared}/translations/locales/bnIn.ts (75%) rename src/{ => shared}/translations/locales/ca.ts (75%) rename src/{ => shared}/translations/locales/cs.ts (75%) rename src/{ => shared}/translations/locales/da.ts (75%) rename src/{ => shared}/translations/locales/de.ts (75%) rename src/{ => shared}/translations/locales/en.ts (74%) rename src/{ => shared}/translations/locales/es.ts (75%) rename src/{ => shared}/translations/locales/et.ts (75%) rename src/{ => shared}/translations/locales/fa.ts (75%) rename src/{ => shared}/translations/locales/fr.ts (75%) rename src/{ => shared}/translations/locales/he.ts (75%) rename src/{ => shared}/translations/locales/hr.ts (75%) rename src/{ => shared}/translations/locales/hu.ts (75%) rename src/{ => shared}/translations/locales/hy.ts (74%) rename src/{ => shared}/translations/locales/id.ts (75%) rename src/{ => shared}/translations/locales/is.ts (74%) rename src/{ => shared}/translations/locales/it.ts (75%) rename src/{ => shared}/translations/locales/ja.ts (75%) rename src/{ => shared}/translations/locales/ko.ts (75%) rename src/{ => shared}/translations/locales/lt.ts (76%) rename src/{ => shared}/translations/locales/lv.ts (76%) rename src/{ => shared}/translations/locales/my.ts (75%) rename src/{ => shared}/translations/locales/nb.ts (75%) rename src/{ => shared}/translations/locales/nl.ts (74%) rename src/{ => shared}/translations/locales/pl.ts (75%) rename src/{ => shared}/translations/locales/pt.ts (75%) rename src/{ => shared}/translations/locales/ro.ts (74%) rename src/{ => shared}/translations/locales/rs.ts (75%) rename src/{ => shared}/translations/locales/rsLatin.ts (75%) rename src/{ => shared}/translations/locales/ru.ts (74%) rename src/{ => shared}/translations/locales/sk.ts (76%) rename src/{ => shared}/translations/locales/sl.ts (75%) rename src/{ => shared}/translations/locales/sv.ts (74%) rename src/{ => shared}/translations/locales/ta.ts (73%) rename src/{ => shared}/translations/locales/th.ts (75%) rename src/{ => shared}/translations/locales/tr.ts (75%) rename src/{ => shared}/translations/locales/uk.ts (74%) rename src/{ => shared}/translations/locales/vi.ts (75%) rename src/{ => shared}/translations/locales/zh.ts (75%) rename src/{ => shared}/translations/locales/zhTw.ts (75%) rename src/{ => shared}/translations/types.ts (81%) create mode 100644 src/shared/types/index.ts rename src/{ => shared}/utils.ts (100%) delete mode 100644 src/spec/security.ts delete mode 100644 src/translations/index.ts delete mode 100644 src/types.ts delete mode 100644 src/ui/createUiPlugin.ts delete mode 100644 src/ui/scalar.ts delete mode 100644 src/ui/swagger.ts create mode 100644 tests/collections/Folders.ts rename tests/{helpers.ts => helpers/context.ts} (71%) rename tests/{int.spec.ts => integration/spec.int.spec.ts} (75%) create mode 100644 tests/unit/hierarchy.spec.ts rename tests/{unit.spec.ts => unit/index.spec.ts} (50%) create mode 100644 tests/unit/telemetry.spec.ts create mode 100644 tests/vitest.config.ts create mode 100644 tsconfig.build.json create mode 100644 tsdown.config.ts delete mode 100644 vitest.config.ts diff --git a/oxfmt.config.ts b/oxfmt.config.ts index 316fe48..38880a9 100644 --- a/oxfmt.config.ts +++ b/oxfmt.config.ts @@ -1,16 +1,7 @@ +import { oxfmtBase } from '@seshuk/payload-plugin-tooling/oxfmt' import { defineConfig } from 'oxfmt' export default defineConfig({ - printWidth: 120, - semi: false, - singleQuote: true, - trailingComma: 'all', - ignorePatterns: [ - '**/node_modules/**', - '**/dist/**', - '**/.next/**', - 'pnpm-lock.yaml', - '**/next-env.d.ts', - 'tests/app/(payload)/**', - ], + ...oxfmtBase, + ignorePatterns: [...oxfmtBase.ignorePatterns, 'tests/.next', '**/next-env.d.ts', 'tests/app/(payload)/**'], }) diff --git a/oxlint.config.ts b/oxlint.config.ts index 1652dd4..59a5058 100644 --- a/oxlint.config.ts +++ b/oxlint.config.ts @@ -1,27 +1,20 @@ +import { oxlintBase } from '@seshuk/payload-plugin-tooling/oxlint' import { defineConfig } from 'oxlint' export default defineConfig({ - plugins: ['typescript', 'unicorn', 'import', 'oxc'], - categories: { - correctness: 'error', - suspicious: 'warn', - }, - env: { - node: true, - es2024: true, - }, - ignorePatterns: ['**/node_modules/**', '**/dist/**', '**/.next/**', '**/next-env.d.ts', 'tests/app/(payload)/**'], + ...oxlintBase, + ignorePatterns: [...oxlintBase.ignorePatterns, 'tests/.next', '**/next-env.d.ts', 'tests/app/(payload)/**'], rules: { + ...oxlintBase.rules, 'import/no-default-export': 'off', - 'import/no-unassigned-import': 'off', 'unicorn/consistent-function-scoping': 'off', }, overrides: [ + ...oxlintBase.overrides, { files: ['tests/**/*.{ts,tsx,mjs}'], rules: { 'no-await-in-loop': 'off', - 'no-underscore-dangle': ['warn', { allow: ['_payload'] }], }, }, ], diff --git a/package.json b/package.json index f657a7e..b788cf2 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@seshuk/payload-plugin-openapi", - "version": "0.2.2", - "description": "OpenAPI 3.0/3.1/3.2 spec generator for Payload CMS, with Scalar/Swagger UI.", + "version": "1.0.0-beta.1", + "description": "OpenAPI 3.0/3.1/3.2 spec generator for Payload, with Scalar/Swagger UI.", "keywords": [ "api-docs", "cms", @@ -23,77 +23,93 @@ "type": "git", "url": "git+https://github.com/maximseshuk/payload-plugin-openapi.git" }, - "funding": "https://ko-fi.com/V7V61UCT39", + "funding": { + "type": "ko-fi", + "url": "https://ko-fi.com/seshuk" + }, "files": [ "dist" ], "type": "module", - "main": "./src/index.ts", - "types": "./src/index.ts", + "main": "./dist/index.js", + "module": "./dist/index.js", + "types": "./dist/index.d.ts", "exports": { - ".": { - "import": "./src/index.ts", - "types": "./src/index.ts", - "default": "./src/index.ts" - }, - "./*": "./*" + ".": "./dist/index.js" }, "publishConfig": { "main": "./dist/index.js", "types": "./dist/index.d.ts", "exports": { ".": { - "import": "./dist/index.js", "types": "./dist/index.d.ts", + "import": "./dist/index.js", "default": "./dist/index.js" } - } + }, + "access": "public", + "registry": "https://registry.npmjs.org/" }, "scripts": { - "build": "rslib build", - "build:watch": "rslib build --watch", - "dev": "cross-env PAYLOAD_CONFIG_PATH=./tests/payload.config.ts next dev tests --turbo", - "dev:safe": "rm -rf .next && cross-env PAYLOAD_CONFIG_PATH=./tests/payload.config.ts next dev tests --turbo --port 3001", + "build": "tsdown", + "build:watch": "tsdown --watch", + "dev": "cross-env PAYLOAD_CONFIG_PATH=./tests/payload.config.ts DATABASE_URL=file:./tests/payload.db next dev tests", "dev:generate-importmap": "cd tests && cross-env PAYLOAD_CONFIG_PATH=./payload.config.ts payload generate:importmap", + "dev:safe": "rm -rf tests/.next && cross-env PAYLOAD_CONFIG_PATH=./tests/payload.config.ts DATABASE_URL=file:./tests/payload.db next dev tests --port 3001", "docs:check-links": "pnpm --dir docs check-links", "docs:dev": "pnpm --dir docs dev", "docs:validate": "pnpm --dir docs validate", - "lint": "oxlint src tests", - "lint:fix": "oxlint src tests --fix", - "format": "oxfmt .", - "format:check": "oxfmt --check .", - "test": "vitest run", - "test:watch": "vitest", - "typecheck": "tsc --noEmit", - "release:patch": "pnpm version patch -m \"chore(release): v%s\"", - "release:minor": "pnpm version minor -m \"chore(release): v%s\"", - "release:major": "pnpm version major -m \"chore(release): v%s\"" + "format": "oxfmt", + "format:check": "oxfmt --check", + "lint": "oxlint", + "lint:fix": "oxlint --fix", + "test": "vitest run -c tests/vitest.config.ts", + "test:coverage": "vitest run -c tests/vitest.config.ts --coverage", + "test:int": "vitest run -c tests/vitest.config.ts --project int", + "test:unit": "vitest run -c tests/vitest.config.ts --project unit", + "test:watch": "vitest -c tests/vitest.config.ts", + "typecheck": "tsc --noEmit" }, "dependencies": { "@scalar/openapi-types": "^0.9.1" }, "devDependencies": { - "@payloadcms/db-sqlite": "^3.85.1", - "@payloadcms/next": "^3.85.1", - "@payloadcms/translations": "^3.85.1", - "@rslib/core": "^0.22.1", + "@payloadcms/db-sqlite": "4.0.0-canary.38", + "@payloadcms/next": "4.0.0-canary.38", + "@payloadcms/translations": "4.0.0-canary.38", + "@payloadcms/ui": "4.0.0-canary.38", "@seriousme/openapi-schema-validator": "^2.9.0", + "@seshuk/payload-plugin-tooling": "0.2.3", + "@types/node": "catalog:", + "@vitest/coverage-v8": "^5.0.3", "cross-env": "^10.1.0", - "next": "^16.2.9", - "oxfmt": "^0.54.0", - "oxlint": "^1.69.0", - "payload": "^3.85.1", - "react": "^19.2.7", - "react-dom": "^19.2.7", - "typescript": "^6.0.3", - "vite-tsconfig-paths": "7.0.0-alpha.1", - "vitest": "^4.1.8" + "next": "16.4.0", + "oxfmt": "^0.71.0", + "oxlint": "^1.86.0", + "payload": "4.0.0-canary.38", + "react": "catalog:", + "react-dom": "catalog:", + "tsdown": "^0.23.0", + "typescript": "catalog:", + "vitest": "^5.0.3" }, "peerDependencies": { - "@payloadcms/translations": "^3.53.0", - "payload": "^3.53.0" + "@payloadcms/translations": "4.0.0-canary.38", + "payload": "4.0.0-canary.38" + }, + "devEngines": { + "packageManager": { + "name": "pnpm", + "version": "^12.0.0", + "onFail": "warn" + }, + "runtime": { + "name": "node", + "version": ">=24.15.0", + "onFail": "warn" + } }, "engines": { - "node": ">=20.9.0" + "node": ">=24.15.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4ef2818..502df97 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,9 +1,182 @@ +--- +lockfileVersion: '9.0' + +importers: + + .: + configDependencies: {} + packageManagerDependencies: + pnpm: + specifier: ^12.0.0 + version: 12.8.1 + +packages: + + '@pnpm/exe.android-arm64@12.8.1': + resolution: {integrity: sha512-siDxcidfz5eM6L2tJfik+i2bbUEw8UC7MZ3Yl2HYdRmqbdhHGAOQACtOOu6QVW4Jul4/w5wVKm+wM8ACtgh1Pg==} + cpu: [arm64] + os: [android] + + '@pnpm/exe.android-x64@12.8.1': + resolution: {integrity: sha512-0F/uqPRc3aN4vLHsa0f+6fTtmsigFQ9/yxEU43fionF//pNHEd5e97b3w/nKIgqYMCUQCJphmBRMA9Q8/wlo9Q==} + cpu: [x64] + os: [android] + + '@pnpm/exe.darwin-arm64@12.8.1': + resolution: {integrity: sha512-/rwavvMQJsl2RIxOHBnDF+4Gk+fhQFAY4M3PQZoKskJOEnaJcygG9p1xby6wcQcbS4d9dIubv09CQhufTgfmpw==} + cpu: [arm64] + os: [darwin] + + '@pnpm/exe.darwin-x64@12.8.1': + resolution: {integrity: sha512-htYt2gN7zqJKLhC99Ft6EUiO7myjZAZXfmGCTJUL1v+o7vE9hZdE1+fAkk4Oj1ZHNRti6b2woOxEkRkAusi8OQ==} + cpu: [x64] + os: [darwin] + + '@pnpm/exe.freebsd-x64@12.8.1': + resolution: {integrity: sha512-PB5BULNgytxvExoOXUUeseS/DcpPENs+cg3iA6xHHeaLT4ctEGKSGXEbuj7USeqvo8JBpAx039hivnbzGqDwnA==} + cpu: [x64] + os: [freebsd] + + '@pnpm/exe.linux-arm64-musl@12.8.1': + resolution: {integrity: sha512-kKeyC/ArjrgdciQRBrOL0j9HtOI4gDRgoyP+eJ1CQKyadpKlf/DhDDyspXe07R3B9KagTVLUF2nlt6Dr5quFIg==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@pnpm/exe.linux-arm64@12.8.1': + resolution: {integrity: sha512-q4s9a9X2O3N9aeUFooW24orNrxvu20+jyVUFR5RanPCqOKKPBrgs2iywMkBBx1aXkkzdWT54/mfz8Be8sJlWEw==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-ppc64@12.8.1': + resolution: {integrity: sha512-F7XSjKJthwCh/L6abZiMpAfo2Cm61SZIT3e4dfgiIjESndlWaXCnfLKngSgZ/SEMsdmHT6dud3+kWIB3tw0/xQ==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-riscv64@12.8.1': + resolution: {integrity: sha512-z1ecpIK/ZY08Unk69KWWE9867oaKUytGUjrHnBoGFAAhsK+q+0cfvBH+f5fWobJ7FX97leMlLO+gkJolddM45Q==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-s390x@12.8.1': + resolution: {integrity: sha512-KU6tnrBfu4uKXTq6N5qJwSm8FSrs9Wc8b5AUEsHK9bRXwNdsZ1/lRxG1++X29SKRW9OSNuDSjxASv5mAvGo1gA==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-x64-musl@12.8.1': + resolution: {integrity: sha512-eUE8BWJkDr2tJb/yrk0yVnszlmNqFarVxUlqiZ9BAkct28Nq/5g2H0tvMDmPA7fAQW3STNj+omJPPRVT+4s5sQ==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@pnpm/exe.linux-x64@12.8.1': + resolution: {integrity: sha512-8bDZ0lZlCdi2rvnFul7EYgcC7zKeWSe76y8JV2oXobaS8p9i9d6PSf6LgLtTM0fI7R9Oh4eLCbveXyNDMmvZ+g==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.win32-arm64@12.8.1': + resolution: {integrity: sha512-MsT0dlrRxnCRCHCaosNbhWpEiAFnFuMEu3KuDJDU41BXgTwiINxU8goloUSUDejWZUqKaUqNC/qUTihl9nFtqg==} + cpu: [arm64] + os: [win32] + + '@pnpm/exe.win32-x64@12.8.1': + resolution: {integrity: sha512-SWtPe0PsbTTk//gJND10tMdOfCYjrkq5+qV49hzVhY7xz2iJ339Rc+xwASlcvbwTfrvH39YNqpKYibD+CRGLwA==} + cpu: [x64] + os: [win32] + + pnpm@12.8.1: + resolution: {integrity: sha512-9kupB1B/XOr+BsjTjmBS0BeURFgOwSed3Vv8EctIqoomRLZlmOB+dh2oSHKp/FfV+QK4f6SdAkGY1VhhKqu+RQ==} + engines: {node: '>=18.*'} + hasBin: true + +snapshots: + + '@pnpm/exe.android-arm64@12.8.1': + optional: true + + '@pnpm/exe.android-x64@12.8.1': + optional: true + + '@pnpm/exe.darwin-arm64@12.8.1': + optional: true + + '@pnpm/exe.darwin-x64@12.8.1': + optional: true + + '@pnpm/exe.freebsd-x64@12.8.1': + optional: true + + '@pnpm/exe.linux-arm64-musl@12.8.1': + optional: true + + '@pnpm/exe.linux-arm64@12.8.1': + optional: true + + '@pnpm/exe.linux-ppc64@12.8.1': + optional: true + + '@pnpm/exe.linux-riscv64@12.8.1': + optional: true + + '@pnpm/exe.linux-s390x@12.8.1': + optional: true + + '@pnpm/exe.linux-x64-musl@12.8.1': + optional: true + + '@pnpm/exe.linux-x64@12.8.1': + optional: true + + '@pnpm/exe.win32-arm64@12.8.1': + optional: true + + '@pnpm/exe.win32-x64@12.8.1': + optional: true + + pnpm@12.8.1: + optionalDependencies: + '@pnpm/exe.android-arm64': 12.8.1 + '@pnpm/exe.android-x64': 12.8.1 + '@pnpm/exe.darwin-arm64': 12.8.1 + '@pnpm/exe.darwin-x64': 12.8.1 + '@pnpm/exe.freebsd-x64': 12.8.1 + '@pnpm/exe.linux-arm64': 12.8.1 + '@pnpm/exe.linux-arm64-musl': 12.8.1 + '@pnpm/exe.linux-ppc64': 12.8.1 + '@pnpm/exe.linux-riscv64': 12.8.1 + '@pnpm/exe.linux-s390x': 12.8.1 + '@pnpm/exe.linux-x64': 12.8.1 + '@pnpm/exe.linux-x64-musl': 12.8.1 + '@pnpm/exe.win32-arm64': 12.8.1 + '@pnpm/exe.win32-x64': 12.8.1 + +--- lockfileVersion: '9.0' settings: autoInstallPeers: true excludeLinksFromLockfile: false +catalogs: + default: + '@types/node': + specifier: ^26.1.2 + version: 26.6.4 + react: + specifier: 19.3.0 + version: 19.3.0 + react-dom: + specifier: 19.3.0 + version: 19.3.0 + typescript: + specifier: 7.0.2 + version: 7.0.2 + importers: .: @@ -13,56 +186,65 @@ importers: version: 0.9.1 devDependencies: '@payloadcms/db-sqlite': - specifier: ^3.85.1 - version: 3.85.1(payload@3.85.1(graphql@16.14.0)(typescript@6.0.3)) + specifier: 4.0.0-canary.38 + version: 4.0.0-canary.38(payload@4.0.0-canary.38(graphql@16.14.0)(typescript@7.0.2)) '@payloadcms/next': - specifier: ^3.85.1 - version: 3.85.1(@types/react@19.2.15)(graphql@16.14.0)(monaco-editor@0.55.1)(next@16.2.9(babel-plugin-macros@3.1.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.77.4))(payload@3.85.1(graphql@16.14.0)(typescript@6.0.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3) + specifier: 4.0.0-canary.38 + version: 4.0.0-canary.38(@types/react@19.2.15)(graphql@16.14.0)(monaco-editor@0.55.1)(next@16.4.0(@types/node@26.6.4)(babel-plugin-macros@3.1.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(sass@1.77.4))(payload@4.0.0-canary.38(graphql@16.14.0)(typescript@7.0.2))(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(typescript@7.0.2) '@payloadcms/translations': - specifier: ^3.85.1 - version: 3.85.1 - '@rslib/core': - specifier: ^0.22.1 - version: 0.22.1(core-js@3.42.0)(typescript@6.0.3) + specifier: 4.0.0-canary.38 + version: 4.0.0-canary.38 + '@payloadcms/ui': + specifier: 4.0.0-canary.38 + version: 4.0.0-canary.38(@types/react@19.2.15)(monaco-editor@0.55.1)(payload@4.0.0-canary.38(graphql@16.14.0)(typescript@7.0.2))(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(typescript@7.0.2) '@seriousme/openapi-schema-validator': specifier: ^2.9.0 version: 2.9.0 + '@seshuk/payload-plugin-tooling': + specifier: 0.2.3 + version: 0.2.3(@payloadcms/db-sqlite@4.0.0-canary.38(payload@4.0.0-canary.38(graphql@16.14.0)(typescript@7.0.2)))(oxfmt@0.71.0)(oxlint@1.86.0)(payload@4.0.0-canary.38(graphql@16.14.0)(typescript@7.0.2))(tsdown@0.23.0(oxc-resolver@11.20.0)(tsx@4.22.4)(typescript@7.0.2))(vitest@5.0.3) + '@types/node': + specifier: 'catalog:' + version: 26.6.4 + '@vitest/coverage-v8': + specifier: ^5.0.3 + version: 5.0.3(vitest@5.0.3) cross-env: specifier: ^10.1.0 version: 10.1.0 next: - specifier: ^16.2.9 - version: 16.2.9(babel-plugin-macros@3.1.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.77.4) + specifier: 16.4.0 + version: 16.4.0(@types/node@26.6.4)(babel-plugin-macros@3.1.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(sass@1.77.4) oxfmt: - specifier: ^0.54.0 - version: 0.54.0 + specifier: ^0.71.0 + version: 0.71.0 oxlint: - specifier: ^1.69.0 - version: 1.69.0 + specifier: ^1.86.0 + version: 1.86.0 payload: - specifier: ^3.85.1 - version: 3.85.1(graphql@16.14.0)(typescript@6.0.3) + specifier: 4.0.0-canary.38 + version: 4.0.0-canary.38(graphql@16.14.0)(typescript@7.0.2) react: - specifier: ^19.2.7 - version: 19.2.7 + specifier: 'catalog:' + version: 19.3.0 react-dom: - specifier: ^19.2.7 - version: 19.2.7(react@19.2.7) + specifier: 'catalog:' + version: 19.3.0(react@19.3.0) + tsdown: + specifier: ^0.23.0 + version: 0.23.0(oxc-resolver@11.20.0)(tsx@4.22.4)(typescript@7.0.2) typescript: - specifier: ^6.0.3 - version: 6.0.3 - vite-tsconfig-paths: - specifier: 7.0.0-alpha.1 - version: 7.0.0-alpha.1(typescript@6.0.3)(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(sass@1.77.4)(tsx@4.22.4)(yaml@2.9.0)) + specifier: 'catalog:' + version: 7.0.2 vitest: - specifier: ^4.1.8 - version: 4.1.8(@types/node@25.9.1)(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(sass@1.77.4)(tsx@4.22.4)(yaml@2.9.0)) + specifier: ^5.0.3 + version: 5.0.3(@types/node@26.6.4)(@vitest/coverage-v8@5.0.3)(vite@7.3.3(@types/node@26.6.4)(jiti@2.7.0)(sass@1.77.4)(tsx@4.22.4)(yaml@2.9.0)) docs: devDependencies: mint: specifier: ^4.2.810 - version: 4.2.810(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.2.7(react@19.2.3))(react@19.2.3))(@types/node@25.9.1)(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.2.7(react@19.2.3))(typescript@6.0.3) + version: 4.2.810(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.3.0(react@19.2.3))(react@19.2.3))(@types/node@26.6.4)(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.3.0(react@19.2.3))(typescript@7.0.2) packages: @@ -84,68 +266,6 @@ packages: '@ark/util@0.56.2': resolution: {integrity: sha512-9kU2sUE38FZEGG7l3hamYMBieLYEJh2L1mrYD2eXpT+78EnQSV1bhjxJhnxGBMSTbtwpBSDNSK+K60WvaI/DTQ==} - '@ast-grep/napi-darwin-arm64@0.37.0': - resolution: {integrity: sha512-QAiIiaAbLvMEg/yBbyKn+p1gX2/FuaC0SMf7D7capm/oG4xGMzdeaQIcSosF4TCxxV+hIH4Bz9e4/u7w6Bnk3Q==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [darwin] - - '@ast-grep/napi-darwin-x64@0.37.0': - resolution: {integrity: sha512-zvcvdgekd4ySV3zUbUp8HF5nk5zqwiMXTuVzTUdl/w08O7JjM6XPOIVT+d2o/MqwM9rsXdzdergY5oY2RdhSPA==} - engines: {node: '>= 10'} - cpu: [x64] - os: [darwin] - - '@ast-grep/napi-linux-arm64-gnu@0.37.0': - resolution: {integrity: sha512-L7Sj0lXy8X+BqSMgr1LB8cCoWk0rericdeu+dC8/c8zpsav5Oo2IQKY1PmiZ7H8IHoFBbURLf8iklY9wsD+cyA==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@ast-grep/napi-linux-arm64-musl@0.37.0': - resolution: {integrity: sha512-LF9sAvYy6es/OdyJDO3RwkX3I82Vkfsng1sqUBcoWC1jVb1wX5YVzHtpQox9JrEhGl+bNp7FYxB4Qba9OdA5GA==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@ast-grep/napi-linux-x64-gnu@0.37.0': - resolution: {integrity: sha512-TViz5/klqre6aSmJzswEIjApnGjJzstG/SE8VDWsrftMBMYt2PTu3MeluZVwzSqDao8doT/P+6U11dU05UOgxw==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@ast-grep/napi-linux-x64-musl@0.37.0': - resolution: {integrity: sha512-/BcCH33S9E3ovOAEoxYngUNXgb+JLg991sdyiNP2bSoYd30a9RHrG7CYwW6fMgua3ijQ474eV6cq9yZO1bCpXg==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - libc: [musl] - - '@ast-grep/napi-win32-arm64-msvc@0.37.0': - resolution: {integrity: sha512-TjQA4cFoIEW2bgjLkaL9yqT4XWuuLa5MCNd0VCDhGRDMNQ9+rhwi9eLOWRaap3xzT7g+nlbcEHL3AkVCD2+b3A==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [win32] - - '@ast-grep/napi-win32-ia32-msvc@0.37.0': - resolution: {integrity: sha512-uNmVka8fJCdYsyOlF9aZqQMLTatEYBynjChVTzUfFMDfmZ0bihs/YTqJVbkSm8TZM7CUX82apvn50z/dX5iWRA==} - engines: {node: '>= 10'} - cpu: [ia32] - os: [win32] - - '@ast-grep/napi-win32-x64-msvc@0.37.0': - resolution: {integrity: sha512-vCiFOT3hSCQuHHfZ933GAwnPzmL0G04JxQEsBRfqONywyT8bSdDc/ECpAfr3S9VcS4JZ9/F6tkePKW/Om2Dq2g==} - engines: {node: '>= 10'} - cpu: [x64] - os: [win32] - - '@ast-grep/napi@0.37.0': - resolution: {integrity: sha512-Hb4o6h1Pf6yRUAX07DR4JVY7dmQw+RVQMW5/m55GoiAT/VRoKCWBtIUPPOnqDVhbx1Cjfil9b6EDrgJsUAujEQ==} - engines: {node: '>= 10'} - '@asyncapi/parser@3.4.0': resolution: {integrity: sha512-Sxn74oHiZSU6+cVeZy62iPZMFMvKp4jupMFHelSICCMw1qELmUHPvuZSr+ZHDmNGgHcEpzJM5HN02kR7T4g+PQ==} @@ -224,6 +344,10 @@ packages: '@types/react': optional: true + '@bcoe/v8-coverage@1.0.2': + resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} + engines: {node: '>=18'} + '@borewit/text-codec@0.2.2': resolution: {integrity: sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==} @@ -270,6 +394,9 @@ packages: '@emnapi/runtime@1.10.0': resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} + '@emnapi/runtime@1.11.3': + resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==} + '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} @@ -925,8 +1052,8 @@ packages: cpu: [x64] os: [win32] - '@faceless-ui/modal@3.0.0': - resolution: {integrity: sha512-o3oEFsot99EQ8RJc1kL3s/nNMHX+y+WMXVzSSmca9L0l2MR6ez2QM1z1yIelJX93jqkLXQ9tW+R9tmsYa+O4Qg==} + '@faceless-ui/modal@3.0.2': + resolution: {integrity: sha512-2BK5ipSTIejnpAH4MsZ8aX9g7HxQrFybPpTAPVq8/RDRXxxShfwOuuhuxOx7KyZ1nWHtMGTWFpB0wkCAtg037w==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -943,9 +1070,6 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - '@floating-ui/core@1.7.5': - resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} - '@floating-ui/core@1.8.0': resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==} @@ -955,12 +1079,6 @@ packages: '@floating-ui/dom@1.8.0': resolution: {integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==} - '@floating-ui/react-dom@2.1.8': - resolution: {integrity: sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==} - peerDependencies: - react: '>=16.8.0' - react-dom: '>=16.8.0' - '@floating-ui/react-dom@2.1.9': resolution: {integrity: sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==} peerDependencies: @@ -973,9 +1091,6 @@ packages: react: '>=17.0.0' react-dom: '>=17.0.0' - '@floating-ui/utils@0.2.11': - resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} - '@floating-ui/utils@0.2.12': resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} @@ -995,6 +1110,12 @@ packages: cpu: [arm64] os: [darwin] + '@img/sharp-darwin-arm64@0.35.5': + resolution: {integrity: sha512-QRUlFQ0WxvdWyqqG/WtI3iupfD5rBzmCHXSdPsY91sAtVtTo7Q4cb6zOccZ3gqEqkr0f1As1ehLqmEpDsRf+lg==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [darwin] + '@img/sharp-darwin-x64@0.33.5': resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -1007,6 +1128,17 @@ packages: cpu: [x64] os: [darwin] + '@img/sharp-darwin-x64@0.35.5': + resolution: {integrity: sha512-+BR255RhDlpygUpOc/Jdt1nT6DQ3XG/ERo5wbcdOf5Q320dKtPCKPLR1LJs9VGXRaMa8l1uUa0tkCNOXiAxZUw==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [darwin] + + '@img/sharp-freebsd-wasm32@0.35.5': + resolution: {integrity: sha512-Y/z91nEZ4uIBX5X3nfTovjU9lHNKFYbL2lpHCLVNmXQK03VIZvXBBt0KxbPGp2SdGSF+2mQU4e+hQaWOt86iAw==} + engines: {node: '>=20.9.0'} + os: [freebsd] + '@img/sharp-libvips-darwin-arm64@1.0.4': resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==} cpu: [arm64] @@ -1017,6 +1149,11 @@ packages: cpu: [arm64] os: [darwin] + '@img/sharp-libvips-darwin-arm64@1.3.4': + resolution: {integrity: sha512-5R89nBYiRdUlSWJxPhO+GVtaXzXSxKnRu/xqMn3KTA3L9EB9Oy/P+Nn2f2vlhPuUdy/Zusb2DarbyTpGCfEDuw==} + cpu: [arm64] + os: [darwin] + '@img/sharp-libvips-darwin-x64@1.0.4': resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==} cpu: [x64] @@ -1027,6 +1164,11 @@ packages: cpu: [x64] os: [darwin] + '@img/sharp-libvips-darwin-x64@1.3.4': + resolution: {integrity: sha512-iR2OKH80yi0U+dUplyh3/xdpFvps6YkCwsXenIJxqxR1v9o+xtKTGbS9H7cps+2Vxjc8B1j96p75NmTGjIhtpQ==} + cpu: [x64] + os: [darwin] + '@img/sharp-libvips-linux-arm64@1.0.4': resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==} cpu: [arm64] @@ -1039,6 +1181,12 @@ packages: os: [linux] libc: [glibc] + '@img/sharp-libvips-linux-arm64@1.3.4': + resolution: {integrity: sha512-Y3dgX/6lE2QhQb+Gxy0WZxfg9MEm/JBjamZpS2IklP7xIQoKN4hzAm7KcMVGtaVDt3neE9OKBC7vAfonA/Lr1A==} + cpu: [arm64] + os: [linux] + libc: [glibc] + '@img/sharp-libvips-linux-arm@1.0.5': resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==} cpu: [arm] @@ -1051,18 +1199,36 @@ packages: os: [linux] libc: [glibc] + '@img/sharp-libvips-linux-arm@1.3.4': + resolution: {integrity: sha512-LmRtTsOHuvM2+wlO2Db37dx5MiZhB0FvSunciw48YjdOkZz9KAiRbm8ujeMOA1INqmei5NapFxYEK1D1ZSidmw==} + cpu: [arm] + os: [linux] + libc: [glibc] + '@img/sharp-libvips-linux-ppc64@1.2.4': resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} cpu: [ppc64] os: [linux] libc: [glibc] + '@img/sharp-libvips-linux-ppc64@1.3.4': + resolution: {integrity: sha512-Le6boB8Tai0Nis+gIxIpKx68UDVVIqdR8Tin5Yf1z2LJJQLDJvCDRqRu+jC2qCoD+eIomonmOwB4smBRxfVpYQ==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + '@img/sharp-libvips-linux-riscv64@1.2.4': resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} cpu: [riscv64] os: [linux] libc: [glibc] + '@img/sharp-libvips-linux-riscv64@1.3.4': + resolution: {integrity: sha512-aHkkIEHPRdQEegJN20MLmGtxYD9R2wQr3Cwpddnu5+YKMt6Uzax7S9h5gpZTo8wyrGuZSlfQ63OevL5mTyOC7Q==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + '@img/sharp-libvips-linux-s390x@1.0.4': resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==} cpu: [s390x] @@ -1075,6 +1241,12 @@ packages: os: [linux] libc: [glibc] + '@img/sharp-libvips-linux-s390x@1.3.4': + resolution: {integrity: sha512-ra/mB6MikESDUO7Yg+Mi95bFBb9GsObURuhnOv3OqknjGe9sZrG8tCe9q0xSIGrtLgvgw0gKnFWcK4blSgQOuQ==} + cpu: [s390x] + os: [linux] + libc: [glibc] + '@img/sharp-libvips-linux-x64@1.0.4': resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==} cpu: [x64] @@ -1087,6 +1259,12 @@ packages: os: [linux] libc: [glibc] + '@img/sharp-libvips-linux-x64@1.3.4': + resolution: {integrity: sha512-GJ//SSXbnwSDes02umB3nDJLFcQzw8a18V8fyhqr6tV515tOEMdImjjxj1AoafMRz56F3PHgftnj1QEKSU1zkw==} + cpu: [x64] + os: [linux] + libc: [glibc] + '@img/sharp-libvips-linuxmusl-arm64@1.0.4': resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==} cpu: [arm64] @@ -1099,6 +1277,12 @@ packages: os: [linux] libc: [musl] + '@img/sharp-libvips-linuxmusl-arm64@1.3.4': + resolution: {integrity: sha512-hvulFwtjUcagsis6BBxHwGFwWoNZjgYmULGVrZcyfNbjA8hKILbRxGg15/7w5HDyXHXUos/j6baAWqnCyQ2DWA==} + cpu: [arm64] + os: [linux] + libc: [musl] + '@img/sharp-libvips-linuxmusl-x64@1.0.4': resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==} cpu: [x64] @@ -1111,6 +1295,12 @@ packages: os: [linux] libc: [musl] + '@img/sharp-libvips-linuxmusl-x64@1.3.4': + resolution: {integrity: sha512-6zXKeE/p39I1AmA3cJG35eyBGNqNddLnUXjhwBnsGjFPWqf5VKkDBEqaEkPDoTEtkxwi2vv8Tcr2mDyP4So7Fg==} + cpu: [x64] + os: [linux] + libc: [musl] + '@img/sharp-linux-arm64@0.33.5': resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -1125,6 +1315,13 @@ packages: os: [linux] libc: [glibc] + '@img/sharp-linux-arm64@0.35.5': + resolution: {integrity: sha512-LYVx5JTsOM2CBzmxreh+nl64/3H6Xb09iSLknqH47z2T2DFFxDeFLP5y4dJwe6H7uGQlHPyEEtIqyo3DYsRwdQ==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + '@img/sharp-linux-arm@0.33.5': resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -1139,6 +1336,13 @@ packages: os: [linux] libc: [glibc] + '@img/sharp-linux-arm@0.35.5': + resolution: {integrity: sha512-LEaXK2WdXVK5ykcw0buWyPMsmLLL2vpHLD6yrNSW+JGEL3BZPA4tpKN6iaMc4AxTTAoaX/sU1rOL51lcIz48ZQ==} + engines: {node: '>=20.9.0'} + cpu: [arm] + os: [linux] + libc: [glibc] + '@img/sharp-linux-ppc64@0.34.5': resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -1146,6 +1350,13 @@ packages: os: [linux] libc: [glibc] + '@img/sharp-linux-ppc64@0.35.5': + resolution: {integrity: sha512-QVxAAq8evVRI9ia2vqgwrmWucn5Dfv+JdWzj75pD8omHLPSP7f8p20O8jxzjCcuCEQEOtYOZUmX1hkiZ0kdevA==} + engines: {node: '>=20.9.0'} + cpu: [ppc64] + os: [linux] + libc: [glibc] + '@img/sharp-linux-riscv64@0.34.5': resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -1153,6 +1364,13 @@ packages: os: [linux] libc: [glibc] + '@img/sharp-linux-riscv64@0.35.5': + resolution: {integrity: sha512-LtdreXguaavKODPIfzJ4kffx7UNt1omwtK0rch4EBbbSTXPnxWmYSayXdLJw0fJzQ97kHt1gL/yh4tvU+nCyRQ==} + engines: {node: '>=20.9.0'} + cpu: [riscv64] + os: [linux] + libc: [glibc] + '@img/sharp-linux-s390x@0.33.5': resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -1167,6 +1385,13 @@ packages: os: [linux] libc: [glibc] + '@img/sharp-linux-s390x@0.35.5': + resolution: {integrity: sha512-UZasTOFiYzotTsGOCu42BfUzP6Tu6Do/947iRm1RsLKvlllxwGcn4RN27LibGWceix4Y+Pmw3jsnTcCQIgWjqA==} + engines: {node: '>=20.9.0'} + cpu: [s390x] + os: [linux] + libc: [glibc] + '@img/sharp-linux-x64@0.33.5': resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -1181,6 +1406,13 @@ packages: os: [linux] libc: [glibc] + '@img/sharp-linux-x64@0.35.5': + resolution: {integrity: sha512-SxFtLTeJInhAA9Q836kux2vZNeOBQEx658qvbboZScr0wIARym3IcGmW7KpVD5sbVg0Ojy+udFQdayYIZyoNog==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + '@img/sharp-linuxmusl-arm64@0.33.5': resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -1195,6 +1427,13 @@ packages: os: [linux] libc: [musl] + '@img/sharp-linuxmusl-arm64@0.35.5': + resolution: {integrity: sha512-9HbMclmI1zlNkFRs3z9/eBtDjfD0sGlrX1z6b1qwmiFY5ElDLh4BC0LPBdVp7z1DXFiKlIcznf+ZlsuZzLxQqg==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + '@img/sharp-linuxmusl-x64@0.33.5': resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -1209,6 +1448,13 @@ packages: os: [linux] libc: [musl] + '@img/sharp-linuxmusl-x64@0.35.5': + resolution: {integrity: sha512-4KOphqB035HrVdqLZfCgMzzERrQkkzOwRhl4OAkRO1YCldbaFjySXMaK534Mo0V+LndnlJk+sbUyLeU0ULyD1A==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + libc: [musl] + '@img/sharp-wasm32@0.33.5': resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -1219,12 +1465,27 @@ packages: engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [wasm32] + '@img/sharp-wasm32@0.35.5': + resolution: {integrity: sha512-Ptsga1su4tQx+LLF1ECS9U6nz5kmrXKo6XVbtR48Ke3ZRxxgaWBu7IDtEe1quo8hiupwm6WFqxVlXaSf7IINGQ==} + engines: {node: '>=20.9.0'} + + '@img/sharp-webcontainers-wasm32@0.35.5': + resolution: {integrity: sha512-hfhF/FmoQyTUkA0bIKFOtw536BQSeBMe6BF6QyWlrPxT754+TFLaZ7sKKTfvvM0yJgKgaYTwnFCIZ/GuDw5SUA==} + engines: {node: '>=20.9.0'} + cpu: [wasm32] + '@img/sharp-win32-arm64@0.34.5': resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [win32] + '@img/sharp-win32-arm64@0.35.5': + resolution: {integrity: sha512-X4t7g+7ZA5DKblCBEXGjUqqemj4vczING/5viFwAL8h4N3qYeyjwdCvRLHi4EdOUI+2Z7UFlp1VM+p/AuEtm6Q==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [win32] + '@img/sharp-win32-ia32@0.33.5': resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -1237,6 +1498,12 @@ packages: cpu: [ia32] os: [win32] + '@img/sharp-win32-ia32@0.35.5': + resolution: {integrity: sha512-5Zm82LoBc43nhwNybZlG7Y1KO//Zhsn306fQl29ZOuStHLGTo3BWL83q3cznX0poxSAMuYL1On/BHBxkBeKr6A==} + engines: {node: ^20.9.0} + cpu: [ia32] + os: [win32] + '@img/sharp-win32-x64@0.33.5': resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -1249,6 +1516,12 @@ packages: cpu: [x64] os: [win32] + '@img/sharp-win32-x64@0.35.5': + resolution: {integrity: sha512-x76eH0vEiHlcMQu8Y8IenntaACtddpT6W0wmXtWrnKcnKI7ME5DdgqhAD6SEWOEl1v2zDvkZDhFA9KnURwpfqg==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [win32] + '@inquirer/ansi@1.0.2': resolution: {integrity: sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==} engines: {node: '>=18'} @@ -1397,6 +1670,9 @@ packages: '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@jridgewell/sourcemap-codec@1.6.0': + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} + '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} @@ -1545,60 +1821,57 @@ packages: '@neon-rs/load@0.0.4': resolution: {integrity: sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw==} - '@next/env@15.5.18': - resolution: {integrity: sha512-hAV85Ckd9QR6RvH04MEKwsfLTksvFpO47j9xwtoIuvuPnlwecpSi+uZTtm8HirVbtlI2Fnz//xpcSTjFdyJk+g==} + '@next/env@16.4.0': + resolution: {integrity: sha512-pyLlT5oafGlX8X7xjgGNXz5dNIDSxREm+qD6i9VLSZiZFopA/XWl8NM/KPmLx/8EG/++c49iid3gNWSQW1+2dQ==} - '@next/env@16.2.9': - resolution: {integrity: sha512-ki5VxxXfzD/9TDe13wyeTKIjQTAwBVpnr8KhRDUr8ltMUq1/NBpWNT5tiPoxiGl+PHM4X2ahSOiPk6iAimIzPg==} - - '@next/swc-darwin-arm64@16.2.9': - resolution: {integrity: sha512-HkfxNYUCmcct0Xsqib5KxqMSHV4AHJq857BNRchyBDs4YS19aHzVfn1kDuBYKqLLQBjXgnkIsjV2Kd4d2wzYhw==} + '@next/swc-darwin-arm64@16.4.0': + resolution: {integrity: sha512-yyZ+9MgViAKLLb5l1gPBGjRwnq3teNLb5Zx6JEw8LzNSTjlsnp+baUHFxTgl7PpjCmKf48VQfyipG1eBflb48Q==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@16.2.9': - resolution: {integrity: sha512-7IAtK4MeybpqRV9GRABWEhJ62mOS+rzWOzOTFie4cSEtm12xsoOMJRcECoZx3FHPzFAqN/IJtHqWAFOLfl152w==} + '@next/swc-darwin-x64@16.4.0': + resolution: {integrity: sha512-RePEEejEj1Auf5s9qEcG0JbcDP3ceNUS/ofNX8AhxzgIjTVlZIHMatlp16YplcSMpsVo/ZMQHJTjbMYJznjGUg==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@16.2.9': - resolution: {integrity: sha512-hBD75iWpUtkL9SmQmcRhmLomn9jgkPzCEkbOcLgHymPEKzv+6ONy13RRiIEz/iEObjkS2Jlb5gYS2XGoS3X4rw==} + '@next/swc-linux-arm64-gnu@16.4.0': + resolution: {integrity: sha512-HhsyCqd+NpmvDDVY/LqkAN24f+QVvXh3ie+PfcwKinWL2Mu826JymQxJXGjAIfmJ4VlHjp4Pe/nZtXtUoEMAqw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [glibc] - '@next/swc-linux-arm64-musl@16.2.9': - resolution: {integrity: sha512-qZTI3pf9SGc/obr8NkQAekBxmp1QK+kVm+VAf3BALLfFAj+1kUhkTxmrWpVos9R/UYIA8AWX2p6cGI5WdwzVUA==} + '@next/swc-linux-arm64-musl@16.4.0': + resolution: {integrity: sha512-sN9Pf+Zh+RpZAOmbR7d8KAA8T61CVhawtbEJ2Bk6QrRzx6Z/6haaF4UiwBzytM96GRRRWZh9/WALIEO3npMamA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [musl] - '@next/swc-linux-x64-gnu@16.2.9': - resolution: {integrity: sha512-xm0HfRNX+UkH4R3c18ynswjj5o5uEj/7iI9p9omdtTSIsRCzQqkGMA+10nzJ4EHnYC3as65IMhbbl5fWRUWHYg==} + '@next/swc-linux-x64-gnu@16.4.0': + resolution: {integrity: sha512-D5M5xLWQVd1CvR+L7BickM1weRfmM41x3L1NWcnKY/oyJJqr1qwaCgmu3dMAhLBOMdZwSuDCRV6DJRRpiUkbDQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [glibc] - '@next/swc-linux-x64-musl@16.2.9': - resolution: {integrity: sha512-QumimHkGEG6vM3PfEDWKyKen03NcqLOkeKB1EfcPe7VxzmEiCa4jNnMyBn/US5zcd/VE1CI+O8Ovb3lfjVHfGw==} + '@next/swc-linux-x64-musl@16.4.0': + resolution: {integrity: sha512-8d5WLS6a9YalqpOgXdlbvonFu+sIgjA+CO6tGzetrpPVcUL56foa7B7664h41rAlbLi5fJwK4vWji3zf4QDy+w==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [musl] - '@next/swc-win32-arm64-msvc@16.2.9': - resolution: {integrity: sha512-hzQpKZvw8rAwI6A2uQh6SacCSvNAXaIkPNsWwzqqfRiIMiXMfH936skDhz1OO6KpvdKkJrgHHtqQOq5PIXOvdQ==} + '@next/swc-win32-arm64-msvc@16.4.0': + resolution: {integrity: sha512-sQuOIiW3snb48UwrBcDMvFA/u03qtw2LeuSIxFIQ9AJuW9UG/4Ddsfo+eV73PnZwBhyncMtsKVh7PrzMol2/fA==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@16.2.9': - resolution: {integrity: sha512-qr2VL3Ce5QrwgO2yh1ujSBawrimjVKX8FGF/cOynmdYKJY0BdHpGVNIRK1tqONB10Vkm25Ub1BD2bkjWs4+96w==} + '@next/swc-win32-x64-msvc@16.4.0': + resolution: {integrity: sha512-6/33lCuxpdHUy27RegyGM/5uTpF7YZd3UR4D0gpkOA/tWY5YR7msbQjqPgCZmSmxrrTLGCXYA4A8vT2H5DPGTQ==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -1621,6 +1894,9 @@ packages: '@openapi-contrib/openapi-schema-to-json-schema@3.2.0': resolution: {integrity: sha512-Gj6C0JwCr8arj0sYuslWXUBSP/KnUlEGnPW4qxlXvAl543oaNQgMgIgkQUA6vs5BCCvwTEiL8m/wdWzfl4UvSw==} + '@oxc-project/types@0.152.0': + resolution: {integrity: sha512-oM/5rLBm2tPkg0iBgkH/FOeR3PCDpY19GTgAZjMFM8h9WI9VW7cLgzp6nwtarYKmovavIQZ+Fe/RKX/8C8O/Rw==} + '@oxc-resolver/binding-android-arm-eabi@11.20.0': resolution: {integrity: sha512-IjfWOXRgJFNdORDl+Uf1aibNgZY2guOD3zmOhx1BGVb/MIiqlFTdmjpQNplSN58lhWehnX4UNqC3QwpUo8pjJg==} cpu: [arm] @@ -1724,284 +2000,283 @@ packages: cpu: [x64] os: [win32] - '@oxfmt/binding-android-arm-eabi@0.54.0': - resolution: {integrity: sha512-NAtpl/SiaeU103e7/OmZw0MvUnsUUopW7hEm/ecegJg7YM0skQaA0IXEZoyTV6NUdiNPupdIUreRqUZTShbn/g==} + '@oxfmt/binding-android-arm-eabi@0.71.0': + resolution: {integrity: sha512-l31EyBfB4egJeFua11aQFEUE9nnDtfpWCadqQovm0WL30t/yUHtdFl/5HyFXvd5xT8Ewh3U4TI9f30GowrQ1fw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxfmt/binding-android-arm64@0.54.0': - resolution: {integrity: sha512-B4VZfBUlKK1rmMChsssNZbkZjE8+FzG3avMjGgMDwbGxXRoXkoeXiAZ+78Oa+eyDPHvDCiUb4zH/vmCOUSafLQ==} + '@oxfmt/binding-android-arm64@0.71.0': + resolution: {integrity: sha512-E8q39SUZzSXQ08oZoFoTkxRADkdBNgL6De4qgZYr2O4JCunwqrZwAyCebxIF3yVHBafHM30WUkhTkDi+jNYZOw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxfmt/binding-darwin-arm64@0.54.0': - resolution: {integrity: sha512-i02vF75b+ePsQP3tHqSxVYI5S6b8X/xqdPu7/mDHXtpgXLTYXi3jJmfHU0j+dnZZDKaYTx/ioCK7QYJmtiJR2g==} + '@oxfmt/binding-darwin-arm64@0.71.0': + resolution: {integrity: sha512-pTteTrN88DicrmJ3DocBmpNDa6Umfh0iveEG8rnAlDFQpclmtDS7hnIvZbIEV9mDO0Ot1U0sjtoJQ1itd/ZMhQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxfmt/binding-darwin-x64@0.54.0': - resolution: {integrity: sha512-8VMFvGvooXj7mswkbrhdVZ2/sgiDaBzWpkkbtO+qGDLV4EfJd67nQadHkQC0ZNbaWA9ajXfqI6i7PZLIeDzxEQ==} + '@oxfmt/binding-darwin-x64@0.71.0': + resolution: {integrity: sha512-G5melRC3IiNEgXlNKJhSExJYQGAQIppHFp2cl/wFrqQcEwmc4k4TkrHaFTR29SDudjoW5AZq5cXPGJDqtlH/Yg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxfmt/binding-freebsd-x64@0.54.0': - resolution: {integrity: sha512-0cRHnp43WN1Jrc5s0BdbdKgR1XirdvHy7TAFi3JEsoEVQVJxTXMbpVd76sxXlgRswNMDhVFSJw+y7Eb8mEavFQ==} + '@oxfmt/binding-freebsd-x64@0.71.0': + resolution: {integrity: sha512-Hb1P5yKb9aiH2VSbm3Ml37mKjzqpunYet9krw/YK7V5JsAUJDQlju3Yk/dEqQvJD/usBvpi01Vbk9ZDNo0a8Cw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxfmt/binding-linux-arm-gnueabihf@0.54.0': - resolution: {integrity: sha512-JyQAk3hK/OEtup7Rw6kZwfdzbKqTVD5jXXb8Xpfay29suwZyfBDMVW/bj4RqEPySYWc6zCp198pOluf8n5uYzg==} + '@oxfmt/binding-linux-arm-gnueabihf@0.71.0': + resolution: {integrity: sha512-r7n4JEO+rkp0kaP3ia+0hv6FzK3PQIHqaF0jxGuhjJ4JHVX9ZVRbgpDVDW+OloyuJsRWpWFDKIHe8gfl1p/4mQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxfmt/binding-linux-arm-musleabihf@0.54.0': - resolution: {integrity: sha512-qnvLatTpM8vtvjOfcckBOzJjk+n6ce/wwpP8OFeUrD5aNLYcKyWAitwj+Rk3PK9jGanbZvKsJnv14JGQ6XqFdw==} + '@oxfmt/binding-linux-arm-musleabihf@0.71.0': + resolution: {integrity: sha512-EEynTF4nakgqDhVCmd3cHQCmTcw0BM8ea6eHigTdtZ3yxdsBNv+dYkt+OpDDZx1n/jzjI7J+Ht7IgkqFWuJUxQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxfmt/binding-linux-arm64-gnu@0.54.0': - resolution: {integrity: sha512-SMkhnCzIYZYDk9vw3W/80eeYKmrMpGF0Giuxt4HruFlCH7jEtnPeb3SdQKMfgYi/dgtaf+hZAb5XWPYnxqCQ3w==} + '@oxfmt/binding-linux-arm64-gnu@0.71.0': + resolution: {integrity: sha512-7VgJIrywCwR/G6YMv+HqsccUt8Z4q4mbM2xR+Ry/6AeMk4pCmZ9aO4Srd3mmrvVu8PsCZGP7oHEHSXWTmyloaQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-arm64-musl@0.54.0': - resolution: {integrity: sha512-QrwJlBFFKnxOd95TAaszpMbZBLzMoYMpGaQTZF8oibacnF5rv8l12IhILhQRPmksWiBqg0YSe2Mnl7ayeJAHSA==} + '@oxfmt/binding-linux-arm64-musl@0.71.0': + resolution: {integrity: sha512-AOCaminv/+fhinUXKvtPZT4POhNYmN9GfvaJdgJa1AbvuzClWGwDIXfJg8eu3sYaVL2B68jNvmS7LZGAYa0z3A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-ppc64-gnu@0.54.0': - resolution: {integrity: sha512-WILatiol/TUHTlhod7R09+7Az/XlhKwmY1MHfLZNmewltPWNN/EwxP2rQSHahibZ/cB8gmckEBjBOByD+5bYsQ==} + '@oxfmt/binding-linux-ppc64-gnu@0.71.0': + resolution: {integrity: sha512-0SHxuaa4QRLM7jfsd8wc6L7KdLg+FR6/ah9k1eAvcrK/Sni7oxchMvsjwUsyakVAta+wBVDkjJTr7/RFU0uUGA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-gnu@0.54.0': - resolution: {integrity: sha512-f05YMG4BH4G8S4ME6UM6fi1MnJ9094mrnvO5Pa4SJlMfWlUM+1/ZWMEF4NnjM7shZAvbHsHRuVYpUo0PHC4P9Q==} + '@oxfmt/binding-linux-riscv64-gnu@0.71.0': + resolution: {integrity: sha512-UNKj1+rPorTK6uSlQzpN96vDNbHbL/63zcpznweo0ImQdkDdtQCS7G3puxX++2cESPKT2B56Y6DICUmFTJde5A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-musl@0.54.0': - resolution: {integrity: sha512-UfL+2hj1ClNqcCRT9s8vBU4axDpjxgVxX96G+9DYAYjoc5b0u15CJtn2jgsi9iM+EbGNc5CW1HVRgwVu76UsSA==} + '@oxfmt/binding-linux-riscv64-musl@0.71.0': + resolution: {integrity: sha512-5cVXKj7a0mWu7GU6/bZgNyv+KtLgM5S9cnT26Os2/abj8pK/bklHsbdYEHGLLniGjNYkyzwem8mZciPCaDtilw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-s390x-gnu@0.54.0': - resolution: {integrity: sha512-3/XZe931Hka+J6NjnaqJzYpsWWxDTuRdUdwSQHnOuJEgbC+SehIMFJS8hsEjV7LBhVSL2OCnRLvbVW8O97XIyw==} + '@oxfmt/binding-linux-s390x-gnu@0.71.0': + resolution: {integrity: sha512-lNpcMuZvyU1V/CY5h19hac03m4E4PLSYRckcxZOynSPojI4Mwt6nEu9wT/gVf0bclH3xGEWjwqApnSTgjkd2oA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-x64-gnu@0.54.0': - resolution: {integrity: sha512-Ik93RlObtu43GbxApafayFjwYE06L6Xr08cSwpBPYbDrLp2ReZx0Jm1DqwRyYRnukUJy+rK2WaEvUQOxdytU9Q==} + '@oxfmt/binding-linux-x64-gnu@0.71.0': + resolution: {integrity: sha512-5/Z6pUewQpknXqC4/ykK6Zc6RiteAnPem1Ci7K1RZLVF6w6MMjwHjR4vsjijW4Czidgv7HKeVglGjElADliT9w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-x64-musl@0.54.0': - resolution: {integrity: sha512-yZcakmPlD86CNymknd7KfW+FH+qfbqJH+i0h69CYfV1+KMoVeM9UED+8+TDVoU4haxI0NxY7RPCvRLy3Sqd2Qg==} + '@oxfmt/binding-linux-x64-musl@0.71.0': + resolution: {integrity: sha512-uVdG2N/4GEbOeljpQ+xv+NeEwJWJGj0WaxSiSYnoiqIYy3RWrWd3rGUmxWXP1A8+ferNvvwFoDAtvgsDUvBuSw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxfmt/binding-openharmony-arm64@0.54.0': - resolution: {integrity: sha512-GiVBZNnEZnKu00f1jTg49nomv187d0GQX+O+ocykoLeiaALuEO+swoTehHn9TehTfi7V8H0i0e/yvUjCqnwk1w==} + '@oxfmt/binding-openharmony-arm64@0.71.0': + resolution: {integrity: sha512-1LwEZmSDRFKeBaGd/Hgj2xI3SmgGFpF4YtgkSx0sTYdTiZfHWbcFvRIBjL1YiDNdQd9Khda3Ij8CoqBULjskDg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxfmt/binding-win32-arm64-msvc@0.54.0': - resolution: {integrity: sha512-J0SSB8Z1Fre2sxRolYcW6Rl1RQmKdQ2hnHyq4YJrfBRiXTObLw4DXnIVraM/UyqGqwOi7yTrQA4VT7DPxlHVKA==} + '@oxfmt/binding-win32-arm64-msvc@0.71.0': + resolution: {integrity: sha512-5tplmkeMXmza3PUptJQy25wCPJbIAg5g5r7zs+DI+7eEYOqbY+7roMdSH2zbDvv1Pb4oTDJ6NWYx9IRxBxXwhQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxfmt/binding-win32-ia32-msvc@0.54.0': - resolution: {integrity: sha512-O61UDVj8zz6yXJjkHPf05VaMLOXmEF8P5kf/N0W7AQMmd6bcQogl+KJc7rMutKTL524oE9iH32JXZClBFmEQIg==} + '@oxfmt/binding-win32-ia32-msvc@0.71.0': + resolution: {integrity: sha512-T2LWgh3Vx4PYDjtcqRRGDyCsVxeF/VE5xTpUQiCm65bNLwMpRnJYj6Hlkf6qRM3+jxCIOqHD1kVHNQCCiAVMNQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxfmt/binding-win32-x64-msvc@0.54.0': - resolution: {integrity: sha512-1MDpqJPiFqxWtIHas8vkb1VZ7f7eKyTffAwmO8isxQYMaG1OFKsH666BWLeXQLO+IWNfiMssLD55hbR1lIPTqg==} + '@oxfmt/binding-win32-x64-msvc@0.71.0': + resolution: {integrity: sha512-L06SVp7Hu1EQTkh3+PHnKK26dxVbEpOG2InMwCooTE4MA437b5FgfTleCCiGn4ykISHnHU6UHaIg7SiMO7rBHA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@oxlint/binding-android-arm-eabi@1.69.0': - resolution: {integrity: sha512-DKQQbD5cZ/MYfDgDI7YGyGD9FSxABlsBsYFo5p26lloob543tP9+4N3guwdXIYJN+7HSZxLe8YJuwcOWw5qnHg==} + '@oxlint/binding-android-arm-eabi@1.86.0': + resolution: {integrity: sha512-63Ozq6yQn79B2UxjSZ9huJmI+3/ZPI7hxAe6TyFh9oQL447KnIMoffOdtog+h4KILbQr61ZRvFPW5gYbAUgpgQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxlint/binding-android-arm64@1.69.0': - resolution: {integrity: sha512-lEhb+I5pr4inux+JFwfCa1HRq3Os7NirEFQ0H1I35SVEHPm6byX0Ah47xmRha3qi6LAkxUcxViL8o/9PivjzBg==} + '@oxlint/binding-android-arm64@1.86.0': + resolution: {integrity: sha512-JdLGp44phbf/0Du236Dt06yfK6Qw8xhTFeDvHJdjk167r59OfP5+BL31mgMYVnswS8e+j6tFuccSY0QDC/lgCw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxlint/binding-darwin-arm64@1.69.0': - resolution: {integrity: sha512-GY2YE8lOZW59BW1Ia1y+1gR0XyjrZRvVWHAr8LGeGhYHE0OQJ/7cRKXTkx1P+E9/6awEc3SX8a68SFTjh/E//A==} + '@oxlint/binding-darwin-arm64@1.86.0': + resolution: {integrity: sha512-h+vkOr4ik6KLFCXdtNVEj9xfnXfNUpPoF95QjORdhiSpLqQePzcN81kGHjAgsw1/G7WBWK5KsFKbI2UK8EyzMA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxlint/binding-darwin-x64@1.69.0': - resolution: {integrity: sha512-ax1oZnOjHX3LB7myQyHEaQkDwfLb6str3/nSP6O7EVUviQGNkEGzGV0EqcBJWK+Ufwx0l4xPgyYayurvhAdl2Q==} + '@oxlint/binding-darwin-x64@1.86.0': + resolution: {integrity: sha512-wbglRuH5eKsp68keTOOeKDHU1astWSRYFsh2bwKQTAPb44rD6kVgzYI1ZrAJRTc6/7PH55ZXFCQx0rQXrj0UdA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxlint/binding-freebsd-x64@1.69.0': - resolution: {integrity: sha512-kHWeHv4g2h8NY+mpCxzCtY4uerMJWTN/TSnNj1CPbakFpHEJ6cTya2wWV0pDSYWOJ2+0UiEbhn3AtXxHtsnKjg==} + '@oxlint/binding-freebsd-x64@1.86.0': + resolution: {integrity: sha512-yTQ7QSOlNfQsrfbeVweTiegQJLKPjUKgXp/JpGiFzza6Hp29S9LAoH77TM3L0RjK39iQRZB5lk5iq2t241Mfag==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxlint/binding-linux-arm-gnueabihf@1.69.0': - resolution: {integrity: sha512-gq84vM1a1oEehXo27YCDzGVcxPsZDI1yswZwz2Da1/cbnWtrL16XZZnz0G/+gIU8edtHpfjxq5c+vWEHqJfWoQ==} + '@oxlint/binding-linux-arm-gnueabihf@1.86.0': + resolution: {integrity: sha512-QYJGy0E2Tv8GAayyBIeHFTwQb8u+zrvrOYrifuDGZDr9ad5XsDhiMScpZymhS9fwWjqrgG2ZUwT4vpY0ISqp4Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm-musleabihf@1.69.0': - resolution: {integrity: sha512-kIqEa98JQ0VRyrcncxA417m2AzasqTlD+FyVT1AksjvjkqQcvm7pBWYvoW3/mpyOP2XYvi5nSCCTIe6De1yu5g==} + '@oxlint/binding-linux-arm-musleabihf@1.86.0': + resolution: {integrity: sha512-oQo83CafLT8p3qh0AGkN0tSUIh5Hm63mfDsoqUmBQUBnSZLNBAbyRaAftfGY5a0cKZ34dG6jAtMYgcY5kZqt5g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm64-gnu@1.69.0': - resolution: {integrity: sha512-j+xYiXozxGWx2cpjCrwwGR4awTxPFsRv3JZrv23RCogEPMc4R7UqjHW47p/RG0aRlbWiROCJ8coUfCwy0dvzHA==} + '@oxlint/binding-linux-arm64-gnu@1.86.0': + resolution: {integrity: sha512-EM6wy5c2UM12qPb7iDJDBAZvHyfEJGH6iosFgaFiaRZpirz99//EHcUyD2KAskz07x1TULuiAtsojf4nvWWksw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-arm64-musl@1.69.0': - resolution: {integrity: sha512-xEPpNppTfN1l/nM7gYSf9iocscu/as+p/7vxkLeLEKnYU+09Dm+5V6IhDYDh+Uz6FajEupWwCLt5SOG0y1PCKg==} + '@oxlint/binding-linux-arm64-musl@1.86.0': + resolution: {integrity: sha512-vCiUQb9ZNzZalxYRU4mlZwUqT5f+c7Sk9DwUOplWHtik3Dl4r3DVnUB5tBeRuTAUe6MUVCTylNW7TkkN8J1oTw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxlint/binding-linux-ppc64-gnu@1.69.0': - resolution: {integrity: sha512-Ug0+eU7HJBlek+SjklYH62IlOMirEJsdxpihH0kSqX0XdrDD4NdHpQc10fK1JC35yn6KrrcN+uYzlHD38XAf8Q==} + '@oxlint/binding-linux-ppc64-gnu@1.86.0': + resolution: {integrity: sha512-T0AzE89yjTYmOhB/hb6i+rWTpe2mYt+xiguBhs9DEPIJTZ1H8OYqsE4VxJHHw2Wlq8Mztwvp2P8NV87ZOuhPRw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-gnu@1.69.0': - resolution: {integrity: sha512-iEyI3GIg0l/s3G4qy2TlaaWKdzj4PJJStwtlocpDTC00PY9hZueotf6OKUj9+yfQh0lrpBW/pLMgTztbAHKJEg==} + '@oxlint/binding-linux-riscv64-gnu@1.86.0': + resolution: {integrity: sha512-s49r0K2f5Oc7fJoMUO1WkdhYK5Ob79uYRwWQ/XNoGysaOq3RUGmGUbHSuXiyydTTA+P9xEdDEnnlZVXiaSJhlw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-musl@1.69.0': - resolution: {integrity: sha512-NjHjpiI4WIKSMwuoJSZi5VToPeoYOS1FR52HLIDG6lidMdqquusgtODb4iLk0+lb1q3Z0nv2/aPRcC/olmpQGg==} + '@oxlint/binding-linux-riscv64-musl@1.86.0': + resolution: {integrity: sha512-JIa2tYR8ayb3nvcMSZzn4O/xTy13S7MHllu7cBD+B5qDQXXSRy/Vwk1aNpiTjZmBJGwKCHOy/NvnY5Xcwg27uQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxlint/binding-linux-s390x-gnu@1.69.0': - resolution: {integrity: sha512-Ai/prDewoItkDXbp38gwGZi41DycZbUTZJ3UidwoHgQC0/DaqC2TGdtBTQLJ6hSD+SAxASzh8+/eSBPmxfOacA==} + '@oxlint/binding-linux-s390x-gnu@1.86.0': + resolution: {integrity: sha512-XwXMyBsAuWZkhZU74UNcfQKr1CJJAKnS80dZzOnjl6A4fKcf91IjFgidegQugMHA5SN8ulwDXEDV1yPntjXgQQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-gnu@1.69.0': - resolution: {integrity: sha512-Gt3KHgp46mRKz4sJeaASmKvD8ayXookRw07RMf+NowhEztGGDZ7VrXpoW96XuKJLjFukWizOFVNjmYb/u7caNQ==} + '@oxlint/binding-linux-x64-gnu@1.86.0': + resolution: {integrity: sha512-C1WjukSyMnr66b+w1/tV8RFVv6d9v0MzDf4p9IxVXknqgmTHBgZh1pccN1eHzFr0b9Tbb3OXoPsAAdAuHAQfeA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-musl@1.69.0': - resolution: {integrity: sha512-7tQhJ2+p/oHv1zcfnjYI7YVzC/7iBaVOfIvFYtxdJ5F45mWgEdrCyXZXZGfiLey5t/5JhOhsaMnnv1kAzckd7g==} + '@oxlint/binding-linux-x64-musl@1.86.0': + resolution: {integrity: sha512-ap6KLmvC38c6MdYzsIh25cXQupqYvjd37tMNftzrX1DCtkX1Gcf2B+S2B17dD4lKa5c5gJog7DQJyDo82PBKyw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxlint/binding-openharmony-arm64@1.69.0': - resolution: {integrity: sha512-vmWz6TKp/3hfA4lksR0zHBv/6xuX1jhym6eqOjdH2DXsDDHZWcp2f0KG0VCAnlVbIrjk29G4wAWMXb/Hn1YobA==} + '@oxlint/binding-openharmony-arm64@1.86.0': + resolution: {integrity: sha512-996Q9w/2GLwNyy5Djs39Jt09aFlr4b7TPhoj7dvo5FHmcyiF68nPz73HNc5UAYKIYeK+M9JzPQ4Wg1r1dXvP6A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxlint/binding-win32-arm64-msvc@1.69.0': - resolution: {integrity: sha512-9RExaLgmaw6IoIkU9cTpT71mLfI0xZ86iZH8x518LVsOkjquJMYqb9P7KpC8lgd1t0Dxs41p2pxynq4XR3Ttzw==} + '@oxlint/binding-win32-arm64-msvc@1.86.0': + resolution: {integrity: sha512-9uTCHYTknNkug5+46ViSoUMuOuGWK61D87QnblU6CXaWKJC630Z7dbls61vdHxya/cR2ycQZ6Mm6ckgF6Cag5g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxlint/binding-win32-ia32-msvc@1.69.0': - resolution: {integrity: sha512-1907kRPF8/PrcIw1E7LMs9JbVrpgnt/MvFdss3an8oDkYNAACXzTntV3t3869ZZhMZxb2AzRGbz1pA/jdFatXA==} + '@oxlint/binding-win32-ia32-msvc@1.86.0': + resolution: {integrity: sha512-a47ga7EKVfMU8rO0i1m3mGwfNRj3FhQyt1d/ukO/E9u1HHF3KpR3tyb3VhuJBGEVPF8uR4exSyHUyx7xbE+uxw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxlint/binding-win32-x64-msvc@1.69.0': - resolution: {integrity: sha512-w8SOXv3mT9Fi6jY8OXdXCfnvX/3KNLXGNr4HEz2TA7S4Mv/PYAOmpB8y/ge40mxvBMgGNaSaaDwZpAsQn7HtWA==} + '@oxlint/binding-win32-x64-msvc@1.86.0': + resolution: {integrity: sha512-az4dzifCRzES1crwu6pDFXzJTIanrdxXFL9TbiJWrx9LKcuacbR83aPvxDA1LKLNswi/RoqitC7Afcflhu3zBQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@payloadcms/db-sqlite@3.85.1': - resolution: {integrity: sha512-+5bVk9ClpwJB9w6zoPeaLABRWbsLAxgfgl5dWGBxo+UwTgc8DYrzDukycvZ3cR/TB/pRnyoA+33rP7mEpeVA7g==} + '@payloadcms/db-sqlite@4.0.0-canary.38': + resolution: {integrity: sha512-4cIMI6XscQaSimiX6J+yEjsIVcdzkaXSGq9HbvyXqoyKWRkDkAqQrJ+aEmQ2PpTQICnFGXeOH1EK57C/5XYhPA==} peerDependencies: - payload: 3.85.1 + payload: 4.0.0-canary.38 - '@payloadcms/drizzle@3.85.1': - resolution: {integrity: sha512-TVvw+tdvINf1y9bhkwjhGYguDybT3BJpTFzftKrsu034HqlN8ecc84ii35PXeiSsPciVWVgfgwAg9TSHQv4DFg==} + '@payloadcms/drizzle@4.0.0-canary.38': + resolution: {integrity: sha512-IntAuQqgNzor1ZvzlhD0Bb9w8XrUWPYH62UKYhDVUcA00XFs6etkggeGt4iULweM7sMZZI82dm0syJ6JfT6eHA==} peerDependencies: - payload: 3.85.1 + payload: 4.0.0-canary.38 - '@payloadcms/graphql@3.85.1': - resolution: {integrity: sha512-jMrSCiGYmsjOUGmZayX5U7Vh3i8IcO0XMmQ9x5jCIZuB+/sNDiBNeHJWArC+H2cfPYh0G+iv9t+RpPg6FemiMg==} + '@payloadcms/graphql@4.0.0-canary.38': + resolution: {integrity: sha512-JZvndTmx1pnVM95HEDEba1zGHVo2SuQwbMz37GlJ+gdDuIVktTLLQBP8gTrRLl9stdf3RL/h6oeA7uxEinkvmw==} hasBin: true peerDependencies: graphql: ^16.8.1 - payload: 3.85.1 + payload: 4.0.0-canary.38 - '@payloadcms/next@3.85.1': - resolution: {integrity: sha512-k/2l2LQNrgfa5r5ibR0o4/nNhLAlQQXtLpwGrJq2/B/yws4Ir6otc+ozTMiprATlvVb6uXcNFT+m1XLYqqSFuQ==} - engines: {node: ^18.20.2 || >=20.9.0} + '@payloadcms/next@4.0.0-canary.38': + resolution: {integrity: sha512-AJrDVOVw4V1gqDvCYCDnEd10quw7rHtRHaCRe6tYWtkyB3s3lcRveuM0mPWxmNFWMeYAgkhp57doDV18Psn66A==} + engines: {node: '>=24.15.0'} peerDependencies: graphql: ^16.8.1 - next: '>=15.2.9 <15.3.0 || >=15.3.9 <15.4.0 || >=15.4.11 <15.5.0 || >=16.2.6 <17.0.0' - payload: 3.85.1 + next: '>=16.4.0 <17.0.0' + payload: 4.0.0-canary.38 - '@payloadcms/translations@3.85.1': - resolution: {integrity: sha512-1DssESAfPjgkp8/f7Xq1ZOCnMjSXp0Xkpbo0I7Zt0DCaA+QY7Go+kZQuSYazKKRF+lgSGzKEHRJ5CWqzhpKh8w==} + '@payloadcms/translations@4.0.0-canary.38': + resolution: {integrity: sha512-7ao8otvtNFYVr13uZvZqr5LiP7ey4DdczL0ikuPi8LvM3QBo7WklCxgK5YrFt8WSAhwZamDNY1V73DGQmAPWjw==} - '@payloadcms/ui@3.85.1': - resolution: {integrity: sha512-WCJBEKAnwIvEi2Hiykl92dLVjGga6sPGsNlkbkssNMDqieEUh6qNAb3HewlJUa0IFrhxVFLoAEyJOzHr1UJXkQ==} - engines: {node: ^18.20.2 || >=20.9.0} + '@payloadcms/ui@4.0.0-canary.38': + resolution: {integrity: sha512-+NIwg3zX+U+O8Bsz1vUhx+raAKn1Dgj437c85Vs/IbOv6wq41Rv2+iAS+yMaB+Uu0kcCiYxAeJC2wbw6oYW47w==} + engines: {node: '>=24.15.0'} peerDependencies: - next: '>=15.2.9 <15.3.0 || >=15.3.9 <15.4.0 || >=15.4.11 <15.5.0 || >=16.2.6 <17.0.0' - payload: 3.85.1 + payload: 4.0.0-canary.38 react: ^19.0.1 || ^19.1.2 || ^19.2.1 react-dom: ^19.0.1 || ^19.1.2 || ^19.2.1 @@ -2016,8 +2291,110 @@ packages: engines: {node: '>=18'} hasBin: true - '@rollup/rollup-android-arm-eabi@4.60.4': - resolution: {integrity: sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==} + '@quansync/fs@1.1.0': + resolution: {integrity: sha512-qAPG/t3HqML1TlN7sY/pTbEjzFVAKsMjNNMGheyDosro+kT4iw2KCUoHcVdmliwWjorm4elZbgNQyU2eD97eDg==} + + '@rolldown/binding-android-arm-eabi@1.2.12': + resolution: {integrity: sha512-dB/a1214qKfHMXCpgqR4OZT+jS4kTyEXbQGJPqzobt5EwH5rX080pxE37alt3RzvR1bf1Yz/yGqRfrYAxuPw0A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@rolldown/binding-android-arm64@1.2.12': + resolution: {integrity: sha512-7KHFgQ5VJxIHcLlrwrc3Xbds7oTNQT7Pgi9gQCJKrd2VGab/UksIOYp6VD8MzCstGxOKMgNamPwUCfxPdP1OHg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.2.12': + resolution: {integrity: sha512-3YIhqHD96nA5SaYNRBR16HnGv4oavZvXfD/ayHM+oYZ0WD/8lBAtf6zQua4kEyAvpqrluKXl0lnOBoiNby7x9w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.2.12': + resolution: {integrity: sha512-UuuJ35MFw4gmFOrE9pEqIV+K3syIKveph+Qc1/ljHZVdoDW4pz/JHR/eMVom+TZGl/5OOvGJOWaOCVt3ZfqhxA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.2.12': + resolution: {integrity: sha512-uMvssit0a4W+/7D8CbHUvG719mH3R2jwXAlh/XcPvuHTE0g++LymF88DCGNX0HM2rBOn0xrzgXktIB6fLSJBTQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.2.12': + resolution: {integrity: sha512-XcFu0R0xWnwzSf4IQgFH1rJIckPN1pLy2R+4r9IDB7Yfu/ys9cVqfa4pBrMHj7a3gl8mIR4nRNPg0e5IvEVs6g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.2.12': + resolution: {integrity: sha512-260UrKgn8tz39ak+SMDOirKzr7V04M9dWPw5llW00SwBivCZoWcRBKV1d8cXnRkUmSZA3BdiUmBHWk7734Ulpw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-musl@1.2.12': + resolution: {integrity: sha512-5YK1I9SqDkbPgc1IA8BgDl34suqUS2q0KWnBrirm0E51YjOs6eo6dV6jbQfNE/argHRSvd0QUGgtpIoYx+WWpw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.2.12': + resolution: {integrity: sha512-Rkcrmp7eFRg74yL5fXEU91JEWbdEPLevWwGtXpmhbjlD1StScbWTmO94Bhly+Mo+ketKYkdmM1vNUKeWSlx8cQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.2.12': + resolution: {integrity: sha512-qvK4DuAsQc2BSjlx+Xr+IzOIvvxbGZqxFwdWfG6F518Erj0GGISyQbJ6pIappnOxlNPzNHvo/L0BwB30GZ+zVw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.2.12': + resolution: {integrity: sha512-Q9uLBO53Xd4QIq1WOycVQyPP1O4HhraEV2qqb3uTrnVw6QZih9duY4vNXOivL1xoUS1/z+W8eF4NMfl2a8Sdjw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.2.12': + resolution: {integrity: sha512-3IBxWFMjbOZskDPKv8Lf9BCnahlKuHthWkYnyIxOH/QcJrFcS4EmcenthApkwr/5+nEqZlLzeYbxeMaX7A5u4g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.2.12': + resolution: {integrity: sha512-xtX61xg4LKPkPWilZU1ynKClz5Gj4bf74LML4r3eVLWumKnGjoEr1OSHQhMdbBDoYTi+yjrujvpZe2pUnqCrrA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-win32-arm64-msvc@1.2.12': + resolution: {integrity: sha512-At7fPB6PCaIjzgIhEZFxuT+BBFqiQibJDT4d3PhiR3f4E7bbMZF4aKblbFfEM3sETRDd1YiQx/+U/g/B/ou5Ew==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.2.12': + resolution: {integrity: sha512-WIw2haVKwjuYdXkHaoC0mF8Le71TuCBxjrdKqLbJGctbBABj+ClfmNvtbOnzpq3RokNo5+V1qhtSzJyXorsklQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + + '@rollup/rollup-android-arm-eabi@4.60.4': + resolution: {integrity: sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==} cpu: [arm] os: [android] @@ -2154,97 +2531,6 @@ packages: cpu: [x64] os: [win32] - '@rsbuild/core@2.0.12': - resolution: {integrity: sha512-6f0M9TO4pAeM/VAshg9PajYK4IbfUB21Xun2kT0q3/SbBQ607KzkQZ6fmReuKUKCLbo26tGnbgg5P77eDDcfaA==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - peerDependencies: - core-js: '>= 3.0.0' - peerDependenciesMeta: - core-js: - optional: true - - '@rslib/core@0.22.1': - resolution: {integrity: sha512-RaqTITHFkpMDJG9fmD7Hu6FLE64hwctCo46asHOD2DipzQJWawg6K0pFGimTAyutYEZysIUfYgCwSYkbctDudg==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - peerDependencies: - '@microsoft/api-extractor': ^7 - typescript: ^5 || ^6 - peerDependenciesMeta: - '@microsoft/api-extractor': - optional: true - typescript: - optional: true - - '@rspack/binding-darwin-arm64@2.0.8': - resolution: {integrity: sha512-vCgbgH7B7qom+uID+RCZsTCOYFb9wC4/4+1U6rMfytrXGVJ72eNQs2tbdjOl0lb18CT3N/n+VkWynUiLk84GwA==} - cpu: [arm64] - os: [darwin] - - '@rspack/binding-darwin-x64@2.0.8': - resolution: {integrity: sha512-satPm2PD4B7jDTVlVAdvMVdUszwLvWUEnUDzLb77mvVkezKNDZmuhb+e8s+FfKs8hJpNbZ9VAejuA2rr8o985w==} - cpu: [x64] - os: [darwin] - - '@rspack/binding-linux-arm64-gnu@2.0.8': - resolution: {integrity: sha512-pSI+npPQE/uDtiboqvcOIRJbEV2+B+H1xffmko/gw50la92oTUW60kVULFwsb6L0+GVCzIcwX3yq60GtYIn+Ug==} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@rspack/binding-linux-arm64-musl@2.0.8': - resolution: {integrity: sha512-igjJ43yxWQ72GZqjDDZSSHax9/Vg+6rLMmOvFglTJUkQpB4Tyvu/YjW+WRjYj2xRw6blOjLxUSJWASvuSqqlvg==} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@rspack/binding-linux-x64-gnu@2.0.8': - resolution: {integrity: sha512-zrkoEOnqj1hOEBO5T2I/2Ts2HSJsYFh1qXwMpK4dMJFGGNWDfNeUa6/LF5uq3VINF3JUl7RL47AgrucoSZJXPA==} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@rspack/binding-linux-x64-musl@2.0.8': - resolution: {integrity: sha512-6CtDaGZjNDvJd9TBp7a9zABbrPORO21W96+3ZcGBn0YNUPUk4ARxIxrTTpeJ/1F41QDM8AYIkGDdqEYMqTYBsA==} - cpu: [x64] - os: [linux] - libc: [musl] - - '@rspack/binding-wasm32-wasi@2.0.8': - resolution: {integrity: sha512-Yf4SiqTUroT5Ju+te0YAY2xxKOb35tECsO21v7hYyGa705wrgoAK/MmF7enOvs9GR1iZIqgiLD/wxsIxl8GjJw==} - cpu: [wasm32] - - '@rspack/binding-win32-arm64-msvc@2.0.8': - resolution: {integrity: sha512-8NCuiQsAhXrwRBy57QZoypqrws/zLBkaQVGiB8hksr6v++8hNigNjqpQARLbd0iyMuHsQQ++8+auGk6xlDXmzw==} - cpu: [arm64] - os: [win32] - - '@rspack/binding-win32-ia32-msvc@2.0.8': - resolution: {integrity: sha512-bxiekytbX7V9KFAra+HkwtNWC6pYfHEBBZFpiT0xUs3mCFOmAAFVBsBSQsoCP9AdCEXoMAvNdnrHNw3iov4OZw==} - cpu: [ia32] - os: [win32] - - '@rspack/binding-win32-x64-msvc@2.0.8': - resolution: {integrity: sha512-7zPs8YCe/ZVJTwd+5lpB0CP0tkn2pONf/T1ycmVY76u21Nrwt8mXQGc/2yH2eWP4B7fikYBr3hGr7mpR2fajqQ==} - cpu: [x64] - os: [win32] - - '@rspack/binding@2.0.8': - resolution: {integrity: sha512-3uZ+y8aQxq33ty2srMxg2Nu0XuBI6vVrG50rkDaXqwWqOohfgGUSfFuQK7EnSUNy4aFUQlCG6NHialQHJov0wg==} - - '@rspack/core@2.0.8': - resolution: {integrity: sha512-+NLGJf8gZxihDmMFzjlly3toc2SMjeDmuvz0/Cai9AMdV4F+Pqcnt2BA9V4e3SY2jmhJQtPwgyyLtR1RiJO77g==} - engines: {node: ^20.19.0 || >=22.12.0} - peerDependencies: - '@module-federation/runtime-tools': ^0.24.1 || ^2.0.0 - '@swc/helpers': ^0.5.23 - peerDependenciesMeta: - '@module-federation/runtime-tools': - optional: true - '@swc/helpers': - optional: true - '@scalar/openapi-types@0.9.1': resolution: {integrity: sha512-gkGhSkxSzADaBiNg+ZAbJuwj+ZUmzP2Pg9CWZ7ZP+0fck2WjPeDDM7aAbouAm0aQQMF9xBjSPXSA9a/qTHYaTw==} engines: {node: '>=22'} @@ -2256,6 +2542,45 @@ packages: resolution: {integrity: sha512-bP/48Il7RMxhohq36Vg0c25tnCKOO+oEv8XSxDefuUBj0W8fYzL2Yb6C8tji0WBXykZ1S/79AWbrzGLfCG2dYw==} hasBin: true + '@seshuk/payload-plugin-tooling@0.2.3': + resolution: {integrity: sha512-qlAfCM1iSVSmKQFBfwP3l3yHbJNhaCOofrvqwk5rtvcdE5JAWZCoSx9HsHoNdka9+0eEjBNLeaHYYkChkOdl+Q==} + engines: {node: '>=24.15.0'} + peerDependencies: + '@electric-sql/pglite': '>=0.5.0' + '@electric-sql/pglite-socket': '>=0.2.0' + '@payloadcms/db-mongodb': '>=4.0.0-0' + '@payloadcms/db-postgres': '>=4.0.0-0' + '@payloadcms/db-sqlite': '>=4.0.0-0' + mongodb-memory-server: '>=10.0.0' + oxfmt: '>=0.71.0' + oxlint: '>=1.77.0' + payload: '>=4.0.0-0' + tsdown: '>=0.23.0' + vitest: '>=4.0.0' + peerDependenciesMeta: + '@electric-sql/pglite': + optional: true + '@electric-sql/pglite-socket': + optional: true + '@payloadcms/db-mongodb': + optional: true + '@payloadcms/db-postgres': + optional: true + '@payloadcms/db-sqlite': + optional: true + mongodb-memory-server: + optional: true + oxfmt: + optional: true + oxlint: + optional: true + payload: + optional: true + tsdown: + optional: true + vitest: + optional: true + '@shikijs/core@3.23.0': resolution: {integrity: sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA==} @@ -2372,9 +2697,6 @@ packages: resolution: {integrity: sha512-JZlVFE6/dYpP9tQmV0/ADfn32L9uFarHWxfcRhReKUnljz1ZiUM5zpX+PH8h5CJs6lao3TuFqnPm9IJJCEkE2w==} engines: {node: '>=10.8'} - '@swc/helpers@0.5.15': - resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} - '@swc/helpers@0.5.23': resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==} @@ -2452,6 +2774,9 @@ packages: '@types/node@25.9.1': resolution: {integrity: sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==} + '@types/node@26.6.4': + resolution: {integrity: sha512-ldVPDCzj7fsaGZrLB0NuHuTvJcsNasysBAqMolr/cgxrLd1xbqxIr3XJiPnHHJUCxj5sNF1vnRj9aWnrVh5Jcg==} + '@types/parse-json@4.0.2': resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} @@ -2481,6 +2806,126 @@ packages: '@types/yauzl@2.10.3': resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} + '@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] + '@typescript/vfs@1.6.4': resolution: {integrity: sha512-PJFXFS4ZJKiJ9Qiuix6Dz/OwEIqHD7Dme1UwZhTK11vR+5dqW2ACbdndWQexBzCx+CPuMe5WBYQWCsFyGlQLlQ==} peerDependencies: @@ -2489,11 +2934,25 @@ packages: '@ungap/structured-clone@1.3.3': resolution: {integrity: sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==} - '@vitest/expect@4.1.8': - resolution: {integrity: sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ==} + '@vitest/coverage-v8@5.0.3': + resolution: {integrity: sha512-+klsyz7BvT1vCU28Zkfzms1Ia78XD0V41U3FUtRaa3S+vOr/EXvK1O6BvVD7l1RzEjm6SONR2aybOvDDf9u0mQ==} + peerDependencies: + '@vitest/browser': 5.0.3 + vitest: 5.0.3 + peerDependenciesMeta: + '@vitest/browser': + optional: true + + '@vitest/istanbul-lib-coverage@1.0.2': + resolution: {integrity: sha512-9J/JMwOf9AoJhAywhrn7ScKTL38hsWQP/qPG60OtaAFcQ5OXPwKsxZFlbnuCKmZ61m8/lGgHYnFpdyQZUvG/iA==} + engines: {node: '>=22'} - '@vitest/mocker@4.1.8': - resolution: {integrity: sha512-LEiN/xe4OSIbKe9HQIp5OC24agGD9J5CnmMgsLohVVoOPWL9a2sBoR6VBx43jQZb7Kr1l4RCuyCJzcAa0+dojw==} + '@vitest/istanbul-lib-report@1.0.2': + resolution: {integrity: sha512-gUsfXZJbzPamoIY5TvHFiMMoXESBrUMo+xqaj+rYrWI69+EnvRlYBlP96ZnHPY4vX8kyUpgAnFUCg5wZG/HkDQ==} + engines: {node: '>=22'} + + '@vitest/mocker@5.0.3': + resolution: {integrity: sha512-T8sWAIbkSyAjkwTcaEc3Iu0o9A27X1/kdXrizhZkGuSKScRQtRzclfAMpOTcGdXCsqxeWlpGy3XjqaW8CpLORg==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -2503,20 +2962,143 @@ packages: vite: optional: true - '@vitest/pretty-format@4.1.8': - resolution: {integrity: sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA==} + '@vitest/spy@5.0.3': + resolution: {integrity: sha512-XhFysQTB8AZ+P4gMi+Lpo99vg2AZi0qKpaB9yXQl37+CaMEAPO3iH/wGVnSyL5MPERiLezpqTVtrR6UZH5GCXg==} + + '@yuku-codegen/binding-android-arm64@0.10.2': + resolution: {integrity: sha512-Oc2KInVkPfUjEB4PeV6X0NvIoyYTzDe/WmqFrTwBqKs6YKnz4A7XwWQvJTb7M0rLz9a6WgpUUiiS5QKaCi4O/g==} + cpu: [arm64] + os: [android] + + '@yuku-codegen/binding-darwin-arm64@0.10.2': + resolution: {integrity: sha512-3H7eNPIHJndUJXZ4QUGBPq1zC6RGcUZfm7bymJes+/N7wTVWUn1bxZ9JcozYHpkWQNm9f23G8YWbaHD9aYH72A==} + cpu: [arm64] + os: [darwin] + + '@yuku-codegen/binding-darwin-x64@0.10.2': + resolution: {integrity: sha512-AJOUpR2s3LF9AWiiub/Uyc/UoXNTWcq8hK3cVI6fUSXtwh34dG21J6hRuNlO6JVJFTo4o3ScVvZOrh/YFfUEAA==} + cpu: [x64] + os: [darwin] + + '@yuku-codegen/binding-freebsd-x64@0.10.2': + resolution: {integrity: sha512-ms7DcZu87u5CiK/wMwvpKAvAmtaqKjCQIXNweMLypG6qbuiaOMQf1jpbB5+j46xBcgCovo8TQMcv0bCQLKIL9w==} + cpu: [x64] + os: [freebsd] + + '@yuku-codegen/binding-linux-arm-gnu@0.10.2': + resolution: {integrity: sha512-xJDpYAsKV5+eaiBhTYl05fvT6sst4PsCeuIFMRu9b0/WCSrNQPfCDtAQ5/MS6xNpsdujdZEPR+gmfWk3ZG5i3A==} + cpu: [arm] + os: [linux] + libc: [glibc] - '@vitest/runner@4.1.8': - resolution: {integrity: sha512-EmVxeBAfMJvycdjd6Hm+RbFBbA9fKvo0Kx37hNpBYoYeavH3RNsBXWDooR1mgD52dCrxIIuP7UotpfiwOikvcg==} + '@yuku-codegen/binding-linux-arm-musl@0.10.2': + resolution: {integrity: sha512-qfTkgd7AEx61l4K9VtXWCGTxc/fmXJ4ZheDz3i+/AAJUtg4sa0bPrc0VBxPggB/rayR8Z3+JfrBItuyanBJJ9Q==} + cpu: [arm] + os: [linux] + libc: [musl] - '@vitest/snapshot@4.1.8': - resolution: {integrity: sha512-acfZboRmAIf05DEKcBQy33VXojFJjtUdLyo7oOmV9kebb2xdU01UknNiPuPZoJZQyO7DF0gZdTGTpeAzET9QPQ==} + '@yuku-codegen/binding-linux-arm64-gnu@0.10.2': + resolution: {integrity: sha512-4e6Mifm/4UdjtU3D8mATIrvgP+xEiH8xtQOjd0zpd72XKWT7ug3sdyhq2utkkZFP6BvYp7SgZrI+aR4VeIOHdw==} + cpu: [arm64] + os: [linux] + libc: [glibc] - '@vitest/spy@4.1.8': - resolution: {integrity: sha512-6EevtBp6OZOPF7bmz36HrGMeP3txgVSrgebWxHOafDXGkhIzfXK14f8KF6MuFfgXXUeHxmpD3BQxkV00/3s5mA==} + '@yuku-codegen/binding-linux-arm64-musl@0.10.2': + resolution: {integrity: sha512-RdsJrUfDYFVV3JOmWFUWwSu31fa1APn12xDeIKxgl/YWxrabwtxsDBNjF2561c7tmcbiewdCUvngqRs8AyGVLA==} + cpu: [arm64] + os: [linux] + libc: [musl] - '@vitest/utils@4.1.8': - resolution: {integrity: sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg==} + '@yuku-codegen/binding-linux-x64-gnu@0.10.2': + resolution: {integrity: sha512-mVzWimEPPreaPyXIUvxsHoJzvO7ckZ5j0LgQFHz04zQIRwt9A9T2hA7K5/jYjJKkMxWG/U2VjhGNwVhtyU+uqg==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@yuku-codegen/binding-linux-x64-musl@0.10.2': + resolution: {integrity: sha512-Y77fyISurmr2mvO1yeq3u+x/WJbACgPR4w+lzEVx30ViCxZzIGrZPRN1yEdZ9xUNPNsIO5thBHIdRNG+UGUG+Q==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@yuku-codegen/binding-win32-arm64@0.10.2': + resolution: {integrity: sha512-1+tGLyG0u5YYy0lev4ck7/0sd8xJ9SZde0dLut7qLDPZV5WVTV8x6OxNXUne/PGuHZyO1vK+txPQzzHOyXHGSw==} + cpu: [arm64] + os: [win32] + + '@yuku-codegen/binding-win32-x64@0.10.2': + resolution: {integrity: sha512-8a2SExRohRbwC0MY4VpOF0/RSckrJ2ASZqAor5/RYSJJaeadR93n10gZzcKT6pY9ukiSOIZCRKVRD2tH73T0qA==} + cpu: [x64] + os: [win32] + + '@yuku-parser/binding-android-arm64@0.10.2': + resolution: {integrity: sha512-2VPBU9fRGRAQ2xPAvghnec3oou5Nrxm2Bezhf+13UMspAxQSkF/32r+ySKXSwIPA5/RivumDJDxAxc301Sgicw==} + cpu: [arm64] + os: [android] + + '@yuku-parser/binding-darwin-arm64@0.10.2': + resolution: {integrity: sha512-LD+PMZE51tCYTOss5HBkm3/AE39MvcMDBfWJx7A4yDcjfNbAQDnHZKtzSOuqrswx+TQHY0ws5xn6+fWOwtmfBA==} + cpu: [arm64] + os: [darwin] + + '@yuku-parser/binding-darwin-x64@0.10.2': + resolution: {integrity: sha512-mmZ8cND+AoIIMRERyMinlg5ApHxP23B9jH2B5wT7T+dliPa9rubLxneB/SUjFwyUjGaFnB5G7t4YvpfbO5zbkQ==} + cpu: [x64] + os: [darwin] + + '@yuku-parser/binding-freebsd-x64@0.10.2': + resolution: {integrity: sha512-gVIjaaIddbRfAhHlC8N809wQWml7mxfSVnzaLtzGXObTeFTPAg/YVnQXt1UOhPM1ah5eG/8Y0RUlNpB4GVe7eQ==} + cpu: [x64] + os: [freebsd] + + '@yuku-parser/binding-linux-arm-gnu@0.10.2': + resolution: {integrity: sha512-q/XPPQQAPdlw05aPj30ygBhekmQryGOwxVraBgApjKK8yY1kNQgqq6XCYLF1WHSee+lhxclrTO7W0/bt7dR1GA==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@yuku-parser/binding-linux-arm-musl@0.10.2': + resolution: {integrity: sha512-A+Cb0I1hFF4wilTQpWs79k1aNnj4B2tkrHx3zsuUNF9BtVY2zPZ4yeQEM/zjXrS/qJlNMZVK9NrWJjwdpnTrfg==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@yuku-parser/binding-linux-arm64-gnu@0.10.2': + resolution: {integrity: sha512-aGNSzqIqqphFwAdIFwVvKIyXD1Iy3CxrEJVIZAT87Ecyi4vCmmDD2v2l9h+Gd3/wSy3JZlOI792LJbKAjyy9rQ==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@yuku-parser/binding-linux-arm64-musl@0.10.2': + resolution: {integrity: sha512-Ddl1sF0rtuCXyHGSOV6dcmdx+ETv1iD+IVFqQuOjzkJZWclxJxdBWX6ZJMRtTiGqGUTbqLEKiTXxpR/Bvqk+2A==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@yuku-parser/binding-linux-x64-gnu@0.10.2': + resolution: {integrity: sha512-/nlcpR6IF5U+0m5L9wvAIXeQa2DT+BXuvqKDsTcOTlcc0B4dHNyqE5hTXsS4hAyimpy2ZK8A1zMNF5hTrjg2cg==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@yuku-parser/binding-linux-x64-musl@0.10.2': + resolution: {integrity: sha512-GX80dxTQD/M/OryyuxJcFzFENzry3cDFPvCFTyWogNWQH3fSHfMrNfpwpl1YzMos1eV9NygK5GSDG7VArQlb4w==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@yuku-parser/binding-win32-arm64@0.10.2': + resolution: {integrity: sha512-agePQBV4VHewiGU0ACSjscZ/hJd283f9JfAF19Gf1rI5+wy5tTgx4GS36i0b3xim15AQ4RCpDRosEvhLZ4zAOw==} + cpu: [arm64] + os: [win32] + + '@yuku-parser/binding-win32-x64@0.10.2': + resolution: {integrity: sha512-s8//CMpgL5+y1lvDCdyh1rwGI5+ytDJywFJRe1vnhI7n0j+caxshNucurikYLLVeQ2SYFex6aPrzgQxkabBQRA==} + cpu: [x64] + os: [win32] + + '@yuku-toolchain/types@0.10.2': + resolution: {integrity: sha512-sSeo4SSSToiS+sSD+bwn/s94EEcaLJ7tG5LCp8gFYC1G5VzxzX7fqB6m9RU1yMD8KTpu6zdU/I1NlQf8hVBJ9Q==} accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} @@ -2567,22 +3149,9 @@ packages: ajv-formats@2.1.1: resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true ajv-formats@3.0.1: resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true - - ajv@8.18.0: - resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} ajv@8.20.0: resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} @@ -2658,6 +3227,9 @@ packages: resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} engines: {node: '>=4'} + ast-v8-to-istanbul@1.0.7: + resolution: {integrity: sha512-kFL68AG6ajd8fg248zwM9GQrUWEp79gsmjum34OEXjs4yHuUMZfYKwOLW9GMmB4oNvVrj+EAGxsP7ye2UR9UlA==} + astring@1.9.0: resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} hasBin: true @@ -2804,6 +3376,10 @@ packages: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} + cac@7.0.0: + resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==} + engines: {node: '>=20.19.0'} + cacheable-lookup@7.0.0: resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==} engines: {node: '>=14.16'} @@ -2962,6 +3538,10 @@ packages: comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + commander@15.0.0: + resolution: {integrity: sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==} + engines: {node: '>=22.12.0'} + commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} @@ -2976,8 +3556,8 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - console-table-printer@2.12.1: - resolution: {integrity: sha512-wKGOQRRvdnd89pCeH96e2Fn4wkbenSP6LMHfjfyNLMbGuHEFbMqQNuxXqd0oXG9caIOQ1FTvc5Uijp9/4jujnQ==} + console-table-printer@2.15.0: + resolution: {integrity: sha512-SrhBq4hYVjLCkBVOWaTzceJalvn5K1Zq5aQA6wXC/cYjI3frKWNPEMK3sZsJfNNQApvCQmgBcc13ZKmFj8qExw==} content-disposition@0.5.4: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} @@ -2990,9 +3570,6 @@ packages: convert-source-map@1.9.0: resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} - convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - convert-to-spaces@2.0.1: resolution: {integrity: sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -3004,9 +3581,6 @@ packages: resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} engines: {node: '>= 0.6'} - core-js@3.42.0: - resolution: {integrity: sha512-Sz4PP4ZA+Rq4II21qkNqOEDTDrCvcANId3xpIgB34NDkWc3UduWj2dqEtN9yZIq8Dk3HyPI33x9sqqU5C8sr0g==} - cors@2.8.6: resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} engines: {node: '>= 0.10'} @@ -3074,12 +3648,12 @@ packages: dataloader@2.2.3: resolution: {integrity: sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA==} - date-fns@3.6.0: - resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} - date-fns@4.1.0: resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} + date-fns@4.4.0: + resolution: {integrity: sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==} + dateformat@4.6.3: resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} @@ -3148,6 +3722,9 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} + defu@6.1.7: + resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} + degenerator@5.0.1: resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} engines: {node: '>= 14'} @@ -3210,6 +3787,14 @@ packages: dompurify@3.2.7: resolution: {integrity: sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==} + dotenv-expand@11.0.6: + resolution: {integrity: sha512-8NHi73otpWsZGBSZwwknTXS5pqMOrk9+Ssrna8xCaxkzEpU9OTf9R5ArQGVw03//Zmk9MOwLPng9WwndvpAJ5g==} + engines: {node: '>=12'} + + dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + engines: {node: '>=12'} + drizzle-kit@0.31.7: resolution: {integrity: sha512-hOzRGSdyKIU4FcTSFYGKdXEjFsncVwHZ43gY3WU5Bz9j5Iadp6Rh6hxLSQ1IWXpKLBKt/d5y1cpSPcV+FcoQ1A==} hasBin: true @@ -3306,6 +3891,15 @@ packages: sqlite3: optional: true + dts-resolver@3.0.0: + resolution: {integrity: sha512-1T1f+z+4tl9XD+m+0HBgWoL/nm0bOIffyWaUuUSBlFg/86IWvfx+wjNaO/ybU0AJzG9/Mi5hBUgGV6zCmWEN7Q==} + engines: {node: ^22.18.0 || >=24.0.0} + peerDependencies: + oxc-resolver: '>=11.0.0' + peerDependenciesMeta: + oxc-resolver: + optional: true + dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} @@ -3319,6 +3913,10 @@ packages: emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + empathic@2.1.0: + resolution: {integrity: sha512-AnfC1ATldl49/cvZdLPDjBfrRNwbDO05aibiOtzQu3qtlbJtomNLhF30HEtn/7iBz50dlMECqATo3fG0LrdEgw==} + engines: {node: '>=14'} + encodeurl@2.0.0: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} @@ -3369,8 +3967,8 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-module-lexer@2.1.0: - resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==} + es-module-lexer@2.3.2: + resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} es-object-atoms@1.1.2: resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} @@ -3487,8 +4085,8 @@ packages: resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} engines: {node: '>=6'} - expect-type@1.3.0: - resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} engines: {node: '>=12.0.0'} expr-eval-fork@3.0.3: @@ -3507,8 +4105,8 @@ packages: engines: {node: '>= 10.17.0'} hasBin: true - fast-copy@3.0.2: - resolution: {integrity: sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==} + fast-copy@4.1.1: + resolution: {integrity: sha512-A4QTJmuiztpGtr6AMeJts9R4hbj2ZBUwtOaKrG6rw2y7t6+IaJKjz5M3XDs8BUznxDH43FVc6A0y/gWlMl4UtA==} fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -3565,9 +4163,9 @@ packages: fflate@0.8.3: resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==} - file-type@21.3.4: - resolution: {integrity: sha512-Ievi/yy8DS3ygGvT47PjSfdFoX+2isQueoYP1cntFW1JLYAuS4GD7NUPGg4zv2iZfV52uDyk5w5Z0TdpRS6Q1g==} - engines: {node: '>=20'} + file-type@22.0.1: + resolution: {integrity: sha512-ww5Mhre0EE+jmBvOXTmXAbEMuZE7uX4a3+oRCQFNj8w++g3ev913N6tXQz0XTXbueQ5TWQfm6BdaViEHHn8bhA==} + engines: {node: '>=22'} fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} @@ -3695,8 +4293,12 @@ packages: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} - get-tsconfig@4.8.1: - resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} + get-tsconfig@4.14.0: + resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==} + + get-tsconfig@5.0.0-beta.6: + resolution: {integrity: sha512-X6fBC0pmImC70gvX2zm56go9hx0MyoGVdG0tUCkg/D+Xnh5TJsOZ7iDbOdI3PvmtrDxnu1YdDufpK2QJX1Meqw==} + engines: {node: '>=20.20.0'} get-uri@6.0.5: resolution: {integrity: sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==} @@ -3848,6 +4450,9 @@ packages: hoist-non-react-statics@3.3.2: resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + hookable@6.1.2: + resolution: {integrity: sha512-+abwxtiEA52GCVIsQqut3S/uKTbUwYIp4Pe/vv+6py5XiXBCqMZHg6pA6Y5qhgLSEys0/cYuPbO0z1QFj5ZCmg==} + html-void-elements@3.0.0: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} @@ -3896,9 +4501,9 @@ packages: resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} - image-size@2.0.2: - resolution: {integrity: sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w==} - engines: {node: '>=16.x'} + image-dimensions@2.5.1: + resolution: {integrity: sha512-It7CkTNp7IYA8jhvGgz8K18OAbHF3/Juk8RNEyTyMFfolJOIU1Y2wGDnzDQPbGCjyxVF/++pwIZE0BKJUEOb1g==} + engines: {node: '>=18'} hasBin: true immer@9.0.21: @@ -3911,6 +4516,10 @@ packages: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} + import-without-cache@0.4.1: + resolution: {integrity: sha512-vXoV9PjKHEednCUu01e98TkImxy67e3BJXbTIOmIq4Hyzw3IAwYRcuPkfeTzJzwyyts4kjuA73x+pWJLc4f86A==} + engines: {node: ^22.18.0 || >=24.0.0} + indent-string@5.0.0: resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} engines: {node: '>=12'} @@ -3970,8 +4579,8 @@ packages: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} - ipaddr.js@2.2.0: - resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==} + ipaddr.js@2.3.0: + resolution: {integrity: sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==} engines: {node: '>= 10'} is-alphabetical@2.0.1: @@ -4155,8 +4764,8 @@ packages: resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true - jose@5.10.0: - resolution: {integrity: sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==} + jose@6.2.3: + resolution: {integrity: sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==} jose@6.2.9: resolution: {integrity: sha512-XrchZOFZUl/T3vTwRe8XK+cJrGtMF4th1ARnDfwbBXFKThGhlsxEE4Zu03AD/bjJSt/9jT/mxrOCkJWOg77aPA==} @@ -4168,6 +4777,9 @@ packages: js-base64@3.7.8: resolution: {integrity: sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow==} + js-tokens@10.0.0: + resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -4175,10 +4787,6 @@ packages: resolution: {integrity: sha512-S99WuO3HlhO3XN41EtYUNl9zzXjoJx7QvmipxsJVxtCBT0YHEFy+iOJhjSvrmV12nYhWpZaM8lPHkJm0yUMbag==} hasBin: true - js-yaml@4.1.1: - resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} - hasBin: true - js-yaml@4.3.1: resolution: {integrity: sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==} hasBin: true @@ -4282,8 +4890,11 @@ packages: resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} engines: {node: '>=12'} - magic-string@0.30.21: - resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + magic-string@1.4.2: + resolution: {integrity: sha512-vG+rjFRj1PqdIBozIxAGMjPlOhaVe+GXpbttY/iSK7rGcJRMlwNJO7dcUwmUqkymsFLJiNGI06t4D7Fr7yRC9g==} + + magicast@0.5.5: + resolution: {integrity: sha512-UicdXN8zQ3JHlxVq+28afMXPr1z7WNY6+7EJnzTdQWkTAlMLF5fNCCKxJHBQwGaNGR11581EiQmQzx73+MvszA==} markdown-extensions@2.0.0: resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} @@ -4566,11 +5177,6 @@ packages: mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - nanoid@3.3.12: - resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - nanoid@3.3.18: resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -4598,8 +5204,8 @@ packages: react: '>= 19.1.0' react-dom: '>= 19.1.0' - next@16.2.9: - resolution: {integrity: sha512-MEOJiq/UvuezAdqVSceHbqDgZt1kDw2tpGVOlsdIoJsQdbN2JY2hpVG4xnXGkbdJUOEWhnRfiu/O4Hpc9Juwww==} + next@16.4.0: + resolution: {integrity: sha512-N2C+9q89Z/oLrsepSRS4jRMtwmUZAoZuAHU0e9T6+sAB5DdhADmrnOW+zjYijlrYFmVfmQhGVGy9eMFoB7UgKg==} engines: {node: '>=20.9.0'} hasBin: true peerDependencies: @@ -4698,8 +5304,13 @@ packages: resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} engines: {node: '>= 0.4'} - obug@2.1.1: - resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + obug@2.2.1: + resolution: {integrity: sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==} + engines: {node: '>=12.20.0'} + + obug@3.0.0: + resolution: {integrity: sha512-5vvB5+W7ePv+p3uqxi+RcW1XAzLW0/hxt3/4X4Lc4qHudzOhmBiBwOY6DRob4WnanAEGvNcLjF+KNOufrUoEQw==} + engines: {node: '>=12.20.0'} on-exit-leak-free@2.1.2: resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} @@ -4742,8 +5353,8 @@ packages: oxc-resolver@11.20.0: resolution: {integrity: sha512-CblytBiV/a/ZXY34dsVU2NxhIOxMXst8CvDCtyBelVITgd7PLrKzbEbA6oKLdPjvDKDzCiW48qzmzZ+mYaqn+g==} - oxfmt@0.54.0: - resolution: {integrity: sha512-DjnMwn7smSLF+Mc2+pRItnuPftm/dkUFpY/d4+33y9TfKrsHZo8GLhmUg9BrOIUEy94Rlom1Q11N6vuhE+e0oQ==} + oxfmt@0.71.0: + resolution: {integrity: sha512-lUPUl0d/+Io5pDrsPXWs6rB4N/bpB78oj9CTDpnbulfDz+0r3XXcHPlQ7kRPJ2GjIT4nX+/mcqunOeP9BvsEtg==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -4755,12 +5366,12 @@ packages: vite-plus: optional: true - oxlint@1.69.0: - resolution: {integrity: sha512-ypZkK/aDc5NQV8zIR6s2H2Tl3aNW8FmJ1m9+2qsaYuRenl8vgnHNCGwTHviWJdUQzglOlHFchgopdtGhSy17Rw==} + oxlint@1.86.0: + resolution: {integrity: sha512-og0lhgvZfgGF//gOOmZXvtr+GmBbAGEnbEhv/QUg7UW2Wi4wHMnJbnMD+zuHgPdJxdklfgpPupdlAaAySyxrZg==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - oxlint-tsgolint: '>=0.22.1' + oxlint-tsgolint: '>=7.0.2003' vite-plus: '*' peerDependenciesMeta: oxlint-tsgolint: @@ -4842,12 +5453,9 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} - pathe@2.0.3: - resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - - payload@3.85.1: - resolution: {integrity: sha512-vfnqwwMOru9wpdiFB3gyb6B+gUt7cjE8+YiNn1g+26dPOyGkAZa9U1QmCMSZfgARj5H42llElQHQw9WX3lYbIw==} - engines: {node: ^18.20.2 || >=20.9.0} + payload@4.0.0-canary.38: + resolution: {integrity: sha512-PJMpPqTIsd4DIOyVo3aYVETfvOfsfVd7PfqwJjnbCCqv0Zafzx/H0zACJbhMt1N5CtT5ID4HL6SnNmLWMsgqBQ==} + engines: {node: '>=24.15.0'} hasBin: true peerDependencies: graphql: ^16.8.1 @@ -4866,22 +5474,26 @@ packages: resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} + engines: {node: '>=12'} + pify@2.3.0: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} - pino-abstract-transport@2.0.0: - resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} + pino-abstract-transport@3.0.0: + resolution: {integrity: sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==} - pino-pretty@13.1.2: - resolution: {integrity: sha512-3cN0tCakkT4f3zo9RXDIhy6GTvtYD6bK4CRBLN9j3E/ePqN1tugAXD5rGVfoChW6s0hiek+eyYlLNqc/BG7vBQ==} + pino-pretty@13.1.3: + resolution: {integrity: sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==} hasBin: true pino-std-serializers@7.1.0: resolution: {integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==} - pino@9.14.0: - resolution: {integrity: sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==} + pino@10.3.1: + resolution: {integrity: sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==} hasBin: true pirates@4.0.7: @@ -4937,10 +5549,6 @@ packages: postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss@8.4.31: - resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} - engines: {node: ^10 || ^12 || >=14} - postcss@8.5.23: resolution: {integrity: sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==} engines: {node: ^10 || ^12 || >=14} @@ -5030,6 +5638,9 @@ packages: resolution: {integrity: sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==} engines: {node: '>=0.6'} + quansync@1.0.0: + resolution: {integrity: sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -5052,16 +5663,20 @@ packages: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true - react-datepicker@7.6.0: - resolution: {integrity: sha512-9cQH6Z/qa4LrGhzdc3XoHbhrxNcMi9MKjZmYgF/1MNNaJwvdSjv3Xd+jjvrEEbKEf71ZgCA3n7fQbdwd70qCRw==} + react-datepicker@9.1.0: + resolution: {integrity: sha512-lOp+m5bc+ttgtB5MHEjwiVu4nlp4CvJLS/PG1OiOe5pmg9kV73pEqO8H0Geqvg2E8gjqTaL9eRhSe+ZpeKP3nA==} peerDependencies: + date-fns-tz: ^3.0.0 react: ^16.9.0 || ^17 || ^18 || ^19 || ^19.0.0-rc react-dom: ^16.9.0 || ^17 || ^18 || ^19 || ^19.0.0-rc + peerDependenciesMeta: + date-fns-tz: + optional: true - react-dom@19.2.7: - resolution: {integrity: sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==} + react-dom@19.3.0: + resolution: {integrity: sha512-JDk8dgif51OjFoDE70+OT9ICyYr+69HlmihNwp1+Nsfbna3t5sIiCa9ZJktDmQ4/1b/rn26hIAR2uYXDMr5r0Q==} peerDependencies: - react: ^19.2.7 + react: ^19.3.0 react-image-crop@10.1.8: resolution: {integrity: sha512-4rb8XtXNx7ZaOZarKKnckgz4xLMvds/YrU6mpJfGhGAsy2Mg4mIw1x+DCCGngVGq2soTBVVOxx2s/C6mTX9+pA==} @@ -5093,8 +5708,8 @@ packages: resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==} engines: {node: '>=0.10.0'} - react@19.2.7: - resolution: {integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==} + react@19.3.0: + resolution: {integrity: sha512-E8LUcbtBWt20bbl2YoHfx4ZDBdxVTfOKtCZn9cDSJ4l6/nuoApcpIBcj47t2wZoVX8g2ZHuMHbiShgCR1T5Sog==} engines: {node: '>=0.10.0'} read-cache@1.0.0: @@ -5112,6 +5727,9 @@ packages: resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} engines: {node: '>= 12.13.0'} + real-require@1.0.0: + resolution: {integrity: sha512-P4nbQYQfePJxRSmY+v/KINxVucm4NF3p3s7pJveMTtom52FR4YGltUQLB8idDXwDDWW+eYrWDFbuzUnjoWHF7g==} + recma-build-jsx@1.0.0: resolution: {integrity: sha512-8GtdyqaBcDfva+GUKDr3nev3VpKAhup1+RvkMvUxURHpW7QyIvk9F5wz7Vzo06CEMSilw6uArgRqhpiUcWp8ew==} @@ -5250,26 +5868,37 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rollup@4.60.4: - resolution: {integrity: sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - - rsbuild-plugin-dts@0.22.1: - resolution: {integrity: sha512-6vBfXgwK4j6GkjSRH13tuceBavc6qwSE5IVSoVCZ/wRuM0ZL7dIjdk+yGSWKJt5lNMfc4XwQoTErGIC9zhgztQ==} - engines: {node: ^20.19.0 || >=22.12.0} + rolldown-plugin-dts@0.28.6: + resolution: {integrity: sha512-qKrFtBfRfR2hP233m7Ic9zf3wv6MSYdZghWKMdEO6dRYZGlNfINJAa1P5ZVvyHh3mrcd7IJQvIY8L9vnm+v5rQ==} + engines: {node: ^22.18.0 || ^24.11.0 || >=26.0.0} peerDependencies: - '@microsoft/api-extractor': ^7 - '@rsbuild/core': ^1.0.0 || ^2.0.0-0 - '@typescript/native-preview': ^7.0.0-0 - typescript: ^5 || ^6 + '@typescript/native-preview': '*' + '@volar/typescript': ~2.4.0 + '@vue/language-core': ~3.2.0 || ~3.3.0 + rolldown: ^1.2.0 + typescript: ^5.0.0 || ^6.0.0 || ~7.0.0 + vue-tsc: ~3.2.0 || ~3.3.0 peerDependenciesMeta: - '@microsoft/api-extractor': - optional: true '@typescript/native-preview': optional: true + '@volar/typescript': + optional: true + '@vue/language-core': + optional: true typescript: optional: true + vue-tsc: + optional: true + + rolldown@1.2.12: + resolution: {integrity: sha512-8wafseiaG80xmXSfqidUNqZcylTlhmPZZt+za2m+js2sFZ8dTNlhIOV2WcbIPx2hgwPBJpEUGFAMZ9bgBBLTSQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + rollup@4.60.4: + resolution: {integrity: sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true run-async@3.0.0: resolution: {integrity: sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==} @@ -5306,8 +5935,8 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - sanitize-filename@1.6.3: - resolution: {integrity: sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==} + sanitize-filename@1.6.4: + resolution: {integrity: sha512-9ZyI08PsvdQl2r/bBIGubpVdR3RR9sY6RDiWFPreA21C/EFlQhmgo20UZlNjZMMZNubusLhAQozkA0Od5J21Eg==} sass@1.77.4: resolution: {integrity: sha512-vcF3Ckow6g939GMA4PeU7b2K/9FALXk2KF9J87txdHzXbUF9XRQRwSxcAs/fGaTnJeBFd7UoV22j3lzMLdM0Pw==} @@ -5324,8 +5953,8 @@ packages: scheduler@0.26.0: resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} - scheduler@0.27.0: - resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + scheduler@0.28.0: + resolution: {integrity: sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==} secure-json-parse@4.1.0: resolution: {integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==} @@ -5335,6 +5964,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + send@0.19.2: resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==} engines: {node: '>= 0.8.0'} @@ -5373,6 +6007,15 @@ packages: resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + sharp@0.35.5: + resolution: {integrity: sha512-Ywn4OnzGukp7CDMrp08RQ50YKmuwG47brZgIVPTvBaaAfQlRlygrRqSrxdCiL9M+LlzLBiJ68IR1QqvzHyjC7g==} + engines: {node: '>=20.9.0'} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -5400,9 +6043,6 @@ packages: resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} engines: {node: '>= 0.4'} - siginfo@2.0.0: - resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} @@ -5498,9 +6138,6 @@ packages: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} - stackback@0.0.2: - resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - state-local@1.0.7: resolution: {integrity: sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==} @@ -5508,8 +6145,8 @@ packages: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} - std-env@4.1.0: - resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} + std-env@4.3.0: + resolution: {integrity: sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag==} stop-iteration-iterator@1.1.0: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} @@ -5649,26 +6286,28 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} - thread-stream@3.1.0: - resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} + thread-stream@4.2.0: + resolution: {integrity: sha512-e2zZ96wSChazBsbENf/Pcm/4swHt2cEKQ92rhUjkL9GCKiTDJIaTBenjE/m9DXi0QBmTMDkFDdOomUy20A1tDQ==} + engines: {node: '>=20'} - tinybench@2.9.0: - resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + tinybench@6.2.0: + resolution: {integrity: sha512-78U2TlB2CnVenajOFzf3BKSm0J6oz5L0NV7g32LCPccvYc0lbWvys4d3uUUCS2B1N8PAf2+aekR8i1KbC3HO7Q==} + engines: {node: '>=20.0.0'} - tinyexec@1.2.4: - resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} + tinyexec@1.3.1: + resolution: {integrity: sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==} engines: {node: '>=18'} - tinyglobby@0.2.16: - resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} - tinypool@2.1.0: - resolution: {integrity: sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==} + tinypool@2.2.0: + resolution: {integrity: sha512-jBrmx4lYmaC9k/mgPbylxs7kBUxHtD8256up+HjLaDFfXScKJQyil+SWXSvhAtT7XHo+yTVlpB81PGmHP8oLSQ==} engines: {node: ^20.0.0 || >=22.0.0} - tinyrainbow@3.1.0: - resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} + tinyrainbow@3.2.0: + resolution: {integrity: sha512-LgO3D9yZJjApUiuUfl9iFAwrtaX4+lok3wJIqttGoKCHlWUqHqbQpnxCf82L8FjgKsh4iGo78hqJwgL8F6To2A==} engines: {node: '>=14.0.0'} to-data-view@1.1.0: @@ -5698,6 +6337,10 @@ packages: tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + trim-lines@3.0.1: resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} @@ -5721,16 +6364,39 @@ packages: ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} - tsconfck@3.1.6: - resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} - engines: {node: ^18 || >=20} - deprecated: unmaintained + tsdown@0.23.0: + resolution: {integrity: sha512-BaT+ep1xnj5hdyICLd5r5SYYE+TXnI1ATePLykv9vcKpHpFTWUWRH+x1AF/E2qcu3YB6+vI8IdyxIIIffEqw5Q==} + engines: {node: ^22.18.0 || ^24.11.0 || >=26.0.0} hasBin: true peerDependencies: - typescript: ^5.0.0 + '@arethetypeswrong/core': ^0.18.1 + '@tsdown/css': 0.23.0 + '@tsdown/exe': 0.23.0 + '@vitejs/devtools': '*' + publint: ^0.3.8 + tsx: '*' + typescript: ^5.0.0 || ^6.0.0 || ^7.0.0 + unplugin-unused: '>=0.5.0' + unrun: '*' peerDependenciesMeta: + '@arethetypeswrong/core': + optional: true + '@tsdown/css': + optional: true + '@tsdown/exe': + optional: true + '@vitejs/devtools': + optional: true + publint: + optional: true + tsx: + optional: true typescript: optional: true + unplugin-unused: + optional: true + unrun: + optional: true tslib@1.14.1: resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} @@ -5743,6 +6409,11 @@ packages: engines: {node: '>=18.0.0'} hasBin: true + tsx@4.23.15: + resolution: {integrity: sha512-Yiex1Ovn8z2xPpOWckIiysV1SSyRMY9BkLF++q0yKiDxCqRhosKfMg3janKkiLBwZ5c/YryloKwGZcrEmtwxKw==} + engines: {node: '>=18.0.0'} + hasBin: true + tunnel-agent@0.6.0: resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} @@ -5789,9 +6460,9 @@ packages: typed-query-selector@2.12.2: resolution: {integrity: sha512-EOPFbyIub4ngnEdqi2yOcNeDLaX/0jcE1JoAXQDDMIthap7FoN795lc/SHfIq2d416VufXpM8z/lD+WRm2gfOQ==} - typescript@6.0.3: - resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} - engines: {node: '>=14.17'} + typescript@7.0.2: + resolution: {integrity: sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==} + engines: {node: '>=16.20.0'} hasBin: true uint8array-extras@1.5.0: @@ -5802,11 +6473,17 @@ packages: resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} engines: {node: '>= 0.4'} + unconfig-core@7.5.0: + resolution: {integrity: sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==} + undici-types@7.24.6: resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} - undici@7.24.4: - resolution: {integrity: sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w==} + undici-types@8.9.0: + resolution: {integrity: sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==} + + undici@7.29.1: + resolution: {integrity: sha512-RYONW2MeafgYlkVOKYKkA/Ag7BmXqgIWCa8t1m0JcxrQg9pI9lEqRhAOruOBCbAohOa/gkCF+iPi9hrgvTzu6Q==} engines: {node: '>=20.18.1'} unified@11.0.5: @@ -5912,14 +6589,18 @@ packages: resolution: {integrity: sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==} hasBin: true - uuid@13.0.2: - resolution: {integrity: sha512-vzi9uRZ926x4XV73S/4qQaTwPXM2JBj6/6lI/byHH1jOpCzb0zDbfytgA9LcN/hzb2l7WQSQnxITOVx5un/wGw==} + uuid@14.0.0: + resolution: {integrity: sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==} hasBin: true vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} + verkit@0.4.1: + resolution: {integrity: sha512-mMVzj0TXExtVdlDEq+Mzp0eyOuyznNpFobNM3uAqe3HVm/Ps2c+u17BligMkZjZCA6EiXpoon8iRTer3iu40SQ==} + engines: {node: '>=18.12.0'} + vfile-location@5.0.3: resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} @@ -5932,12 +6613,6 @@ packages: vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - vite-tsconfig-paths@7.0.0-alpha.1: - resolution: {integrity: sha512-R2xUKqgU3lETsbx66sOoVj6/2hf9J9Aizavs03obMY5TxC99UbF1tM4bSfn1KZ8HxtOEaK3gdm/CEutS3yBvog==} - engines: {node: '>=18'} - peerDependencies: - vite: '>=5.0.0' - vite@7.3.3: resolution: {integrity: sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -5978,23 +6653,23 @@ packages: yaml: optional: true - vitest@4.1.8: - resolution: {integrity: sha512-flY6ScbCIt9HThs+C5HS7jvGOB560DJtk/Z15IQROTA6zEy49Nh8T/dofWTQL+n3vswqn87sbJNiuqw1SDp5Ig==} - engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + vitest@5.0.3: + resolution: {integrity: sha512-xMw97S3rjdtj5dkVat7jCsqWBpvchs3RlpQctUqwJD0KkERk40vz2fJ77lDwW/Vzh/pk18eItYAzkodhSes3jQ==} + engines: {node: ^22.12.0 || ^24.0.0 || >=26.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 - '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.8 - '@vitest/browser-preview': 4.1.8 - '@vitest/browser-webdriverio': 4.1.8 - '@vitest/coverage-istanbul': 4.1.8 - '@vitest/coverage-v8': 4.1.8 - '@vitest/ui': 4.1.8 + '@types/node': ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 5.0.3 + '@vitest/browser-preview': 5.0.3 + '@vitest/browser-webdriverio': ^5.0.0-beta.5 || >=5.0.0 + '@vitest/coverage-istanbul': 5.0.3 + '@vitest/coverage-v8': 5.0.3 + '@vitest/ui': 5.0.3 happy-dom: '*' jsdom: '*' - vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + vite: ^6.4.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: '@edge-runtime/vm': optional: true @@ -6053,9 +6728,9 @@ packages: engines: {node: '>= 8'} hasBin: true - why-is-node-running@2.3.0: - resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} - engines: {node: '>=8'} + why-is-node-running@3.2.1: + resolution: {integrity: sha512-Tb2FUhB4vUsGQlfSquQLYkApkuPAFQXGFzxWKHHumVz2dK+X1RUm/HnID4+TfIGYJ1kTcwOaCk/buYCEJr6YjQ==} + engines: {node: '>=20.11'} hasBin: true widest-line@5.0.0: @@ -6145,6 +6820,15 @@ packages: yoga-layout@3.2.1: resolution: {integrity: sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==} + yuku-ast@0.10.2: + resolution: {integrity: sha512-UnG9mA6giglCvSErft2/40TVFX750Sj1xgwddPLpG7J7rlr/P1wPADg9G2RK+d/1tNLtRVoLdMMOMVBB9561TQ==} + + yuku-codegen@0.10.2: + resolution: {integrity: sha512-hentl2dtrF6cPjiAirtkfxfFZBA6Hdm61UqRJ7cA0qrlDNMk9PZGOjw5w1mx3E0hu/6pHcJF4dJW+D0SXHPZOA==} + + yuku-parser@0.10.2: + resolution: {integrity: sha512-CgaU0/PPjCAIEZ3WQroosOxTY3eeKldAN3h+vk8pMNz4+jl1CZzBR4pW+K/rRPF112VooCL5FdjJoiMNjDrL2A==} + zod-to-json-schema@3.20.4: resolution: {integrity: sha512-Un9+kInJ2Zt63n6Z7mLqBifzzPcOyX+b+Exuzf7L1+xqck9Q2EPByyTRduV3kmSPaXaRer1JCsucubpgL1fipg==} peerDependencies: @@ -6159,6 +6843,9 @@ packages: zod@4.3.6: resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + zod@4.6.5: + resolution: {integrity: sha512-v5l/aFXZQeai4awLbOpSoHecE9UiMrnfx75tEXLjNonXVARxQ5mOeipTjROUchszUNCqnE+hqAMujRsRHsut2Q==} + zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} @@ -6175,7 +6862,7 @@ snapshots: dependencies: '@jsdevtools/ono': 7.1.3 '@types/json-schema': 7.0.15 - js-yaml: 4.1.1 + js-yaml: 4.3.1 '@ark/schema@0.56.2': dependencies: @@ -6183,45 +6870,6 @@ snapshots: '@ark/util@0.56.2': {} - '@ast-grep/napi-darwin-arm64@0.37.0': - optional: true - - '@ast-grep/napi-darwin-x64@0.37.0': - optional: true - - '@ast-grep/napi-linux-arm64-gnu@0.37.0': - optional: true - - '@ast-grep/napi-linux-arm64-musl@0.37.0': - optional: true - - '@ast-grep/napi-linux-x64-gnu@0.37.0': - optional: true - - '@ast-grep/napi-linux-x64-musl@0.37.0': - optional: true - - '@ast-grep/napi-win32-arm64-msvc@0.37.0': - optional: true - - '@ast-grep/napi-win32-ia32-msvc@0.37.0': - optional: true - - '@ast-grep/napi-win32-x64-msvc@0.37.0': - optional: true - - '@ast-grep/napi@0.37.0': - optionalDependencies: - '@ast-grep/napi-darwin-arm64': 0.37.0 - '@ast-grep/napi-darwin-x64': 0.37.0 - '@ast-grep/napi-linux-arm64-gnu': 0.37.0 - '@ast-grep/napi-linux-arm64-musl': 0.37.0 - '@ast-grep/napi-linux-x64-gnu': 0.37.0 - '@ast-grep/napi-linux-x64-musl': 0.37.0 - '@ast-grep/napi-win32-arm64-msvc': 0.37.0 - '@ast-grep/napi-win32-ia32-msvc': 0.37.0 - '@ast-grep/napi-win32-x64-msvc': 0.37.0 - '@asyncapi/parser@3.4.0': dependencies: '@asyncapi/specs': 6.11.1 @@ -6238,7 +6886,7 @@ snapshots: '@types/urijs': 1.19.26 ajv: 8.20.0 ajv-errors: 3.0.0(ajv@8.20.0) - ajv-formats: 2.1.1(ajv@8.20.0) + ajv-formats: 2.1.1 avsc: 5.7.9 js-yaml: 4.3.1 jsonpath-plus: 10.4.0 @@ -6306,67 +6954,69 @@ snapshots: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 - '@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.2.7(react@19.2.3))(react@19.2.3)': + '@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.3.0(react@19.2.3))(react@19.2.3)': dependencies: '@babel/runtime': 7.29.7 - '@base-ui/utils': 0.3.2(@types/react@19.2.15)(react-dom@19.2.7(react@19.2.3))(react@19.2.3) - '@floating-ui/react-dom': 2.1.9(react-dom@19.2.7(react@19.2.3))(react@19.2.3) + '@base-ui/utils': 0.3.2(@types/react@19.2.15)(react-dom@19.3.0(react@19.2.3))(react@19.2.3) + '@floating-ui/react-dom': 2.1.9(react-dom@19.3.0(react@19.2.3))(react@19.2.3) '@floating-ui/utils': 0.2.12 react: 19.2.3 - react-dom: 19.2.7(react@19.2.3) + react-dom: 19.3.0(react@19.2.3) use-sync-external-store: 1.6.0(react@19.2.3) optionalDependencies: '@date-fns/tz': 1.2.0 '@types/react': 19.2.15 date-fns: 4.1.0 - '@base-ui/utils@0.3.2(@types/react@19.2.15)(react-dom@19.2.7(react@19.2.3))(react@19.2.3)': + '@base-ui/utils@0.3.2(@types/react@19.2.15)(react-dom@19.3.0(react@19.2.3))(react@19.2.3)': dependencies: '@babel/runtime': 7.29.7 '@floating-ui/utils': 0.2.12 react: 19.2.3 - react-dom: 19.2.7(react@19.2.3) + react-dom: 19.3.0(react@19.2.3) reselect: 5.2.0 use-sync-external-store: 1.6.0(react@19.2.3) optionalDependencies: '@types/react': 19.2.15 + '@bcoe/v8-coverage@1.0.2': {} + '@borewit/text-codec@0.2.2': {} '@canvas/image-data@1.1.0': {} '@date-fns/tz@1.2.0': {} - '@dnd-kit/accessibility@3.1.1(react@19.2.7)': + '@dnd-kit/accessibility@3.1.1(react@19.3.0)': dependencies: - react: 19.2.7 + react: 19.3.0 tslib: 2.8.1 - '@dnd-kit/core@6.3.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@dnd-kit/core@6.3.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': dependencies: - '@dnd-kit/accessibility': 3.1.1(react@19.2.7) - '@dnd-kit/utilities': 3.2.2(react@19.2.7) - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) + '@dnd-kit/accessibility': 3.1.1(react@19.3.0) + '@dnd-kit/utilities': 3.2.2(react@19.3.0) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) tslib: 2.8.1 - '@dnd-kit/modifiers@9.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)': + '@dnd-kit/modifiers@9.0.0(@dnd-kit/core@6.3.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(react@19.3.0)': dependencies: - '@dnd-kit/core': 6.3.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@dnd-kit/utilities': 3.2.2(react@19.2.7) - react: 19.2.7 + '@dnd-kit/core': 6.3.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@dnd-kit/utilities': 3.2.2(react@19.3.0) + react: 19.3.0 tslib: 2.8.1 - '@dnd-kit/sortable@10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)': + '@dnd-kit/sortable@10.0.0(@dnd-kit/core@6.3.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(react@19.3.0)': dependencies: - '@dnd-kit/core': 6.3.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@dnd-kit/utilities': 3.2.2(react@19.2.7) - react: 19.2.7 + '@dnd-kit/core': 6.3.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@dnd-kit/utilities': 3.2.2(react@19.3.0) + react: 19.3.0 tslib: 2.8.1 - '@dnd-kit/utilities@3.2.2(react@19.2.7)': + '@dnd-kit/utilities@3.2.2(react@19.3.0)': dependencies: - react: 19.2.7 + react: 19.3.0 tslib: 2.8.1 '@drizzle-team/brocli@0.10.2': {} @@ -6382,6 +7032,11 @@ snapshots: tslib: 2.8.1 optional: true + '@emnapi/runtime@1.11.3': + dependencies: + tslib: 2.8.1 + optional: true + '@emnapi/wasi-threads@1.2.1': dependencies: tslib: 2.8.1 @@ -6415,17 +7070,17 @@ snapshots: '@emotion/memoize@0.9.0': {} - '@emotion/react@11.14.0(@types/react@19.2.15)(react@19.2.7)': + '@emotion/react@11.14.0(@types/react@19.2.15)(react@19.3.0)': dependencies: '@babel/runtime': 7.29.7 '@emotion/babel-plugin': 11.13.5 '@emotion/cache': 11.14.0 '@emotion/serialize': 1.3.3 - '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.2.7) + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.3.0) '@emotion/utils': 1.4.2 '@emotion/weak-memoize': 0.4.0 hoist-non-react-statics: 3.3.2 - react: 19.2.7 + react: 19.3.0 optionalDependencies: '@types/react': 19.2.15 transitivePeerDependencies: @@ -6443,9 +7098,9 @@ snapshots: '@emotion/unitless@0.10.0': {} - '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@19.2.7)': + '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@19.3.0)': dependencies: - react: 19.2.7 + react: 19.3.0 '@emotion/utils@1.4.2': {} @@ -6461,7 +7116,7 @@ snapshots: '@esbuild-kit/esm-loader@2.6.5': dependencies: '@esbuild-kit/core-utils': 3.3.2 - get-tsconfig: 4.8.1 + get-tsconfig: 4.14.0 '@esbuild/aix-ppc64@0.25.12': optional: true @@ -6763,27 +7418,23 @@ snapshots: '@esbuild/win32-x64@0.28.1': optional: true - '@faceless-ui/modal@3.0.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@faceless-ui/modal@3.0.2(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': dependencies: body-scroll-lock: 4.0.0-beta.0 focus-trap: 7.5.4 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - react-transition-group: 4.4.5(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + react-transition-group: 4.4.5(react-dom@19.3.0(react@19.3.0))(react@19.3.0) - '@faceless-ui/scroll-info@2.0.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@faceless-ui/scroll-info@2.0.0(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': dependencies: - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) - '@faceless-ui/window-info@3.0.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@faceless-ui/window-info@3.0.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': dependencies: - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - - '@floating-ui/core@1.7.5': - dependencies: - '@floating-ui/utils': 0.2.11 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) '@floating-ui/core@1.8.0': dependencies: @@ -6791,36 +7442,34 @@ snapshots: '@floating-ui/dom@1.7.6': dependencies: - '@floating-ui/core': 1.7.5 - '@floating-ui/utils': 0.2.11 + '@floating-ui/core': 1.8.0 + '@floating-ui/utils': 0.2.12 '@floating-ui/dom@1.8.0': dependencies: '@floating-ui/core': 1.8.0 '@floating-ui/utils': 0.2.12 - '@floating-ui/react-dom@2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@floating-ui/react-dom@2.1.9(react-dom@19.3.0(react@19.2.3))(react@19.2.3)': dependencies: - '@floating-ui/dom': 1.7.6 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) + '@floating-ui/dom': 1.8.0 + react: 19.2.3 + react-dom: 19.3.0(react@19.2.3) - '@floating-ui/react-dom@2.1.9(react-dom@19.2.7(react@19.2.3))(react@19.2.3)': + '@floating-ui/react-dom@2.1.9(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': dependencies: '@floating-ui/dom': 1.8.0 - react: 19.2.3 - react-dom: 19.2.7(react@19.2.3) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) - '@floating-ui/react@0.27.19(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@floating-ui/react@0.27.19(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': dependencies: - '@floating-ui/react-dom': 2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@floating-ui/utils': 0.2.11 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) + '@floating-ui/react-dom': 2.1.9(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@floating-ui/utils': 0.2.12 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) tabbable: 6.4.0 - '@floating-ui/utils@0.2.11': {} - '@floating-ui/utils@0.2.12': {} '@img/colour@1.1.0': {} @@ -6835,6 +7484,11 @@ snapshots: '@img/sharp-libvips-darwin-arm64': 1.2.4 optional: true + '@img/sharp-darwin-arm64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.3.4 + optional: true + '@img/sharp-darwin-x64@0.33.5': optionalDependencies: '@img/sharp-libvips-darwin-x64': 1.0.4 @@ -6845,60 +7499,100 @@ snapshots: '@img/sharp-libvips-darwin-x64': 1.2.4 optional: true + '@img/sharp-darwin-x64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.3.4 + optional: true + + '@img/sharp-freebsd-wasm32@0.35.5': + dependencies: + '@img/sharp-wasm32': 0.35.5 + optional: true + '@img/sharp-libvips-darwin-arm64@1.0.4': optional: true '@img/sharp-libvips-darwin-arm64@1.2.4': optional: true + '@img/sharp-libvips-darwin-arm64@1.3.4': + optional: true + '@img/sharp-libvips-darwin-x64@1.0.4': optional: true '@img/sharp-libvips-darwin-x64@1.2.4': optional: true + '@img/sharp-libvips-darwin-x64@1.3.4': + optional: true + '@img/sharp-libvips-linux-arm64@1.0.4': optional: true '@img/sharp-libvips-linux-arm64@1.2.4': optional: true + '@img/sharp-libvips-linux-arm64@1.3.4': + optional: true + '@img/sharp-libvips-linux-arm@1.0.5': optional: true '@img/sharp-libvips-linux-arm@1.2.4': optional: true + '@img/sharp-libvips-linux-arm@1.3.4': + optional: true + '@img/sharp-libvips-linux-ppc64@1.2.4': optional: true + '@img/sharp-libvips-linux-ppc64@1.3.4': + optional: true + '@img/sharp-libvips-linux-riscv64@1.2.4': optional: true + '@img/sharp-libvips-linux-riscv64@1.3.4': + optional: true + '@img/sharp-libvips-linux-s390x@1.0.4': optional: true '@img/sharp-libvips-linux-s390x@1.2.4': optional: true + '@img/sharp-libvips-linux-s390x@1.3.4': + optional: true + '@img/sharp-libvips-linux-x64@1.0.4': optional: true '@img/sharp-libvips-linux-x64@1.2.4': optional: true + '@img/sharp-libvips-linux-x64@1.3.4': + optional: true + '@img/sharp-libvips-linuxmusl-arm64@1.0.4': optional: true '@img/sharp-libvips-linuxmusl-arm64@1.2.4': optional: true + '@img/sharp-libvips-linuxmusl-arm64@1.3.4': + optional: true + '@img/sharp-libvips-linuxmusl-x64@1.0.4': optional: true '@img/sharp-libvips-linuxmusl-x64@1.2.4': optional: true + '@img/sharp-libvips-linuxmusl-x64@1.3.4': + optional: true + '@img/sharp-linux-arm64@0.33.5': optionalDependencies: '@img/sharp-libvips-linux-arm64': 1.0.4 @@ -6909,6 +7603,11 @@ snapshots: '@img/sharp-libvips-linux-arm64': 1.2.4 optional: true + '@img/sharp-linux-arm64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.3.4 + optional: true + '@img/sharp-linux-arm@0.33.5': optionalDependencies: '@img/sharp-libvips-linux-arm': 1.0.5 @@ -6919,16 +7618,31 @@ snapshots: '@img/sharp-libvips-linux-arm': 1.2.4 optional: true + '@img/sharp-linux-arm@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.3.4 + optional: true + '@img/sharp-linux-ppc64@0.34.5': optionalDependencies: '@img/sharp-libvips-linux-ppc64': 1.2.4 optional: true + '@img/sharp-linux-ppc64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.3.4 + optional: true + '@img/sharp-linux-riscv64@0.34.5': optionalDependencies: '@img/sharp-libvips-linux-riscv64': 1.2.4 optional: true + '@img/sharp-linux-riscv64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.3.4 + optional: true + '@img/sharp-linux-s390x@0.33.5': optionalDependencies: '@img/sharp-libvips-linux-s390x': 1.0.4 @@ -6939,6 +7653,11 @@ snapshots: '@img/sharp-libvips-linux-s390x': 1.2.4 optional: true + '@img/sharp-linux-s390x@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.3.4 + optional: true + '@img/sharp-linux-x64@0.33.5': optionalDependencies: '@img/sharp-libvips-linux-x64': 1.0.4 @@ -6949,6 +7668,11 @@ snapshots: '@img/sharp-libvips-linux-x64': 1.2.4 optional: true + '@img/sharp-linux-x64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.3.4 + optional: true + '@img/sharp-linuxmusl-arm64@0.33.5': optionalDependencies: '@img/sharp-libvips-linuxmusl-arm64': 1.0.4 @@ -6959,6 +7683,11 @@ snapshots: '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 optional: true + '@img/sharp-linuxmusl-arm64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.3.4 + optional: true + '@img/sharp-linuxmusl-x64@0.33.5': optionalDependencies: '@img/sharp-libvips-linuxmusl-x64': 1.0.4 @@ -6969,6 +7698,11 @@ snapshots: '@img/sharp-libvips-linuxmusl-x64': 1.2.4 optional: true + '@img/sharp-linuxmusl-x64@0.35.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.3.4 + optional: true + '@img/sharp-wasm32@0.33.5': dependencies: '@emnapi/runtime': 1.10.0 @@ -6979,145 +7713,164 @@ snapshots: '@emnapi/runtime': 1.10.0 optional: true + '@img/sharp-wasm32@0.35.5': + dependencies: + '@emnapi/runtime': 1.11.3 + optional: true + + '@img/sharp-webcontainers-wasm32@0.35.5': + dependencies: + '@img/sharp-wasm32': 0.35.5 + optional: true + '@img/sharp-win32-arm64@0.34.5': optional: true + '@img/sharp-win32-arm64@0.35.5': + optional: true + '@img/sharp-win32-ia32@0.33.5': optional: true '@img/sharp-win32-ia32@0.34.5': optional: true + '@img/sharp-win32-ia32@0.35.5': + optional: true + '@img/sharp-win32-x64@0.33.5': optional: true '@img/sharp-win32-x64@0.34.5': optional: true + '@img/sharp-win32-x64@0.35.5': + optional: true + '@inquirer/ansi@1.0.2': {} - '@inquirer/checkbox@4.3.2(@types/node@25.9.1)': + '@inquirer/checkbox@4.3.2(@types/node@26.6.4)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@25.9.1) + '@inquirer/core': 10.3.2(@types/node@26.6.4) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.9.1) + '@inquirer/type': 3.0.10(@types/node@26.6.4) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 26.6.4 - '@inquirer/confirm@5.1.21(@types/node@25.9.1)': + '@inquirer/confirm@5.1.21(@types/node@26.6.4)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.9.1) - '@inquirer/type': 3.0.10(@types/node@25.9.1) + '@inquirer/core': 10.3.2(@types/node@26.6.4) + '@inquirer/type': 3.0.10(@types/node@26.6.4) optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 26.6.4 - '@inquirer/core@10.3.2(@types/node@25.9.1)': + '@inquirer/core@10.3.2(@types/node@26.6.4)': dependencies: '@inquirer/ansi': 1.0.2 '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.9.1) + '@inquirer/type': 3.0.10(@types/node@26.6.4) cli-width: 4.1.0 mute-stream: 2.0.0 signal-exit: 4.1.0 wrap-ansi: 6.2.0 yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 26.6.4 - '@inquirer/editor@4.2.23(@types/node@25.9.1)': + '@inquirer/editor@4.2.23(@types/node@26.6.4)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.9.1) - '@inquirer/external-editor': 1.0.3(@types/node@25.9.1) - '@inquirer/type': 3.0.10(@types/node@25.9.1) + '@inquirer/core': 10.3.2(@types/node@26.6.4) + '@inquirer/external-editor': 1.0.3(@types/node@26.6.4) + '@inquirer/type': 3.0.10(@types/node@26.6.4) optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 26.6.4 - '@inquirer/expand@4.0.23(@types/node@25.9.1)': + '@inquirer/expand@4.0.23(@types/node@26.6.4)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.9.1) - '@inquirer/type': 3.0.10(@types/node@25.9.1) + '@inquirer/core': 10.3.2(@types/node@26.6.4) + '@inquirer/type': 3.0.10(@types/node@26.6.4) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 26.6.4 - '@inquirer/external-editor@1.0.3(@types/node@25.9.1)': + '@inquirer/external-editor@1.0.3(@types/node@26.6.4)': dependencies: chardet: 2.2.0 iconv-lite: 0.7.3 optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 26.6.4 '@inquirer/figures@1.0.15': {} - '@inquirer/input@4.3.1(@types/node@25.9.1)': + '@inquirer/input@4.3.1(@types/node@26.6.4)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.9.1) - '@inquirer/type': 3.0.10(@types/node@25.9.1) + '@inquirer/core': 10.3.2(@types/node@26.6.4) + '@inquirer/type': 3.0.10(@types/node@26.6.4) optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 26.6.4 - '@inquirer/number@3.0.23(@types/node@25.9.1)': + '@inquirer/number@3.0.23(@types/node@26.6.4)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.9.1) - '@inquirer/type': 3.0.10(@types/node@25.9.1) + '@inquirer/core': 10.3.2(@types/node@26.6.4) + '@inquirer/type': 3.0.10(@types/node@26.6.4) optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 26.6.4 - '@inquirer/password@4.0.23(@types/node@25.9.1)': + '@inquirer/password@4.0.23(@types/node@26.6.4)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@25.9.1) - '@inquirer/type': 3.0.10(@types/node@25.9.1) + '@inquirer/core': 10.3.2(@types/node@26.6.4) + '@inquirer/type': 3.0.10(@types/node@26.6.4) optionalDependencies: - '@types/node': 25.9.1 - - '@inquirer/prompts@7.9.0(@types/node@25.9.1)': - dependencies: - '@inquirer/checkbox': 4.3.2(@types/node@25.9.1) - '@inquirer/confirm': 5.1.21(@types/node@25.9.1) - '@inquirer/editor': 4.2.23(@types/node@25.9.1) - '@inquirer/expand': 4.0.23(@types/node@25.9.1) - '@inquirer/input': 4.3.1(@types/node@25.9.1) - '@inquirer/number': 3.0.23(@types/node@25.9.1) - '@inquirer/password': 4.0.23(@types/node@25.9.1) - '@inquirer/rawlist': 4.1.11(@types/node@25.9.1) - '@inquirer/search': 3.2.2(@types/node@25.9.1) - '@inquirer/select': 4.4.2(@types/node@25.9.1) + '@types/node': 26.6.4 + + '@inquirer/prompts@7.9.0(@types/node@26.6.4)': + dependencies: + '@inquirer/checkbox': 4.3.2(@types/node@26.6.4) + '@inquirer/confirm': 5.1.21(@types/node@26.6.4) + '@inquirer/editor': 4.2.23(@types/node@26.6.4) + '@inquirer/expand': 4.0.23(@types/node@26.6.4) + '@inquirer/input': 4.3.1(@types/node@26.6.4) + '@inquirer/number': 3.0.23(@types/node@26.6.4) + '@inquirer/password': 4.0.23(@types/node@26.6.4) + '@inquirer/rawlist': 4.1.11(@types/node@26.6.4) + '@inquirer/search': 3.2.2(@types/node@26.6.4) + '@inquirer/select': 4.4.2(@types/node@26.6.4) optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 26.6.4 - '@inquirer/rawlist@4.1.11(@types/node@25.9.1)': + '@inquirer/rawlist@4.1.11(@types/node@26.6.4)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.9.1) - '@inquirer/type': 3.0.10(@types/node@25.9.1) + '@inquirer/core': 10.3.2(@types/node@26.6.4) + '@inquirer/type': 3.0.10(@types/node@26.6.4) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 26.6.4 - '@inquirer/search@3.2.2(@types/node@25.9.1)': + '@inquirer/search@3.2.2(@types/node@26.6.4)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.9.1) + '@inquirer/core': 10.3.2(@types/node@26.6.4) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.9.1) + '@inquirer/type': 3.0.10(@types/node@26.6.4) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 26.6.4 - '@inquirer/select@4.4.2(@types/node@25.9.1)': + '@inquirer/select@4.4.2(@types/node@26.6.4)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@25.9.1) + '@inquirer/core': 10.3.2(@types/node@26.6.4) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.9.1) + '@inquirer/type': 3.0.10(@types/node@26.6.4) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 26.6.4 - '@inquirer/type@3.0.10(@types/node@25.9.1)': + '@inquirer/type@3.0.10(@types/node@26.6.4)': optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 26.6.4 '@isaacs/fs-minipass@4.0.1': dependencies: @@ -7132,6 +7885,8 @@ snapshots: '@jridgewell/sourcemap-codec@1.5.5': {} + '@jridgewell/sourcemap-codec@1.6.0': {} + '@jridgewell/trace-mapping@0.3.31': dependencies: '@jridgewell/resolve-uri': 3.1.2 @@ -7245,22 +8000,22 @@ snapshots: '@types/react': 19.2.15 react: 19.2.3 - '@mintlify/cli@4.0.1413(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.2.7(react@19.2.3))(react@19.2.3))(@types/node@25.9.1)(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.2.7(react@19.2.3))(typescript@6.0.3)': + '@mintlify/cli@4.0.1413(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.3.0(react@19.2.3))(react@19.2.3))(@types/node@26.6.4)(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.3.0(react@19.2.3))(typescript@7.0.2)': dependencies: - '@inquirer/prompts': 7.9.0(@types/node@25.9.1) - '@mintlify/common': 1.0.1097(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.2.7(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.2.7(react@19.2.3))(react@19.2.3)(typescript@6.0.3) - '@mintlify/link-rot': 3.0.1298(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.2.7(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.2.7(react@19.2.3))(react@19.2.3)(typescript@6.0.3) + '@inquirer/prompts': 7.9.0(@types/node@26.6.4) + '@mintlify/common': 1.0.1097(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.3.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.3.0(react@19.2.3))(react@19.2.3)(typescript@7.0.2) + '@mintlify/link-rot': 3.0.1298(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.3.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.3.0(react@19.2.3))(react@19.2.3)(typescript@7.0.2) '@mintlify/models': 0.0.350(debug@4.4.3) - '@mintlify/prebuild': 1.0.1250(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.2.7(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.2.7(react@19.2.3))(react@19.2.3)(typescript@6.0.3) - '@mintlify/previewing': 4.0.1322(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.2.7(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.2.7(react@19.2.3))(typescript@6.0.3) - '@mintlify/validation': 0.1.832(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.2.7(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.2.7(react@19.2.3))(react@19.2.3)(typescript@6.0.3) + '@mintlify/prebuild': 1.0.1250(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.3.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.3.0(react@19.2.3))(react@19.2.3)(typescript@7.0.2) + '@mintlify/previewing': 4.0.1322(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.3.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.3.0(react@19.2.3))(typescript@7.0.2) + '@mintlify/validation': 0.1.832(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.3.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.3.0(react@19.2.3))(react@19.2.3)(typescript@7.0.2) adm-zip: 0.6.0 chalk: 5.2.0 color: 4.2.3 detect-port: 1.5.1 fs-extra: 11.2.0 ink: 6.3.0(@types/react@19.2.15)(react@19.2.3) - inquirer: 12.3.0(@types/node@25.9.1) + inquirer: 12.3.0(@types/node@26.6.4) js-yaml: 4.3.1 jsonc-parser: 3.3.1 mdast-util-mdx-jsx: 3.2.0 @@ -7299,13 +8054,13 @@ snapshots: - typescript - utf-8-validate - '@mintlify/common@1.0.1097(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.2.7(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.2.7(react@19.2.3))(react@19.2.3)(typescript@6.0.3)': + '@mintlify/common@1.0.1097(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.3.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.3.0(react@19.2.3))(react@19.2.3)(typescript@7.0.2)': dependencies: '@asyncapi/parser': 3.4.0 - '@mintlify/mdx': 4.0.2(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.2.7(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(react-dom@19.2.7(react@19.2.3))(react@19.2.3)(typescript@6.0.3) + '@mintlify/mdx': 4.0.2(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.3.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(react-dom@19.3.0(react@19.2.3))(react@19.2.3)(typescript@7.0.2) '@mintlify/models': 0.0.350(debug@4.4.3) '@mintlify/openapi-parser': 0.0.8 - '@mintlify/validation': 0.1.832(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.2.7(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.2.7(react@19.2.3))(react@19.2.3)(typescript@6.0.3) + '@mintlify/validation': 0.1.832(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.3.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.3.0(react@19.2.3))(react@19.2.3)(typescript@7.0.2) '@sindresorhus/slugify': 2.2.0 '@types/mdast': 4.0.4 acorn: 8.11.2 @@ -7357,14 +8112,14 @@ snapshots: - ts-node - typescript - '@mintlify/link-rot@3.0.1298(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.2.7(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.2.7(react@19.2.3))(react@19.2.3)(typescript@6.0.3)': + '@mintlify/link-rot@3.0.1298(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.3.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.3.0(react@19.2.3))(react@19.2.3)(typescript@7.0.2)': dependencies: - '@mintlify/common': 1.0.1097(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.2.7(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.2.7(react@19.2.3))(react@19.2.3)(typescript@6.0.3) + '@mintlify/common': 1.0.1097(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.3.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.3.0(react@19.2.3))(react@19.2.3)(typescript@7.0.2) '@mintlify/models': 0.0.350(debug@4.4.3) - '@mintlify/prebuild': 1.0.1250(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.2.7(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.2.7(react@19.2.3))(react@19.2.3)(typescript@6.0.3) - '@mintlify/previewing': 4.0.1322(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.2.7(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.2.7(react@19.2.3))(typescript@6.0.3) - '@mintlify/scraping': 4.0.965(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.2.7(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.2.7(react@19.2.3))(react@19.2.3)(typescript@6.0.3) - '@mintlify/validation': 0.1.832(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.2.7(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.2.7(react@19.2.3))(react@19.2.3)(typescript@6.0.3) + '@mintlify/prebuild': 1.0.1250(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.3.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.3.0(react@19.2.3))(react@19.2.3)(typescript@7.0.2) + '@mintlify/previewing': 4.0.1322(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.3.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.3.0(react@19.2.3))(typescript@7.0.2) + '@mintlify/scraping': 4.0.965(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.3.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.3.0(react@19.2.3))(react@19.2.3)(typescript@7.0.2) + '@mintlify/validation': 0.1.832(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.3.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.3.0(react@19.2.3))(react@19.2.3)(typescript@7.0.2) fs-extra: 11.1.0 unist-util-visit: 4.1.2 transitivePeerDependencies: @@ -7384,26 +8139,26 @@ snapshots: - typescript - utf-8-validate - '@mintlify/mdx@4.0.2(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.2.7(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(react-dom@19.2.7(react@19.2.3))(react@19.2.3)(typescript@6.0.3)': + '@mintlify/mdx@4.0.2(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.3.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(react-dom@19.3.0(react@19.2.3))(react@19.2.3)(typescript@7.0.2)': dependencies: - '@base-ui/react': 1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.2.7(react@19.2.3))(react@19.2.3) + '@base-ui/react': 1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.3.0(react@19.2.3))(react@19.2.3) '@shikijs/transformers': 3.23.0 - '@shikijs/twoslash': 3.23.0(typescript@6.0.3) + '@shikijs/twoslash': 3.23.0(typescript@7.0.2) arktype: 2.2.3 hast-util-to-string: 3.0.1 mdast-util-from-markdown: 2.0.3 mdast-util-gfm: 3.1.0 mdast-util-mdx-jsx: 3.2.0 mdast-util-to-hast: 13.2.1 - next-mdx-remote-client: 2.1.12(@types/react@19.2.15)(react-dom@19.2.7(react@19.2.3))(react@19.2.3)(unified@11.0.5) + next-mdx-remote-client: 2.1.12(@types/react@19.2.15)(react-dom@19.3.0(react@19.2.3))(react@19.2.3)(unified@11.0.5) react: 19.2.3 - react-dom: 19.2.7(react@19.2.3) + react-dom: 19.3.0(react@19.2.3) rehype-katex: 7.0.1 remark-gfm: 4.0.1 remark-math: 6.0.0 remark-smartypants: 3.0.3 shiki: 3.23.0 - twoslash: 0.3.9(typescript@6.0.3) + twoslash: 0.3.9(typescript@7.0.2) unified: 11.0.5 unist-util-visit: 5.0.0 transitivePeerDependencies: @@ -7423,16 +8178,16 @@ snapshots: dependencies: ajv: 8.20.0 ajv-draft-04: 1.0.0(ajv@8.20.0) - ajv-formats: 3.0.1(ajv@8.20.0) + ajv-formats: 3.0.1 jsonpointer: 5.0.1 leven: 4.1.0 yaml: 2.9.0 - '@mintlify/prebuild@1.0.1250(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.2.7(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.2.7(react@19.2.3))(react@19.2.3)(typescript@6.0.3)': + '@mintlify/prebuild@1.0.1250(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.3.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.3.0(react@19.2.3))(react@19.2.3)(typescript@7.0.2)': dependencies: - '@mintlify/common': 1.0.1097(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.2.7(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.2.7(react@19.2.3))(react@19.2.3)(typescript@6.0.3) - '@mintlify/scraping': 4.0.965(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.2.7(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.2.7(react@19.2.3))(react@19.2.3)(typescript@6.0.3) - '@mintlify/validation': 0.1.832(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.2.7(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.2.7(react@19.2.3))(react@19.2.3)(typescript@6.0.3) + '@mintlify/common': 1.0.1097(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.3.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.3.0(react@19.2.3))(react@19.2.3)(typescript@7.0.2) + '@mintlify/scraping': 4.0.965(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.3.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.3.0(react@19.2.3))(react@19.2.3)(typescript@7.0.2) + '@mintlify/validation': 0.1.832(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.3.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.3.0(react@19.2.3))(react@19.2.3)(typescript@7.0.2) chalk: 5.3.0 favicons: 7.2.0 fflate: 0.8.3 @@ -7458,11 +8213,11 @@ snapshots: - typescript - utf-8-validate - '@mintlify/previewing@4.0.1322(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.2.7(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.2.7(react@19.2.3))(typescript@6.0.3)': + '@mintlify/previewing@4.0.1322(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.3.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.3.0(react@19.2.3))(typescript@7.0.2)': dependencies: - '@mintlify/common': 1.0.1097(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.2.7(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.2.7(react@19.2.3))(react@19.2.3)(typescript@6.0.3) - '@mintlify/prebuild': 1.0.1250(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.2.7(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.2.7(react@19.2.3))(react@19.2.3)(typescript@6.0.3) - '@mintlify/validation': 0.1.832(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.2.7(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.2.7(react@19.2.3))(react@19.2.3)(typescript@6.0.3) + '@mintlify/common': 1.0.1097(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.3.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.3.0(react@19.2.3))(react@19.2.3)(typescript@7.0.2) + '@mintlify/prebuild': 1.0.1250(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.3.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.3.0(react@19.2.3))(react@19.2.3)(typescript@7.0.2) + '@mintlify/validation': 0.1.832(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.3.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.3.0(react@19.2.3))(react@19.2.3)(typescript@7.0.2) adm-zip: 0.6.0 better-opn: 3.0.2 chalk: 5.2.0 @@ -7494,9 +8249,9 @@ snapshots: - typescript - utf-8-validate - '@mintlify/scraping@4.0.965(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.2.7(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.2.7(react@19.2.3))(react@19.2.3)(typescript@6.0.3)': + '@mintlify/scraping@4.0.965(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.3.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.3.0(react@19.2.3))(react@19.2.3)(typescript@7.0.2)': dependencies: - '@mintlify/common': 1.0.1097(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.2.7(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.2.7(react@19.2.3))(react@19.2.3)(typescript@6.0.3) + '@mintlify/common': 1.0.1097(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.3.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.3.0(react@19.2.3))(react@19.2.3)(typescript@7.0.2) fast-xml-parser: 5.7.3 fs-extra: 11.1.1 graphql: 16.11.0 @@ -7504,7 +8259,7 @@ snapshots: js-yaml: 4.3.1 mdast-util-mdx-jsx: 3.1.3 neotraverse: 0.6.18 - puppeteer: 24.3.1(typescript@6.0.3) + puppeteer: 24.3.1(typescript@7.0.2) rehype-parse: 9.0.1 remark-gfm: 4.0.0 remark-mdx: 3.0.1 @@ -7530,9 +8285,9 @@ snapshots: - typescript - utf-8-validate - '@mintlify/validation@0.1.832(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.2.7(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.2.7(react@19.2.3))(react@19.2.3)(typescript@6.0.3)': + '@mintlify/validation@0.1.832(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.3.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.3.0(react@19.2.3))(react@19.2.3)(typescript@7.0.2)': dependencies: - '@mintlify/mdx': 4.0.2(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.2.7(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(react-dom@19.2.7(react@19.2.3))(react@19.2.3)(typescript@6.0.3) + '@mintlify/mdx': 4.0.2(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.3.0(react@19.2.3))(react@19.2.3))(@types/react@19.2.15)(react-dom@19.3.0(react@19.2.3))(react@19.2.3)(typescript@7.0.2) '@mintlify/models': 0.0.350(debug@4.4.3) fractional-indexing: 3.2.0 js-yaml: 4.3.1 @@ -7557,12 +8312,12 @@ snapshots: dependencies: state-local: 1.0.7 - '@monaco-editor/react@4.7.0(monaco-editor@0.55.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@monaco-editor/react@4.7.0(monaco-editor@0.55.1)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)': dependencies: '@monaco-editor/loader': 1.7.0 monaco-editor: 0.55.1 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': dependencies: @@ -7573,32 +8328,30 @@ snapshots: '@neon-rs/load@0.0.4': {} - '@next/env@15.5.18': {} - - '@next/env@16.2.9': {} + '@next/env@16.4.0': {} - '@next/swc-darwin-arm64@16.2.9': + '@next/swc-darwin-arm64@16.4.0': optional: true - '@next/swc-darwin-x64@16.2.9': + '@next/swc-darwin-x64@16.4.0': optional: true - '@next/swc-linux-arm64-gnu@16.2.9': + '@next/swc-linux-arm64-gnu@16.4.0': optional: true - '@next/swc-linux-arm64-musl@16.2.9': + '@next/swc-linux-arm64-musl@16.4.0': optional: true - '@next/swc-linux-x64-gnu@16.2.9': + '@next/swc-linux-x64-gnu@16.4.0': optional: true - '@next/swc-linux-x64-musl@16.2.9': + '@next/swc-linux-x64-musl@16.4.0': optional: true - '@next/swc-win32-arm64-msvc@16.2.9': + '@next/swc-win32-arm64-msvc@16.4.0': optional: true - '@next/swc-win32-x64-msvc@16.2.9': + '@next/swc-win32-x64-msvc@16.4.0': optional: true '@nodable/entities@2.2.0': {} @@ -7619,6 +8372,8 @@ snapshots: dependencies: fast-deep-equal: 3.1.3 + '@oxc-project/types@0.152.0': {} + '@oxc-resolver/binding-android-arm-eabi@11.20.0': optional: true @@ -7680,131 +8435,131 @@ snapshots: '@oxc-resolver/binding-win32-x64-msvc@11.20.0': optional: true - '@oxfmt/binding-android-arm-eabi@0.54.0': + '@oxfmt/binding-android-arm-eabi@0.71.0': optional: true - '@oxfmt/binding-android-arm64@0.54.0': + '@oxfmt/binding-android-arm64@0.71.0': optional: true - '@oxfmt/binding-darwin-arm64@0.54.0': + '@oxfmt/binding-darwin-arm64@0.71.0': optional: true - '@oxfmt/binding-darwin-x64@0.54.0': + '@oxfmt/binding-darwin-x64@0.71.0': optional: true - '@oxfmt/binding-freebsd-x64@0.54.0': + '@oxfmt/binding-freebsd-x64@0.71.0': optional: true - '@oxfmt/binding-linux-arm-gnueabihf@0.54.0': + '@oxfmt/binding-linux-arm-gnueabihf@0.71.0': optional: true - '@oxfmt/binding-linux-arm-musleabihf@0.54.0': + '@oxfmt/binding-linux-arm-musleabihf@0.71.0': optional: true - '@oxfmt/binding-linux-arm64-gnu@0.54.0': + '@oxfmt/binding-linux-arm64-gnu@0.71.0': optional: true - '@oxfmt/binding-linux-arm64-musl@0.54.0': + '@oxfmt/binding-linux-arm64-musl@0.71.0': optional: true - '@oxfmt/binding-linux-ppc64-gnu@0.54.0': + '@oxfmt/binding-linux-ppc64-gnu@0.71.0': optional: true - '@oxfmt/binding-linux-riscv64-gnu@0.54.0': + '@oxfmt/binding-linux-riscv64-gnu@0.71.0': optional: true - '@oxfmt/binding-linux-riscv64-musl@0.54.0': + '@oxfmt/binding-linux-riscv64-musl@0.71.0': optional: true - '@oxfmt/binding-linux-s390x-gnu@0.54.0': + '@oxfmt/binding-linux-s390x-gnu@0.71.0': optional: true - '@oxfmt/binding-linux-x64-gnu@0.54.0': + '@oxfmt/binding-linux-x64-gnu@0.71.0': optional: true - '@oxfmt/binding-linux-x64-musl@0.54.0': + '@oxfmt/binding-linux-x64-musl@0.71.0': optional: true - '@oxfmt/binding-openharmony-arm64@0.54.0': + '@oxfmt/binding-openharmony-arm64@0.71.0': optional: true - '@oxfmt/binding-win32-arm64-msvc@0.54.0': + '@oxfmt/binding-win32-arm64-msvc@0.71.0': optional: true - '@oxfmt/binding-win32-ia32-msvc@0.54.0': + '@oxfmt/binding-win32-ia32-msvc@0.71.0': optional: true - '@oxfmt/binding-win32-x64-msvc@0.54.0': + '@oxfmt/binding-win32-x64-msvc@0.71.0': optional: true - '@oxlint/binding-android-arm-eabi@1.69.0': + '@oxlint/binding-android-arm-eabi@1.86.0': optional: true - '@oxlint/binding-android-arm64@1.69.0': + '@oxlint/binding-android-arm64@1.86.0': optional: true - '@oxlint/binding-darwin-arm64@1.69.0': + '@oxlint/binding-darwin-arm64@1.86.0': optional: true - '@oxlint/binding-darwin-x64@1.69.0': + '@oxlint/binding-darwin-x64@1.86.0': optional: true - '@oxlint/binding-freebsd-x64@1.69.0': + '@oxlint/binding-freebsd-x64@1.86.0': optional: true - '@oxlint/binding-linux-arm-gnueabihf@1.69.0': + '@oxlint/binding-linux-arm-gnueabihf@1.86.0': optional: true - '@oxlint/binding-linux-arm-musleabihf@1.69.0': + '@oxlint/binding-linux-arm-musleabihf@1.86.0': optional: true - '@oxlint/binding-linux-arm64-gnu@1.69.0': + '@oxlint/binding-linux-arm64-gnu@1.86.0': optional: true - '@oxlint/binding-linux-arm64-musl@1.69.0': + '@oxlint/binding-linux-arm64-musl@1.86.0': optional: true - '@oxlint/binding-linux-ppc64-gnu@1.69.0': + '@oxlint/binding-linux-ppc64-gnu@1.86.0': optional: true - '@oxlint/binding-linux-riscv64-gnu@1.69.0': + '@oxlint/binding-linux-riscv64-gnu@1.86.0': optional: true - '@oxlint/binding-linux-riscv64-musl@1.69.0': + '@oxlint/binding-linux-riscv64-musl@1.86.0': optional: true - '@oxlint/binding-linux-s390x-gnu@1.69.0': + '@oxlint/binding-linux-s390x-gnu@1.86.0': optional: true - '@oxlint/binding-linux-x64-gnu@1.69.0': + '@oxlint/binding-linux-x64-gnu@1.86.0': optional: true - '@oxlint/binding-linux-x64-musl@1.69.0': + '@oxlint/binding-linux-x64-musl@1.86.0': optional: true - '@oxlint/binding-openharmony-arm64@1.69.0': + '@oxlint/binding-openharmony-arm64@1.86.0': optional: true - '@oxlint/binding-win32-arm64-msvc@1.69.0': + '@oxlint/binding-win32-arm64-msvc@1.86.0': optional: true - '@oxlint/binding-win32-ia32-msvc@1.69.0': + '@oxlint/binding-win32-ia32-msvc@1.86.0': optional: true - '@oxlint/binding-win32-x64-msvc@1.69.0': + '@oxlint/binding-win32-x64-msvc@1.86.0': optional: true - '@payloadcms/db-sqlite@3.85.1(payload@3.85.1(graphql@16.14.0)(typescript@6.0.3))': + '@payloadcms/db-sqlite@4.0.0-canary.38(payload@4.0.0-canary.38(graphql@16.14.0)(typescript@7.0.2))': dependencies: '@libsql/client': 0.14.0 - '@payloadcms/drizzle': 3.85.1(@libsql/client@0.14.0)(payload@3.85.1(graphql@16.14.0)(typescript@6.0.3)) - console-table-printer: 2.12.1 + '@payloadcms/drizzle': 4.0.0-canary.38(@libsql/client@0.14.0)(payload@4.0.0-canary.38(graphql@16.14.0)(typescript@7.0.2)) + console-table-printer: 2.15.0 drizzle-kit: 0.31.7 drizzle-orm: 0.45.2(@libsql/client@0.14.0) - payload: 3.85.1(graphql@16.14.0)(typescript@6.0.3) + payload: 4.0.0-canary.38(graphql@16.14.0)(typescript@7.0.2) prompts: 2.4.2 to-snake-case: 1.0.0 - uuid: 13.0.2 + uuid: 14.0.0 transitivePeerDependencies: - '@aws-sdk/client-rds-data' - '@cloudflare/workers-types' @@ -7838,15 +8593,15 @@ snapshots: - supports-color - utf-8-validate - '@payloadcms/drizzle@3.85.1(@libsql/client@0.14.0)(payload@3.85.1(graphql@16.14.0)(typescript@6.0.3))': + '@payloadcms/drizzle@4.0.0-canary.38(@libsql/client@0.14.0)(payload@4.0.0-canary.38(graphql@16.14.0)(typescript@7.0.2))': dependencies: - console-table-printer: 2.12.1 + console-table-printer: 2.15.0 dequal: 2.0.3 drizzle-orm: 0.45.2(@libsql/client@0.14.0) - payload: 3.85.1(graphql@16.14.0)(typescript@6.0.3) + payload: 4.0.0-canary.38(graphql@16.14.0)(typescript@7.0.2) prompts: 2.4.2 to-snake-case: 1.0.0 - uuid: 13.0.2 + uuid: 14.0.0 transitivePeerDependencies: - '@aws-sdk/client-rds-data' - '@cloudflare/workers-types' @@ -7878,81 +8633,86 @@ snapshots: - sql.js - sqlite3 - '@payloadcms/graphql@3.85.1(graphql@16.14.0)(payload@3.85.1(graphql@16.14.0)(typescript@6.0.3))(typescript@6.0.3)': + '@payloadcms/graphql@4.0.0-canary.38(graphql@16.14.0)(payload@4.0.0-canary.38(graphql@16.14.0)(typescript@7.0.2))(typescript@7.0.2)': dependencies: graphql: 16.14.0 graphql-scalars: 1.22.2(graphql@16.14.0) - payload: 3.85.1(graphql@16.14.0)(typescript@6.0.3) + payload: 4.0.0-canary.38(graphql@16.14.0)(typescript@7.0.2) pluralize: 8.0.0 - ts-essentials: 10.0.3(typescript@6.0.3) - tsx: 4.22.4 + ts-essentials: 10.0.3(typescript@7.0.2) + tsx: 4.23.15 transitivePeerDependencies: - typescript - '@payloadcms/next@3.85.1(@types/react@19.2.15)(graphql@16.14.0)(monaco-editor@0.55.1)(next@16.2.9(babel-plugin-macros@3.1.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.77.4))(payload@3.85.1(graphql@16.14.0)(typescript@6.0.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3)': + '@payloadcms/next@4.0.0-canary.38(@types/react@19.2.15)(graphql@16.14.0)(monaco-editor@0.55.1)(next@16.4.0(@types/node@26.6.4)(babel-plugin-macros@3.1.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(sass@1.77.4))(payload@4.0.0-canary.38(graphql@16.14.0)(typescript@7.0.2))(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(typescript@7.0.2)': dependencies: - '@dnd-kit/core': 6.3.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@dnd-kit/modifiers': 9.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7) - '@dnd-kit/sortable': 10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7) - '@payloadcms/graphql': 3.85.1(graphql@16.14.0)(payload@3.85.1(graphql@16.14.0)(typescript@6.0.3))(typescript@6.0.3) - '@payloadcms/translations': 3.85.1 - '@payloadcms/ui': 3.85.1(@types/react@19.2.15)(monaco-editor@0.55.1)(next@16.2.9(babel-plugin-macros@3.1.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.77.4))(payload@3.85.1(graphql@16.14.0)(typescript@6.0.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3) + '@dnd-kit/core': 6.3.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@dnd-kit/modifiers': 9.0.0(@dnd-kit/core@6.3.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(react@19.3.0) + '@dnd-kit/sortable': 10.0.0(@dnd-kit/core@6.3.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(react@19.3.0) + '@payloadcms/graphql': 4.0.0-canary.38(graphql@16.14.0)(payload@4.0.0-canary.38(graphql@16.14.0)(typescript@7.0.2))(typescript@7.0.2) + '@payloadcms/translations': 4.0.0-canary.38 + '@payloadcms/ui': 4.0.0-canary.38(@types/react@19.2.15)(monaco-editor@0.55.1)(payload@4.0.0-canary.38(graphql@16.14.0)(typescript@7.0.2))(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(typescript@7.0.2) busboy: 1.6.0 dequal: 2.0.3 - file-type: 21.3.4 + file-type: 22.0.1 graphql: 16.14.0 graphql-http: 1.22.4(graphql@16.14.0) graphql-playground-html: 1.6.30 http-status: 2.1.0 - next: 16.2.9(babel-plugin-macros@3.1.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.77.4) + next: 16.4.0(@types/node@26.6.4)(babel-plugin-macros@3.1.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(sass@1.77.4) path-to-regexp: 6.3.0 - payload: 3.85.1(graphql@16.14.0)(typescript@6.0.3) + payload: 4.0.0-canary.38(graphql@16.14.0)(typescript@7.0.2) qs-esm: 8.0.1 sass: 1.77.4 - uuid: 13.0.2 + uuid: 14.0.0 transitivePeerDependencies: - '@types/react' + - date-fns-tz - monaco-editor - react - react-dom - supports-color - typescript - '@payloadcms/translations@3.85.1': + '@payloadcms/translations@4.0.0-canary.38': dependencies: - date-fns: 4.1.0 + date-fns: 4.4.0 - '@payloadcms/ui@3.85.1(@types/react@19.2.15)(monaco-editor@0.55.1)(next@16.2.9(babel-plugin-macros@3.1.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.77.4))(payload@3.85.1(graphql@16.14.0)(typescript@6.0.3))(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(typescript@6.0.3)': + '@payloadcms/ui@4.0.0-canary.38(@types/react@19.2.15)(monaco-editor@0.55.1)(payload@4.0.0-canary.38(graphql@16.14.0)(typescript@7.0.2))(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(typescript@7.0.2)': dependencies: '@date-fns/tz': 1.2.0 - '@dnd-kit/core': 6.3.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@dnd-kit/sortable': 10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7) - '@dnd-kit/utilities': 3.2.2(react@19.2.7) - '@faceless-ui/modal': 3.0.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@faceless-ui/scroll-info': 2.0.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@faceless-ui/window-info': 3.0.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@monaco-editor/react': 4.7.0(monaco-editor@0.55.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - '@payloadcms/translations': 3.85.1 + '@dnd-kit/core': 6.3.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@dnd-kit/modifiers': 9.0.0(@dnd-kit/core@6.3.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(react@19.3.0) + '@dnd-kit/sortable': 10.0.0(@dnd-kit/core@6.3.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0))(react@19.3.0) + '@dnd-kit/utilities': 3.2.2(react@19.3.0) + '@faceless-ui/modal': 3.0.2(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@faceless-ui/scroll-info': 2.0.0(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@faceless-ui/window-info': 3.0.1(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@floating-ui/dom': 1.7.6 + '@monaco-editor/react': 4.7.0(monaco-editor@0.55.1)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + '@payloadcms/translations': 4.0.0-canary.38 + '@swc/helpers': 0.5.23 bson-objectid: 2.0.4 - date-fns: 4.1.0 + date-fns: 4.4.0 dequal: 2.0.3 md5: 2.3.0 - next: 16.2.9(babel-plugin-macros@3.1.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.77.4) object-to-formdata: 4.5.1 - payload: 3.85.1(graphql@16.14.0)(typescript@6.0.3) + path-to-regexp: 6.3.0 + payload: 4.0.0-canary.38(graphql@16.14.0)(typescript@7.0.2) qs-esm: 8.0.1 - react: 19.2.7 - react-datepicker: 7.6.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - react-dom: 19.2.7(react@19.2.7) - react-image-crop: 10.1.8(react@19.2.7) - react-select: 5.9.0(@types/react@19.2.15)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.3.0 + react-datepicker: 9.1.0(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + react-dom: 19.3.0(react@19.3.0) + react-image-crop: 10.1.8(react@19.3.0) + react-select: 5.9.0(@types/react@19.2.15)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) scheduler: 0.25.0 - sonner: 1.7.4(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - ts-essentials: 10.0.3(typescript@6.0.3) - use-context-selector: 2.0.0(react@19.2.7)(scheduler@0.25.0) - uuid: 13.0.2 + sonner: 1.7.4(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + ts-essentials: 10.0.3(typescript@7.0.2) + use-context-selector: 2.0.0(react@19.3.0)(scheduler@0.25.0) + uuid: 14.0.0 transitivePeerDependencies: - '@types/react' + - date-fns-tz - monaco-editor - supports-color - typescript @@ -7978,153 +8738,131 @@ snapshots: - react-native-b4a - supports-color - '@rollup/rollup-android-arm-eabi@4.60.4': + '@quansync/fs@1.1.0': + dependencies: + quansync: 1.0.0 + + '@rolldown/binding-android-arm-eabi@1.2.12': optional: true - '@rollup/rollup-android-arm64@4.60.4': + '@rolldown/binding-android-arm64@1.2.12': optional: true - '@rollup/rollup-darwin-arm64@4.60.4': + '@rolldown/binding-darwin-arm64@1.2.12': optional: true - '@rollup/rollup-darwin-x64@4.60.4': + '@rolldown/binding-darwin-x64@1.2.12': optional: true - '@rollup/rollup-freebsd-arm64@4.60.4': + '@rolldown/binding-freebsd-x64@1.2.12': optional: true - '@rollup/rollup-freebsd-x64@4.60.4': + '@rolldown/binding-linux-arm-gnueabihf@1.2.12': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.60.4': + '@rolldown/binding-linux-arm64-gnu@1.2.12': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.60.4': + '@rolldown/binding-linux-arm64-musl@1.2.12': optional: true - '@rollup/rollup-linux-arm64-gnu@4.60.4': + '@rolldown/binding-linux-ppc64-gnu@1.2.12': optional: true - '@rollup/rollup-linux-arm64-musl@4.60.4': + '@rolldown/binding-linux-s390x-gnu@1.2.12': optional: true - '@rollup/rollup-linux-loong64-gnu@4.60.4': + '@rolldown/binding-linux-x64-gnu@1.2.12': optional: true - '@rollup/rollup-linux-loong64-musl@4.60.4': + '@rolldown/binding-linux-x64-musl@1.2.12': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.60.4': + '@rolldown/binding-openharmony-arm64@1.2.12': optional: true - '@rollup/rollup-linux-ppc64-musl@4.60.4': + '@rolldown/binding-win32-arm64-msvc@1.2.12': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.60.4': + '@rolldown/binding-win32-x64-msvc@1.2.12': optional: true - '@rollup/rollup-linux-riscv64-musl@4.60.4': + '@rolldown/pluginutils@1.0.1': {} + + '@rollup/rollup-android-arm-eabi@4.60.4': optional: true - '@rollup/rollup-linux-s390x-gnu@4.60.4': + '@rollup/rollup-android-arm64@4.60.4': optional: true - '@rollup/rollup-linux-x64-gnu@4.60.4': + '@rollup/rollup-darwin-arm64@4.60.4': optional: true - '@rollup/rollup-linux-x64-musl@4.60.4': + '@rollup/rollup-darwin-x64@4.60.4': optional: true - '@rollup/rollup-openbsd-x64@4.60.4': + '@rollup/rollup-freebsd-arm64@4.60.4': optional: true - '@rollup/rollup-openharmony-arm64@4.60.4': + '@rollup/rollup-freebsd-x64@4.60.4': optional: true - '@rollup/rollup-win32-arm64-msvc@4.60.4': + '@rollup/rollup-linux-arm-gnueabihf@4.60.4': optional: true - '@rollup/rollup-win32-ia32-msvc@4.60.4': + '@rollup/rollup-linux-arm-musleabihf@4.60.4': optional: true - '@rollup/rollup-win32-x64-gnu@4.60.4': + '@rollup/rollup-linux-arm64-gnu@4.60.4': optional: true - '@rollup/rollup-win32-x64-msvc@4.60.4': + '@rollup/rollup-linux-arm64-musl@4.60.4': optional: true - '@rsbuild/core@2.0.12(core-js@3.42.0)': - dependencies: - '@rspack/core': 2.0.8(@swc/helpers@0.5.23) - '@swc/helpers': 0.5.23 - optionalDependencies: - core-js: 3.42.0 - transitivePeerDependencies: - - '@module-federation/runtime-tools' + '@rollup/rollup-linux-loong64-gnu@4.60.4': + optional: true - '@rslib/core@0.22.1(core-js@3.42.0)(typescript@6.0.3)': - dependencies: - '@rsbuild/core': 2.0.12(core-js@3.42.0) - rsbuild-plugin-dts: 0.22.1(@rsbuild/core@2.0.12(core-js@3.42.0))(typescript@6.0.3) - optionalDependencies: - typescript: 6.0.3 - transitivePeerDependencies: - - '@module-federation/runtime-tools' - - '@typescript/native-preview' - - core-js + '@rollup/rollup-linux-loong64-musl@4.60.4': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.60.4': + optional: true - '@rspack/binding-darwin-arm64@2.0.8': + '@rollup/rollup-linux-ppc64-musl@4.60.4': optional: true - '@rspack/binding-darwin-x64@2.0.8': + '@rollup/rollup-linux-riscv64-gnu@4.60.4': optional: true - '@rspack/binding-linux-arm64-gnu@2.0.8': + '@rollup/rollup-linux-riscv64-musl@4.60.4': optional: true - '@rspack/binding-linux-arm64-musl@2.0.8': + '@rollup/rollup-linux-s390x-gnu@4.60.4': optional: true - '@rspack/binding-linux-x64-gnu@2.0.8': + '@rollup/rollup-linux-x64-gnu@4.60.4': optional: true - '@rspack/binding-linux-x64-musl@2.0.8': + '@rollup/rollup-linux-x64-musl@4.60.4': optional: true - '@rspack/binding-wasm32-wasi@2.0.8': - dependencies: - '@emnapi/core': 1.10.0 - '@emnapi/runtime': 1.10.0 - '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@rollup/rollup-openbsd-x64@4.60.4': optional: true - '@rspack/binding-win32-arm64-msvc@2.0.8': + '@rollup/rollup-openharmony-arm64@4.60.4': optional: true - '@rspack/binding-win32-ia32-msvc@2.0.8': + '@rollup/rollup-win32-arm64-msvc@4.60.4': optional: true - '@rspack/binding-win32-x64-msvc@2.0.8': + '@rollup/rollup-win32-ia32-msvc@4.60.4': optional: true - '@rspack/binding@2.0.8': - optionalDependencies: - '@rspack/binding-darwin-arm64': 2.0.8 - '@rspack/binding-darwin-x64': 2.0.8 - '@rspack/binding-linux-arm64-gnu': 2.0.8 - '@rspack/binding-linux-arm64-musl': 2.0.8 - '@rspack/binding-linux-x64-gnu': 2.0.8 - '@rspack/binding-linux-x64-musl': 2.0.8 - '@rspack/binding-wasm32-wasi': 2.0.8 - '@rspack/binding-win32-arm64-msvc': 2.0.8 - '@rspack/binding-win32-ia32-msvc': 2.0.8 - '@rspack/binding-win32-x64-msvc': 2.0.8 - - '@rspack/core@2.0.8(@swc/helpers@0.5.23)': - dependencies: - '@rspack/binding': 2.0.8 - optionalDependencies: - '@swc/helpers': 0.5.23 + '@rollup/rollup-win32-x64-gnu@4.60.4': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.60.4': + optional: true '@scalar/openapi-types@0.9.1': {} @@ -8134,9 +8872,18 @@ snapshots: dependencies: ajv: 8.20.0 ajv-draft-04: 1.0.0(ajv@8.20.0) - ajv-formats: 3.0.1(ajv@8.20.0) + ajv-formats: 3.0.1 yaml: 2.9.0 + '@seshuk/payload-plugin-tooling@0.2.3(@payloadcms/db-sqlite@4.0.0-canary.38(payload@4.0.0-canary.38(graphql@16.14.0)(typescript@7.0.2)))(oxfmt@0.71.0)(oxlint@1.86.0)(payload@4.0.0-canary.38(graphql@16.14.0)(typescript@7.0.2))(tsdown@0.23.0(oxc-resolver@11.20.0)(tsx@4.22.4)(typescript@7.0.2))(vitest@5.0.3)': + optionalDependencies: + '@payloadcms/db-sqlite': 4.0.0-canary.38(payload@4.0.0-canary.38(graphql@16.14.0)(typescript@7.0.2)) + oxfmt: 0.71.0 + oxlint: 1.86.0 + payload: 4.0.0-canary.38(graphql@16.14.0)(typescript@7.0.2) + tsdown: 0.23.0(oxc-resolver@11.20.0)(tsx@4.22.4)(typescript@7.0.2) + vitest: 5.0.3(@types/node@26.6.4)(@vitest/coverage-v8@5.0.3)(vite@7.3.3(@types/node@26.6.4)(jiti@2.7.0)(sass@1.77.4)(tsx@4.22.4)(yaml@2.9.0)) + '@shikijs/core@3.23.0': dependencies: '@shikijs/types': 3.23.0 @@ -8168,12 +8915,12 @@ snapshots: '@shikijs/core': 3.23.0 '@shikijs/types': 3.23.0 - '@shikijs/twoslash@3.23.0(typescript@6.0.3)': + '@shikijs/twoslash@3.23.0(typescript@7.0.2)': dependencies: '@shikijs/core': 3.23.0 '@shikijs/types': 3.23.0 - twoslash: 0.3.9(typescript@6.0.3) - typescript: 6.0.3 + twoslash: 0.3.9(typescript@7.0.2) + typescript: 7.0.2 transitivePeerDependencies: - supports-color @@ -8252,7 +8999,7 @@ snapshots: '@types/json-schema': 7.0.15 ajv: 8.20.0 ajv-errors: 3.0.0(ajv@8.20.0) - ajv-formats: 2.1.1(ajv@8.20.0) + ajv-formats: 2.1.1 es-aggregate-error: 1.0.14 expr-eval-fork: 3.0.3 jsonpath-plus: 10.4.0 @@ -8286,7 +9033,7 @@ snapshots: ajv: 8.20.0 ajv-draft-04: 1.0.0(ajv@8.20.0) ajv-errors: 3.0.0(ajv@8.20.0) - ajv-formats: 2.1.1(ajv@8.20.0) + ajv-formats: 2.1.1 lodash: 4.18.1 tslib: 2.8.1 transitivePeerDependencies: @@ -8344,10 +9091,6 @@ snapshots: '@stoplight/yaml-ast-parser': 0.0.50 tslib: 2.8.1 - '@swc/helpers@0.5.15': - dependencies: - tslib: 2.8.1 - '@swc/helpers@0.5.23': dependencies: tslib: 2.8.1 @@ -8374,7 +9117,7 @@ snapshots: '@types/busboy@1.5.4': dependencies: - '@types/node': 25.9.1 + '@types/node': 26.6.4 '@types/chai@5.2.3': dependencies: @@ -8431,6 +9174,10 @@ snapshots: dependencies: undici-types: 7.24.6 + '@types/node@26.6.4': + dependencies: + undici-types: 8.9.0 + '@types/parse-json@4.0.2': {} '@types/react-transition-group@4.4.12(@types/react@19.2.15)': @@ -8452,62 +9199,184 @@ snapshots: '@types/ws@8.18.1': dependencies: - '@types/node': 25.9.1 + '@types/node': 26.6.4 '@types/yauzl@2.10.3': dependencies: '@types/node': 25.9.1 optional: true - '@typescript/vfs@1.6.4(typescript@6.0.3)': + '@typescript/typescript-aix-ppc64@7.0.2': + optional: true + + '@typescript/typescript-darwin-arm64@7.0.2': + optional: true + + '@typescript/typescript-darwin-x64@7.0.2': + optional: true + + '@typescript/typescript-freebsd-arm64@7.0.2': + optional: true + + '@typescript/typescript-freebsd-x64@7.0.2': + optional: true + + '@typescript/typescript-linux-arm64@7.0.2': + optional: true + + '@typescript/typescript-linux-arm@7.0.2': + optional: true + + '@typescript/typescript-linux-loong64@7.0.2': + optional: true + + '@typescript/typescript-linux-mips64el@7.0.2': + optional: true + + '@typescript/typescript-linux-ppc64@7.0.2': + optional: true + + '@typescript/typescript-linux-riscv64@7.0.2': + optional: true + + '@typescript/typescript-linux-s390x@7.0.2': + optional: true + + '@typescript/typescript-linux-x64@7.0.2': + optional: true + + '@typescript/typescript-netbsd-arm64@7.0.2': + optional: true + + '@typescript/typescript-netbsd-x64@7.0.2': + optional: true + + '@typescript/typescript-openbsd-arm64@7.0.2': + optional: true + + '@typescript/typescript-openbsd-x64@7.0.2': + optional: true + + '@typescript/typescript-sunos-x64@7.0.2': + optional: true + + '@typescript/typescript-win32-arm64@7.0.2': + optional: true + + '@typescript/typescript-win32-x64@7.0.2': + optional: true + + '@typescript/vfs@1.6.4(typescript@7.0.2)': dependencies: debug: 4.4.3 - typescript: 6.0.3 + typescript: 7.0.2 transitivePeerDependencies: - supports-color '@ungap/structured-clone@1.3.3': {} - '@vitest/expect@4.1.8': + '@vitest/coverage-v8@5.0.3(vitest@5.0.3)': dependencies: - '@standard-schema/spec': 1.1.0 - '@types/chai': 5.2.3 - '@vitest/spy': 4.1.8 - '@vitest/utils': 4.1.8 - chai: 6.2.2 - tinyrainbow: 3.1.0 + '@bcoe/v8-coverage': 1.0.2 + '@vitest/istanbul-lib-coverage': 1.0.2 + '@vitest/istanbul-lib-report': 1.0.2 + ast-v8-to-istanbul: 1.0.7 + magicast: 0.5.5 + obug: 2.2.1 + std-env: 4.3.0 + tinyrainbow: 3.2.0 + vitest: 5.0.3(@types/node@26.6.4)(@vitest/coverage-v8@5.0.3)(vite@7.3.3(@types/node@26.6.4)(jiti@2.7.0)(sass@1.77.4)(tsx@4.22.4)(yaml@2.9.0)) + + '@vitest/istanbul-lib-coverage@1.0.2': {} + + '@vitest/istanbul-lib-report@1.0.2': + dependencies: + '@vitest/istanbul-lib-coverage': 1.0.2 - '@vitest/mocker@4.1.8(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(sass@1.77.4)(tsx@4.22.4)(yaml@2.9.0))': + '@vitest/mocker@5.0.3(vite@7.3.3(@types/node@26.6.4)(jiti@2.7.0)(sass@1.77.4)(tsx@4.22.4)(yaml@2.9.0))': dependencies: - '@vitest/spy': 4.1.8 + '@jridgewell/trace-mapping': 0.3.31 + '@vitest/spy': 5.0.3 estree-walker: 3.0.3 - magic-string: 0.30.21 + magic-string: 1.4.2 optionalDependencies: - vite: 7.3.3(@types/node@25.9.1)(jiti@2.7.0)(sass@1.77.4)(tsx@4.22.4)(yaml@2.9.0) + vite: 7.3.3(@types/node@26.6.4)(jiti@2.7.0)(sass@1.77.4)(tsx@4.22.4)(yaml@2.9.0) - '@vitest/pretty-format@4.1.8': - dependencies: - tinyrainbow: 3.1.0 + '@vitest/spy@5.0.3': {} - '@vitest/runner@4.1.8': - dependencies: - '@vitest/utils': 4.1.8 - pathe: 2.0.3 + '@yuku-codegen/binding-android-arm64@0.10.2': + optional: true - '@vitest/snapshot@4.1.8': - dependencies: - '@vitest/pretty-format': 4.1.8 - '@vitest/utils': 4.1.8 - magic-string: 0.30.21 - pathe: 2.0.3 + '@yuku-codegen/binding-darwin-arm64@0.10.2': + optional: true + + '@yuku-codegen/binding-darwin-x64@0.10.2': + optional: true - '@vitest/spy@4.1.8': {} + '@yuku-codegen/binding-freebsd-x64@0.10.2': + optional: true - '@vitest/utils@4.1.8': - dependencies: - '@vitest/pretty-format': 4.1.8 - convert-source-map: 2.0.0 - tinyrainbow: 3.1.0 + '@yuku-codegen/binding-linux-arm-gnu@0.10.2': + optional: true + + '@yuku-codegen/binding-linux-arm-musl@0.10.2': + optional: true + + '@yuku-codegen/binding-linux-arm64-gnu@0.10.2': + optional: true + + '@yuku-codegen/binding-linux-arm64-musl@0.10.2': + optional: true + + '@yuku-codegen/binding-linux-x64-gnu@0.10.2': + optional: true + + '@yuku-codegen/binding-linux-x64-musl@0.10.2': + optional: true + + '@yuku-codegen/binding-win32-arm64@0.10.2': + optional: true + + '@yuku-codegen/binding-win32-x64@0.10.2': + optional: true + + '@yuku-parser/binding-android-arm64@0.10.2': + optional: true + + '@yuku-parser/binding-darwin-arm64@0.10.2': + optional: true + + '@yuku-parser/binding-darwin-x64@0.10.2': + optional: true + + '@yuku-parser/binding-freebsd-x64@0.10.2': + optional: true + + '@yuku-parser/binding-linux-arm-gnu@0.10.2': + optional: true + + '@yuku-parser/binding-linux-arm-musl@0.10.2': + optional: true + + '@yuku-parser/binding-linux-arm64-gnu@0.10.2': + optional: true + + '@yuku-parser/binding-linux-arm64-musl@0.10.2': + optional: true + + '@yuku-parser/binding-linux-x64-gnu@0.10.2': + optional: true + + '@yuku-parser/binding-linux-x64-musl@0.10.2': + optional: true + + '@yuku-parser/binding-win32-arm64@0.10.2': + optional: true + + '@yuku-parser/binding-win32-x64@0.10.2': + optional: true + + '@yuku-toolchain/types@0.10.2': {} accepts@1.3.8: dependencies: @@ -8545,20 +9414,13 @@ snapshots: dependencies: ajv: 8.20.0 - ajv-formats@2.1.1(ajv@8.20.0): - optionalDependencies: - ajv: 8.20.0 - - ajv-formats@3.0.1(ajv@8.20.0): - optionalDependencies: + ajv-formats@2.1.1: + dependencies: ajv: 8.20.0 - ajv@8.18.0: + ajv-formats@3.0.1: dependencies: - fast-deep-equal: 3.1.3 - fast-uri: 3.1.2 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 + ajv: 8.20.0 ajv@8.20.0: dependencies: @@ -8637,6 +9499,12 @@ snapshots: dependencies: tslib: 2.8.1 + ast-v8-to-istanbul@1.0.7: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + estree-walker: 3.0.3 + js-tokens: 10.0.0 + astring@1.9.0: {} async-function@1.0.0: {} @@ -8772,6 +9640,8 @@ snapshots: bytes@3.1.2: {} + cac@7.0.0: {} + cacheable-lookup@7.0.0: {} cacheable-request@10.2.14: @@ -8925,6 +9795,8 @@ snapshots: comma-separated-tokens@2.0.3: {} + commander@15.0.0: {} + commander@2.20.3: {} commander@4.1.1: {} @@ -8933,7 +9805,7 @@ snapshots: concat-map@0.0.1: {} - console-table-printer@2.12.1: + console-table-printer@2.15.0: dependencies: simple-wcswidth: 1.1.2 @@ -8945,17 +9817,12 @@ snapshots: convert-source-map@1.9.0: {} - convert-source-map@2.0.0: {} - convert-to-spaces@2.0.1: {} cookie-signature@1.0.7: {} cookie@0.7.2: {} - core-js@3.42.0: - optional: true - cors@2.8.6: dependencies: object-assign: 4.1.1 @@ -8969,14 +9836,14 @@ snapshots: path-type: 4.0.0 yaml: 1.10.3 - cosmiconfig@9.0.2(typescript@6.0.3): + cosmiconfig@9.0.2(typescript@7.0.2): dependencies: env-paths: 2.2.1 import-fresh: 3.3.1 js-yaml: 4.3.1 parse-json: 5.2.0 optionalDependencies: - typescript: 6.0.3 + typescript: 7.0.2 croner@10.0.1: {} @@ -9023,10 +9890,10 @@ snapshots: dataloader@2.2.3: {} - date-fns@3.6.0: {} - date-fns@4.1.0: {} + date-fns@4.4.0: {} + dateformat@4.6.3: {} debug@2.6.9: @@ -9081,6 +9948,8 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 + defu@6.1.7: {} + degenerator@5.0.1: dependencies: ast-types: 0.13.4 @@ -9135,6 +10004,12 @@ snapshots: optionalDependencies: '@types/trusted-types': 2.0.7 + dotenv-expand@11.0.6: + dependencies: + dotenv: 16.4.5 + + dotenv@16.4.5: {} + drizzle-kit@0.31.7: dependencies: '@drizzle-team/brocli': 0.10.2 @@ -9148,6 +10023,10 @@ snapshots: optionalDependencies: '@libsql/client': 0.14.0 + dts-resolver@3.0.0(oxc-resolver@11.20.0): + optionalDependencies: + oxc-resolver: 11.20.0 + dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 @@ -9160,6 +10039,8 @@ snapshots: emoji-regex@8.0.0: {} + empathic@2.1.0: {} + encodeurl@2.0.0: {} end-of-stream@1.4.5: @@ -9274,7 +10155,7 @@ snapshots: es-errors@1.3.0: {} - es-module-lexer@2.1.0: {} + es-module-lexer@2.3.2: {} es-object-atoms@1.1.2: dependencies: @@ -9499,7 +10380,7 @@ snapshots: expand-template@2.0.3: optional: true - expect-type@1.3.0: {} + expect-type@1.4.0: {} expr-eval-fork@3.0.3: {} @@ -9551,7 +10432,7 @@ snapshots: transitivePeerDependencies: - supports-color - fast-copy@3.0.2: {} + fast-copy@4.1.1: {} fast-deep-equal@3.1.3: {} @@ -9605,6 +10486,10 @@ snapshots: optionalDependencies: picomatch: 4.0.4 + fdir@6.5.0(picomatch@4.0.7): + optionalDependencies: + picomatch: 4.0.7 + fetch-blob@3.2.0: dependencies: node-domexception: 1.0.0 @@ -9612,7 +10497,7 @@ snapshots: fflate@0.8.3: {} - file-type@21.3.4: + file-type@22.0.1: dependencies: '@tokenizer/inflate': 0.4.1 strtok3: 10.3.5 @@ -9757,7 +10642,11 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 - get-tsconfig@4.8.1: + get-tsconfig@4.14.0: + dependencies: + resolve-pkg-maps: 1.0.0 + + get-tsconfig@5.0.0-beta.6: dependencies: resolve-pkg-maps: 1.0.0 @@ -10048,6 +10937,8 @@ snapshots: dependencies: react-is: 16.13.1 + hookable@6.1.2: {} + html-void-elements@3.0.0: {} http-cache-semantics@4.2.0: {} @@ -10102,7 +10993,7 @@ snapshots: ignore@7.0.5: {} - image-size@2.0.2: {} + image-dimensions@2.5.1: {} immer@9.0.21: {} @@ -10113,6 +11004,8 @@ snapshots: parent-module: 1.0.1 resolve-from: 4.0.0 + import-without-cache@0.4.1: {} + indent-string@5.0.0: {} inflight@1.0.6: @@ -10165,12 +11058,12 @@ snapshots: inline-style-parser@0.2.7: {} - inquirer@12.3.0(@types/node@25.9.1): + inquirer@12.3.0(@types/node@26.6.4): dependencies: - '@inquirer/core': 10.3.2(@types/node@25.9.1) - '@inquirer/prompts': 7.9.0(@types/node@25.9.1) - '@inquirer/type': 3.0.10(@types/node@25.9.1) - '@types/node': 25.9.1 + '@inquirer/core': 10.3.2(@types/node@26.6.4) + '@inquirer/prompts': 7.9.0(@types/node@26.6.4) + '@inquirer/type': 3.0.10(@types/node@26.6.4) + '@types/node': 26.6.4 ansi-escapes: 4.3.2 mute-stream: 2.0.0 run-async: 3.0.0 @@ -10188,7 +11081,7 @@ snapshots: ipaddr.js@1.9.1: {} - ipaddr.js@2.2.0: {} + ipaddr.js@2.3.0: {} is-alphabetical@2.0.1: {} @@ -10361,7 +11254,7 @@ snapshots: jiti@2.7.0: optional: true - jose@5.10.0: {} + jose@6.2.3: {} jose@6.2.9: {} @@ -10369,6 +11262,8 @@ snapshots: js-base64@3.7.8: {} + js-tokens@10.0.0: {} + js-tokens@4.0.0: {} js-yaml@3.15.1: @@ -10376,10 +11271,6 @@ snapshots: argparse: 1.0.10 esprima: 4.0.1 - js-yaml@4.1.1: - dependencies: - argparse: 2.0.1 - js-yaml@4.3.1: dependencies: argparse: 2.0.1 @@ -10398,11 +11289,11 @@ snapshots: '@types/json-schema': 7.0.15 '@types/lodash': 4.17.24 is-glob: 4.0.3 - js-yaml: 4.1.1 + js-yaml: 4.3.1 lodash: 4.18.1 minimist: 1.2.8 prettier: 3.8.3 - tinyglobby: 0.2.16 + tinyglobby: 0.2.17 json-schema-traverse@1.0.0: {} @@ -10479,9 +11370,15 @@ snapshots: lru-cache@7.18.3: {} - magic-string@0.30.21: + magic-string@1.4.2: dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/sourcemap-codec': 1.6.0 + + magicast@0.5.5: + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + source-map-js: 1.2.1 markdown-extensions@2.0.0: {} @@ -11051,9 +11948,9 @@ snapshots: dependencies: minipass: 7.1.3 - mint@4.2.810(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.2.7(react@19.2.3))(react@19.2.3))(@types/node@25.9.1)(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.2.7(react@19.2.3))(typescript@6.0.3): + mint@4.2.810(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.3.0(react@19.2.3))(react@19.2.3))(@types/node@26.6.4)(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.3.0(react@19.2.3))(typescript@7.0.2): dependencies: - '@mintlify/cli': 4.0.1413(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.2.7(react@19.2.3))(react@19.2.3))(@types/node@25.9.1)(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.2.7(react@19.2.3))(typescript@6.0.3) + '@mintlify/cli': 4.0.1413(@base-ui/react@1.7.0(@date-fns/tz@1.2.0)(@types/react@19.2.15)(date-fns@4.1.0)(react-dom@19.3.0(react@19.2.3))(react@19.2.3))(@types/node@26.6.4)(@types/react@19.2.15)(debug@4.4.3)(react-dom@19.3.0(react@19.2.3))(typescript@7.0.2) transitivePeerDependencies: - '@base-ui/react' - '@types/node' @@ -11093,8 +11990,6 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 - nanoid@3.3.12: {} - nanoid@3.3.18: {} napi-build-utils@2.0.0: @@ -11106,14 +12001,14 @@ snapshots: netmask@2.1.1: {} - next-mdx-remote-client@2.1.12(@types/react@19.2.15)(react-dom@19.2.7(react@19.2.3))(react@19.2.3)(unified@11.0.5): + next-mdx-remote-client@2.1.12(@types/react@19.2.15)(react-dom@19.3.0(react@19.2.3))(react@19.2.3)(unified@11.0.5): dependencies: '@babel/code-frame': 7.29.7 '@mdx-js/mdx': 3.1.1 '@mdx-js/react': 3.1.1(@types/react@19.2.15)(react@19.2.3) '@types/mdx': 2.0.14 react: 19.2.3 - react-dom: 19.2.7(react@19.2.3) + react-dom: 19.3.0(react@19.2.3) remark-mdx-remove-esm: 1.3.3(unified@11.0.5) serialize-error: 13.0.1 vfile: 6.0.3 @@ -11123,29 +12018,30 @@ snapshots: - supports-color - unified - next@16.2.9(babel-plugin-macros@3.1.0)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.77.4): + next@16.4.0(@types/node@26.6.4)(babel-plugin-macros@3.1.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)(sass@1.77.4): dependencies: - '@next/env': 16.2.9 - '@swc/helpers': 0.5.15 + '@next/env': 16.4.0 + '@swc/helpers': 0.5.23 baseline-browser-mapping: 2.10.32 caniuse-lite: 1.0.30001793 - postcss: 8.4.31 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - styled-jsx: 5.1.6(babel-plugin-macros@3.1.0)(react@19.2.7) + postcss: 8.5.23 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + styled-jsx: 5.1.6(babel-plugin-macros@3.1.0)(react@19.3.0) optionalDependencies: - '@next/swc-darwin-arm64': 16.2.9 - '@next/swc-darwin-x64': 16.2.9 - '@next/swc-linux-arm64-gnu': 16.2.9 - '@next/swc-linux-arm64-musl': 16.2.9 - '@next/swc-linux-x64-gnu': 16.2.9 - '@next/swc-linux-x64-musl': 16.2.9 - '@next/swc-win32-arm64-msvc': 16.2.9 - '@next/swc-win32-x64-msvc': 16.2.9 + '@next/swc-darwin-arm64': 16.4.0 + '@next/swc-darwin-x64': 16.4.0 + '@next/swc-linux-arm64-gnu': 16.4.0 + '@next/swc-linux-arm64-musl': 16.4.0 + '@next/swc-linux-x64-gnu': 16.4.0 + '@next/swc-linux-x64-musl': 16.4.0 + '@next/swc-win32-arm64-msvc': 16.4.0 + '@next/swc-win32-x64-msvc': 16.4.0 sass: 1.77.4 - sharp: 0.34.5 + sharp: 0.35.5(@types/node@26.6.4) transitivePeerDependencies: - '@babel/core' + - '@types/node' - babel-plugin-macros nimma@0.2.3: @@ -11213,7 +12109,9 @@ snapshots: has-symbols: 1.1.0 object-keys: 1.1.1 - obug@2.1.1: {} + obug@2.2.1: {} + + obug@3.0.0: {} on-exit-leak-free@2.1.2: {} @@ -11280,52 +12178,53 @@ snapshots: '@oxc-resolver/binding-wasm32-wasi': 11.20.0 '@oxc-resolver/binding-win32-arm64-msvc': 11.20.0 '@oxc-resolver/binding-win32-x64-msvc': 11.20.0 + optional: true - oxfmt@0.54.0: + oxfmt@0.71.0: dependencies: - tinypool: 2.1.0 + tinypool: 2.2.0 optionalDependencies: - '@oxfmt/binding-android-arm-eabi': 0.54.0 - '@oxfmt/binding-android-arm64': 0.54.0 - '@oxfmt/binding-darwin-arm64': 0.54.0 - '@oxfmt/binding-darwin-x64': 0.54.0 - '@oxfmt/binding-freebsd-x64': 0.54.0 - '@oxfmt/binding-linux-arm-gnueabihf': 0.54.0 - '@oxfmt/binding-linux-arm-musleabihf': 0.54.0 - '@oxfmt/binding-linux-arm64-gnu': 0.54.0 - '@oxfmt/binding-linux-arm64-musl': 0.54.0 - '@oxfmt/binding-linux-ppc64-gnu': 0.54.0 - '@oxfmt/binding-linux-riscv64-gnu': 0.54.0 - '@oxfmt/binding-linux-riscv64-musl': 0.54.0 - '@oxfmt/binding-linux-s390x-gnu': 0.54.0 - '@oxfmt/binding-linux-x64-gnu': 0.54.0 - '@oxfmt/binding-linux-x64-musl': 0.54.0 - '@oxfmt/binding-openharmony-arm64': 0.54.0 - '@oxfmt/binding-win32-arm64-msvc': 0.54.0 - '@oxfmt/binding-win32-ia32-msvc': 0.54.0 - '@oxfmt/binding-win32-x64-msvc': 0.54.0 - - oxlint@1.69.0: + '@oxfmt/binding-android-arm-eabi': 0.71.0 + '@oxfmt/binding-android-arm64': 0.71.0 + '@oxfmt/binding-darwin-arm64': 0.71.0 + '@oxfmt/binding-darwin-x64': 0.71.0 + '@oxfmt/binding-freebsd-x64': 0.71.0 + '@oxfmt/binding-linux-arm-gnueabihf': 0.71.0 + '@oxfmt/binding-linux-arm-musleabihf': 0.71.0 + '@oxfmt/binding-linux-arm64-gnu': 0.71.0 + '@oxfmt/binding-linux-arm64-musl': 0.71.0 + '@oxfmt/binding-linux-ppc64-gnu': 0.71.0 + '@oxfmt/binding-linux-riscv64-gnu': 0.71.0 + '@oxfmt/binding-linux-riscv64-musl': 0.71.0 + '@oxfmt/binding-linux-s390x-gnu': 0.71.0 + '@oxfmt/binding-linux-x64-gnu': 0.71.0 + '@oxfmt/binding-linux-x64-musl': 0.71.0 + '@oxfmt/binding-openharmony-arm64': 0.71.0 + '@oxfmt/binding-win32-arm64-msvc': 0.71.0 + '@oxfmt/binding-win32-ia32-msvc': 0.71.0 + '@oxfmt/binding-win32-x64-msvc': 0.71.0 + + oxlint@1.86.0: optionalDependencies: - '@oxlint/binding-android-arm-eabi': 1.69.0 - '@oxlint/binding-android-arm64': 1.69.0 - '@oxlint/binding-darwin-arm64': 1.69.0 - '@oxlint/binding-darwin-x64': 1.69.0 - '@oxlint/binding-freebsd-x64': 1.69.0 - '@oxlint/binding-linux-arm-gnueabihf': 1.69.0 - '@oxlint/binding-linux-arm-musleabihf': 1.69.0 - '@oxlint/binding-linux-arm64-gnu': 1.69.0 - '@oxlint/binding-linux-arm64-musl': 1.69.0 - '@oxlint/binding-linux-ppc64-gnu': 1.69.0 - '@oxlint/binding-linux-riscv64-gnu': 1.69.0 - '@oxlint/binding-linux-riscv64-musl': 1.69.0 - '@oxlint/binding-linux-s390x-gnu': 1.69.0 - '@oxlint/binding-linux-x64-gnu': 1.69.0 - '@oxlint/binding-linux-x64-musl': 1.69.0 - '@oxlint/binding-openharmony-arm64': 1.69.0 - '@oxlint/binding-win32-arm64-msvc': 1.69.0 - '@oxlint/binding-win32-ia32-msvc': 1.69.0 - '@oxlint/binding-win32-x64-msvc': 1.69.0 + '@oxlint/binding-android-arm-eabi': 1.86.0 + '@oxlint/binding-android-arm64': 1.86.0 + '@oxlint/binding-darwin-arm64': 1.86.0 + '@oxlint/binding-darwin-x64': 1.86.0 + '@oxlint/binding-freebsd-x64': 1.86.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.86.0 + '@oxlint/binding-linux-arm-musleabihf': 1.86.0 + '@oxlint/binding-linux-arm64-gnu': 1.86.0 + '@oxlint/binding-linux-arm64-musl': 1.86.0 + '@oxlint/binding-linux-ppc64-gnu': 1.86.0 + '@oxlint/binding-linux-riscv64-gnu': 1.86.0 + '@oxlint/binding-linux-riscv64-musl': 1.86.0 + '@oxlint/binding-linux-s390x-gnu': 1.86.0 + '@oxlint/binding-linux-x64-gnu': 1.86.0 + '@oxlint/binding-linux-x64-musl': 1.86.0 + '@oxlint/binding-openharmony-arm64': 1.86.0 + '@oxlint/binding-win32-arm64-msvc': 1.86.0 + '@oxlint/binding-win32-ia32-msvc': 1.86.0 + '@oxlint/binding-win32-x64-msvc': 1.86.0 p-any@4.0.0: dependencies: @@ -11411,42 +12310,44 @@ snapshots: path-type@4.0.0: {} - pathe@2.0.3: {} - - payload@3.85.1(graphql@16.14.0)(typescript@6.0.3): + payload@4.0.0-canary.38(graphql@16.14.0)(typescript@7.0.2): dependencies: - '@next/env': 15.5.18 - '@payloadcms/translations': 3.85.1 + '@next/env': 16.4.0 + '@payloadcms/translations': 4.0.0-canary.38 + '@standard-schema/spec': 1.1.0 '@types/busboy': 1.5.4 - ajv: 8.18.0 bson-objectid: 2.0.4 busboy: 1.6.0 ci-info: 4.4.0 - console-table-printer: 2.12.1 + commander: 15.0.0 + console-table-printer: 2.15.0 croner: 10.0.1 dataloader: 2.2.3 deepmerge: 4.3.1 - file-type: 21.3.4 - get-tsconfig: 4.8.1 + dotenv: 16.4.5 + dotenv-expand: 11.0.6 + file-type: 22.0.1 + get-tsconfig: 4.14.0 graphql: 16.14.0 http-status: 2.1.0 - image-size: 2.0.2 - ipaddr.js: 2.2.0 - jose: 5.10.0 + image-dimensions: 2.5.1 + ipaddr.js: 2.3.0 + jose: 6.2.3 json-schema-to-typescript: 15.0.3 - minimist: 1.2.8 path-to-regexp: 6.3.0 - pino: 9.14.0 - pino-pretty: 13.1.2 + pino: 10.3.1 + pino-pretty: 13.1.3 pluralize: 8.0.0 qs-esm: 8.0.1 range-parser: 1.2.1 - sanitize-filename: 1.6.3 - ts-essentials: 10.0.3(typescript@6.0.3) - tsx: 4.22.4 - undici: 7.24.4 - uuid: 13.0.2 + sanitize-filename: 1.6.4 + sax: 1.6.1 + ts-essentials: 10.0.3(typescript@7.0.2) + tsx: 4.23.15 + undici: 7.29.1 + uuid: 14.0.0 ws: 8.21.0 + zod: 4.6.5 transitivePeerDependencies: - bufferutil - supports-color @@ -11461,23 +12362,25 @@ snapshots: picomatch@4.0.4: {} + picomatch@4.0.7: {} + pify@2.3.0: {} - pino-abstract-transport@2.0.0: + pino-abstract-transport@3.0.0: dependencies: split2: 4.2.0 - pino-pretty@13.1.2: + pino-pretty@13.1.3: dependencies: colorette: 2.0.20 dateformat: 4.6.3 - fast-copy: 3.0.2 + fast-copy: 4.1.1 fast-safe-stringify: 2.1.1 help-me: 5.0.0 joycon: 3.1.1 minimist: 1.2.8 on-exit-leak-free: 2.1.2 - pino-abstract-transport: 2.0.0 + pino-abstract-transport: 3.0.0 pump: 3.0.4 secure-json-parse: 4.1.0 sonic-boom: 4.2.1 @@ -11485,19 +12388,19 @@ snapshots: pino-std-serializers@7.1.0: {} - pino@9.14.0: + pino@10.3.1: dependencies: '@pinojs/redact': 0.4.0 atomic-sleep: 1.0.0 on-exit-leak-free: 2.1.2 - pino-abstract-transport: 2.0.0 + pino-abstract-transport: 3.0.0 pino-std-serializers: 7.1.0 process-warning: 5.0.0 quick-format-unescaped: 4.0.4 real-require: 0.2.0 safe-stable-stringify: 2.5.0 sonic-boom: 4.2.1 - thread-stream: 3.1.0 + thread-stream: 4.2.0 pirates@4.0.7: {} @@ -11538,12 +12441,6 @@ snapshots: postcss-value-parser@4.2.0: {} - postcss@8.4.31: - dependencies: - nanoid: 3.3.12 - picocolors: 1.1.1 - source-map-js: 1.2.1 - postcss@8.5.23: dependencies: nanoid: 3.3.18 @@ -11646,11 +12543,11 @@ snapshots: - supports-color - utf-8-validate - puppeteer@24.3.1(typescript@6.0.3): + puppeteer@24.3.1(typescript@7.0.2): dependencies: '@puppeteer/browsers': 2.7.1 chromium-bidi: 2.1.2(devtools-protocol@0.0.1402036) - cosmiconfig: 9.0.2(typescript@6.0.3) + cosmiconfig: 9.0.2(typescript@7.0.2) devtools-protocol: 0.0.1402036 puppeteer-core: 24.3.1 typed-query-selector: 2.12.2 @@ -11674,6 +12571,8 @@ snapshots: es-define-property: 1.0.1 side-channel: 1.1.1 + quansync@1.0.0: {} + queue-microtask@1.2.3: {} quick-format-unescaped@4.0.4: {} @@ -11697,27 +12596,27 @@ snapshots: strip-json-comments: 2.0.1 optional: true - react-datepicker@7.6.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7): + react-datepicker@9.1.0(react-dom@19.3.0(react@19.3.0))(react@19.3.0): dependencies: - '@floating-ui/react': 0.27.19(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@floating-ui/react': 0.27.19(react-dom@19.3.0(react@19.3.0))(react@19.3.0) clsx: 2.1.1 - date-fns: 3.6.0 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) + date-fns: 4.1.0 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) - react-dom@19.2.7(react@19.2.3): + react-dom@19.3.0(react@19.2.3): dependencies: react: 19.2.3 - scheduler: 0.27.0 + scheduler: 0.28.0 - react-dom@19.2.7(react@19.2.7): + react-dom@19.3.0(react@19.3.0): dependencies: - react: 19.2.7 - scheduler: 0.27.0 + react: 19.3.0 + scheduler: 0.28.0 - react-image-crop@10.1.8(react@19.2.7): + react-image-crop@10.1.8(react@19.3.0): dependencies: - react: 19.2.7 + react: 19.3.0 react-is@16.13.1: {} @@ -11726,35 +12625,35 @@ snapshots: react: 19.2.3 scheduler: 0.26.0 - react-select@5.9.0(@types/react@19.2.15)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): + react-select@5.9.0(@types/react@19.2.15)(react-dom@19.3.0(react@19.3.0))(react@19.3.0): dependencies: '@babel/runtime': 7.29.7 '@emotion/cache': 11.14.0 - '@emotion/react': 11.14.0(@types/react@19.2.15)(react@19.2.7) + '@emotion/react': 11.14.0(@types/react@19.2.15)(react@19.3.0) '@floating-ui/dom': 1.7.6 '@types/react-transition-group': 4.4.12(@types/react@19.2.15) memoize-one: 6.0.0 prop-types: 15.8.1 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - react-transition-group: 4.4.5(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - use-isomorphic-layout-effect: 1.2.1(@types/react@19.2.15)(react@19.2.7) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + react-transition-group: 4.4.5(react-dom@19.3.0(react@19.3.0))(react@19.3.0) + use-isomorphic-layout-effect: 1.2.1(@types/react@19.2.15)(react@19.3.0) transitivePeerDependencies: - '@types/react' - supports-color - react-transition-group@4.4.5(react-dom@19.2.7(react@19.2.7))(react@19.2.7): + react-transition-group@4.4.5(react-dom@19.3.0(react@19.3.0))(react@19.3.0): dependencies: '@babel/runtime': 7.29.7 dom-helpers: 5.2.1 loose-envify: 1.4.0 prop-types: 15.8.1 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) react@19.2.3: {} - react@19.2.7: {} + react@19.3.0: {} read-cache@1.0.0: dependencies: @@ -11773,6 +12672,8 @@ snapshots: real-require@0.2.0: {} + real-require@1.0.0: {} + recma-build-jsx@1.0.0: dependencies: '@types/estree': 1.0.9 @@ -12028,6 +12929,41 @@ snapshots: reusify@1.1.0: {} + rolldown-plugin-dts@0.28.6(oxc-resolver@11.20.0)(rolldown@1.2.12)(typescript@7.0.2): + dependencies: + dts-resolver: 3.0.0(oxc-resolver@11.20.0) + get-tsconfig: 5.0.0-beta.6 + obug: 3.0.0 + rolldown: 1.2.12 + yuku-ast: 0.10.2 + yuku-codegen: 0.10.2 + yuku-parser: 0.10.2 + optionalDependencies: + typescript: 7.0.2 + transitivePeerDependencies: + - oxc-resolver + + rolldown@1.2.12: + dependencies: + '@oxc-project/types': 0.152.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm-eabi': 1.2.12 + '@rolldown/binding-android-arm64': 1.2.12 + '@rolldown/binding-darwin-arm64': 1.2.12 + '@rolldown/binding-darwin-x64': 1.2.12 + '@rolldown/binding-freebsd-x64': 1.2.12 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.12 + '@rolldown/binding-linux-arm64-gnu': 1.2.12 + '@rolldown/binding-linux-arm64-musl': 1.2.12 + '@rolldown/binding-linux-ppc64-gnu': 1.2.12 + '@rolldown/binding-linux-s390x-gnu': 1.2.12 + '@rolldown/binding-linux-x64-gnu': 1.2.12 + '@rolldown/binding-linux-x64-musl': 1.2.12 + '@rolldown/binding-openharmony-arm64': 1.2.12 + '@rolldown/binding-win32-arm64-msvc': 1.2.12 + '@rolldown/binding-win32-x64-msvc': 1.2.12 + rollup@4.60.4: dependencies: '@types/estree': 1.0.8 @@ -12059,13 +12995,6 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.60.4 fsevents: 2.3.3 - rsbuild-plugin-dts@0.22.1(@rsbuild/core@2.0.12(core-js@3.42.0))(typescript@6.0.3): - dependencies: - '@ast-grep/napi': 0.37.0 - '@rsbuild/core': 2.0.12(core-js@3.42.0) - optionalDependencies: - typescript: 6.0.3 - run-async@3.0.0: {} run-parallel@1.2.0: @@ -12103,7 +13032,7 @@ snapshots: safer-buffer@2.1.2: {} - sanitize-filename@1.6.3: + sanitize-filename@1.6.4: dependencies: truncate-utf8-bytes: 1.0.2 @@ -12119,12 +13048,15 @@ snapshots: scheduler@0.26.0: {} - scheduler@0.27.0: {} + scheduler@0.28.0: {} secure-json-parse@4.1.0: {} semver@7.8.1: {} + semver@7.8.5: + optional: true + send@0.19.2: dependencies: debug: 2.6.9 @@ -12244,6 +13176,40 @@ snapshots: '@img/sharp-win32-ia32': 0.34.5 '@img/sharp-win32-x64': 0.34.5 + sharp@0.35.5(@types/node@26.6.4): + dependencies: + '@img/colour': 1.1.0 + detect-libc: 2.1.2 + semver: 7.8.5 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.35.5 + '@img/sharp-darwin-x64': 0.35.5 + '@img/sharp-freebsd-wasm32': 0.35.5 + '@img/sharp-libvips-darwin-arm64': 1.3.4 + '@img/sharp-libvips-darwin-x64': 1.3.4 + '@img/sharp-libvips-linux-arm': 1.3.4 + '@img/sharp-libvips-linux-arm64': 1.3.4 + '@img/sharp-libvips-linux-ppc64': 1.3.4 + '@img/sharp-libvips-linux-riscv64': 1.3.4 + '@img/sharp-libvips-linux-s390x': 1.3.4 + '@img/sharp-libvips-linux-x64': 1.3.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.4 + '@img/sharp-libvips-linuxmusl-x64': 1.3.4 + '@img/sharp-linux-arm': 0.35.5 + '@img/sharp-linux-arm64': 0.35.5 + '@img/sharp-linux-ppc64': 0.35.5 + '@img/sharp-linux-riscv64': 0.35.5 + '@img/sharp-linux-s390x': 0.35.5 + '@img/sharp-linux-x64': 0.35.5 + '@img/sharp-linuxmusl-arm64': 0.35.5 + '@img/sharp-linuxmusl-x64': 0.35.5 + '@img/sharp-webcontainers-wasm32': 0.35.5 + '@img/sharp-win32-arm64': 0.35.5 + '@img/sharp-win32-ia32': 0.35.5 + '@img/sharp-win32-x64': 0.35.5 + '@types/node': 26.6.4 + optional: true + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -12289,8 +13255,6 @@ snapshots: side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 - siginfo@2.0.0: {} - signal-exit@3.0.7: {} signal-exit@4.1.0: {} @@ -12372,10 +13336,10 @@ snapshots: dependencies: atomic-sleep: 1.0.0 - sonner@1.7.4(react-dom@19.2.7(react@19.2.7))(react@19.2.7): + sonner@1.7.4(react-dom@19.3.0(react@19.3.0))(react@19.3.0): dependencies: - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) source-map-js@1.2.1: {} @@ -12400,13 +13364,11 @@ snapshots: dependencies: escape-string-regexp: 2.0.0 - stackback@0.0.2: {} - state-local@1.0.7: {} statuses@2.0.2: {} - std-env@4.1.0: {} + std-env@4.3.0: {} stop-iteration-iterator@1.1.0: dependencies: @@ -12499,10 +13461,10 @@ snapshots: dependencies: inline-style-parser: 0.2.7 - styled-jsx@5.1.6(babel-plugin-macros@3.1.0)(react@19.2.7): + styled-jsx@5.1.6(babel-plugin-macros@3.1.0)(react@19.3.0): dependencies: client-only: 0.0.1 - react: 19.2.7 + react: 19.3.0 optionalDependencies: babel-plugin-macros: 3.1.0 @@ -12525,7 +13487,7 @@ snapshots: lines-and-columns: 1.2.4 mz: 2.7.0 pirates: 4.0.7 - tinyglobby: 0.2.16 + tinyglobby: 0.2.17 ts-interface-checker: 0.1.13 supports-preserve-symlinks-flag@1.0.0: {} @@ -12630,22 +13592,22 @@ snapshots: dependencies: any-promise: 1.3.0 - thread-stream@3.1.0: + thread-stream@4.2.0: dependencies: - real-require: 0.2.0 + real-require: 1.0.0 - tinybench@2.9.0: {} + tinybench@6.2.0: {} - tinyexec@1.2.4: {} + tinyexec@1.3.1: {} - tinyglobby@0.2.16: + tinyglobby@0.2.17: dependencies: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - tinypool@2.1.0: {} + tinypool@2.2.0: {} - tinyrainbow@3.1.0: {} + tinyrainbow@3.2.0: {} to-data-view@1.1.0: {} @@ -12673,6 +13635,8 @@ snapshots: tr46@0.0.3: {} + tree-kill@1.2.2: {} + trim-lines@3.0.1: {} trim-trailing-lines@2.1.0: {} @@ -12683,15 +13647,37 @@ snapshots: dependencies: utf8-byte-length: 1.0.5 - ts-essentials@10.0.3(typescript@6.0.3): + ts-essentials@10.0.3(typescript@7.0.2): optionalDependencies: - typescript: 6.0.3 + typescript: 7.0.2 ts-interface-checker@0.1.13: {} - tsconfck@3.1.6(typescript@6.0.3): + tsdown@0.23.0(oxc-resolver@11.20.0)(tsx@4.22.4)(typescript@7.0.2): + dependencies: + cac: 7.0.0 + defu: 6.1.7 + empathic: 2.1.0 + hookable: 6.1.2 + import-without-cache: 0.4.1 + obug: 2.2.1 + picomatch: 4.0.7 + rolldown: 1.2.12 + rolldown-plugin-dts: 0.28.6(oxc-resolver@11.20.0)(rolldown@1.2.12)(typescript@7.0.2) + tinyexec: 1.3.1 + tinyglobby: 0.2.17 + tree-kill: 1.2.2 + unconfig-core: 7.5.0 + verkit: 0.4.1 optionalDependencies: - typescript: 6.0.3 + tsx: 4.22.4 + typescript: 7.0.2 + transitivePeerDependencies: + - '@typescript/native-preview' + - '@volar/typescript' + - '@vue/language-core' + - oxc-resolver + - vue-tsc tslib@1.14.1: {} @@ -12702,6 +13688,13 @@ snapshots: esbuild: 0.28.1 optionalDependencies: fsevents: 2.3.3 + optional: true + + tsx@4.23.15: + dependencies: + esbuild: 0.28.1 + optionalDependencies: + fsevents: 2.3.3 tunnel-agent@0.6.0: dependencies: @@ -12710,11 +13703,11 @@ snapshots: twoslash-protocol@0.3.9: {} - twoslash@0.3.9(typescript@6.0.3): + twoslash@0.3.9(typescript@7.0.2): dependencies: - '@typescript/vfs': 1.6.4(typescript@6.0.3) + '@typescript/vfs': 1.6.4(typescript@7.0.2) twoslash-protocol: 0.3.9 - typescript: 6.0.3 + typescript: 7.0.2 transitivePeerDependencies: - supports-color @@ -12766,7 +13759,28 @@ snapshots: typed-query-selector@2.12.2: {} - typescript@6.0.3: {} + 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 uint8array-extras@1.5.0: {} @@ -12777,9 +13791,16 @@ snapshots: has-symbols: 1.1.0 which-boxed-primitive: 1.1.1 + unconfig-core@7.5.0: + dependencies: + '@quansync/fs': 1.1.0 + quansync: 1.0.0 + undici-types@7.24.6: {} - undici@7.24.4: {} + undici-types@8.9.0: {} + + undici@7.29.1: {} unified@11.0.5: dependencies: @@ -12877,14 +13898,14 @@ snapshots: urijs@1.19.11: {} - use-context-selector@2.0.0(react@19.2.7)(scheduler@0.25.0): + use-context-selector@2.0.0(react@19.3.0)(scheduler@0.25.0): dependencies: - react: 19.2.7 + react: 19.3.0 scheduler: 0.25.0 - use-isomorphic-layout-effect@1.2.1(@types/react@19.2.15)(react@19.2.7): + use-isomorphic-layout-effect@1.2.1(@types/react@19.2.15)(react@19.3.0): dependencies: - react: 19.2.7 + react: 19.3.0 optionalDependencies: '@types/react': 19.2.15 @@ -12902,10 +13923,12 @@ snapshots: uuid@11.1.1: {} - uuid@13.0.2: {} + uuid@14.0.0: {} vary@1.1.2: {} + verkit@0.4.1: {} + vfile-location@5.0.3: dependencies: '@types/unist': 3.0.3 @@ -12926,56 +13949,41 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-tsconfig-paths@7.0.0-alpha.1(typescript@6.0.3)(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(sass@1.77.4)(tsx@4.22.4)(yaml@2.9.0)): - dependencies: - debug: 4.4.3 - oxc-resolver: 11.20.0 - tsconfck: 3.1.6(typescript@6.0.3) - vite: 7.3.3(@types/node@25.9.1)(jiti@2.7.0)(sass@1.77.4)(tsx@4.22.4)(yaml@2.9.0) - transitivePeerDependencies: - - supports-color - - typescript - - vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(sass@1.77.4)(tsx@4.22.4)(yaml@2.9.0): + vite@7.3.3(@types/node@26.6.4)(jiti@2.7.0)(sass@1.77.4)(tsx@4.22.4)(yaml@2.9.0): dependencies: esbuild: 0.27.7 - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 postcss: 8.5.23 rollup: 4.60.4 - tinyglobby: 0.2.16 + tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 26.6.4 fsevents: 2.3.3 jiti: 2.7.0 sass: 1.77.4 tsx: 4.22.4 yaml: 2.9.0 - vitest@4.1.8(@types/node@25.9.1)(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(sass@1.77.4)(tsx@4.22.4)(yaml@2.9.0)): - dependencies: - '@vitest/expect': 4.1.8 - '@vitest/mocker': 4.1.8(vite@7.3.3(@types/node@25.9.1)(jiti@2.7.0)(sass@1.77.4)(tsx@4.22.4)(yaml@2.9.0)) - '@vitest/pretty-format': 4.1.8 - '@vitest/runner': 4.1.8 - '@vitest/snapshot': 4.1.8 - '@vitest/spy': 4.1.8 - '@vitest/utils': 4.1.8 - es-module-lexer: 2.1.0 - expect-type: 1.3.0 - magic-string: 0.30.21 - obug: 2.1.1 - pathe: 2.0.3 - picomatch: 4.0.4 - std-env: 4.1.0 - tinybench: 2.9.0 - tinyexec: 1.2.4 - tinyglobby: 0.2.16 - tinyrainbow: 3.1.0 - vite: 7.3.3(@types/node@25.9.1)(jiti@2.7.0)(sass@1.77.4)(tsx@4.22.4)(yaml@2.9.0) - why-is-node-running: 2.3.0 + vitest@5.0.3(@types/node@26.6.4)(@vitest/coverage-v8@5.0.3)(vite@7.3.3(@types/node@26.6.4)(jiti@2.7.0)(sass@1.77.4)(tsx@4.22.4)(yaml@2.9.0)): + dependencies: + '@types/chai': 5.2.3 + '@vitest/mocker': 5.0.3(vite@7.3.3(@types/node@26.6.4)(jiti@2.7.0)(sass@1.77.4)(tsx@4.22.4)(yaml@2.9.0)) + chai: 6.2.2 + es-module-lexer: 2.3.2 + expect-type: 1.4.0 + magic-string: 1.4.2 + obug: 2.2.1 + picomatch: 4.0.7 + std-env: 4.3.0 + tinybench: 6.2.0 + tinyexec: 1.3.1 + tinyglobby: 0.2.17 + vite: 7.3.3(@types/node@26.6.4)(jiti@2.7.0)(sass@1.77.4)(tsx@4.22.4)(yaml@2.9.0) + why-is-node-running: 3.2.1 optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 26.6.4 + '@vitest/coverage-v8': 5.0.3(vitest@5.0.3) transitivePeerDependencies: - msw @@ -13035,10 +14043,7 @@ snapshots: dependencies: isexe: 2.0.0 - why-is-node-running@2.3.0: - dependencies: - siginfo: 2.0.0 - stackback: 0.0.2 + why-is-node-running@3.2.1: {} widest-line@5.0.0: dependencies: @@ -13119,6 +14124,45 @@ snapshots: yoga-layout@3.2.1: {} + yuku-ast@0.10.2: + dependencies: + '@yuku-toolchain/types': 0.10.2 + + yuku-codegen@0.10.2: + dependencies: + '@yuku-toolchain/types': 0.10.2 + optionalDependencies: + '@yuku-codegen/binding-android-arm64': 0.10.2 + '@yuku-codegen/binding-darwin-arm64': 0.10.2 + '@yuku-codegen/binding-darwin-x64': 0.10.2 + '@yuku-codegen/binding-freebsd-x64': 0.10.2 + '@yuku-codegen/binding-linux-arm-gnu': 0.10.2 + '@yuku-codegen/binding-linux-arm-musl': 0.10.2 + '@yuku-codegen/binding-linux-arm64-gnu': 0.10.2 + '@yuku-codegen/binding-linux-arm64-musl': 0.10.2 + '@yuku-codegen/binding-linux-x64-gnu': 0.10.2 + '@yuku-codegen/binding-linux-x64-musl': 0.10.2 + '@yuku-codegen/binding-win32-arm64': 0.10.2 + '@yuku-codegen/binding-win32-x64': 0.10.2 + + yuku-parser@0.10.2: + dependencies: + '@yuku-toolchain/types': 0.10.2 + yuku-ast: 0.10.2 + optionalDependencies: + '@yuku-parser/binding-android-arm64': 0.10.2 + '@yuku-parser/binding-darwin-arm64': 0.10.2 + '@yuku-parser/binding-darwin-x64': 0.10.2 + '@yuku-parser/binding-freebsd-x64': 0.10.2 + '@yuku-parser/binding-linux-arm-gnu': 0.10.2 + '@yuku-parser/binding-linux-arm-musl': 0.10.2 + '@yuku-parser/binding-linux-arm64-gnu': 0.10.2 + '@yuku-parser/binding-linux-arm64-musl': 0.10.2 + '@yuku-parser/binding-linux-x64-gnu': 0.10.2 + '@yuku-parser/binding-linux-x64-musl': 0.10.2 + '@yuku-parser/binding-win32-arm64': 0.10.2 + '@yuku-parser/binding-win32-x64': 0.10.2 + zod-to-json-schema@3.20.4(zod@3.24.0): dependencies: zod: 3.24.0 @@ -13129,4 +14173,6 @@ snapshots: zod@4.3.6: {} + zod@4.6.5: {} + zwitch@2.0.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 6649888..80b7c5a 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,10 +1,28 @@ +nodeVersion: 24.15.0 +pmOnFail: download packages: - - . - docs + +catalog: + '@types/node': ^26.1.2 + react: 19.3.0 + react-dom: 19.3.0 + typescript: 7.0.2 + allowBuilds: '@scarf/scarf': false core-js: false esbuild: true keytar: false + oxc-resolver: true puppeteer: false sharp: true +minimumReleaseAgeExclude: + - '@seshuk/payload-plugin-tooling' + - '@payloadcms/db-sqlite@4.0.0-canary.38' + - '@payloadcms/drizzle@4.0.0-canary.38' + - '@payloadcms/graphql@4.0.0-canary.38' + - '@payloadcms/next@4.0.0-canary.38' + - '@payloadcms/translations@4.0.0-canary.38' + - '@payloadcms/ui@4.0.0-canary.38' + - payload@4.0.0-canary.38 diff --git a/rslib.config.ts b/rslib.config.ts deleted file mode 100644 index 36692ee..0000000 --- a/rslib.config.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { defineConfig } from '@rslib/core' - -export default defineConfig({ - source: { - entry: { index: ['./src/**/*.ts'] }, - }, - lib: [ - { - format: 'esm', - bundle: false, - dts: true, - }, - ], - output: { - target: 'node', - }, -}) diff --git a/src/bin/generateSpec.ts b/src/bin/generateSpec.ts deleted file mode 100644 index 6811336..0000000 --- a/src/bin/generateSpec.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { writeFile } from 'node:fs/promises' -import path from 'node:path' -import { getPayload } from 'payload' -import type { SanitizedConfig } from 'payload' - -import { PLUGIN_NAME } from '../constants.js' -import type { ResolvedOptions } from '../types.js' -import { buildOpenApiDocument } from '../spec/build.js' - -interface CliArgs { - lang?: string - out?: string - server?: string -} - -export const parseArgs = (argv: string[]): CliArgs => { - const args: CliArgs = {} - for (let i = 0; i < argv.length; i++) { - const arg = argv[i] - if (arg === undefined) continue - const read = (key: keyof CliArgs, flag: string): boolean => { - if (arg === flag) { - args[key] = argv[++i] - return true - } - if (arg.startsWith(`${flag}=`)) { - args[key] = arg.slice(flag.length + 1) - return true - } - return false - } - if (read('lang', '--lang')) continue - if (read('out', '--out')) continue - read('server', '--server') - } - return args -} - -/** - * Bin entrypoint registered automatically by the OpenAPI plugin. Reads resolved - * plugin options from `config.custom[PLUGIN_NAME]` (stashed during config build), - * then writes the OpenAPI document(s) to disk — no HTTP request required. - * - * Flags: - * --lang locale for translated descriptions; `all` writes one file - * per supported language. Defaults to the i18n fallback. - * --out output file for a single-language run (default openapi.json). - * --server base URL written to the document's `servers`. The HTTP - * endpoint derives this per request; on disk there's no - * request, so pass it here or leave `servers` empty. - * - * Note: the spec reflects public-access marking only (no per-user access), - * identical to the HTTP endpoint. - */ -export const script = async (config: SanitizedConfig): Promise => { - const resolved = config.custom?.[PLUGIN_NAME] as ResolvedOptions | undefined - if (!resolved) { - throw new Error( - `${PLUGIN_NAME}: plugin is not registered in payload.config — no resolved options found on config.custom['${PLUGIN_NAME}'].`, - ) - } - - const payload = await getPayload({ config }) - const { lang, out, server } = parseArgs(process.argv) - const servers = server ? [{ url: server }] : undefined - - const fallback = config.i18n?.fallbackLanguage ?? 'en' - const supported = config.i18n?.supportedLanguages ? Object.keys(config.i18n.supportedLanguages) : [fallback] - const langs = lang === 'all' ? supported : [lang ?? fallback] - - for (const language of langs) { - const doc = await buildOpenApiDocument({ payload, options: resolved, language, servers }) - const file = langs.length > 1 ? `openapi.${language}.json` : (out ?? 'openapi.json') - await writeFile(path.resolve(file), `${JSON.stringify(doc, null, 2)}\n`) - payload.logger.info(`Wrote ${file}`) - } - - await payload.destroy() - process.exit(0) -} diff --git a/src/cli/generateSpec.ts b/src/cli/generateSpec.ts new file mode 100644 index 0000000..3701b5b --- /dev/null +++ b/src/cli/generateSpec.ts @@ -0,0 +1,48 @@ +import { writeFile } from 'node:fs/promises' +import path from 'node:path' + +import { strictObject, z } from 'payload' +import { defineCLICommand } from 'payload/cli' + +import { buildOpenApiDocument } from '@/server/spec/build.js' +import { PLUGIN_NAME } from '@/shared/constants.js' +import type { ResolvedOptions } from '@/shared/types/index.js' + +const optionalString = (description: string) => z.optional(z.string()).check(z.describe(description)) + +export const generateSpecCommand = defineCLICommand({ + description: 'Write the OpenAPI document to a file.', + input: strictObject({ + lang: optionalString( + 'Locale for translated descriptions; `all` writes one file per supported language. Defaults to the i18n fallback.', + ), + out: optionalString('Output file for a single-language run (default openapi.json).'), + server: optionalString( + 'Base URL written to the document `servers`. Defaults to the `servers` option, then `serverURL`.', + ), + }), + handler: async ({ args, getConfig, getPayload }) => { + const config = await getConfig() + const resolved = config.custom?.[PLUGIN_NAME] as ResolvedOptions | undefined + if (!resolved) { + throw new Error( + `${PLUGIN_NAME}: plugin is not registered in payload.config — no resolved options found on config.custom['${PLUGIN_NAME}'].`, + ) + } + + const payload = await getPayload() + const { lang, out, server } = args + const servers = server ? [{ url: server }] : undefined + + const fallback = config.i18n?.fallbackLanguage ?? 'en' + const supported = config.i18n?.supportedLanguages ? Object.keys(config.i18n.supportedLanguages) : [fallback] + const langs = lang === 'all' ? supported : [lang ?? fallback] + + for (const language of langs) { + const doc = await buildOpenApiDocument({ payload, options: resolved, language, servers }) + const file = langs.length > 1 ? `openapi.${language}.json` : (out ?? 'openapi.json') + await writeFile(path.resolve(file), `${JSON.stringify(doc, null, 2)}\n`) + payload.logger.info(`Wrote ${file}`) + } + }, +}) diff --git a/src/constants.ts b/src/constants.ts deleted file mode 100644 index 31c77de..0000000 --- a/src/constants.ts +++ /dev/null @@ -1 +0,0 @@ -export const PLUGIN_NAME = '@seshuk/payload-plugin-openapi' diff --git a/src/index.ts b/src/index.ts index 0280f7d..f95f9ca 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,28 +1,34 @@ import { fileURLToPath } from 'node:url' -import type { Config, Plugin } from 'payload' -import { PLUGIN_NAME } from './constants.js' -import { resolveOptions } from './options.js' -import type { OpenApiPluginOptions } from './types.js' -import { interactiveAuthHandler } from './endpoints/interactiveAuth.js' -import { specHandler } from './endpoints/spec.js' -import { translations } from './translations/index.js' +import { definePlugin } from 'payload' -const BIN_KEY = 'openapi:generate' +import { interactiveAuthHandler } from '@/server/endpoints/interactiveAuth.js' +import { specHandler } from '@/server/endpoints/spec.js' +import { resolveOptions } from '@/server/options/resolveOptions.js' +import { readSecurityOverride } from '@/server/spec/security.js' +import { buildFeatures, reportOpenApiTelemetry } from '@/server/telemetry.js' +import { PLUGIN_NAME } from '@/shared/constants.js' +import { translations } from '@/shared/translations/index.js' +import type { OpenApiPluginOptions } from '@/shared/types/index.js' -const resolveBinScriptPath = (): string => { +const CLI_COMMAND = 'openapi:generate' + +const resolveCommandPath = (): string => { const selfUrl = new URL(import.meta.url) const ext = selfUrl.pathname.endsWith('.ts') ? '.ts' : '.js' - return fileURLToPath(new URL(`./bin/generateSpec${ext}`, selfUrl)) + return `${fileURLToPath(new URL(`./cli/generateSpec${ext}`, selfUrl))}#generateSpecCommand` } -export const openapi = - (options: OpenApiPluginOptions): Plugin => - (config: Config): Config => { +export const openapi = definePlugin({ + slug: PLUGIN_NAME, + plugin: ({ config, options, plugins }) => { + const resolved = resolveOptions(options) + for (const entity of [...(config.collections ?? []), ...(config.globals ?? [])]) readSecurityOverride(entity) if (options.enabled === false) return config - const resolved = resolveOptions(options) - const authCollection = (config.collections ?? []).find((c) => Boolean(c.auth))?.slug ?? 'users' + const authCollection = + resolved.interactiveAuth.collection ?? (config.collections ?? []).find((c) => Boolean(c.auth))?.slug ?? 'users' + const features = buildFeatures({ plugins, resolved }) config.i18n = config.i18n ?? {} config.i18n.translations = config.i18n.translations ?? {} @@ -43,21 +49,26 @@ export const openapi = ...config.custom, [PLUGIN_NAME]: resolved, }, - bin: [...(config.bin ?? []), { key: BIN_KEY, scriptPath: resolveBinScriptPath() }], + cli: + config.cli === false + ? config.cli + : { + ...config.cli, + commands: { [CLI_COMMAND]: resolveCommandPath(), ...config.cli?.commands }, + }, endpoints: [ ...(config.endpoints ?? []), - // `serve: false` registers the CLI bin only — nothing is mounted over HTTP. ...(resolved.serve ? [ { - path: resolved.specEndpoint, + path: resolved.path, method: 'get' as const, handler: specHandler(resolved), }, ...(resolved.interactiveAuth.enabled ? [ { - path: resolved.interactiveAuth.endpoint, + path: resolved.interactiveAuth.path, method: 'post' as const, handler: interactiveAuthHandler(authCollection), }, @@ -66,24 +77,40 @@ export const openapi = ] : []), ], + onInit: async (payload) => { + await config.onInit?.(payload) + void reportOpenApiTelemetry({ features, options, payload }) + }, } - } + }, +}) -export { buildOpenApiDocument } from './spec/build.js' -export type { BuildOpenApiInput } from './spec/build.js' -export { toOpenApi30, toOpenApi31 } from './spec/downconvert.js' -export { scalar } from './ui/scalar.js' -export { swaggerUi } from './ui/swagger.js' +export { buildOpenApiDocument } from '@/server/spec/build.js' +export type { BuildOpenApiInput } from '@/server/spec/build.js' +export { toOpenApi30, toOpenApi31 } from '@/server/spec/downconvert.js' +export { scalar } from '@/server/ui/scalar.js' +export { swaggerUi } from '@/server/ui/swagger.js' export type { + AccessOption, BuildContext, + EntityOpenApiMeta, EntityOperation, EntitySecurityOverride, + ExtensionContext, + FieldOpenApiMeta, FilterOptions, + LocalizedText, OpenApiExtension, - OpenApiMetadata, + OpenApiInfo, OpenApiPluginOptions, OpenApiVersion, OperationContext, + OperationFilter, + OperationKind, + OperationRule, + SecurityMarking, + SecurityOption, + ServersOption, UiPluginOptions, -} from './types.js' +} from '@/shared/types/index.js' diff --git a/src/options.ts b/src/options.ts deleted file mode 100644 index 2f85d50..0000000 --- a/src/options.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { PLUGIN_NAME } from './constants.js' -import type { FilterOptions, OpenApiPluginOptions, ResolvedFilters, ResolvedOptions } from './types.js' - -const resolveFilters = (filters: FilterOptions = {}): ResolvedFilters => ({ - include: filters.include ?? [], - exclude: filters.exclude ?? [], - includeHidden: filters.includeHidden ?? false, - includeSystem: filters.includeSystem ?? false, - includeCustom: filters.includeCustom ?? true, - includeAuth: filters.includeAuth ?? true, - includeAdminAuth: filters.includeAdminAuth ?? false, - includeVersions: filters.includeVersions ?? true, - includeJobs: filters.includeJobs ?? true, - excludeOperations: filters.excludeOperations ?? [], - excludeWhen: filters.excludeWhen, -}) - -const DEFAULT_AUTH_ENDPOINT = '/openapi-auth' - -const resolveInteractiveAuth = ( - option: OpenApiPluginOptions['interactiveAuth'], -): ResolvedOptions['interactiveAuth'] => { - if (!option) return { enabled: false, endpoint: DEFAULT_AUTH_ENDPOINT } - if (option === true) return { enabled: true, endpoint: DEFAULT_AUTH_ENDPOINT } - return { enabled: true, endpoint: option.endpoint ?? DEFAULT_AUTH_ENDPOINT } -} - -export const resolveOptions = (options: OpenApiPluginOptions): ResolvedOptions => { - if (!options.metadata?.title || !options.metadata?.version) { - throw new Error(`${PLUGIN_NAME}: metadata.title and metadata.version are required.`) - } - - return { - metadata: options.metadata, - openapiVersion: options.openapiVersion ?? '3.2', - specEndpoint: options.specEndpoint ?? '/openapi.json', - serve: options.serve ?? true, - filters: resolveFilters(options.filters), - interactiveAuth: resolveInteractiveAuth(options.interactiveAuth), - nestedTags: options.nestedTags ?? false, - securityWhen: options.securityWhen, - cache: options.cache ?? true, - extensions: options.extensions ?? [], - } -} diff --git a/src/endpoints/interactiveAuth.ts b/src/server/endpoints/interactiveAuth.ts similarity index 87% rename from src/endpoints/interactiveAuth.ts rename to src/server/endpoints/interactiveAuth.ts index 888fb1f..57d144f 100644 --- a/src/endpoints/interactiveAuth.ts +++ b/src/server/endpoints/interactiveAuth.ts @@ -15,7 +15,7 @@ export const interactiveAuthHandler: InteractiveAuthHandler = (authCollectionSlu return Response.json({ access_token: result.token, token_type: 'JWT', - expires_in: result.exp, + expires_in: result.exp === undefined ? undefined : result.exp - Math.floor(Date.now() / 1000), }) } catch { return Response.json({ error: 'invalid_grant' }, { status: 400 }) diff --git a/src/endpoints/spec.ts b/src/server/endpoints/spec.ts similarity index 51% rename from src/endpoints/spec.ts rename to src/server/endpoints/spec.ts index f7f0441..1f53722 100644 --- a/src/endpoints/spec.ts +++ b/src/server/endpoints/spec.ts @@ -1,14 +1,20 @@ +import type { Document, ServerObject } from '@scalar/openapi-types/3.2' +import { Forbidden } from 'payload' import type { PayloadHandler, PayloadRequest } from 'payload' -import type { Document } from '@scalar/openapi-types/3.2' -import type { ResolvedOptions } from '../types.js' -import { buildOpenApiDocument } from '../spec/build.js' + +import { buildOpenApiDocument, staticServers } from '@/server/spec/build.js' +import type { ResolvedOptions } from '@/shared/types/index.js' type SpecHandler = (options: ResolvedOptions) => PayloadHandler -const deriveServerUrl = (req: PayloadRequest): string => { - const host = req.headers.get('host') ?? 'localhost:3000' - const protocol = host.startsWith('localhost') || host.startsWith('127.') ? 'http' : 'https' - return `${protocol}://${host}` +const requestServers = async (req: PayloadRequest, options: ResolvedOptions): Promise => { + if (typeof options.servers === 'function') return options.servers({ req }) + const host = req.headers.get('host')?.toLowerCase() + if (!options.servers && host && options.trustedHosts.includes(host)) { + const protocol = host.startsWith('localhost') || host.startsWith('127.') ? 'http' : 'https' + return [{ url: `${protocol}://${host}` }] + } + return staticServers(options, req.payload.config) } const requestedLang = (req: PayloadRequest): string | undefined => { @@ -22,6 +28,7 @@ export const specHandler: SpecHandler = (options) => { const cache = new Map() return async (req: PayloadRequest) => { + if (options.access && (await options.access({ req })) !== true) throw new Forbidden(req.t) const requested = requestedLang(req) const lang = requested ?? req.i18n?.language ?? req.payload.config.i18n?.fallbackLanguage ?? 'en' @@ -35,7 +42,6 @@ export const specHandler: SpecHandler = (options) => { }) cache.set(lang, doc) } - const document = { ...doc, servers: [{ url: deriveServerUrl(req) }] } - return Response.json(document) + return Response.json({ ...doc, servers: await requestServers(req, options) }) } } diff --git a/src/server/options/resolveOptions.ts b/src/server/options/resolveOptions.ts new file mode 100644 index 0000000..52f65b4 --- /dev/null +++ b/src/server/options/resolveOptions.ts @@ -0,0 +1,69 @@ +import { PLUGIN_NAME } from '@/shared/constants.js' +import type { FilterOptions, OpenApiPluginOptions, ResolvedFilters, ResolvedOptions } from '@/shared/types/index.js' +import { isPlainObject } from '@/shared/utils.js' + +const resolveFilters = (filters: FilterOptions = {}): ResolvedFilters => ({ + include: filters.include ?? [], + exclude: filters.exclude ?? [], + includeHidden: filters.includeHidden ?? false, + includeSystem: filters.includeSystem ?? false, + includeCustom: filters.includeCustom ?? true, + includeAuth: filters.includeAuth ?? true, + includeAdminAuth: filters.includeAdminAuth ?? false, + includeVersions: filters.includeVersions ?? true, + includeJobs: filters.includeJobs ?? true, + excludeOperations: filters.excludeOperations ?? [], +}) + +const DEFAULT_AUTH_PATH = '/openapi-auth' + +const resolveInteractiveAuth = ( + option: OpenApiPluginOptions['interactiveAuth'], +): ResolvedOptions['interactiveAuth'] => { + if (!option) return { enabled: false, path: DEFAULT_AUTH_PATH } + if (option === true) return { enabled: true, path: DEFAULT_AUTH_PATH } + return { enabled: true, path: option.path ?? DEFAULT_AUTH_PATH, collection: option.collection } +} + +export const assertNoRemovedKeys = (removed: Array<[unknown, string]>): void => { + for (const [value, message] of removed) { + if (value !== undefined) throw new Error(`[${PLUGIN_NAME}] ${message}`) + } +} + +export const resolveOptions = (options: OpenApiPluginOptions): ResolvedOptions => { + const legacy = options as Record + const legacyFilters = isPlainObject(legacy.filters) ? legacy.filters : {} + const legacyAuth = isPlainObject(legacy.interactiveAuth) ? legacy.interactiveAuth : {} + assertNoRemovedKeys([ + [legacy.metadata, 'metadata was renamed to info'], + [legacy.specEndpoint, 'specEndpoint was renamed to path'], + [legacy.securityWhen, "securityWhen was removed, use security (return 'public' or 'secured', not true or false)"], + [legacyFilters.excludeWhen, 'filters.excludeWhen was removed, use a function in filters.excludeOperations'], + [legacyAuth.endpoint, 'interactiveAuth.endpoint was renamed to interactiveAuth.path'], + [ + options.extensions?.some((extension) => (extension.transform?.length ?? 0) > 1) || undefined, + 'extensions[].transform takes one object now, use ({ doc, payload }) instead of (doc, ctx)', + ], + ]) + + if (!options.info?.title || !options.info?.version) { + throw new Error(`[${PLUGIN_NAME}] info.title and info.version are required`) + } + + return { + info: options.info, + openapiVersion: options.openapiVersion ?? '3.2', + path: options.path ?? '/openapi.json', + serve: options.serve ?? true, + access: options.access, + servers: options.servers, + trustedHosts: (options.trustedHosts ?? []).map((host) => host.toLowerCase()), + filters: resolveFilters(options.filters), + interactiveAuth: resolveInteractiveAuth(options.interactiveAuth), + nestedTags: options.nestedTags ?? false, + security: options.security, + cache: options.cache ?? true, + extensions: options.extensions ?? [], + } +} diff --git a/src/spec/build.ts b/src/server/spec/build.ts similarity index 72% rename from src/spec/build.ts rename to src/server/spec/build.ts index b011cb7..40b3d9a 100644 --- a/src/spec/build.ts +++ b/src/server/spec/build.ts @@ -1,9 +1,10 @@ -import { getLocalI18n } from 'payload' -import type { BasePayload } from 'payload' import type { AcceptedLanguages, I18n } from '@payloadcms/translations' -import type { Document } from '@scalar/openapi-types/3.2' +import type { Document, ServerObject } from '@scalar/openapi-types/3.2' +import { getLocalI18n } from 'payload' +import type { BasePayload, SanitizedConfig } from 'payload' + +import type { BuildContext, ResolvedOptions } from '@/shared/types/index.js' -import type { BuildContext, ResolvedOptions } from '../types.js' import { buildDocument } from './buildDocument.js' import { toOpenApi30, toOpenApi31 } from './downconvert.js' @@ -12,7 +13,12 @@ export interface BuildOpenApiInput { options: ResolvedOptions language?: string i18n?: I18n - servers?: { url: string }[] + servers?: ServerObject[] +} + +export const staticServers = (options: ResolvedOptions, config: SanitizedConfig): ServerObject[] => { + if (Array.isArray(options.servers)) return options.servers + return config.serverURL ? [{ url: config.serverURL }] : [] } export const buildOpenApiDocument = async (input: BuildOpenApiInput): Promise => { @@ -37,13 +43,13 @@ export const buildOpenApiDocument = async (input: BuildOpenApiInput): Promise collection.config), globals: config.globals, config, ctx, - logger: payload.logger, }) if (options.openapiVersion === '3.0') return toOpenApi30(doc) diff --git a/src/spec/buildDocument.ts b/src/server/spec/buildDocument.ts similarity index 67% rename from src/spec/buildDocument.ts rename to src/server/spec/buildDocument.ts index ddce4a3..04b0ee3 100644 --- a/src/spec/buildDocument.ts +++ b/src/server/spec/buildDocument.ts @@ -1,4 +1,4 @@ -import type { Field, PayloadLogger, SanitizedCollectionConfig, SanitizedConfig, SanitizedGlobalConfig } from 'payload' +import { getTranslation } from '@payloadcms/translations' import type { ComponentsObject, Document, @@ -7,12 +7,27 @@ import type { SecuritySchemeObject, TagObject, } from '@scalar/openapi-types/3.2' -import { getTranslation } from '@payloadcms/translations' +import type { + Access, + BasePayload, + Field, + SanitizedCollectionConfig, + SanitizedConfig, + SanitizedGlobalConfig, +} from 'payload' + +import { buildAuthPaths } from '@/server/spec/paths/auth.js' +import { buildCollectionPaths } from '@/server/spec/paths/collections.js' +import { buildCustomEndpointPaths } from '@/server/spec/paths/custom.js' +import { buildGlobalPaths } from '@/server/spec/paths/globals.js' +import { buildJobsPaths } from '@/server/spec/paths/jobs.js' +import { buildSystemPaths } from '@/server/spec/paths/system.js' +import { buildVersionPaths, versionComponentSchemas } from '@/server/spec/paths/versions.js' +import { PLUGIN_NAME } from '@/shared/constants.js' +import { makeT } from '@/shared/translations/index.js' +import type { BuildContext, OperationKind, ResolvedOptions } from '@/shared/types/index.js' +import { deepMerge } from '@/shared/utils.js' -import type { BuildContext, ResolvedOptions } from '../types.js' -import { PLUGIN_NAME } from '../constants.js' -import { deepMerge } from '../utils.js' -import { makeT } from '../translations/index.js' import { ERROR_SCHEMA_NAME, INTERACTIVE_SCHEME_NAME, @@ -24,6 +39,7 @@ import { } from './components.js' import { buildBlockSchema, buildEntitySchemas } from './entitySchemas.js' import { filterOperations, shouldIncludeCollection, shouldIncludeGlobal } from './filters.js' +import { applyHierarchy } from './hierarchy.js' import { blockSchemaName, createSchemaName, @@ -37,14 +53,8 @@ import { updateSchemaName, } from './names.js' import { buildParamSchemas, buildQueryOperationsSchema, collectionHasFilters } from './params.js' -import { applySecurityWhen, resolveEntitySecurity } from './security.js' -import { buildTagHierarchy, type CollectionTagInfo, type GlobalTagInfo } from './tags.js' -import { buildAuthPaths } from './paths/auth.js' -import { buildCollectionPaths } from './paths/collections.js' -import { buildCustomEndpointPaths } from './paths/custom.js' -import { buildGlobalPaths } from './paths/globals.js' -import { buildJobsPaths } from './paths/jobs.js' -import { buildVersionPaths, versionComponentSchemas } from './paths/versions.js' +import { applySecurity, evaluateAccess, resolveEntitySecurity, securedRequirement } from './security.js' +import { buildTagHierarchy, type CollectionTagInfo, type GlobalTagInfo, type SystemTag } from './tags.js' const resolveDescription = (description: unknown, ctx: BuildContext): string | undefined => { if (description == null) return undefined @@ -71,16 +81,27 @@ export interface BuildInput { globals: SanitizedGlobalConfig[] config: SanitizedConfig ctx: BuildContext - logger: PayloadLogger + payload: BasePayload } export const buildDocument = async (input: BuildInput): Promise => { - const { options, ctx, config, logger } = input + const { options, ctx, config, payload } = input + const { logger } = payload const t = makeT(ctx.i18n) const paths: PathsObject = {} const schemas: Record = {} const { filters } = options + const login = options.serve && options.interactiveAuth.enabled + const finalize = async (group: PathsObject, kind: OperationKind, slug?: string): Promise => + applySecurity({ + paths: await filterOperations({ paths: group, slug, kind, filters }), + slug, + kind, + security: options.security, + login, + }) + const access = filters.includeAuth && filters.includeAdminAuth const collections = input.collections.filter((c) => shouldIncludeCollection(c, filters)) const globals = input.globals.filter((g) => shouldIncludeGlobal(g, filters)) @@ -116,10 +137,11 @@ export const buildDocument = async (input: BuildInput): Promise => { const security = await resolveEntitySecurity({ entity: collection, - operations: ['read', 'create', 'update', 'delete'], + operations: ['read', 'create', 'update', 'delete', 'validate', 'readVersions'], locale: ctx.locales[0], + baseAccess: config.baseAccess, }) - const collectionPaths: PathsObject = { ...buildCollectionPaths({ collection, ctx, security }) } + const collectionPaths: PathsObject = { ...buildCollectionPaths({ collection, ctx, security, access }) } if (filters.includeAuth) { Object.assign( @@ -145,25 +167,13 @@ export const buildDocument = async (input: BuildInput): Promise => { ctx, schemaBase: base, nestedTags: options.nestedTags, + security, }), ) } - const filteredCollectionPaths = filterOperations({ - paths: collectionPaths, - slug: collection.slug, - kind: 'collection', - filters, - }) - Object.assign( - paths, - applySecurityWhen({ - paths: filteredCollectionPaths, - slug: collection.slug, - kind: 'collection', - securityWhen: options.securityWhen, - }), - ) + applyHierarchy({ collection, base, paths: collectionPaths, schemas, ctx }) + Object.assign(paths, await finalize(collectionPaths, 'collection', collection.slug)) } catch (error) { logger.warn(`${PLUGIN_NAME}: skipped collection "${collection.slug}": ${(error as Error).message}`) } @@ -184,10 +194,11 @@ export const buildDocument = async (input: BuildInput): Promise => { const security = await resolveEntitySecurity({ entity: global, - operations: ['read', 'update'], + operations: ['read', 'update', 'validate', 'readVersions'], locale: ctx.locales[0], + baseAccess: config.baseAccess, }) - const globalPaths: PathsObject = { ...buildGlobalPaths({ global, ctx, security }) } + const globalPaths: PathsObject = { ...buildGlobalPaths({ global, ctx, security, access }) } if (filters.includeVersions && global.versions) { const { version, versionList } = versionComponentSchemas({ @@ -205,21 +216,13 @@ export const buildDocument = async (input: BuildInput): Promise => { ctx, schemaBase: gbase, nestedTags: options.nestedTags, + security, global: true, }), ) } - const filteredGlobalPaths = filterOperations({ paths: globalPaths, slug: global.slug, kind: 'global', filters }) - Object.assign( - paths, - applySecurityWhen({ - paths: filteredGlobalPaths, - slug: global.slug, - kind: 'global', - securityWhen: options.securityWhen, - }), - ) + Object.assign(paths, await finalize(globalPaths, 'global', global.slug)) } catch (error) { logger.warn(`${PLUGIN_NAME}: skipped global "${global.slug}": ${(error as Error).message}`) } @@ -227,7 +230,7 @@ export const buildDocument = async (input: BuildInput): Promise => { if (filters.includeCustom) { try { - Object.assign(paths, buildCustomEndpointPaths({ config, collections, globals, ctx })) + Object.assign(paths, await finalize(buildCustomEndpointPaths({ config, collections, globals, ctx }), 'custom')) } catch (error) { logger.warn(`${PLUGIN_NAME}: failed to collect custom endpoints: ${(error as Error).message}`) } @@ -240,12 +243,24 @@ export const buildDocument = async (input: BuildInput): Promise => { if (filters.includeJobs) { try { - Object.assign(paths, buildJobsPaths({ config, ctx })) + const runAccess = (jobsConfig?.access as { run?: Access } | undefined)?.run + const jobsSecurity = (await evaluateAccess(runAccess, { baseAccess: config.baseAccess })) + ? undefined + : securedRequirement() + Object.assign(paths, await finalize(buildJobsPaths({ config, ctx, security: jobsSecurity }), 'jobs')) } catch (error) { logger.warn(`${PLUGIN_NAME}: failed to add jobs endpoint: ${(error as Error).message}`) } } + const uploadSlugs = collections.filter((c) => c.upload).map((c) => c.slug) + Object.assign(paths, await finalize(buildSystemPaths({ ctx, access, uploadSlugs }), 'system')) + + const systemTags: SystemTag[] = [] + if (hasJobs) systemTags.push('Jobs') + if (uploadSlugs.length > 0) systemTags.push('Uploads') + if (access) systemTags.push('Access') + const supportedTimezones = config.admin?.timezones?.supportedTimezones if (Array.isArray(supportedTimezones) && supportedTimezones.length > 0) { schemas[schemaName('supportedTimezones')] = { @@ -261,9 +276,9 @@ export const buildDocument = async (input: BuildInput): Promise => { const securitySchemes: Record = { [SECURITY_SCHEME_NAME]: securityScheme({ cookiePrefix, t }), } - if (options.interactiveAuth.enabled) { + if (login) { securitySchemes[INTERACTIVE_SCHEME_NAME] = interactiveSecurityScheme({ - tokenUrl: `${ctx.apiRoute}${options.interactiveAuth.endpoint}`, + tokenUrl: `${ctx.apiRoute}${options.interactiveAuth.path}`, t, }) } @@ -280,8 +295,9 @@ export const buildDocument = async (input: BuildInput): Promise => { ? `\n\n${t('docLanguagesNote', { languages: ctx.docLanguages.map((l) => `\`${l}\``).join(', ') })}` : '' const info = { - ...options.metadata, - description: `${options.metadata.description ?? ''}${localeNote}${docLanguagesNote}`.trim() || undefined, + ...options.info, + description: + `${resolveDescription(options.info.description, ctx) ?? ''}${localeNote}${docLanguagesNote}`.trim() || undefined, } let doc = { @@ -296,7 +312,7 @@ export const buildDocument = async (input: BuildInput): Promise => { tags: buildTagHierarchy({ collections: collectionTagInfos, globals: globalTagInfos, - hasJobs, + systemTags, t, nested: options.nestedTags, }), @@ -304,23 +320,24 @@ export const buildDocument = async (input: BuildInput): Promise => { } as Document for (const ext of options.extensions) { - try { - if (ext.paths) { - doc.paths = deepMerge(doc.paths ?? {}, ext.paths) as PathsObject - } - if (ext.components) { - doc.components = deepMerge( - (doc.components ?? {}) as Record, - ext.components as Record, - ) as ComponentsObject - } - if (ext.tags) { - const seen = new Set((doc.tags ?? []).map((tag: TagObject) => tag.name)) - doc.tags = [...(doc.tags ?? []), ...ext.tags.filter((tag: TagObject) => !seen.has(tag.name))] + if (ext.paths) { + doc.paths = deepMerge(doc.paths ?? {}, ext.paths) as PathsObject + } + if (ext.components) { + doc.components = deepMerge( + (doc.components ?? {}) as Record, + ext.components as Record, + ) as ComponentsObject + } + if (ext.tags) { + const seen = new Set((doc.tags ?? []).map((tag: TagObject) => tag.name)) + doc.tags = [...(doc.tags ?? []), ...ext.tags.filter((tag: TagObject) => !seen.has(tag.name))] + } + if (ext.transform) { + doc = await ext.transform({ ...ctx, doc, payload, options }) + if (typeof doc?.openapi !== 'string' || 'payload' in doc) { + throw new Error(`[${PLUGIN_NAME}] extensions[].transform must return the document, it gets ({ doc, payload })`) } - if (ext.transform) doc = ext.transform(doc, ctx) - } catch (error) { - logger.warn(`${PLUGIN_NAME}: extension failed and was skipped: ${(error as Error).message}`) } } diff --git a/src/spec/components.ts b/src/server/spec/components.ts similarity index 67% rename from src/spec/components.ts rename to src/server/spec/components.ts index d054373..cfba8e8 100644 --- a/src/spec/components.ts +++ b/src/server/spec/components.ts @@ -1,13 +1,17 @@ import type { + OperationObject, + ParameterObject, ReferenceObject, RequestBodyObject, ResponseObject, ResponsesObject, SchemaObject, + SecurityRequirementObject, SecuritySchemeObject, } from '@scalar/openapi-types/3.2' -import type { Translate } from '../translations/types.js' +import type { Translate } from '@/shared/translations/types.js' + import { refTo } from './names.js' type SchemaOrRef = SchemaObject | ReferenceObject @@ -35,6 +39,103 @@ export const messageResponse = (description: string): ResponsesObject => export type ErrorCode = '400' | '401' | '403' | '404' | '500' +const allowed = (): SchemaObject => ({ + oneOf: [ + { type: 'boolean', enum: [true] }, + { + type: 'object', + properties: { permission: { type: 'boolean' }, where: { type: 'object', additionalProperties: true } }, + required: ['permission'], + }, + ], +}) + +export const docAccessOperation = ({ + tag, + operationId, + operations, + bodyRef, + errors, + t, +}: { + tag: string + operationId: string + operations: string[] + bodyRef: ReferenceObject + errors: ErrorCode[] + t: Translate +}): OperationObject => ({ + tags: [tag], + operationId, + requestBody: { description: t('docAccessBody'), content: { 'application/json': { schema: bodyRef } } }, + responses: { + ...jsonOk(t('docAccess'), { + type: 'object', + properties: { + ...Object.fromEntries(operations.map((op) => [op, allowed()])), + fields: { + oneOf: [ + { type: 'boolean', enum: [true] }, + { type: 'object', additionalProperties: true }, + ], + }, + }, + }), + ...errorResponses(errors, t), + }, +}) + +export const validateOperation = ({ + tag, + operationId, + parameters, + requestBody, + errors, + security, + t, +}: { + tag: string + operationId: string + parameters: ParameterObject[] + requestBody: RequestBodyObject + errors: ErrorCode[] + security?: SecurityRequirementObject[] + t: Translate +}): OperationObject => ({ + tags: [tag], + operationId, + parameters, + requestBody: { + ...requestBody, + description: [requestBody.description, t('validateBody')].filter(Boolean).join(' '), + }, + responses: { + ...jsonOk(t('validateResult'), { + type: 'object', + properties: { + valid: { type: 'boolean' }, + errors: { + type: 'array', + items: { + type: 'object', + properties: { + message: { type: 'string' }, + path: { type: 'string' }, + label: { oneOf: [{ type: 'string' }, { type: 'object', additionalProperties: { type: 'string' } }] }, + locale: { type: 'string' }, + tableName: { type: 'string' }, + }, + required: ['message', 'path'], + }, + }, + }, + required: ['valid', 'errors'], + }), + ...errorResponses(errors, t), + }, + security, +}) + export const ERROR_SCHEMA_NAME = 'ErrorResponse' const ERROR_KEYS = { diff --git a/src/spec/downconvert.ts b/src/server/spec/downconvert.ts similarity index 96% rename from src/spec/downconvert.ts rename to src/server/spec/downconvert.ts index 3f4664a..0f991cf 100644 --- a/src/spec/downconvert.ts +++ b/src/server/spec/downconvert.ts @@ -74,5 +74,7 @@ export const toOpenApi31 = (doc: Document): Document => ({ export const toOpenApi30 = (doc: Document): Document => { const converted = downconvertTo30(toOpenApi31(doc)) as Document converted.openapi = '3.0.4' + delete converted.info.summary + delete converted.info.license?.identifier return converted } diff --git a/src/spec/entitySchemas.ts b/src/server/spec/entitySchemas.ts similarity index 72% rename from src/spec/entitySchemas.ts rename to src/server/spec/entitySchemas.ts index 99f7827..ffa254e 100644 --- a/src/spec/entitySchemas.ts +++ b/src/server/spec/entitySchemas.ts @@ -1,20 +1,17 @@ -import { entityToJSONSchema } from 'payload' -import type { Block, Field, SanitizedConfig } from 'payload' -import type { SchemaObject } from '@scalar/openapi-types/3.2' import { getTranslation } from '@payloadcms/translations' +import type { SchemaObject } from '@scalar/openapi-types/3.2' +import { entityToJSONSchema } from 'payload' +import type { Block, Field, SanitizedConfig, SchemaVariant } from 'payload' + +import { makeT } from '@/shared/translations/index.js' +import type { BuildContext, Entity, IDType } from '@/shared/types/index.js' +import { deepMerge, isPlainObject } from '@/shared/utils.js' -import type { BuildContext, Entity, IDType } from '../types.js' -import { deepMerge, isPlainObject } from '../utils.js' -import { makeT } from '../translations/index.js' import { AUTH_FIELDS, flattenFields } from './fields.js' -import { refTo, rewriteRefs, schemaName } from './names.js' +import { rewriteRefs } from './names.js' const READ_HIDDEN_FIELDS = new Set([...AUTH_FIELDS, 'collection']) -const SERVER_FIELDS = ['id', 'createdAt', 'updatedAt'] - -// Drop interfaceName: entityToJSONSchema emits dangling $refs otherwise. -// Shallow copy only, config has functions, structuredClone would throw. const stripInterfaceName = (entity: T): T => { const { interfaceName: _omit, ...rest } = entity as T & { interfaceName?: string } return rest as T @@ -24,18 +21,28 @@ const toComponentSchema = ({ config, source, idType, - write = false, + defs = new Map(), + variant, }: { config: SanitizedConfig source: Entity | Block idType: IDType - write?: boolean + defs?: Map + variant?: SchemaVariant }): SchemaObject => { - const jsonSchema = entityToJSONSchema(config, source as never, new Map(), idType, undefined, undefined, { - forceInlineBlocks: write, - }) + const jsonSchema = entityToJSONSchema( + config, + source as never, + defs as never, + idType, + new Set(), + undefined, + undefined, + undefined, + variant, + ) const blockSlugs = new Set((config.blocks ?? []).map((b) => b.slug)) - const schema = rewriteRefs(jsonSchema as never, blockSlugs) as unknown as SchemaObject + const schema = rewriteRefs(jsonSchema as never, blockSlugs, defs) as unknown as SchemaObject delete schema.title return schema } @@ -55,8 +62,6 @@ const isLocaleKeyed = (value: unknown, ctx: BuildContext): value is Record { @@ -129,6 +134,19 @@ const addBlockDiscriminators = (node: unknown): void => { for (const value of Object.values(node)) addBlockDiscriminators(value) } +const fixPolymorphicJoinRequired = (node: unknown): void => { + if (Array.isArray(node)) { + for (const item of node) fixPolymorphicJoinRequired(item) + return + } + if (!isPlainObject(node)) return + const { properties, required } = node as SchemaObject + if (isPlainObject(properties) && 'relationTo' in properties && Array.isArray(required)) { + ;(node as SchemaObject).required = required.map((r: string) => (r === 'collectionSlug' ? 'relationTo' : r)) + } + for (const value of Object.values(node)) fixPolymorphicJoinRequired(value) +} + const markLocalizedFields = ({ schema, fields, @@ -166,47 +184,19 @@ const removeProps = (schema: SchemaObject, names: Iterable): void => { } } -const toIdType = (type: unknown): unknown => - Array.isArray(type) ? type.map(toIdType) : type === 'number' ? 'integer' : type - -const writeRelationshipsAsIds = (node: unknown, refs: Set): void => { - if (Array.isArray(node)) { - for (const item of node) writeRelationshipsAsIds(item, refs) - return +const isConstantDeny = (access: unknown): boolean => { + if (typeof access !== 'function' || access.length > 0) return false + try { + return access() === false + } catch { + return false } - if (!isPlainObject(node)) return - const oneOf = node.oneOf - if (Array.isArray(oneOf) && oneOf.length === 2 && isRef(oneOf[1]) && refs.has(oneOf[1].$ref)) { - delete node.oneOf - node.type = toIdType((oneOf[0] as SchemaObject).type) - return - } - for (const value of Object.values(node)) writeRelationshipsAsIds(value, refs) } -const buildBaseSchema = ({ - entity, - config, - ctx, - write = false, -}: { - entity: Entity - config: SanitizedConfig - ctx: BuildContext - write?: boolean -}): SchemaObject => { - const idType = ctx.defaultIDType - const schema = toComponentSchema({ config, source: stripInterfaceName(entity), idType, write }) - if (write) { - const refs = new Set((config.collections ?? []).map((c) => refTo(schemaName(c.slug)))) - writeRelationshipsAsIds(schema, refs) - } - const isAuth = 'auth' in entity && Boolean(entity.auth) - if (isAuth) removeProps(schema, READ_HIDDEN_FIELDS) - applyFieldOverrides({ schema, fields: entity.fields, ctx }) - addBlockDiscriminators(schema) - return schema -} +const deniedWrites = (fields: Field[], operation: 'create' | 'update'): string[] => + flattenFields(fields) + .filter((field) => 'name' in field && 'access' in field && isConstantDeny(field.access?.[operation])) + .map((field) => (field as { name: string }).name) const cloneSchema = (schema: SchemaObject): SchemaObject => ({ ...schema, @@ -229,21 +219,30 @@ export const buildEntitySchemas = async ({ config: SanitizedConfig ctx: BuildContext }): Promise => { - const read = buildBaseSchema({ entity, config, ctx }) - const write = buildBaseSchema({ entity, config, ctx, write: true }) + const source = stripInterfaceName(entity) + const defs = new Map() + const idType = ctx.defaultIDType + const read = toComponentSchema({ config, source, idType, defs }) + const write = toComponentSchema({ config, source, idType, defs, variant: 'input' }) const isAuth = 'auth' in entity && Boolean(entity.auth) - const create = cloneSchema(write) - if (Array.isArray(create.required)) { - const optional = new Set(SERVER_FIELDS) - create.required = create.required.filter((r: string) => !optional.has(r)) + for (const schema of [read, write]) { + if (isAuth) removeProps(schema, READ_HIDDEN_FIELDS) + applyFieldOverrides({ schema, fields: entity.fields, ctx }) + addBlockDiscriminators(schema) } + fixPolymorphicJoinRequired(read) + + const create = cloneSchema(write) if (isAuth && create.properties) { create.properties.password = { type: 'string' } create.required = [...new Set([...(create.required ?? []), 'password'])] } + removeProps(create, deniedWrites(entity.fields, 'create')) + const update = cloneSchema(write) + removeProps(update, deniedWrites(entity.fields, 'update')) delete update.required if (update.properties) delete update.properties.id if (isAuth && update.properties) { diff --git a/src/spec/fields.ts b/src/server/spec/fields.ts similarity index 100% rename from src/spec/fields.ts rename to src/server/spec/fields.ts diff --git a/src/spec/filters.ts b/src/server/spec/filters.ts similarity index 75% rename from src/spec/filters.ts rename to src/server/spec/filters.ts index e4eabd1..b3ee782 100644 --- a/src/spec/filters.ts +++ b/src/server/spec/filters.ts @@ -1,24 +1,28 @@ -import type { SanitizedCollectionConfig, SanitizedGlobalConfig } from 'payload' import type { PathsObject } from '@scalar/openapi-types/3.2' +import type { SanitizedCollectionConfig, SanitizedGlobalConfig } from 'payload' + import type { EntityKind, EntityMatcher, HttpMethod, OperationContext, + OperationKind, OperationRule, ResolvedFilters, -} from '../types.js' +} from '@/shared/types/index.js' const SYSTEM_COLLECTIONS = new Set([ 'payload-jobs', 'payload-locked-documents', 'payload-preferences', 'payload-migrations', - 'payload-folders', 'payload-kv', + 'payload-llm-instructions', 'payload-query-presets', ]) +const SYSTEM_GLOBALS = new Set(['payload-jobs-stats']) + const HTTP_METHODS: HttpMethod[] = ['get', 'post', 'patch', 'put', 'delete'] interface EntityRef { @@ -61,16 +65,19 @@ export const shouldIncludeCollection = (collection: SanitizedCollectionConfig, f return passesIncludeExclude({ slug, kind: 'collection' }, filters) } -export const shouldIncludeGlobal = (global: SanitizedGlobalConfig, filters: ResolvedFilters): boolean => - passesIncludeExclude({ slug: global.slug, kind: 'global' }, filters) +export const shouldIncludeGlobal = (global: SanitizedGlobalConfig, filters: ResolvedFilters): boolean => { + if (SYSTEM_GLOBALS.has(global.slug) && !filters.includeSystem) return false + return passesIncludeExclude({ slug: global.slug, kind: 'global' }, filters) +} const ruleMatchesMethod = (rule: OperationRule, method: HttpMethod): boolean => { if (rule.method === undefined) return true return Array.isArray(rule.method) ? rule.method.includes(method) : rule.method === method } -const ruleMatchesSlug = (rule: OperationRule, slug: string): boolean => { +const ruleMatchesSlug = (rule: OperationRule, slug: string | undefined): boolean => { if (rule.slug === undefined) return true + if (slug === undefined) return false return rule.slug instanceof RegExp ? rule.slug.test(slug) : rule.slug === slug } @@ -80,23 +87,25 @@ const ruleMatches = (rule: OperationRule, { method, slug, kind, path }: Operatio (rule.kind === undefined || rule.kind === kind) && (rule.path === undefined || rule.path.test(path)) -const isOperationExcluded = (filters: ResolvedFilters, op: OperationContext): boolean => { - if (filters.excludeOperations.some((rule) => ruleMatches(rule, op))) return true - return filters.excludeWhen?.(op) ?? false +const isOperationExcluded = async (filters: ResolvedFilters, op: OperationContext): Promise => { + for (const rule of filters.excludeOperations) { + if (typeof rule === 'function' ? (await rule(op)) === true : ruleMatches(rule, op)) return true + } + return false } -export const filterOperations = ({ +export const filterOperations = async ({ paths, slug, kind, filters, }: { paths: PathsObject - slug: string - kind: EntityKind + slug?: string + kind: OperationKind filters: ResolvedFilters -}): PathsObject => { - if (filters.excludeOperations.length === 0 && !filters.excludeWhen) return paths +}): Promise => { + if (filters.excludeOperations.length === 0) return paths for (const [path, item] of Object.entries(paths)) { if (!item) continue @@ -104,7 +113,7 @@ export const filterOperations = ({ const ops = item as Record for (const method of HTTP_METHODS) { if (!ops[method]) continue - if (isOperationExcluded(filters, { method, slug, kind, path })) { + if (await isOperationExcluded(filters, { method, path, slug, kind })) { delete ops[method] } else { remaining += 1 diff --git a/src/server/spec/hierarchy.ts b/src/server/spec/hierarchy.ts new file mode 100644 index 0000000..66e6fe6 --- /dev/null +++ b/src/server/spec/hierarchy.ts @@ -0,0 +1,50 @@ +import type { OperationObject, ParameterObject, PathsObject, SchemaObject } from '@scalar/openapi-types/3.2' +import type { SanitizedCollectionConfig } from 'payload' + +import { createSchemaName, querySchemaName, updateSchemaName } from '@/server/spec/names.js' +import { makeT } from '@/shared/translations/index.js' +import type { BuildContext } from '@/shared/types/index.js' + +export const applyHierarchy = ({ + collection, + base, + paths, + schemas, + ctx, +}: { + collection: SanitizedCollectionConfig + base: string + paths: PathsObject + schemas: Record + ctx: BuildContext +}): void => { + const { hierarchy } = collection + if (!hierarchy) return + const t = makeT(ctx.i18n) + const pathFields = { + [hierarchy.slugPathFieldName]: t('schemaHierarchySlugPath'), + [hierarchy.titlePathFieldName]: t('schemaHierarchyTitlePath'), + } + + const read = schemas[base]?.properties + for (const [name, description] of Object.entries(pathFields)) { + if (read?.[name]) read[name] = { ...read[name], readOnly: true, description } + for (const other of [createSchemaName(base), updateSchemaName(base), querySchemaName(base)]) { + delete schemas[other]?.properties?.[name] + } + } + + const param: ParameterObject = { + name: 'computeHierarchyPaths', + in: 'query', + description: t('paramComputeHierarchyPaths', { + slugPath: hierarchy.slugPathFieldName, + titlePath: hierarchy.titlePathFieldName, + }), + schema: { type: 'boolean' }, + } + const route = `${ctx.apiRoute}/${collection.slug}` + for (const op of [paths[route]?.get, paths[`${route}/{id}`]?.get] as (OperationObject | undefined)[]) { + op?.parameters?.push(param) + } +} diff --git a/src/spec/names.ts b/src/server/spec/names.ts similarity index 67% rename from src/spec/names.ts rename to src/server/spec/names.ts index 634ea52..15f2c89 100644 --- a/src/spec/names.ts +++ b/src/server/spec/names.ts @@ -1,4 +1,4 @@ -import { upperFirst } from '../utils.js' +import { upperFirst } from '@/shared/utils.js' export const schemaName = (slug: string): string => upperFirst(slug.replace(/-([a-z])/g, (_, c: string) => c.toUpperCase())) @@ -24,25 +24,36 @@ type JsonValue = Record | unknown[] | string | number | boolean const REF = /^#\/(?:definitions|\$defs|components\/schemas)\/(.+)$/ +const findBlockSlug = (name: string, blockSlugs: Set): string | undefined => + [...blockSlugs].find((s) => s === name || schemaName(s) === schemaName(name)) + const normalizeRef = (ref: string, blockSlugs: Set): string => { const match = REF.exec(ref) if (!match) return ref const captured = match[1]! - const pascal = schemaName(captured) - const blockSlug = [...blockSlugs].find((s) => s === captured || schemaName(s) === pascal) + const blockSlug = findBlockSlug(captured, blockSlugs) if (blockSlug) return `#/components/schemas/${blockSchemaName(blockSlug)}` - return `#/components/schemas/${pascal}` + return `#/components/schemas/${schemaName(captured)}` } -export const rewriteRefs = (node: JsonValue, blockSlugs: Set = new Set()): JsonValue => { - if (Array.isArray(node)) return node.map((item) => rewriteRefs(item as JsonValue, blockSlugs)) +export const rewriteRefs = ( + node: JsonValue, + blockSlugs: Set = new Set(), + defs: Map = new Map(), +): JsonValue => { + if (Array.isArray(node)) return node.map((item) => rewriteRefs(item as JsonValue, blockSlugs, defs)) if (node && typeof node === 'object') { + const { $ref, ...rest } = node + const name = typeof $ref === 'string' ? REF.exec($ref)?.[1] : undefined + if (name && defs.has(name) && !findBlockSlug(name, blockSlugs)) { + return rewriteRefs({ ...(defs.get(name) as Record), ...rest }, blockSlugs, defs) + } const out: Record = {} for (const [key, value] of Object.entries(node)) { out[key] = key === '$ref' && typeof value === 'string' ? normalizeRef(value, blockSlugs) - : rewriteRefs(value as JsonValue, blockSlugs) + : rewriteRefs(value as JsonValue, blockSlugs, defs) } return out } diff --git a/src/spec/params.ts b/src/server/spec/params.ts similarity index 94% rename from src/spec/params.ts rename to src/server/spec/params.ts index 0177402..ddc952f 100644 --- a/src/spec/params.ts +++ b/src/server/spec/params.ts @@ -1,8 +1,9 @@ -import type { Field } from 'payload' import type { ParameterObject, ReferenceObject, SchemaObject } from '@scalar/openapi-types/3.2' +import type { Field } from 'payload' + +import { makeT } from '@/shared/translations/index.js' +import type { BuildContext, Entity } from '@/shared/types/index.js' -import type { BuildContext, Entity } from '../types.js' -import { makeT } from '../translations/index.js' import { exposableFields } from './fields.js' import { joinsSchemaName, populateSchemaName, querySchemaName, refTo, selectSchemaName } from './names.js' @@ -286,6 +287,20 @@ export const writeParams = ({ WRITE_PARAMS[operation].has(p.name), ) +export const validateParams = (ctx: BuildContext): ParameterObject[] => + ctx.locales.length > 0 + ? [ + { + name: 'locale', + in: 'query', + description: makeT(ctx.i18n)('paramValidateLocale'), + style: 'form', + explode: true, + schema: { type: 'array', items: { type: 'string', enum: [...ctx.locales, 'all'] } }, + }, + ] + : [] + export const whereParam = (base: string): ParameterObject => objectParam('where', refTo(querySchemaName(base))) export const buildListParams = ({ diff --git a/src/spec/paths/auth.ts b/src/server/spec/paths/auth.ts similarity index 78% rename from src/spec/paths/auth.ts rename to src/server/spec/paths/auth.ts index 1ae9677..724926d 100644 --- a/src/spec/paths/auth.ts +++ b/src/server/spec/paths/auth.ts @@ -1,4 +1,3 @@ -import type { SanitizedCollectionConfig } from 'payload' import type { OperationObject, PathsObject, @@ -6,12 +5,23 @@ import type { ResponsesObject, SchemaObject, } from '@scalar/openapi-types/3.2' +import type { SanitizedCollectionConfig } from 'payload' + +import { + ERRORS, + type ErrorCode, + SECURITY_SCHEME_NAME, + errorResponses, + jsonBody, + jsonResponse, + messageResponse, +} from '@/server/spec/components.js' +import { refTo, schemaName } from '@/server/spec/names.js' +import { authTagName } from '@/server/spec/tags.js' +import { makeT } from '@/shared/translations/index.js' +import type { BuildContext } from '@/shared/types/index.js' -import type { BuildContext } from '../../types.js' -import { makeT } from '../../translations/index.js' -import { ERRORS, type ErrorCode, errorResponses, jsonBody, jsonResponse, messageResponse } from '../components.js' -import { refTo, schemaName } from '../names.js' -import { authTagName } from '../tags.js' +const LOCAL_STRATEGY_ROUTES = ['login', 'forgot-password', 'reset-password', 'first-register', 'unlock', 'verify/{id}'] const identifierBody = ( auth: SanitizedCollectionConfig['auth'], @@ -49,7 +59,7 @@ export const buildAuthPaths = ({ nestedTags: boolean }): PathsObject => { const auth = collection.auth - if (!auth || auth.disableLocalStrategy) return {} + if (!auth) return {} const t = makeT(ctx.i18n) const name = schemaName(collection.slug) @@ -173,16 +183,6 @@ export const buildAuthPaths = ({ }, }), } - paths[`${base}/access`] = { - get: op(`access${name}`, ERRORS.session, { - responses: { - '200': jsonResponse(t('authAccess'), { - type: 'object', - additionalProperties: true, - }), - }, - }), - } } paths[`${base}/unlock`] = { @@ -201,5 +201,39 @@ export const buildAuthPaths = ({ } } + if (auth.disableLocalStrategy) { + for (const route of LOCAL_STRATEGY_ROUTES) delete paths[`${base}/${route}`] + } + + if (typeof auth.useAPIKey === 'object' && auth.useAPIKey.reveal === true) { + const errorBody = (description: string) => + jsonResponse(description, { type: 'object', properties: { error: { type: 'string' } } }) + paths[`${base}/{id}/api-key/reveal`] = { + parameters: [ + { + name: 'id', + in: 'path', + required: true, + schema: { type: ctx.defaultIDType === 'number' ? 'integer' : 'string' }, + }, + ], + post: { + tags: tag, + operationId: `revealApiKey${name}`, + responses: { + '200': jsonResponse(t('apiKeyReveal'), { + type: 'object', + properties: { apiKey: { type: 'string' } }, + required: ['apiKey'], + }), + '403': errorBody(t('error403')), + '404': errorBody(t('error404')), + ...errorResponses(['500'], t), + }, + security: [{ [SECURITY_SCHEME_NAME]: [] }], + }, + } + } + return paths } diff --git a/src/spec/paths/collections.ts b/src/server/spec/paths/collections.ts similarity index 71% rename from src/spec/paths/collections.ts rename to src/server/spec/paths/collections.ts index 175411c..48b1fbd 100644 --- a/src/spec/paths/collections.ts +++ b/src/server/spec/paths/collections.ts @@ -1,4 +1,3 @@ -import type { SanitizedCollectionConfig } from 'payload' import type { PathsObject, ReferenceObject, @@ -6,29 +5,41 @@ import type { ResponsesObject, SchemaObject, } from '@scalar/openapi-types/3.2' +import type { SanitizedCollectionConfig } from 'payload' -import type { BuildContext } from '../../types.js' -import { makeT } from '../../translations/index.js' -import { ERRORS, errorResponses, jsonOk, jsonResponse, uploadRequestBody } from '../components.js' -import { createSchemaName, listSchemaName, refTo, schemaName, updateSchemaName } from '../names.js' +import { + ERRORS, + docAccessOperation, + errorResponses, + jsonOk, + jsonResponse, + uploadRequestBody, + validateOperation, +} from '@/server/spec/components.js' +import { createSchemaName, listSchemaName, refTo, schemaName, updateSchemaName } from '@/server/spec/names.js' import { buildListParams, buildParamSchemas, commonReadParams, type ReadParamRefs, + validateParams, writeParams, type WriteOperation, -} from '../params.js' -import type { EntitySecurity } from '../security.js' +} from '@/server/spec/params.js' +import type { EntitySecurity } from '@/server/spec/security.js' +import { makeT } from '@/shared/translations/index.js' +import type { BuildContext } from '@/shared/types/index.js' export const buildCollectionPaths = ({ collection, ctx, security = {}, + access = false, }: { collection: SanitizedCollectionConfig ctx: BuildContext security?: EntitySecurity + access?: boolean }): PathsObject => { const t = makeT(ctx.i18n) const name = schemaName(collection.slug) @@ -38,7 +49,7 @@ export const buildCollectionPaths = ({ const createRef: ReferenceObject = { $ref: refTo(createSchemaName(name)) } const updateRef: ReferenceObject = { $ref: refTo(updateSchemaName(name)) } const idType = ctx.defaultIDType === 'number' ? 'integer' : 'string' - const { read: secRead, create: secCreate, update: secUpdate, delete: secDelete } = security + const { read: secRead, create: secCreate, update: secUpdate, delete: secDelete, validate: secValidate } = security const allowBulk = collection.disableBulkEdit !== true const allowDuplicate = collection.disableDuplicate !== true @@ -166,6 +177,17 @@ export const buildCollectionPaths = ({ security: secRead, }, }, + [`${base}/validate`]: { + post: validateOperation({ + tag: name, + operationId: `validate${name}`, + parameters: validateParams(ctx), + requestBody: { ...requestBody(createRef, false), required: true }, + errors: ERRORS.create, + security: secValidate, + t, + }), + }, [`${base}/{id}`]: { parameters: [{ name: 'id', in: 'path', required: true, schema: { type: idType } }], get: { @@ -193,6 +215,19 @@ export const buildCollectionPaths = ({ }, } + paths[`${base}/{id}/validate`] = { + parameters: [{ name: 'id', in: 'path', required: true, schema: { type: idType } }], + post: validateOperation({ + tag: name, + operationId: `validate${name}ById`, + parameters: validateParams(ctx), + requestBody: requestBody(updateRef, false), + errors: ERRORS.update, + security: secValidate, + t, + }), + } + if (allowDuplicate) { paths[`${base}/{id}/duplicate`] = { parameters: [{ name: 'id', in: 'path', required: true, schema: { type: idType } }], @@ -206,5 +241,42 @@ export const buildCollectionPaths = ({ } } + if (access) { + const auth = collection.auth + const operations = [ + 'create', + 'read', + 'update', + 'delete', + 'validate', + ...(auth && auth.maxLoginAttempts ? ['unlock'] : []), + ...(collection.versions ? ['readVersions'] : []), + ] + const accessOp = (operationId: string, errors: typeof ERRORS.read) => + docAccessOperation({ tag: name, operationId, operations, bodyRef: updateRef, errors, t }) + paths[`${base}/access`] = { post: accessOp(`access${name}`, ['500']) } + paths[`${base}/access/{id}`] = { + parameters: [{ name: 'id', in: 'path', required: true, schema: { type: idType } }], + post: accessOp(`access${name}ById`, ['404', '500']), + } + } + + if (isUpload) { + const file = { content: { '*/*': { schema: { type: 'string', format: 'binary' } } } } as const + paths[`${base}/file/{filename}`] = { + parameters: [{ name: 'filename', in: 'path', required: true, schema: { type: 'string' } }], + get: { + tags: [name], + operationId: `get${name}File`, + responses: { + '200': { description: t('fileServe'), ...file }, + '206': { description: t('filePartial'), ...file }, + ...errorResponses(['400', '403', '404', '500'], t), + }, + security: secRead, + }, + } + } + return paths } diff --git a/src/spec/paths/custom.ts b/src/server/spec/paths/custom.ts similarity index 89% rename from src/spec/paths/custom.ts rename to src/server/spec/paths/custom.ts index 389433a..2bee942 100644 --- a/src/spec/paths/custom.ts +++ b/src/server/spec/paths/custom.ts @@ -1,6 +1,7 @@ -import type { Endpoint, SanitizedCollectionConfig, SanitizedConfig, SanitizedGlobalConfig } from 'payload' import type { OperationObject, PathsObject } from '@scalar/openapi-types/3.2' -import type { BuildContext } from '../../types.js' +import type { Endpoint, SanitizedCollectionConfig, SanitizedConfig, SanitizedGlobalConfig } from 'payload' + +import type { BuildContext } from '@/shared/types/index.js' const getOpenapiMeta = (endpoint: Endpoint): OperationObject | undefined => { const meta = endpoint.custom?.openapi @@ -27,7 +28,7 @@ const collect = ({ if (!meta) continue const path = normalizePath(prefix, endpoint.path) const method = endpoint.method.toLowerCase() - out[path] = { ...out[path], [method]: meta } + out[path] = { ...out[path], [method]: { ...meta } } } } @@ -43,7 +44,7 @@ export const buildCustomEndpointPaths = ({ ctx: BuildContext }): PathsObject => { const out: PathsObject = {} - collect({ endpoints: config.endpoints, prefix: '', out }) + collect({ endpoints: config.endpoints, prefix: ctx.apiRoute, out }) for (const collection of collections) { collect({ endpoints: collection.endpoints, prefix: `${ctx.apiRoute}/${collection.slug}`, out }) } diff --git a/src/spec/paths/globals.ts b/src/server/spec/paths/globals.ts similarity index 53% rename from src/spec/paths/globals.ts rename to src/server/spec/paths/globals.ts index affd967..efcc95c 100644 --- a/src/spec/paths/globals.ts +++ b/src/server/spec/paths/globals.ts @@ -1,27 +1,35 @@ -import type { SanitizedGlobalConfig } from 'payload' import type { PathsObject, ReferenceObject } from '@scalar/openapi-types/3.2' +import type { SanitizedGlobalConfig } from 'payload' -import type { BuildContext } from '../../types.js' -import { makeT } from '../../translations/index.js' -import { ERRORS, errorResponses, jsonOk } from '../components.js' -import { globalSchemaName, refTo, updateSchemaName } from '../names.js' -import { buildParamSchemas, commonReadParams, type ReadParamRefs, writeParams } from '../params.js' -import type { EntitySecurity } from '../security.js' +import { ERRORS, docAccessOperation, errorResponses, jsonOk, validateOperation } from '@/server/spec/components.js' +import { globalSchemaName, refTo, updateSchemaName } from '@/server/spec/names.js' +import { + buildParamSchemas, + commonReadParams, + type ReadParamRefs, + validateParams, + writeParams, +} from '@/server/spec/params.js' +import type { EntitySecurity } from '@/server/spec/security.js' +import { makeT } from '@/shared/translations/index.js' +import type { BuildContext } from '@/shared/types/index.js' export const buildGlobalPaths = ({ global, ctx, security = {}, + access = false, }: { global: SanitizedGlobalConfig ctx: BuildContext security?: EntitySecurity + access?: boolean }): PathsObject => { const t = makeT(ctx.i18n) const name = globalSchemaName(global.slug) const docRef: ReferenceObject = { $ref: refTo(name) } const updateRef: ReferenceObject = { $ref: refTo(updateSchemaName(name)) } - const { read: secRead, update: secUpdate } = security + const { read: secRead, update: secUpdate, validate: secValidate } = security const paramSchemas = buildParamSchemas({ fields: global.fields, ctx }) const refs: ReadParamRefs = { @@ -30,7 +38,7 @@ export const buildGlobalPaths = ({ joins: Boolean(paramSchemas.joins), } - return { + const paths: PathsObject = { [`${ctx.apiRoute}/globals/${global.slug}`]: { get: { tags: [name], @@ -54,5 +62,31 @@ export const buildGlobalPaths = ({ security: secUpdate, }, }, + [`${ctx.apiRoute}/globals/${global.slug}/validate`]: { + post: validateOperation({ + tag: name, + operationId: `validate${name}`, + parameters: validateParams(ctx), + requestBody: { content: { 'application/json': { schema: updateRef } } }, + errors: ERRORS.globalUpdate, + security: secValidate, + t, + }), + }, } + + if (access) { + paths[`${ctx.apiRoute}/globals/${global.slug}/access`] = { + post: docAccessOperation({ + tag: name, + operationId: `access${name}`, + operations: ['read', 'update', 'validate', ...(global.versions ? ['readVersions'] : [])], + bodyRef: updateRef, + errors: ['500'], + t, + }), + } + } + + return paths } diff --git a/src/spec/paths/jobs.ts b/src/server/spec/paths/jobs.ts similarity index 86% rename from src/spec/paths/jobs.ts rename to src/server/spec/paths/jobs.ts index 8bbed9f..7760b05 100644 --- a/src/spec/paths/jobs.ts +++ b/src/server/spec/paths/jobs.ts @@ -1,9 +1,9 @@ +import type { ParameterObject, PathsObject, SecurityRequirementObject } from '@scalar/openapi-types/3.2' import type { SanitizedConfig } from 'payload' -import type { ParameterObject, PathsObject } from '@scalar/openapi-types/3.2' -import type { BuildContext } from '../../types.js' -import { makeT } from '../../translations/index.js' -import { errorResponses, jsonResponse } from '../components.js' +import { errorResponses, jsonResponse } from '@/server/spec/components.js' +import { makeT } from '@/shared/translations/index.js' +import type { BuildContext } from '@/shared/types/index.js' const boolParam = (name: string, description: string): ParameterObject => ({ name, @@ -48,7 +48,15 @@ const queueParam = (queues: string[], ctx: BuildContext): ParameterObject => ({ schema: { type: 'string', enum: queues }, }) -export const buildJobsPaths = ({ config, ctx }: { config: SanitizedConfig; ctx: BuildContext }): PathsObject => { +export const buildJobsPaths = ({ + config, + ctx, + security, +}: { + config: SanitizedConfig + ctx: BuildContext + security?: SecurityRequirementObject[] +}): PathsObject => { const jobs = config.jobs as Record | undefined const hasJobs = Boolean((jobs?.tasks as unknown[])?.length || (jobs?.workflows as unknown[])?.length) if (!hasJobs) return {} @@ -81,6 +89,7 @@ export const buildJobsPaths = ({ config, ctx }: { config: SanitizedConfig; ctx: }), ...errors, }, + security, }, }, [`${ctx.apiRoute}/payload-jobs/handle-schedules`]: { @@ -101,6 +110,7 @@ export const buildJobsPaths = ({ config, ctx }: { config: SanitizedConfig; ctx: }), ...errors, }, + security, }, }, } diff --git a/src/server/spec/paths/system.ts b/src/server/spec/paths/system.ts new file mode 100644 index 0000000..8364549 --- /dev/null +++ b/src/server/spec/paths/system.ts @@ -0,0 +1,113 @@ +import type { PathsObject, ResponsesObject } from '@scalar/openapi-types/3.2' + +import { SECURITY_SCHEME_NAME, errorResponses, jsonBody, jsonResponse } from '@/server/spec/components.js' +import { makeT } from '@/shared/translations/index.js' +import type { BuildContext } from '@/shared/types/index.js' + +export const buildSystemPaths = ({ + ctx, + access, + uploadSlugs, +}: { + ctx: BuildContext + access: boolean + uploadSlugs: string[] +}): PathsObject => { + const t = makeT(ctx.i18n) + const paths: PathsObject = {} + + if (access) { + paths[`${ctx.apiRoute}/access`] = { + get: { + tags: ['Access'], + operationId: 'access', + responses: { + '200': jsonResponse(t('authAccess'), { type: 'object', additionalProperties: true }), + ...errorResponses(['500'], t), + }, + }, + } + } + + if (uploadSlugs.length > 0) { + const staged: ResponsesObject = { + '204': { description: t('uploadStageResult') }, + ...errorResponses(['400', '500'], t), + } + + paths[`${ctx.apiRoute}/upload-instructions`] = { + post: { + tags: ['Uploads'], + operationId: 'getUploadInstructions', + summary: t('uploadInstructionsSummary'), + requestBody: jsonBody({ + type: 'object', + additionalProperties: false, + properties: { + collectionSlug: { type: 'string', enum: uploadSlugs }, + docPrefix: { type: 'string' }, + filename: { type: 'string' }, + filesize: { type: 'integer', minimum: 0 }, + mimeType: { type: 'string' }, + }, + required: ['collectionSlug', 'filename', 'filesize', 'mimeType'], + }), + responses: { + '200': jsonResponse(t('uploadInstructionsResult'), { + type: 'object', + properties: { + type: { type: 'string', enum: ['http', 'dispatch'] }, + file: { + type: 'object', + properties: { + filename: { type: 'string' }, + mimeType: { type: 'string' }, + size: { type: 'integer' }, + uploadReference: { type: 'object', additionalProperties: true }, + }, + required: ['filename', 'mimeType', 'size', 'uploadReference'], + }, + request: { + type: 'object', + properties: { + method: { type: 'string', enum: ['POST', 'PUT'] }, + url: { type: 'string' }, + headers: { type: 'object', additionalProperties: { type: 'string' } }, + }, + required: ['method', 'url'], + }, + name: { type: 'string' }, + data: {}, + }, + required: ['type', 'file'], + }), + ...errorResponses(['400', '403', '500'], t), + }, + security: [{ [SECURITY_SCHEME_NAME]: [] }], + }, + } + paths[`${ctx.apiRoute}/upload-instructions/{uploadId}`] = { + parameters: [{ name: 'uploadId', in: 'path', required: true, schema: { type: 'string' } }], + put: { + tags: ['Uploads'], + operationId: 'uploadStagedFile', + summary: t('uploadStagePutSummary'), + requestBody: { + required: true, + content: { '*/*': { schema: { type: 'string', format: 'binary' } } }, + }, + responses: staged, + security: [{ [SECURITY_SCHEME_NAME]: [] }], + }, + delete: { + tags: ['Uploads'], + operationId: 'deleteStagedFile', + summary: t('uploadStageDeleteSummary'), + responses: staged, + security: [{ [SECURITY_SCHEME_NAME]: [] }], + }, + } + } + + return paths +} diff --git a/src/spec/paths/versions.ts b/src/server/spec/paths/versions.ts similarity index 90% rename from src/spec/paths/versions.ts rename to src/server/spec/paths/versions.ts index 3d80a1e..cb897b3 100644 --- a/src/spec/paths/versions.ts +++ b/src/server/spec/paths/versions.ts @@ -1,11 +1,12 @@ import type { ParameterObject, PathsObject, ReferenceObject, SchemaObject } from '@scalar/openapi-types/3.2' -import type { BuildContext, Entity, IDType } from '../../types.js' -import { makeT } from '../../translations/index.js' -import { ERRORS, errorResponses, jsonResponse, nullableType } from '../components.js' -import { refTo, schemaName } from '../names.js' -import { buildParamSchemas, commonReadParams, type ReadParamRefs } from '../params.js' -import { versionsTagName } from '../tags.js' +import { ERRORS, errorResponses, jsonResponse, nullableType } from '@/server/spec/components.js' +import { refTo, schemaName } from '@/server/spec/names.js' +import { buildParamSchemas, commonReadParams, type ReadParamRefs } from '@/server/spec/params.js' +import type { EntitySecurity } from '@/server/spec/security.js' +import { versionsTagName } from '@/server/spec/tags.js' +import { makeT } from '@/shared/translations/index.js' +import type { BuildContext, Entity, IDType } from '@/shared/types/index.js' const buildVersionSchema = (base: string, idType: IDType): SchemaObject => { const id: SchemaObject = { type: idType === 'number' ? 'integer' : 'string' } @@ -105,6 +106,7 @@ export const buildVersionPaths = ({ ctx, schemaBase, nestedTags, + security = {}, global = false, }: { entity: Entity @@ -112,6 +114,7 @@ export const buildVersionPaths = ({ ctx: BuildContext schemaBase?: string nestedTags: boolean + security?: EntitySecurity global?: boolean }): PathsObject => { if (!entity.versions) return {} @@ -147,6 +150,7 @@ export const buildVersionPaths = ({ '200': jsonResponse(t('versionList'), versionListRef(name)), ...errorResponses(ERRORS.versionList, t), }, + security: security.readVersions, }, }, [`${pathBase}/versions/{id}`]: { @@ -159,6 +163,7 @@ export const buildVersionPaths = ({ '200': jsonResponse(t('versionSingle'), versionRef(name)), ...errorResponses(ERRORS.versionRead, t), }, + security: security.readVersions, }, post: { tags: versionsTag, @@ -171,6 +176,7 @@ export const buildVersionPaths = ({ '200': jsonResponse(t('versionRestored'), restoreResponse), ...errorResponses(ERRORS.versionRestore, t), }, + security: security.update, }, }, } diff --git a/src/server/spec/security.ts b/src/server/spec/security.ts new file mode 100644 index 0000000..cc21b58 --- /dev/null +++ b/src/server/spec/security.ts @@ -0,0 +1,155 @@ +import type { PathsObject, SecurityRequirementObject } from '@scalar/openapi-types/3.2' +import type { Access, SanitizedConfig } from 'payload' + +import { PLUGIN_NAME } from '@/shared/constants.js' +import type { + Entity, + EntityOperation, + EntitySecurityOverride, + HttpMethod, + OperationKind, + SecurityOption, +} from '@/shared/types/index.js' +import { isPlainObject } from '@/shared/utils.js' + +import { INTERACTIVE_SCHEME_NAME, SECURITY_SCHEME_NAME } from './components.js' + +const HTTP_METHODS: HttpMethod[] = ['get', 'post', 'patch', 'put', 'delete'] + +export const securedRequirement = (login = false): SecurityRequirementObject[] => + login ? [{ [SECURITY_SCHEME_NAME]: [] }, { [INTERACTIVE_SCHEME_NAME]: [] }] : [{ [SECURITY_SCHEME_NAME]: [] }] + +type ProbedOperation = EntityOperation | 'readVersions' + +const OVERRIDE_FALLBACK: Partial> = { + readVersions: 'read', + validate: 'update', +} + +export type EntitySecurity = Partial> + +const withTimeout = async (promise: Promise, timeoutMs: number): Promise => { + let timer: ReturnType | undefined + const guard = new Promise((resolve) => { + timer = setTimeout(() => resolve(false), timeoutMs) + }) + try { + return await Promise.race([ + promise.then( + (value) => value === true, + () => false, + ), + guard, + ]) + } finally { + if (timer) clearTimeout(timer) + } +} + +export const evaluateAccess = async ( + access: Access | undefined, + opts: { locale?: string; timeoutMs?: number; baseAccess?: SanitizedConfig['baseAccess'] } = {}, +): Promise => { + if (!access) return false + const { locale, timeoutMs = 250, baseAccess } = opts + const trap = (allowed: PropertyKey, value: unknown): T => + new Proxy({} as T, { + get(_target, key) { + if (key === allowed) return value + throw new Error('req.payload accessed during build') + }, + }) + const payloadTrap = trap('config', trap('baseAccess', baseAccess)) + const req = { user: null, headers: new Headers(), context: {}, locale, payload: payloadTrap } + try { + const result = access({ req } as never) + if (result && typeof (result as Promise).then === 'function') { + return await withTimeout(result as Promise, timeoutMs) + } + return result === true + } catch { + return false + } +} + +export const readSecurityOverride = (entity: { + slug: string + custom?: { openapi?: { security?: unknown } } +}): EntitySecurityOverride | undefined => { + const meta = entity.custom?.openapi?.security + const values = isPlainObject(meta) ? Object.values(meta) : [meta] + if (values.some((value) => typeof value === 'boolean')) { + throw new Error( + `[${PLUGIN_NAME}] custom.openapi.security true/false on "${entity.slug}" was removed, use 'public' or 'secured'`, + ) + } + return meta === undefined ? undefined : (meta as EntitySecurityOverride) +} + +const overrideFor = (override: EntitySecurityOverride | undefined, op: ProbedOperation): boolean | undefined => { + const fallback = OVERRIDE_FALLBACK[op] + const marking = + typeof override === 'string' + ? override + : (override?.[op as EntityOperation] ?? (fallback ? override?.[fallback] : undefined)) + return marking === undefined ? undefined : marking === 'public' +} + +export const resolveEntitySecurity = async ({ + entity, + operations, + locale, + baseAccess, +}: { + entity: Entity + operations: ProbedOperation[] + locale?: string + baseAccess?: SanitizedConfig['baseAccess'] +}): Promise => { + const override = readSecurityOverride(entity) + const access = entity.access as Partial> | undefined + const out: EntitySecurity = {} + for (const op of operations) { + const declared = overrideFor(override, op) + const isPublic = declared ?? (await evaluateAccess(access?.[op], { locale, baseAccess })) + out[op] = isPublic ? undefined : securedRequirement() + } + return out +} + +const withLogin = (security: SecurityRequirementObject[]): SecurityRequirementObject[] => + security.some((r) => SECURITY_SCHEME_NAME in r) && !security.some((r) => INTERACTIVE_SCHEME_NAME in r) + ? [...security, { [INTERACTIVE_SCHEME_NAME]: [] }] + : security + +export const applySecurity = async ({ + paths, + slug, + kind, + security, + login = false, +}: { + paths: PathsObject + slug?: string + kind: OperationKind + security?: SecurityOption + login?: boolean +}): Promise => { + if (!security && !login) return paths + for (const [path, item] of Object.entries(paths)) { + if (!item) continue + const ops = item as Record + for (const method of HTTP_METHODS) { + const op = ops[method] + if (!op) continue + if (login && op.security) op.security = withLogin(op.security) + if (!security) continue + const detected = op.security?.length ? 'secured' : 'public' + const decision = await security({ method, path, slug, kind, detected }) + if (decision === 'public') delete op.security + else if (decision === 'secured') op.security = securedRequirement(login) + else if (Array.isArray(decision)) op.security = decision + } + } + return paths +} diff --git a/src/spec/tags.ts b/src/server/spec/tags.ts similarity index 79% rename from src/spec/tags.ts rename to src/server/spec/tags.ts index 4dd2358..e24f60b 100644 --- a/src/spec/tags.ts +++ b/src/server/spec/tags.ts @@ -1,5 +1,6 @@ import type { TagObject } from '@scalar/openapi-types/3.2' -import type { Translate } from '../translations/types.js' + +import type { Translate } from '@/shared/translations/types.js' export const NAV_COLLECTIONS = 'Collections' export const NAV_GLOBALS = 'Globals' @@ -9,6 +10,10 @@ export const entityTagName = (base: string): string => base export const authTagName = (base: string): string => `${base} Auth` export const versionsTagName = (base: string): string => `${base} Versions` +const SYSTEM_TAG_SUMMARIES = { Jobs: 'tagJobs', Uploads: 'tagUploads', Access: 'tagAccess' } as const + +export type SystemTag = keyof typeof SYSTEM_TAG_SUMMARIES + export interface CollectionTagInfo { base: string hasAuth: boolean @@ -25,12 +30,12 @@ export interface GlobalTagInfo { export interface TagHierarchyInput { collections: CollectionTagInfo[] globals: GlobalTagInfo[] - hasJobs: boolean + systemTags: SystemTag[] t: Translate nested: boolean } -export const buildTagHierarchy = ({ collections, globals, hasJobs, t, nested }: TagHierarchyInput): TagObject[] => { +export const buildTagHierarchy = ({ collections, globals, systemTags, t, nested }: TagHierarchyInput): TagObject[] => { if (!nested) { return [ ...collections.map((c) => ({ name: entityTagName(c.base), description: c.description })), @@ -62,9 +67,9 @@ export const buildTagHierarchy = ({ collections, globals, hasJobs, t, nested }: } } - if (hasJobs) { + if (systemTags.length > 0) { tags.push({ name: NAV_SYSTEM, summary: t('tagSystem'), description: t('tagSystemDesc'), kind: 'nav' }) - tags.push({ name: 'Jobs', parent: NAV_SYSTEM, summary: t('tagJobs') }) + for (const name of systemTags) tags.push({ name, parent: NAV_SYSTEM, summary: t(SYSTEM_TAG_SUMMARIES[name]) }) } return tags diff --git a/src/server/telemetry.ts b/src/server/telemetry.ts new file mode 100644 index 0000000..e8fde2c --- /dev/null +++ b/src/server/telemetry.ts @@ -0,0 +1,70 @@ +import { reportTelemetry } from '@seshuk/payload-plugin-tooling/telemetry' +import type { Payload, PluginsMap } from 'payload' + +import { PLUGIN_NAME, SCALAR_PLUGIN_NAME, SWAGGER_UI_PLUGIN_NAME } from '@/shared/constants.js' +import type { OpenApiPluginOptions, ResolvedOptions } from '@/shared/types/index.js' + +export type TelemetryFeatures = { + access: boolean + cache: boolean + extensions: boolean + extensionTransform: boolean + filtersEntities: boolean + filtersExcludeOperations: boolean + filtersIncludeSystem: boolean + interactiveAuth: boolean + nestedTags: boolean + openapiVersion30: boolean + openapiVersion31: boolean + security: boolean + serve: boolean + servers: boolean + trustedHosts: boolean + uiScalar: boolean + uiSwagger: boolean +} + +export const buildFeatures = ({ + plugins, + resolved, +}: { + plugins: PluginsMap + resolved: ResolvedOptions +}): TelemetryFeatures => ({ + access: Boolean(resolved.access), + cache: resolved.cache, + extensions: resolved.extensions.length > 0, + extensionTransform: resolved.extensions.some((extension) => Boolean(extension.transform)), + filtersEntities: resolved.filters.include.length > 0 || resolved.filters.exclude.length > 0, + filtersExcludeOperations: resolved.filters.excludeOperations.length > 0, + filtersIncludeSystem: resolved.filters.includeSystem, + interactiveAuth: resolved.interactiveAuth.enabled, + nestedTags: resolved.nestedTags, + openapiVersion30: resolved.openapiVersion === '3.0', + openapiVersion31: resolved.openapiVersion === '3.1', + security: Boolean(resolved.security), + serve: resolved.serve, + servers: Boolean(resolved.servers), + trustedHosts: resolved.trustedHosts.length > 0, + uiScalar: Boolean(plugins[SCALAR_PLUGIN_NAME]), + uiSwagger: Boolean(plugins[SWAGGER_UI_PLUGIN_NAME]), +}) + +export const reportOpenApiTelemetry = ({ + features, + options, + payload, +}: { + features: TelemetryFeatures + options: OpenApiPluginOptions + payload: Payload +}): Promise => + reportTelemetry({ + disableEnv: 'OPENAPI_TELEMETRY_DISABLED', + docsUrl: 'https://payload-plugin-openapi.seshuk.im/v1/configuration/telemetry', + features, + option: options.telemetry, + packageName: PLUGIN_NAME, + payload, + product: 'payload-plugin-openapi', + }) diff --git a/src/server/ui/createUiPlugin.ts b/src/server/ui/createUiPlugin.ts new file mode 100644 index 0000000..3aec3e2 --- /dev/null +++ b/src/server/ui/createUiPlugin.ts @@ -0,0 +1,53 @@ +import { definePlugin, Forbidden } from 'payload' + +import { assertNoRemovedKeys } from '@/server/options/resolveOptions.js' +import { PLUGIN_NAME } from '@/shared/constants.js' +import type { ResolvedOptions, UiPluginOptions } from '@/shared/types/index.js' + +export interface UiRenderArgs { + specUrl: string + cdnBase: string + pinned: boolean + title: string + configuration: Record +} + +export interface UiPluginSpec { + slug: string + defaultDocsUrl: string + defaultCdnBase: string + render: (args: UiRenderArgs) => string +} + +export const createUiPlugin = (spec: UiPluginSpec) => + definePlugin({ + slug: spec.slug, + plugin: ({ config, options = {} }) => { + assertNoRemovedKeys([[(options as Record).specEndpoint, 'specEndpoint was renamed to specURL']]) + if (options.enabled === false) return config + + return { + ...config, + endpoints: [ + ...(config.endpoints ?? []), + { + path: options.path ?? spec.defaultDocsUrl, + method: 'get', + handler: async (req) => { + if (options.access && (await options.access({ req })) !== true) throw new Forbidden(req.t) + const { custom, routes } = req.payload.config + const resolved = custom?.[PLUGIN_NAME] as ResolvedOptions | undefined + const html = spec.render({ + specUrl: options.specURL ?? `${routes?.api ?? '/api'}${resolved?.path ?? '/openapi.json'}`, + cdnBase: options.cdnBase ?? spec.defaultCdnBase, + pinned: (options.cdnBase ?? spec.defaultCdnBase) === spec.defaultCdnBase, + title: resolved?.info.title ?? 'API Reference', + configuration: options.configuration ?? {}, + }) + return new Response(html, { headers: { 'content-type': 'text/html; charset=utf-8' } }) + }, + }, + ], + } + }, + }) diff --git a/src/ui/html.ts b/src/server/ui/html.ts similarity index 85% rename from src/ui/html.ts rename to src/server/ui/html.ts index 194aa14..a6539d1 100644 --- a/src/ui/html.ts +++ b/src/server/ui/html.ts @@ -12,3 +12,6 @@ export const specUrlExpr = (specUrl: string): string => { const u = JSON.stringify(specUrl) return `(function(){var l=new URLSearchParams(location.search).get('lang');return ${u}+(l?((${u}.indexOf('?')>-1?'&':'?')+'lang='+encodeURIComponent(l)):'')})()` } + +export const integrityAttrs = (pinned: boolean, integrity: string): string => + pinned ? ` integrity="${integrity}" crossorigin="anonymous"` : '' diff --git a/src/server/ui/scalar.ts b/src/server/ui/scalar.ts new file mode 100644 index 0000000..52ed1a9 --- /dev/null +++ b/src/server/ui/scalar.ts @@ -0,0 +1,22 @@ +import { SCALAR_PLUGIN_NAME } from '@/shared/constants.js' + +import { createUiPlugin } from './createUiPlugin.js' +import { escapeHtml, htmlPage, integrityAttrs, jsonForScript, specUrlExpr } from './html.js' + +const SCRIPT_INTEGRITY = 'sha384-omTRdD9MbjA1vm12DqRUVvqJlr3VzSixvAdF1Jruu9AJOiJKyTKraIB6DyX+m10M' + +export const scalar = createUiPlugin({ + slug: SCALAR_PLUGIN_NAME, + defaultDocsUrl: '/docs', + defaultCdnBase: 'https://cdn.jsdelivr.net/npm/@scalar/api-reference@1.72.4/dist/browser/standalone.js', + render: ({ specUrl, cdnBase, pinned, title, configuration }) => + htmlPage({ + title, + head: '', + body: + `
` + + `` + + ``, + }), +}) diff --git a/src/server/ui/swagger.ts b/src/server/ui/swagger.ts new file mode 100644 index 0000000..2189d9d --- /dev/null +++ b/src/server/ui/swagger.ts @@ -0,0 +1,23 @@ +import { SWAGGER_UI_PLUGIN_NAME } from '@/shared/constants.js' + +import { createUiPlugin } from './createUiPlugin.js' +import { escapeHtml, htmlPage, integrityAttrs, jsonForScript, specUrlExpr } from './html.js' + +const STYLE_INTEGRITY = 'sha384-Ov4/wv3j2bmct8cDc5X4ngJZohVPzEmc6uDPH8WeljUxO5vtoykvMEfbu9Vh6RaW' +const SCRIPT_INTEGRITY = 'sha384-ZPehFMQommnnuaZ4rpxgkgTT2DKFVp4hZC/7pLit+9Lek9T1YGSo23eHFbvNkXkw' + +export const swaggerUi = createUiPlugin({ + slug: SWAGGER_UI_PLUGIN_NAME, + defaultDocsUrl: '/docs', + defaultCdnBase: 'https://cdn.jsdelivr.net/npm/swagger-ui-dist@5.33.1', + render: ({ specUrl, cdnBase, pinned, title, configuration }) => + htmlPage({ + title, + head: ``, + body: + `
` + + `` + + ``, + }), +}) diff --git a/src/shared/constants.ts b/src/shared/constants.ts new file mode 100644 index 0000000..021aeb2 --- /dev/null +++ b/src/shared/constants.ts @@ -0,0 +1,3 @@ +export const PLUGIN_NAME = '@seshuk/payload-plugin-openapi' +export const SCALAR_PLUGIN_NAME = `${PLUGIN_NAME}/scalar` +export const SWAGGER_UI_PLUGIN_NAME = `${PLUGIN_NAME}/swagger-ui` diff --git a/src/shared/translations/index.ts b/src/shared/translations/index.ts new file mode 100644 index 0000000..b4f5a2b --- /dev/null +++ b/src/shared/translations/index.ts @@ -0,0 +1,103 @@ +import type { AcceptedLanguages, I18n } from '@payloadcms/translations' + +import { PLUGIN_NAME } from '@/shared/constants.js' +import { ar } from '@/shared/translations/locales/ar.js' +import { az } from '@/shared/translations/locales/az.js' +import { bg } from '@/shared/translations/locales/bg.js' +import { bnBd } from '@/shared/translations/locales/bnBd.js' +import { bnIn } from '@/shared/translations/locales/bnIn.js' +import { ca } from '@/shared/translations/locales/ca.js' +import { cs } from '@/shared/translations/locales/cs.js' +import { da } from '@/shared/translations/locales/da.js' +import { de } from '@/shared/translations/locales/de.js' +import { en } from '@/shared/translations/locales/en.js' +import { es } from '@/shared/translations/locales/es.js' +import { et } from '@/shared/translations/locales/et.js' +import { fa } from '@/shared/translations/locales/fa.js' +import { fr } from '@/shared/translations/locales/fr.js' +import { he } from '@/shared/translations/locales/he.js' +import { hr } from '@/shared/translations/locales/hr.js' +import { hu } from '@/shared/translations/locales/hu.js' +import { hy } from '@/shared/translations/locales/hy.js' +import { id } from '@/shared/translations/locales/id.js' +import { is } from '@/shared/translations/locales/is.js' +import { it } from '@/shared/translations/locales/it.js' +import { ja } from '@/shared/translations/locales/ja.js' +import { ko } from '@/shared/translations/locales/ko.js' +import { lt } from '@/shared/translations/locales/lt.js' +import { lv } from '@/shared/translations/locales/lv.js' +import { my } from '@/shared/translations/locales/my.js' +import { nb } from '@/shared/translations/locales/nb.js' +import { nl } from '@/shared/translations/locales/nl.js' +import { pl } from '@/shared/translations/locales/pl.js' +import { pt } from '@/shared/translations/locales/pt.js' +import { ro } from '@/shared/translations/locales/ro.js' +import { rs } from '@/shared/translations/locales/rs.js' +import { rsLatin } from '@/shared/translations/locales/rsLatin.js' +import { ru } from '@/shared/translations/locales/ru.js' +import { sk } from '@/shared/translations/locales/sk.js' +import { sl } from '@/shared/translations/locales/sl.js' +import { sv } from '@/shared/translations/locales/sv.js' +import { ta } from '@/shared/translations/locales/ta.js' +import { th } from '@/shared/translations/locales/th.js' +import { tr } from '@/shared/translations/locales/tr.js' +import { uk } from '@/shared/translations/locales/uk.js' +import { vi } from '@/shared/translations/locales/vi.js' +import { zh } from '@/shared/translations/locales/zh.js' +import { zhTw } from '@/shared/translations/locales/zhTw.js' + +import type { PluginDefaultTranslationsObject, Translate } from './types.js' + +export const makeT = (i18n: Pick): Translate => { + const t = i18n.t as ((key: string, vars?: Record) => string) | undefined + return (key, vars) => (t ? t(`${PLUGIN_NAME}:${key}`, vars) : key) +} + +export const translations: { + [key in AcceptedLanguages]?: PluginDefaultTranslationsObject +} = { + ar, + az, + bg, + 'bn-BD': bnBd, + 'bn-IN': bnIn, + ca, + cs, + da, + de, + en, + es, + et, + fa, + fr, + he, + hr, + hu, + hy, + id, + is, + it, + ja, + ko, + lt, + lv, + my, + nb, + nl, + pl, + pt, + ro, + rs, + 'rs-latin': rsLatin, + ru, + sk, + sl, + sv, + ta, + th, + tr, + uk, + vi, + zh, + 'zh-TW': zhTw, +} diff --git a/src/translations/locales/ar.ts b/src/shared/translations/locales/ar.ts similarity index 75% rename from src/translations/locales/ar.ts rename to src/shared/translations/locales/ar.ts index 42fabda..61771f2 100644 --- a/src/translations/locales/ar.ts +++ b/src/shared/translations/locales/ar.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const ar: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -16,6 +16,9 @@ export const ar: PluginDefaultTranslationsObject = { 'مع `locale=all`، اضبط على false للإبقاء على الحقول المترجمة ككائنات لكل لغة. القيمة الافتراضية true.', paramLocale: 'اللغة المراد إرجاعها، أو `all` لكل اللغات. راجع قسم الترجمة في وصف الـ API.', paramFallbackLocale: 'اللغة الاحتياطية للقيم المترجمة المفقودة، أو `none` للتعطيل.', + paramValidateLocale: 'اللغات المراد التحقق منها، أو `all` لكل اللغات. كرر المعامل لأكثر من لغة.', + paramComputeHierarchyPaths: + 'اضبطه على true لحساب مسارات `{{slugPath}}` و`{{titlePath}}`. تحديد أي من الحقلين يحسبها أيضًا.', schemaSelect: 'اختر الحقول المراد إرجاعها، مثل `select[title]=true`. اتركه فارغًا لإرجاع الكل.', schemaPopulate: 'جلب المستندات المرتبطة لكل مجموعة، مثل `populate[posts][title]=true`.', @@ -24,6 +27,10 @@ export const ar: PluginDefaultTranslationsObject = { 'مرشح `where` الخاص بـ Payload. ضمّن حقلاً ثم عاملاً: `where[field][equals]=value`. ادمج الجمل باستخدام المصفوفتين `and` / `or`، مثل `where[or][0][field][equals]=value`. يسرد كل حقل فقط العوامل الصالحة لنوعه.', schemaSupportedTimezones: 'المناطق الزمنية المدعومة بتنسيق IANA.', schemaPerLocale: 'القيم لكل لغة، تُرجَع عند استخدام `locale=all`.', + schemaHierarchySlugPath: + 'مسار الأصل بالمعرّفات النصية، مثل `parent/child`. يُحسب عند القراءة مع `computeHierarchyPaths=true` أو عند تحديده. لا يمكن استخدامه في `where`.', + schemaHierarchyTitlePath: + 'مسار الأصل بالعناوين، مثل `Parent/Child`. يُحسب عند القراءة مع `computeHierarchyPaths=true` أو عند تحديده. لا يمكن استخدامه في `where`.', collectionList: 'قائمة مستندات مقسّمة إلى صفحات', collectionDoc: 'مستند واحد', @@ -34,6 +41,10 @@ export const ar: PluginDefaultTranslationsObject = { collectionBulkDelete: 'نتيجة الحذف المجمّع', collectionCount: 'عدد المستندات', collectionDuplicated: 'المستند المُكرَّر', + validateResult: + 'نتيجة التحقق. لا يتم حفظ أي شيء. القيم غير الصالحة للحقول تُرجع `valid: false` مع الأخطاء، وليس رمز حالة خطأ.', + validateBody: + 'بيانات المستند المراد التحقق منها. في المستند المحفوظ أو المستند العام، تُدمج البيانات فوق أحدث مسودة، أو المستند المحفوظ إن لم توجد مسودة.', globalDoc: 'المستند العام', @@ -45,9 +56,12 @@ export const ar: PluginDefaultTranslationsObject = { authResetPassword: 'نتيجة إعادة تعيين كلمة المرور', authFirstRegister: 'المستخدم الأول، تم إنشاؤه برمز مصادقة', authInit: 'ما إذا كان لدى مجموعة المصادقة هذه أي مستخدمين بعد', - authAccess: 'صلاحيات (أذونات) المستخدم الحالي لهذه المجموعة', + authAccess: 'صلاحيات المستخدم الحالي لكل المجموعات والعناصر العامة', + docAccess: 'صلاحيات المستخدم الحالي لهذا المستند', + docAccessBody: 'بيانات المستند التي يتم التحقق من الصلاحيات عليها. بدونها يستخدم Payload المستند المحفوظ إن وجد.', authUnlock: 'نتيجة إلغاء القفل', authVerify: 'نتيجة التحقق', + apiKeyReveal: 'مفتاح API بعد فك تشفيره', versionList: 'قائمة إصدارات مقسّمة إلى صفحات', versionSingle: 'إصدار واحد', @@ -69,6 +83,13 @@ export const ar: PluginDefaultTranslationsObject = { uploadBody: 'أرسِل `multipart/form-data` لرفع ملف (جزء ثنائي `file` بالإضافة إلى جزء `_payload` يحتوي على الحقول بصيغة JSON)، أو `application/json` بالحقول فقط في حال عدم وجود ملف.', uploadPayloadField: 'حقول {{schema}} بصيغة JSON. مثال: `{"alt":"A caption"}`.', + fileServe: 'الملف', + filePartial: 'جزء من الملف، لطلب `Range`', + uploadInstructionsSummary: 'الحصول على تعليمات رفع ملف قبل حفظ المستند', + uploadInstructionsResult: 'مكان إرسال بايتات الملف، وقيمة `file` التي تُرسل مع طلب الإنشاء أو التحديث', + uploadStagePutSummary: 'إرسال بايتات الملف لرفع مؤقت', + uploadStageDeleteSummary: 'حذف رفع مؤقت', + uploadStageResult: 'تم، بدون محتوى', error400: 'خطأ في التحقق أو الاستعلام (ValidationError، QueryError)', error401: 'غير مُصادَق عليه (AuthenticationError)', @@ -90,6 +111,8 @@ export const ar: PluginDefaultTranslationsObject = { tagAuth: 'المصادقة', tagVersions: 'الإصدارات', tagJobs: 'المهام', + tagUploads: 'الرفع', + tagAccess: 'الوصول', localizationHeading: 'الترجمة', localizationNote: diff --git a/src/translations/locales/az.ts b/src/shared/translations/locales/az.ts similarity index 75% rename from src/translations/locales/az.ts rename to src/shared/translations/locales/az.ts index 6d590a7..6bbe406 100644 --- a/src/translations/locales/az.ts +++ b/src/shared/translations/locales/az.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const az: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -17,6 +17,10 @@ export const az: PluginDefaultTranslationsObject = { '`locale=all` ilə, lokallaşdırılmış sahələri hər lokal üzrə obyekt kimi saxlamaq üçün false təyin edin. Standart olaraq true.', paramLocale: 'Qaytarılacaq lokal, və ya hər lokal üçün `all`. API təsvirindəki Lokallaşdırma bölməsinə baxın.', paramFallbackLocale: 'Çatışmayan lokallaşdırılmış dəyərlər üçün geri dönüləcək lokal, və ya söndürmək üçün `none`.', + paramValidateLocale: + 'Yoxlanılacaq lokallar, və ya hər lokal üçün `all`. Birdən çox lokal üçün parametri təkrarlayın.', + paramComputeHierarchyPaths: + '`{{slugPath}}` və `{{titlePath}}` yollarını hesablamaq üçün true təyin edin. Bu sahələrdən birini seçmək də onları hesablayır.', schemaSelect: 'Qaytarılacaq sahələri seçin, məs. `select[title]=true`. Hamısını qaytarmaq üçün buraxın.', schemaPopulate: 'Hər kolleksiya üzrə əlaqəli sənədləri doldurun, məs. `populate[posts][title]=true`.', @@ -25,6 +29,10 @@ export const az: PluginDefaultTranslationsObject = { 'Payload `where` filtri. Əvvəlcə sahəni, sonra operatoru yuvalandırın: `where[field][equals]=value`. Şərtləri `and` / `or` massivləri ilə birləşdirin, məs. `where[or][0][field][equals]=value`. Hər sahə yalnız öz tipinə uyğun operatorları sadalayır.', schemaSupportedTimezones: 'IANA formatında dəstəklənən saat qurşaqları.', schemaPerLocale: '`locale=all` zamanı qaytarılan hər lokal üzrə dəyərlər.', + schemaHierarchySlugPath: + 'Slug yolu, məs. `parent/child`. Oxuma zamanı `computeHierarchyPaths=true` ilə və ya seçildikdə hesablanır. `where`-də istifadə edilə bilməz.', + schemaHierarchyTitlePath: + 'Başlıq yolu, məs. `Parent/Child`. Oxuma zamanı `computeHierarchyPaths=true` ilə və ya seçildikdə hesablanır. `where`-də istifadə edilə bilməz.', collectionList: 'Səhifələnmiş sənəd siyahısı', collectionDoc: 'Tək sənəd', @@ -35,6 +43,10 @@ export const az: PluginDefaultTranslationsObject = { collectionBulkDelete: 'Toplu silmə nəticəsi', collectionCount: 'Sənəd sayı', collectionDuplicated: 'Dublikat edilmiş sənəd', + validateResult: + 'Yoxlama nəticəsi. Heç nə saxlanılmır. Yanlış sahə dəyərləri xəta statusu əvəzinə xətalarla birlikdə `valid: false` qaytarır.', + validateBody: + 'Yoxlanılacaq sənəd məlumatları. Saxlanmış sənəddə və ya qlobal sənəddə məlumatlar ən son qaralamanın, qaralama yoxdursa saxlanmış sənədin üzərinə birləşdirilir.', globalDoc: 'Qlobal sənəd', @@ -46,9 +58,13 @@ export const az: PluginDefaultTranslationsObject = { authResetPassword: 'Parol sıfırlama nəticəsi', authFirstRegister: 'Autentifikasiya tokeni ilə yaradılmış ilk istifadəçi', authInit: 'Bu autentifikasiya kolleksiyasında hələ hər hansı istifadəçinin olub-olmaması', - authAccess: 'Cari istifadəçinin bu kolleksiya üçün girişi (icazələri)', + authAccess: 'Cari istifadəçinin bütün kolleksiyalar və qloballar üçün icazələri', + docAccess: 'Cari istifadəçinin bu sənəd üçün icazələri', + docAccessBody: + 'Girişin yoxlanılacağı sənəd məlumatları. Olmadıqda Payload saxlanmış sənədi istifadə edir (əgər varsa).', authUnlock: 'Kiliddən çıxarma nəticəsi', authVerify: 'Doğrulama nəticəsi', + apiKeyReveal: 'Deşifrə edilmiş API açarı', versionList: 'Səhifələnmiş versiya siyahısı', versionSingle: 'Tək versiya', @@ -70,6 +86,14 @@ export const az: PluginDefaultTranslationsObject = { uploadBody: 'Fayl yükləmək üçün `multipart/form-data` göndərin (binar `file` hissəsi və JSON-a çevrilmiş sahələri olan `_payload` hissəsi), və ya fayl olmadıqda yalnız sahələrlə `application/json` göndərin.', uploadPayloadField: 'JSON-a çevrilmiş {{schema}} sahələri. Nümunə: `{"alt":"A caption"}`.', + fileServe: 'Fayl', + filePartial: 'Faylın bir hissəsi, `Range` sorğusu üçün', + uploadInstructionsSummary: 'Sənədi saxlamazdan əvvəl fayl yükləmək üçün təlimat alın', + uploadInstructionsResult: + 'Fayl baytlarının hara göndəriləcəyi və yaratma və ya yeniləmə sorğusu ilə göndəriləcək `file` dəyəri', + uploadStagePutSummary: 'Mərhələli yükləmə üçün fayl baytlarını göndərin', + uploadStageDeleteSummary: 'Mərhələli yükləməni silin', + uploadStageResult: 'Hazırdır, məzmun yoxdur', error400: 'Doğrulama və ya sorğu xətası (ValidationError, QueryError)', error401: 'Autentifikasiya olunmayıb (AuthenticationError)', @@ -91,6 +115,8 @@ export const az: PluginDefaultTranslationsObject = { tagAuth: 'Autentifikasiya', tagVersions: 'Versiyalar', tagJobs: 'İşlər', + tagUploads: 'Yükləmələr', + tagAccess: 'Giriş', localizationHeading: 'Lokallaşdırma', localizationNote: diff --git a/src/translations/locales/bg.ts b/src/shared/translations/locales/bg.ts similarity index 74% rename from src/translations/locales/bg.ts rename to src/shared/translations/locales/bg.ts index a7416c6..84106cd 100644 --- a/src/translations/locales/bg.ts +++ b/src/shared/translations/locales/bg.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const bg: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -16,6 +16,9 @@ export const bg: PluginDefaultTranslationsObject = { 'С `locale=all` задайте false, за да запазите локализираните полета като обекти по локал. По подразбиране true.', paramLocale: 'Локал за връщане или `all` за всеки локал. Вижте раздела Localization в описанието на API.', paramFallbackLocale: 'Локал, към който да се връща при липсващи локализирани стойности, или `none` за изключване.', + paramValidateLocale: 'Локали за валидиране или `all` за всеки локал. Повторете параметъра за повече от един локал.', + paramComputeHierarchyPaths: + 'Задайте true, за да се изчислят пътищата `{{slugPath}}` и `{{titlePath}}`. Изборът на някое от полетата също ги изчислява.', schemaSelect: 'Изберете полета за връщане, напр. `select[title]=true`. Пропуснете, за да върнете всички.', schemaPopulate: 'Populate-ване на свързани документи по колекция, напр. `populate[posts][title]=true`.', @@ -24,6 +27,10 @@ export const bg: PluginDefaultTranslationsObject = { 'Payload `where` филтър. Вложете поле, след това оператор: `where[field][equals]=value`. Комбинирайте клаузи с масивите `and` / `or`, напр. `where[or][0][field][equals]=value`. Всяко поле изброява само операторите, валидни за неговия тип.', schemaSupportedTimezones: 'Поддържани часови зони във формат IANA.', schemaPerLocale: 'Стойности по локал, връщани когато `locale=all`.', + schemaHierarchySlugPath: + 'Път от slug стойности, напр. `parent/child`. Изчислява се при четене с `computeHierarchyPaths=true` или когато е избран. Не може да се ползва в `where`.', + schemaHierarchyTitlePath: + 'Път от заглавия, напр. `Parent/Child`. Изчислява се при четене с `computeHierarchyPaths=true` или когато е избран. Не може да се ползва в `where`.', collectionList: 'Списък на документи с пагинация', collectionDoc: 'Един документ', @@ -34,6 +41,10 @@ export const bg: PluginDefaultTranslationsObject = { collectionBulkDelete: 'Резултат от групово изтриване', collectionCount: 'Брой документи', collectionDuplicated: 'Дублираният документ', + validateResult: + 'Резултат от валидирането. Нищо не се записва. Невалидните стойности на полета връщат `valid: false` с грешките, а не статус за грешка.', + validateBody: + 'Данни на документа за валидиране. При записан документ или глобален документ данните се сливат върху последната чернова, а ако няма чернова, върху записания документ.', globalDoc: 'Глобалният документ', @@ -45,9 +56,13 @@ export const bg: PluginDefaultTranslationsObject = { authResetPassword: 'Резултат от нулиране на парола', authFirstRegister: 'Първият потребител, създаден с токен за удостоверяване', authInit: 'Дали тази auth колекция вече има потребители', - authAccess: 'Достъпът (правата) на текущия потребител за тази колекция', + authAccess: 'Правата на текущия потребител за всички колекции и глобални', + docAccess: 'Правата на текущия потребител за този документ', + docAccessBody: + 'Данни на документа, спрямо които се проверява достъпът. Без тях Payload използва записания документ, ако има такъв.', authUnlock: 'Резултат от отключване', authVerify: 'Резултат от верификация', + apiKeyReveal: 'Дешифрираният API ключ', versionList: 'Списък на версии с пагинация', versionSingle: 'Една версия', @@ -69,6 +84,14 @@ export const bg: PluginDefaultTranslationsObject = { uploadBody: 'Изпратете `multipart/form-data`, за да качите файл (двоична `file` част плюс `_payload` част с JSON-сериализираните полета), или `application/json` само с полетата, когато няма файл.', uploadPayloadField: 'JSON-сериализирани {{schema}} полета. Пример: `{"alt":"A caption"}`.', + fileServe: 'Файлът', + filePartial: 'Част от файла при заявка с `Range`', + uploadInstructionsSummary: 'Получаване на инструкции за качване на файл преди запис на документа', + uploadInstructionsResult: + 'Къде да се изпратят байтовете на файла и стойността `file` за заявката за създаване или обновяване', + uploadStagePutSummary: 'Изпращане на байтовете на файла за временно качване', + uploadStageDeleteSummary: 'Изтриване на временно качване', + uploadStageResult: 'Готово, без съдържание', error400: 'Грешка при валидация или заявка (ValidationError, QueryError)', error401: 'Не сте удостоверени (AuthenticationError)', @@ -90,6 +113,8 @@ export const bg: PluginDefaultTranslationsObject = { tagAuth: 'Удостоверяване', tagVersions: 'Версии', tagJobs: 'Задачи', + tagUploads: 'Качвания', + tagAccess: 'Достъп', localizationHeading: 'Локализация', localizationNote: diff --git a/src/translations/locales/bnBd.ts b/src/shared/translations/locales/bnBd.ts similarity index 75% rename from src/translations/locales/bnBd.ts rename to src/shared/translations/locales/bnBd.ts index 28ba45d..6da1c6e 100644 --- a/src/translations/locales/bnBd.ts +++ b/src/shared/translations/locales/bnBd.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const bnBd: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -16,6 +16,10 @@ export const bnBd: PluginDefaultTranslationsObject = { '`locale=all` এর সাথে, লোকালাইজড ফিল্ডগুলো প্রতি-লোকেল অবজেক্ট হিসেবে রাখতে false সেট করুন। ডিফল্ট true।', paramLocale: 'যে লোকেল ফেরত দিতে হবে, অথবা প্রতিটি লোকেলের জন্য `all`। API বিবরণের Localization অংশটি দেখুন।', paramFallbackLocale: 'অনুপস্থিত লোকালাইজড মানের জন্য যে লোকেলে ফিরে যাওয়া হবে, অথবা নিষ্ক্রিয় করতে `none`।', + paramValidateLocale: + 'যে লোকেলগুলো যাচাই করতে হবে, অথবা প্রতিটি লোকেলের জন্য `all`। একাধিক লোকেলের জন্য প্যারামিটারটি পুনরাবৃত্তি করুন।', + paramComputeHierarchyPaths: + '`{{slugPath}}` এবং `{{titlePath}}` পাথ গণনা করতে true দিন। যেকোনো একটি ফিল্ড নির্বাচন করলেও সেগুলো গণনা হয়।', schemaSelect: 'কোন ফিল্ডগুলো ফেরত দেওয়া হবে তা নির্বাচন করুন, যেমন `select[title]=true`। সবগুলো ফেরত পেতে বাদ দিন।', schemaPopulate: 'প্রতি কালেকশন অনুযায়ী সম্পর্কিত ডকুমেন্ট পপুলেট করুন, যেমন `populate[posts][title]=true`।', @@ -24,6 +28,10 @@ export const bnBd: PluginDefaultTranslationsObject = { 'Payload `where` ফিল্টার। একটি ফিল্ড তারপর একটি অপারেটর নেস্ট করুন: `where[field][equals]=value`। `and` / `or` অ্যারে দিয়ে শর্তসমূহ একত্র করুন, যেমন `where[or][0][field][equals]=value`। প্রতিটি ফিল্ড শুধু তার টাইপের জন্য বৈধ অপারেটরগুলোই তালিকাভুক্ত করে।', schemaSupportedTimezones: 'IANA ফরম্যাটে সমর্থিত টাইমজোন।', schemaPerLocale: 'প্রতি-লোকেল মান, `locale=all` দেওয়া হলে ফেরত দেওয়া হয়।', + schemaHierarchySlugPath: + 'স্লাগ পাথ, যেমন `parent/child`। পড়ার সময় `computeHierarchyPaths=true` দিলে বা নির্বাচন করলে গণনা হয়। `where`-এ ব্যবহার করা যায় না।', + schemaHierarchyTitlePath: + 'শিরোনাম পাথ, যেমন `Parent/Child`। পড়ার সময় `computeHierarchyPaths=true` দিলে বা নির্বাচন করলে গণনা হয়। `where`-এ ব্যবহার করা যায় না।', collectionList: 'ডকুমেন্টের পেজিনেটেড তালিকা', collectionDoc: 'একটি একক ডকুমেন্ট', @@ -34,6 +42,10 @@ export const bnBd: PluginDefaultTranslationsObject = { collectionBulkDelete: 'বাল্ক ডিলিটের ফলাফল', collectionCount: 'ডকুমেন্ট সংখ্যা', collectionDuplicated: 'ডুপ্লিকেট করা ডকুমেন্ট', + validateResult: + 'যাচাইয়ের ফলাফল। কিছুই সংরক্ষণ হয় না। ফিল্ডের অবৈধ মানের ক্ষেত্রে এরর স্ট্যাটাস নয়, ত্রুটিসহ `valid: false` ফেরত আসে।', + validateBody: + 'যাচাই করার ডকুমেন্ট ডেটা। সংরক্ষিত ডকুমেন্ট বা গ্লোবালে ডেটা সর্বশেষ ড্রাফটের উপর মার্জ হয়, ড্রাফট না থাকলে সংরক্ষিত ডকুমেন্টের উপর।', globalDoc: 'গ্লোবাল ডকুমেন্ট', @@ -45,9 +57,12 @@ export const bnBd: PluginDefaultTranslationsObject = { authResetPassword: 'পাসওয়ার্ড রিসেটের ফলাফল', authFirstRegister: 'প্রথম ব্যবহারকারী, একটি auth টোকেনসহ তৈরি করা হয়েছে', authInit: 'এই auth কালেকশনে এখনও কোনো ব্যবহারকারী আছে কিনা', - authAccess: 'এই কালেকশনের জন্য বর্তমান ব্যবহারকারীর অ্যাক্সেস (অনুমতি)', + authAccess: 'সব কালেকশন ও গ্লোবালের জন্য বর্তমান ব্যবহারকারীর অনুমতি', + docAccess: 'এই ডকুমেন্টের জন্য বর্তমান ব্যবহারকারীর অনুমতি', + docAccessBody: 'যে ডকুমেন্ট ডেটার বিপরীতে অ্যাক্সেস যাচাই হবে। না দিলে Payload সংরক্ষিত ডকুমেন্ট ব্যবহার করে, যদি থাকে।', authUnlock: 'আনলকের ফলাফল', authVerify: 'যাচাইয়ের ফলাফল', + apiKeyReveal: 'ডিক্রিপ্ট করা API কী', versionList: 'সংস্করণের পেজিনেটেড তালিকা', versionSingle: 'একটি একক সংস্করণ', @@ -69,6 +84,13 @@ export const bnBd: PluginDefaultTranslationsObject = { uploadBody: 'একটি ফাইল আপলোড করতে `multipart/form-data` পাঠান (একটি বাইনারি `file` অংশ এবং JSON-স্ট্রিংফাইড ফিল্ডসহ একটি `_payload` অংশ), অথবা কোনো ফাইল না থাকলে শুধু ফিল্ডসহ `application/json` পাঠান।', uploadPayloadField: 'JSON-স্ট্রিংফাইড {{schema}} ফিল্ড। উদাহরণ: `{"alt":"A caption"}`।', + fileServe: 'ফাইলটি', + filePartial: 'ফাইলের একটি অংশ, `Range` রিকোয়েস্টের জন্য', + uploadInstructionsSummary: 'ডকুমেন্ট সেভ করার আগে ফাইল আপলোডের নির্দেশনা নিন', + uploadInstructionsResult: 'ফাইলের বাইট কোথায় পাঠাতে হবে এবং তৈরি বা আপডেট রিকোয়েস্টে কোন `file` মান পাঠাতে হবে', + uploadStagePutSummary: 'অস্থায়ী আপলোডের জন্য ফাইলের বাইট পাঠান', + uploadStageDeleteSummary: 'অস্থায়ী আপলোড মুছুন', + uploadStageResult: 'সম্পন্ন, কোনো কনটেন্ট নেই', error400: 'ভ্যালিডেশন বা কোয়েরি ত্রুটি (ValidationError, QueryError)', error401: 'প্রমাণীকৃত নয় (AuthenticationError)', @@ -89,6 +111,8 @@ export const bnBd: PluginDefaultTranslationsObject = { tagAuth: 'অথেনটিকেশন', tagVersions: 'সংস্করণ', tagJobs: 'জব', + tagUploads: 'আপলোড', + tagAccess: 'অ্যাক্সেস', localizationHeading: 'লোকালাইজেশন', localizationNote: diff --git a/src/translations/locales/bnIn.ts b/src/shared/translations/locales/bnIn.ts similarity index 75% rename from src/translations/locales/bnIn.ts rename to src/shared/translations/locales/bnIn.ts index 5ef1948..f8ecfec 100644 --- a/src/translations/locales/bnIn.ts +++ b/src/shared/translations/locales/bnIn.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const bnIn: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -15,6 +15,10 @@ export const bnIn: PluginDefaultTranslationsObject = { paramFlattenLocales: '`locale=all` সহ, স্থানীয়কৃত ফিল্ডগুলো প্রতি-লোকেল অবজেক্ট হিসেবে রাখতে false সেট করুন। ডিফল্ট true।', paramLocale: 'যে লোকেল ফেরত দেওয়া হবে, অথবা প্রতিটি লোকেলের জন্য `all`। API বিবরণে Localization বিভাগটি দেখুন।', paramFallbackLocale: 'অনুপস্থিত স্থানীয়কৃত মানের জন্য যে লোকেলে ফিরে যাওয়া হবে, অথবা নিষ্ক্রিয় করতে `none`।', + paramValidateLocale: + 'যে লোকেলগুলি যাচাই করা হবে, অথবা প্রতিটি লোকেলের জন্য `all`। একাধিক লোকেলের জন্য প্যারামিটারটি পুনরাবৃত্তি করুন।', + paramComputeHierarchyPaths: + '`{{slugPath}}` এবং `{{titlePath}}` পাথ গণনা করতে true দিন। যেকোনো একটি ফিল্ড নির্বাচন করলেও সেগুলো গণনা হয়।', schemaSelect: 'যে ফিল্ডগুলো ফেরত দেওয়া হবে তা বেছে নিন, যেমন `select[title]=true`। সব ফেরত দিতে বাদ দিন।', schemaPopulate: 'প্রতিটি কালেকশন অনুযায়ী সম্পর্কিত নথি পপুলেট করুন, যেমন `populate[posts][title]=true`।', @@ -23,6 +27,10 @@ export const bnIn: PluginDefaultTranslationsObject = { 'Payload `where` ফিল্টার। একটি ফিল্ড তারপর একটি অপারেটর নেস্ট করুন: `where[field][equals]=value`। `and` / `or` অ্যারে দিয়ে ক্লজগুলো একত্র করুন, যেমন `where[or][0][field][equals]=value`। প্রতিটি ফিল্ড কেবল তার টাইপের জন্য বৈধ অপারেটরগুলোই তালিকাভুক্ত করে।', schemaSupportedTimezones: 'IANA ফরম্যাটে সমর্থিত টাইমজোন।', schemaPerLocale: 'প্রতি-লোকেল মান, `locale=all` দিলে ফেরত দেওয়া হয়।', + schemaHierarchySlugPath: + 'স্লাগ পাথ, যেমন `parent/child`। পড়ার সময় `computeHierarchyPaths=true` দিলে বা নির্বাচন করলে গণনা হয়। `where`-এ ব্যবহার করা যায় না।', + schemaHierarchyTitlePath: + 'শিরোনাম পাথ, যেমন `Parent/Child`। পড়ার সময় `computeHierarchyPaths=true` দিলে বা নির্বাচন করলে গণনা হয়। `where`-এ ব্যবহার করা যায় না।', collectionList: 'নথির পেজিনেটেড তালিকা', collectionDoc: 'একটি একক নথি', @@ -33,6 +41,9 @@ export const bnIn: PluginDefaultTranslationsObject = { collectionBulkDelete: 'বাল্ক ডিলিটের ফলাফল', collectionCount: 'নথির সংখ্যা', collectionDuplicated: 'নকল করা নথি', + validateResult: + 'যাচাইয়ের ফলাফল। কিছুই সংরক্ষিত হয় না। ফিল্ডের অবৈধ মানের ক্ষেত্রে এরর স্ট্যাটাস নয়, ত্রুটিসহ `valid: false` ফেরত আসে।', + validateBody: 'যাচাই করার নথির ডেটা। সংরক্ষিত নথি বা গ্লোবালে ডেটা সর্বশেষ খসড়ার উপর মার্জ হয়, খসড়া না থাকলে সংরক্ষিত নথির উপর।', globalDoc: 'গ্লোবাল নথি', @@ -44,9 +55,12 @@ export const bnIn: PluginDefaultTranslationsObject = { authResetPassword: 'পাসওয়ার্ড রিসেটের ফলাফল', authFirstRegister: 'প্রথম ব্যবহারকারী, একটি auth টোকেন সহ তৈরি করা হয়েছে', authInit: 'এই auth কালেকশনে এখনও কোনো ব্যবহারকারী আছে কিনা', - authAccess: 'এই কালেকশনের জন্য বর্তমান ব্যবহারকারীর অ্যাক্সেস (অনুমতি)', + authAccess: 'সব কালেকশন ও গ্লোবালের জন্য বর্তমান ব্যবহারকারীর অনুমতি', + docAccess: 'এই নথির জন্য বর্তমান ব্যবহারকারীর অনুমতি', + docAccessBody: 'যে নথির ডেটার বিপরীতে অ্যাক্সেস যাচাই হবে। না দিলে Payload সংরক্ষিত নথি ব্যবহার করে, যদি থাকে।', authUnlock: 'আনলকের ফলাফল', authVerify: 'যাচাইয়ের ফলাফল', + apiKeyReveal: 'ডিক্রিপ্ট করা API কী', versionList: 'সংস্করণের পেজিনেটেড তালিকা', versionSingle: 'একটি একক সংস্করণ', @@ -68,6 +82,13 @@ export const bnIn: PluginDefaultTranslationsObject = { uploadBody: 'কোনো ফাইল আপলোড করতে `multipart/form-data` পাঠান (একটি বাইনারি `file` অংশ এবং JSON-স্ট্রিংকৃত ফিল্ডসহ একটি `_payload` অংশ), অথবা যখন কোনো ফাইল নেই তখন শুধু ফিল্ডগুলোসহ `application/json` পাঠান।', uploadPayloadField: 'JSON-স্ট্রিংকৃত {{schema}} ফিল্ড। উদাহরণ: `{"alt":"A caption"}`।', + fileServe: 'ফাইলটি', + filePartial: 'ফাইলের একটি অংশ, `Range` অনুরোধের জন্য', + uploadInstructionsSummary: 'নথি সংরক্ষণের আগে ফাইল আপলোডের নির্দেশনা নিন', + uploadInstructionsResult: 'ফাইলের বাইট কোথায় পাঠাতে হবে এবং তৈরি বা হালনাগাদ অনুরোধে কোন `file` মান পাঠাতে হবে', + uploadStagePutSummary: 'অস্থায়ী আপলোডের জন্য ফাইলের বাইট পাঠান', + uploadStageDeleteSummary: 'অস্থায়ী আপলোড মুছুন', + uploadStageResult: 'সম্পন্ন, কোনো বিষয়বস্তু নেই', error400: 'যাচাই বা কোয়েরি ত্রুটি (ValidationError, QueryError)', error401: 'প্রমাণীকৃত নয় (AuthenticationError)', @@ -88,6 +109,8 @@ export const bnIn: PluginDefaultTranslationsObject = { tagAuth: 'প্রমাণীকরণ', tagVersions: 'সংস্করণ', tagJobs: 'জব', + tagUploads: 'আপলোড', + tagAccess: 'অ্যাক্সেস', localizationHeading: 'স্থানীয়করণ', localizationNote: diff --git a/src/translations/locales/ca.ts b/src/shared/translations/locales/ca.ts similarity index 75% rename from src/translations/locales/ca.ts rename to src/shared/translations/locales/ca.ts index 7a44b97..3a6af26 100644 --- a/src/translations/locales/ca.ts +++ b/src/shared/translations/locales/ca.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const ca: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -18,6 +18,10 @@ export const ca: PluginDefaultTranslationsObject = { paramLocale: "Localització a retornar, o `all` per a totes les localitzacions. Vegeu la secció Localització a la descripció de l'API.", paramFallbackLocale: 'Localització de reserva per als valors localitzats que manquin, o `none` per desactivar-la.', + paramValidateLocale: + "Localitzacions a validar, o `all` per a totes les localitzacions. Repeteix el paràmetre per a més d'una localització.", + paramComputeHierarchyPaths: + 'Posa true per calcular els camins `{{slugPath}}` i `{{titlePath}}`. Seleccionar qualsevol dels dos camps també els calcula.', schemaSelect: 'Trieu els camps a retornar, p. ex. `select[title]=true`. Ometeu-ho per retornar-los tots.', schemaPopulate: 'Ompliu els documents relacionats per col·lecció, p. ex. `populate[posts][title]=true`.', @@ -26,6 +30,10 @@ export const ca: PluginDefaultTranslationsObject = { 'Filtre `where` de Payload. Imbriqueu un camp i després un operador: `where[field][equals]=value`. Combineu clàusules amb els arrays `and` / `or`, p. ex. `where[or][0][field][equals]=value`. Cada camp només llista els operadors vàlids per al seu tipus.', schemaSupportedTimezones: 'Zones horàries admeses en format IANA.', schemaPerLocale: 'Valors per localització, retornats quan `locale=all`.', + schemaHierarchySlugPath: + 'Camí de slugs, p. ex. `parent/child`. Es calcula en llegir amb `computeHierarchyPaths=true` o quan se selecciona. No es pot fer servir a `where`.', + schemaHierarchyTitlePath: + 'Camí de títols, p. ex. `Parent/Child`. Es calcula en llegir amb `computeHierarchyPaths=true` o quan se selecciona. No es pot fer servir a `where`.', collectionList: 'Llista paginada de documents', collectionDoc: 'Un únic document', @@ -36,6 +44,10 @@ export const ca: PluginDefaultTranslationsObject = { collectionBulkDelete: "Resultat de l'eliminació massiva", collectionCount: 'Recompte de documents', collectionDuplicated: 'El document duplicat', + validateResult: + "Resultat de la validació. No es desa res. Els valors de camp no vàlids retornen `valid: false` amb els errors, no un estat d'error.", + validateBody: + "Dades del document a validar. En un document desat o un global, les dades es fusionen sobre l'últim esborrany, o sobre el document desat si no hi ha esborrany.", globalDoc: 'El document global', @@ -47,9 +59,13 @@ export const ca: PluginDefaultTranslationsObject = { authResetPassword: 'Resultat del restabliment de contrasenya', authFirstRegister: "El primer usuari, creat amb un token d'autenticació", authInit: "Si aquesta col·lecció d'autenticació ja té usuaris", - authAccess: "L'accés (permisos) de l'usuari actual per a aquesta col·lecció", + authAccess: "Els permisos de l'usuari actual per a totes les col·leccions i globals", + docAccess: "Els permisos de l'usuari actual per a aquest document", + docAccessBody: + "Dades del document amb què es comprova l'accés. Sense elles, Payload fa servir el document desat, si n'hi ha.", authUnlock: 'Resultat del desbloqueig', authVerify: 'Resultat de la verificació', + apiKeyReveal: "La clau d'API desxifrada", versionList: 'Llista paginada de versions', versionSingle: 'Una única versió', @@ -71,6 +87,14 @@ export const ca: PluginDefaultTranslationsObject = { uploadBody: 'Envieu `multipart/form-data` per pujar un fitxer (una part binària `file` més una part `_payload` amb els camps en format JSON), o `application/json` només amb els camps quan no hi ha cap fitxer.', uploadPayloadField: 'Camps {{schema}} en format JSON. Exemple: `{"alt":"A caption"}`.', + fileServe: 'El fitxer', + filePartial: 'Una part del fitxer, per a una petició `Range`', + uploadInstructionsSummary: 'Obté instruccions per pujar un fitxer abans de desar el document', + uploadInstructionsResult: + "On enviar els bytes del fitxer i el valor `file` que cal enviar amb la petició de creació o d'actualització", + uploadStagePutSummary: 'Envia els bytes del fitxer per a una pujada temporal', + uploadStageDeleteSummary: 'Elimina una pujada temporal', + uploadStageResult: 'Fet, sense contingut', error400: 'Error de validació o de consulta (ValidationError, QueryError)', error401: 'No autenticat (AuthenticationError)', @@ -92,6 +116,8 @@ export const ca: PluginDefaultTranslationsObject = { tagAuth: 'Autenticació', tagVersions: 'Versions', tagJobs: 'Tasques', + tagUploads: 'Pujades', + tagAccess: 'Accés', localizationHeading: 'Localització', localizationNote: diff --git a/src/translations/locales/cs.ts b/src/shared/translations/locales/cs.ts similarity index 75% rename from src/translations/locales/cs.ts rename to src/shared/translations/locales/cs.ts index 466dfba..702655e 100644 --- a/src/translations/locales/cs.ts +++ b/src/shared/translations/locales/cs.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const cs: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -17,6 +17,9 @@ export const cs: PluginDefaultTranslationsObject = { 'S `locale=all` nastavte false, aby lokalizovaná pole zůstala jako objekty po jednotlivých jazycích. Výchozí hodnota je true.', paramLocale: 'Jazyk, který se má vrátit, nebo `all` pro všechny jazyky. Viz sekce Lokalizace v popisu API.', paramFallbackLocale: 'Jazyk, na který se přejde u chybějících lokalizovaných hodnot, nebo `none` pro vypnutí.', + paramValidateLocale: 'Jazyky k ověření, nebo `all` pro všechny jazyky. Pro více jazyků parametr zopakujte.', + paramComputeHierarchyPaths: + 'Nastavte true pro výpočet cest `{{slugPath}}` a `{{titlePath}}`. Výběr kteréhokoli z těchto polí je vypočítá také.', schemaSelect: 'Vyberte pole, která se mají vrátit, např. `select[title]=true`. Vynechte pro vrácení všech.', schemaPopulate: 'Naplňte související dokumenty podle kolekce, např. `populate[posts][title]=true`.', @@ -25,6 +28,10 @@ export const cs: PluginDefaultTranslationsObject = { 'Filtr `where` v Payloadu. Vnořte pole a poté operátor: `where[field][equals]=value`. Klauzule kombinujte pomocí polí `and` / `or`, např. `where[or][0][field][equals]=value`. Každé pole uvádí pouze operátory platné pro jeho typ.', schemaSupportedTimezones: 'Podporované časové zóny ve formátu IANA.', schemaPerLocale: 'Hodnoty po jednotlivých jazycích, vrácené při `locale=all`.', + schemaHierarchySlugPath: + 'Cesta ze slugů, např. `parent/child`. Počítá se při čtení s `computeHierarchyPaths=true` nebo při výběru. Nelze použít ve `where`.', + schemaHierarchyTitlePath: + 'Cesta z názvů, např. `Parent/Child`. Počítá se při čtení s `computeHierarchyPaths=true` nebo při výběru. Nelze použít ve `where`.', collectionList: 'Stránkovaný seznam dokumentů', collectionDoc: 'Jeden dokument', @@ -35,6 +42,10 @@ export const cs: PluginDefaultTranslationsObject = { collectionBulkDelete: 'Výsledek hromadného smazání', collectionCount: 'Počet dokumentů', collectionDuplicated: 'Duplikovaný dokument', + validateResult: + 'Výsledek ověření. Nic se neukládá. Neplatné hodnoty polí vrátí `valid: false` s chybami, nikoli chybový stav.', + validateBody: + 'Data dokumentu k ověření. U uloženého dokumentu nebo globálního dokumentu se data sloučí s nejnovějším konceptem, případně s uloženým dokumentem, pokud koncept neexistuje.', globalDoc: 'Globální dokument', @@ -46,9 +57,13 @@ export const cs: PluginDefaultTranslationsObject = { authResetPassword: 'Výsledek obnovení hesla', authFirstRegister: 'První uživatel vytvořený s autentizačním tokenem', authInit: 'Zda tato autentizační kolekce již obsahuje nějaké uživatele', - authAccess: 'Přístup (oprávnění) aktuálního uživatele k této kolekci', + authAccess: 'Oprávnění aktuálního uživatele pro všechny kolekce a globální', + docAccess: 'Oprávnění aktuálního uživatele pro tento dokument', + docAccessBody: + 'Data dokumentu, vůči kterým se ověřuje přístup. Bez nich Payload použije uložený dokument, pokud existuje.', authUnlock: 'Výsledek odemčení', authVerify: 'Výsledek ověření', + apiKeyReveal: 'Dešifrovaný API klíč', versionList: 'Stránkovaný seznam verzí', versionSingle: 'Jedna verze', @@ -70,6 +85,13 @@ export const cs: PluginDefaultTranslationsObject = { uploadBody: 'Pro nahrání souboru odešlete `multipart/form-data` (binární část `file` plus část `_payload` s poli serializovanými do JSON), nebo `application/json` jen s poli, pokud žádný soubor není.', uploadPayloadField: 'Pole {{schema}} serializovaná do JSON. Příklad: `{\\"alt\\":\\"A caption\\"}`.', + fileServe: 'Soubor', + filePartial: 'Část souboru pro požadavek `Range`', + uploadInstructionsSummary: 'Získat pokyny k nahrání souboru před uložením dokumentu', + uploadInstructionsResult: 'Kam poslat bajty souboru a hodnota `file` pro požadavek na vytvoření nebo úpravu', + uploadStagePutSummary: 'Odeslat bajty souboru pro dočasné nahrání', + uploadStageDeleteSummary: 'Smazat dočasné nahrání', + uploadStageResult: 'Hotovo, bez obsahu', error400: 'Chyba validace nebo dotazu (ValidationError, QueryError)', error401: 'Neautentizováno (AuthenticationError)', @@ -91,6 +113,8 @@ export const cs: PluginDefaultTranslationsObject = { tagAuth: 'Autentizace', tagVersions: 'Verze', tagJobs: 'Úlohy', + tagUploads: 'Nahrávání', + tagAccess: 'Přístup', localizationHeading: 'Lokalizace', localizationNote: diff --git a/src/translations/locales/da.ts b/src/shared/translations/locales/da.ts similarity index 75% rename from src/translations/locales/da.ts rename to src/shared/translations/locales/da.ts index eed07ef..85c9a43 100644 --- a/src/translations/locales/da.ts +++ b/src/shared/translations/locales/da.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const da: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -17,6 +17,10 @@ export const da: PluginDefaultTranslationsObject = { paramLocale: 'Sprog der skal returneres, eller `all` for alle sprog. Se afsnittet Lokalisering i API-beskrivelsen.', paramFallbackLocale: 'Sprog der skal falde tilbage til for manglende lokaliserede værdier, eller `none` for at deaktivere.', + paramValidateLocale: + 'Sprog der skal valideres, eller `all` for alle sprog. Gentag parameteren for mere end ét sprog.', + paramComputeHierarchyPaths: + 'Sæt til true for at beregne stierne `{{slugPath}}` og `{{titlePath}}`. Valg af et af felterne beregner dem også.', schemaSelect: 'Vælg hvilke felter der skal returneres, f.eks. `select[title]=true`. Udelad for at returnere alle.', schemaPopulate: 'Populér relaterede dokumenter pr. collection, f.eks. `populate[posts][title]=true`.', @@ -25,6 +29,10 @@ export const da: PluginDefaultTranslationsObject = { 'Payload `where`-filter. Indlejr et felt og derefter en operator: `where[field][equals]=value`. Kombinér betingelser med `and`- / `or`-arrays, f.eks. `where[or][0][field][equals]=value`. Hvert felt angiver kun de operatorer, der er gyldige for dets type.', schemaSupportedTimezones: 'Understøttede tidszoner i IANA-format.', schemaPerLocale: 'Værdier pr. sprog, returneres når `locale=all`.', + schemaHierarchySlugPath: + 'Sti af slugs, f.eks. `parent/child`. Beregnes ved læsning med `computeHierarchyPaths=true` eller når feltet vælges. Kan ikke bruges i `where`.', + schemaHierarchyTitlePath: + 'Sti af titler, f.eks. `Parent/Child`. Beregnes ved læsning med `computeHierarchyPaths=true` eller når feltet vælges. Kan ikke bruges i `where`.', collectionList: 'Pagineret liste over dokumenter', collectionDoc: 'Et enkelt dokument', @@ -35,6 +43,10 @@ export const da: PluginDefaultTranslationsObject = { collectionBulkDelete: 'Resultat af masseslettelse', collectionCount: 'Antal dokumenter', collectionDuplicated: 'Det duplikerede dokument', + validateResult: + 'Valideringsresultat. Intet gemmes. Ugyldige feltværdier returnerer `valid: false` med fejlene, ikke en fejlstatus.', + validateBody: + 'Dokumentdata der skal valideres. På et gemt dokument eller en global flettes dataene oven på den seneste kladde, eller det gemte dokument, hvis der ikke er nogen kladde.', globalDoc: 'Det globale dokument', @@ -46,9 +58,13 @@ export const da: PluginDefaultTranslationsObject = { authResetPassword: 'Resultat af nulstilling af adgangskode', authFirstRegister: 'Den første bruger, oprettet med en auth-token', authInit: 'Om denne auth-collection allerede har brugere', - authAccess: 'Den aktuelle brugers adgang (tilladelser) for denne collection', + authAccess: 'Den aktuelle brugers tilladelser for alle collections og globals', + docAccess: 'Den aktuelle brugers tilladelser for dette dokument', + docAccessBody: + 'Dokumentdata, som adgang kontrolleres mod. Uden dem bruger Payload det gemte dokument, hvis der er et.', authUnlock: 'Resultat af oplåsning', authVerify: 'Resultat af verifikation', + apiKeyReveal: 'Den dekrypterede API-nøgle', versionList: 'Pagineret liste over versioner', versionSingle: 'En enkelt version', @@ -70,6 +86,14 @@ export const da: PluginDefaultTranslationsObject = { uploadBody: 'Send `multipart/form-data` for at uploade en fil (en binær `file`-del plus en `_payload`-del med de JSON-strengkodede felter), eller `application/json` med blot felterne, når der ikke er nogen fil.', uploadPayloadField: 'JSON-strengkodede {{schema}}-felter. Eksempel: `{"alt":"A caption"}`.', + fileServe: 'Filen', + filePartial: 'En del af filen ved en `Range`-anmodning', + uploadInstructionsSummary: 'Hent instruktioner til at uploade en fil, før dokumentet gemmes', + uploadInstructionsResult: + 'Hvor filens bytes skal sendes hen, og den `file`-værdi, der skal sendes med opret- eller opdater-anmodningen', + uploadStagePutSummary: 'Send filens bytes til en midlertidig upload', + uploadStageDeleteSummary: 'Slet en midlertidig upload', + uploadStageResult: 'Udført, intet indhold', error400: 'Validerings- eller forespørgselsfejl (ValidationError, QueryError)', error401: 'Ikke godkendt (AuthenticationError)', @@ -91,6 +115,8 @@ export const da: PluginDefaultTranslationsObject = { tagAuth: 'Auth', tagVersions: 'Versioner', tagJobs: 'Jobs', + tagUploads: 'Uploads', + tagAccess: 'Adgang', localizationHeading: 'Lokalisering', localizationNote: diff --git a/src/translations/locales/de.ts b/src/shared/translations/locales/de.ts similarity index 75% rename from src/translations/locales/de.ts rename to src/shared/translations/locales/de.ts index 7199fc1..98a2c0e 100644 --- a/src/translations/locales/de.ts +++ b/src/shared/translations/locales/de.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const de: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -19,6 +19,10 @@ export const de: PluginDefaultTranslationsObject = { 'Zurückzugebende Sprache oder `all` für alle Sprachen. Siehe den Abschnitt zur Lokalisierung in der API-Beschreibung.', paramFallbackLocale: 'Sprache, auf die bei fehlenden lokalisierten Werten zurückgegriffen wird, oder `none` zum Deaktivieren.', + paramValidateLocale: + 'Zu validierende Sprachen oder `all` für alle Sprachen. Für mehrere Sprachen den Parameter wiederholen.', + paramComputeHierarchyPaths: + 'Auf true setzen, um die Pfade `{{slugPath}}` und `{{titlePath}}` zu berechnen. Die Auswahl eines der beiden Felder berechnet sie ebenfalls.', schemaSelect: 'Zurückzugebende Felder auswählen, z. B. `select[title]=true`. Weglassen, um alle zurückzugeben.', schemaPopulate: 'Verknüpfte Dokumente je Collection befüllen, z. B. `populate[posts][title]=true`.', @@ -27,6 +31,10 @@ export const de: PluginDefaultTranslationsObject = { 'Payload-`where`-Filter. Verschachteln Sie ein Feld und dann einen Operator: `where[field][equals]=value`. Verknüpfen Sie Klauseln mit den Arrays `and` / `or`, z. B. `where[or][0][field][equals]=value`. Jedes Feld listet nur die für seinen Typ gültigen Operatoren auf.', schemaSupportedTimezones: 'Unterstützte Zeitzonen im IANA-Format.', schemaPerLocale: 'Werte je Sprache, zurückgegeben bei `locale=all`.', + schemaHierarchySlugPath: + 'Slug-Pfad, z. B. `parent/child`. Wird beim Lesen mit `computeHierarchyPaths=true` oder bei Auswahl berechnet. Nicht in `where` verwendbar.', + schemaHierarchyTitlePath: + 'Titel-Pfad, z. B. `Parent/Child`. Wird beim Lesen mit `computeHierarchyPaths=true` oder bei Auswahl berechnet. Nicht in `where` verwendbar.', collectionList: 'Paginierte Liste von Dokumenten', collectionDoc: 'Ein einzelnes Dokument', @@ -37,6 +45,10 @@ export const de: PluginDefaultTranslationsObject = { collectionBulkDelete: 'Ergebnis der Massenlöschung', collectionCount: 'Anzahl der Dokumente', collectionDuplicated: 'Das duplizierte Dokument', + validateResult: + 'Validierungsergebnis. Es wird nichts gespeichert. Ungültige Feldwerte liefern `valid: false` mit den Fehlern, keinen Fehlerstatus.', + validateBody: + 'Zu validierende Dokumentdaten. Bei einem gespeicherten Dokument oder einem Global werden die Daten über den neuesten Entwurf gelegt, oder über das gespeicherte Dokument, falls es keinen Entwurf gibt.', globalDoc: 'Das globale Dokument', @@ -48,9 +60,13 @@ export const de: PluginDefaultTranslationsObject = { authResetPassword: 'Ergebnis des Passwort-Zurücksetzens', authFirstRegister: 'Der erste Benutzer, erstellt mit einem Auth-Token', authInit: 'Ob diese Auth-Collection bereits Benutzer hat', - authAccess: 'Die Zugriffsrechte (Berechtigungen) des aktuellen Benutzers für diese Collection', + authAccess: 'Die Berechtigungen des aktuellen Benutzers für alle Collections und Globals', + docAccess: 'Die Berechtigungen des aktuellen Benutzers für dieses Dokument', + docAccessBody: + 'Dokumentdaten, gegen die der Zugriff geprüft wird. Ohne sie nutzt Payload das gespeicherte Dokument, falls es eines gibt.', authUnlock: 'Ergebnis des Entsperrens', authVerify: 'Verifizierungsergebnis', + apiKeyReveal: 'Der entschlüsselte API-Schlüssel', versionList: 'Paginierte Liste von Versionen', versionSingle: 'Eine einzelne Version', @@ -72,6 +88,14 @@ export const de: PluginDefaultTranslationsObject = { uploadBody: 'Senden Sie `multipart/form-data`, um eine Datei hochzuladen (ein binärer `file`-Teil plus ein `_payload`-Teil mit den als JSON-String serialisierten Feldern), oder `application/json` nur mit den Feldern, wenn keine Datei vorhanden ist.', uploadPayloadField: 'Als JSON-String serialisierte {{schema}}-Felder. Beispiel: `{"alt":"A caption"}`.', + fileServe: 'Die Datei', + filePartial: 'Ein Teil der Datei bei einer `Range`-Anfrage', + uploadInstructionsSummary: 'Anweisungen zum Hochladen einer Datei vor dem Speichern des Dokuments abrufen', + uploadInstructionsResult: + 'Wohin die Bytes der Datei gesendet werden und welcher `file`-Wert mit der Erstellungs- oder Aktualisierungsanfrage gesendet wird', + uploadStagePutSummary: 'Die Bytes der Datei für einen Zwischen-Upload senden', + uploadStageDeleteSummary: 'Einen Zwischen-Upload löschen', + uploadStageResult: 'Erledigt, kein Inhalt', error400: 'Validierungs- oder Abfragefehler (ValidationError, QueryError)', error401: 'Nicht authentifiziert (AuthenticationError)', @@ -93,6 +117,8 @@ export const de: PluginDefaultTranslationsObject = { tagAuth: 'Authentifizierung', tagVersions: 'Versionen', tagJobs: 'Jobs', + tagUploads: 'Uploads', + tagAccess: 'Zugriff', localizationHeading: 'Lokalisierung', localizationNote: diff --git a/src/translations/locales/en.ts b/src/shared/translations/locales/en.ts similarity index 74% rename from src/translations/locales/en.ts rename to src/shared/translations/locales/en.ts index 962bdb8..2e62ad5 100644 --- a/src/translations/locales/en.ts +++ b/src/shared/translations/locales/en.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const en: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -15,6 +15,10 @@ export const en: PluginDefaultTranslationsObject = { paramFlattenLocales: 'With `locale=all`, set false to keep localized fields as per-locale objects. Default true.', paramLocale: 'Locale to return, or `all` for every locale. See the Localization section in the API description.', paramFallbackLocale: 'Locale to fall back to for missing localized values, or `none` to disable.', + paramValidateLocale: + 'Locales to validate, or `all` for every locale. Repeat the parameter for more than one locale.', + paramComputeHierarchyPaths: + 'Set true to compute the `{{slugPath}}` and `{{titlePath}}` paths. Selecting either field computes them too.', schemaSelect: 'Choose fields to return, e.g. `select[title]=true`. Omit to return all.', schemaPopulate: 'Populate related documents per collection, e.g. `populate[posts][title]=true`.', @@ -23,6 +27,10 @@ export const en: PluginDefaultTranslationsObject = { 'Payload `where` filter. Nest a field then an operator: `where[field][equals]=value`. Combine clauses with the `and` / `or` arrays, e.g. `where[or][0][field][equals]=value`. Each field lists only the operators valid for its type.', schemaSupportedTimezones: 'Supported timezones in IANA format.', schemaPerLocale: 'Per-locale values, returned when `locale=all`.', + schemaHierarchySlugPath: + 'Slug path, e.g. `parent/child`. Computed on read with `computeHierarchyPaths=true` or when selected. Cannot be used in `where`.', + schemaHierarchyTitlePath: + 'Title path, e.g. `Parent/Child`. Computed on read with `computeHierarchyPaths=true` or when selected. Cannot be used in `where`.', collectionList: 'Paginated list of documents', collectionDoc: 'A single document', @@ -33,6 +41,10 @@ export const en: PluginDefaultTranslationsObject = { collectionBulkDelete: 'Bulk delete result', collectionCount: 'Document count', collectionDuplicated: 'The duplicated document', + validateResult: + 'Validation result. Nothing is saved. Invalid field values return `valid: false` with the errors, not an error status.', + validateBody: + 'Document data to validate. On a saved document or a global, the data is merged over the latest draft, or the saved document if there is no draft.', globalDoc: 'The global document', @@ -44,9 +56,13 @@ export const en: PluginDefaultTranslationsObject = { authResetPassword: 'Password reset result', authFirstRegister: 'The first user, created with an auth token', authInit: 'Whether this auth collection has any users yet', - authAccess: 'The current user’s access (permissions) for this collection', + authAccess: 'The current user’s permissions for every collection and global', + docAccess: 'The current user’s permissions for this document', + docAccessBody: + 'Document data to check access against. Without it, Payload uses the saved document, if there is one.', authUnlock: 'Unlock result', authVerify: 'Verification result', + apiKeyReveal: 'The decrypted API key', versionList: 'Paginated list of versions', versionSingle: 'A single version', @@ -68,6 +84,14 @@ export const en: PluginDefaultTranslationsObject = { uploadBody: 'Send `multipart/form-data` to upload a file (a binary `file` part plus a `_payload` part with the JSON-stringified fields), or `application/json` with just the fields when there’s no file.', uploadPayloadField: 'JSON-stringified {{schema}} fields. Example: `{"alt":"A caption"}`.', + fileServe: 'The file', + filePartial: 'Part of the file, for a `Range` request', + uploadInstructionsSummary: 'Get instructions to upload a file before saving the document', + uploadInstructionsResult: + 'Where to send the file bytes, and the `file` value to send with the create or update request', + uploadStagePutSummary: 'Send the file bytes for a staged upload', + uploadStageDeleteSummary: 'Delete a staged upload', + uploadStageResult: 'Done, no content', error400: 'Validation or query error (ValidationError, QueryError)', error401: 'Not authenticated (AuthenticationError)', @@ -88,6 +112,8 @@ export const en: PluginDefaultTranslationsObject = { tagAuth: 'Auth', tagVersions: 'Versions', tagJobs: 'Jobs', + tagUploads: 'Uploads', + tagAccess: 'Access', localizationHeading: 'Localization', localizationNote: diff --git a/src/translations/locales/es.ts b/src/shared/translations/locales/es.ts similarity index 75% rename from src/translations/locales/es.ts rename to src/shared/translations/locales/es.ts index 32da51b..04f2174 100644 --- a/src/translations/locales/es.ts +++ b/src/shared/translations/locales/es.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const es: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -17,6 +17,10 @@ export const es: PluginDefaultTranslationsObject = { paramLocale: 'Idioma a devolver, o `all` para todos los idiomas. Consulta la sección de Localización en la descripción de la API.', paramFallbackLocale: 'Idioma de reserva para los valores localizados que falten, o `none` para desactivarlo.', + paramValidateLocale: + 'Idiomas a validar, o `all` para todos los idiomas. Repite el parámetro para más de un idioma.', + paramComputeHierarchyPaths: + 'Usa true para calcular las rutas `{{slugPath}}` y `{{titlePath}}`. Seleccionar cualquiera de los dos campos también las calcula.', schemaSelect: 'Elige los campos a devolver, p. ej. `select[title]=true`. Omítelo para devolver todos.', schemaPopulate: 'Pobla documentos relacionados por colección, p. ej. `populate[posts][title]=true`.', @@ -25,6 +29,10 @@ export const es: PluginDefaultTranslationsObject = { 'Filtro `where` de Payload. Anida un campo y luego un operador: `where[field][equals]=value`. Combina cláusulas con los arrays `and` / `or`, p. ej. `where[or][0][field][equals]=value`. Cada campo lista únicamente los operadores válidos para su tipo.', schemaSupportedTimezones: 'Zonas horarias admitidas en formato IANA.', schemaPerLocale: 'Valores por idioma, devueltos cuando `locale=all`.', + schemaHierarchySlugPath: + 'Ruta de slugs, p. ej. `parent/child`. Se calcula al leer con `computeHierarchyPaths=true` o al seleccionarla. No se puede usar en `where`.', + schemaHierarchyTitlePath: + 'Ruta de títulos, p. ej. `Parent/Child`. Se calcula al leer con `computeHierarchyPaths=true` o al seleccionarla. No se puede usar en `where`.', collectionList: 'Lista paginada de documentos', collectionDoc: 'Un único documento', @@ -35,6 +43,10 @@ export const es: PluginDefaultTranslationsObject = { collectionBulkDelete: 'Resultado de la eliminación masiva', collectionCount: 'Recuento de documentos', collectionDuplicated: 'El documento duplicado', + validateResult: + 'Resultado de la validación. No se guarda nada. Los valores de campo no válidos devuelven `valid: false` con los errores, no un estado de error.', + validateBody: + 'Datos del documento a validar. En un documento guardado o un global, los datos se combinan sobre el último borrador, o sobre el documento guardado si no hay borrador.', globalDoc: 'El documento global', @@ -46,9 +58,13 @@ export const es: PluginDefaultTranslationsObject = { authResetPassword: 'Resultado del restablecimiento de contraseña', authFirstRegister: 'El primer usuario, creado con un token de autenticación', authInit: 'Si esta colección de autenticación ya tiene algún usuario', - authAccess: 'El acceso (permisos) del usuario actual para esta colección', + authAccess: 'Los permisos del usuario actual para todas las colecciones y globales', + docAccess: 'Los permisos del usuario actual para este documento', + docAccessBody: + 'Datos del documento con los que se comprueba el acceso. Sin ellos, Payload usa el documento guardado, si existe.', authUnlock: 'Resultado del desbloqueo', authVerify: 'Resultado de la verificación', + apiKeyReveal: 'La clave de API descifrada', versionList: 'Lista paginada de versiones', versionSingle: 'Una única versión', @@ -70,6 +86,14 @@ export const es: PluginDefaultTranslationsObject = { uploadBody: 'Envía `multipart/form-data` para subir un archivo (una parte binaria `file` más una parte `_payload` con los campos serializados como JSON), o `application/json` con solo los campos cuando no hay archivo.', uploadPayloadField: 'Campos {{schema}} serializados como JSON. Ejemplo: `{"alt":"A caption"}`.', + fileServe: 'El archivo', + filePartial: 'Una parte del archivo, para una petición `Range`', + uploadInstructionsSummary: 'Obtener instrucciones para subir un archivo antes de guardar el documento', + uploadInstructionsResult: + 'Dónde enviar los bytes del archivo y el valor `file` que se envía con la petición de creación o actualización', + uploadStagePutSummary: 'Enviar los bytes del archivo para una subida temporal', + uploadStageDeleteSummary: 'Eliminar una subida temporal', + uploadStageResult: 'Hecho, sin contenido', error400: 'Error de validación o de consulta (ValidationError, QueryError)', error401: 'No autenticado (AuthenticationError)', @@ -91,6 +115,8 @@ export const es: PluginDefaultTranslationsObject = { tagAuth: 'Autenticación', tagVersions: 'Versiones', tagJobs: 'Trabajos', + tagUploads: 'Subidas', + tagAccess: 'Acceso', localizationHeading: 'Localización', localizationNote: diff --git a/src/translations/locales/et.ts b/src/shared/translations/locales/et.ts similarity index 75% rename from src/translations/locales/et.ts rename to src/shared/translations/locales/et.ts index 89d5533..174de0e 100644 --- a/src/translations/locales/et.ts +++ b/src/shared/translations/locales/et.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const et: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -18,6 +18,10 @@ export const et: PluginDefaultTranslationsObject = { paramLocale: 'Tagastatav lokaat või `all` kõigi lokaatide jaoks. Vaata API kirjelduses jaotist Lokaliseerimine.', paramFallbackLocale: 'Lokaat, millele puuduvate lokaliseeritud väärtuste korral tagasi pöörduda, või `none` selle keelamiseks.', + paramValidateLocale: + 'Valideeritavad lokaadid või `all` kõigi lokaatide jaoks. Mitme lokaadi jaoks korda parameetrit.', + paramComputeHierarchyPaths: + 'Määra true, et arvutada teed `{{slugPath}}` ja `{{titlePath}}`. Kumbagi välja valimine arvutab need samuti.', schemaSelect: 'Vali tagastatavad väljad, nt `select[title]=true`. Kõigi tagastamiseks jäta tühjaks.', schemaPopulate: 'Täida seotud dokumendid kollektsiooni kaupa, nt `populate[posts][title]=true`.', @@ -26,6 +30,10 @@ export const et: PluginDefaultTranslationsObject = { 'Payloadi `where` filter. Pesasta väli ja seejärel operaator: `where[field][equals]=value`. Kombineeri klausleid massiividega `and` / `or`, nt `where[or][0][field][equals]=value`. Iga väli loetleb ainult tema tüübi jaoks sobivad operaatorid.', schemaSupportedTimezones: 'Toetatud ajavööndid IANA vormingus.', schemaPerLocale: 'Lokaadipõhised väärtused, tagastatakse väärtusega `locale=all`.', + schemaHierarchySlugPath: + 'Slugide tee, nt `parent/child`. Arvutatakse lugemisel koos `computeHierarchyPaths=true` või valimisel. Ei saa kasutada `where` sees.', + schemaHierarchyTitlePath: + 'Pealkirjade tee, nt `Parent/Child`. Arvutatakse lugemisel koos `computeHierarchyPaths=true` või valimisel. Ei saa kasutada `where` sees.', collectionList: 'Lehekülgedeks jaotatud dokumentide loend', collectionDoc: 'Üksik dokument', @@ -36,6 +44,10 @@ export const et: PluginDefaultTranslationsObject = { collectionBulkDelete: 'Hulgikustutuse tulemus', collectionCount: 'Dokumentide arv', collectionDuplicated: 'Dubleeritud dokument', + validateResult: + 'Valideerimise tulemus. Midagi ei salvestata. Kehtetud väljaväärtused tagastavad `valid: false` koos vigadega, mitte veastaatuse.', + validateBody: + 'Valideeritavad dokumendi andmed. Salvestatud dokumendi või globaalse dokumendi puhul liidetakse andmed viimase mustandi peale või mustandi puudumisel salvestatud dokumendi peale.', globalDoc: 'Globaalne dokument', @@ -47,9 +59,13 @@ export const et: PluginDefaultTranslationsObject = { authResetPassword: 'Parooli lähtestamise tulemus', authFirstRegister: 'Esimene kasutaja, loodud koos autentimistokeniga', authInit: 'Kas sellel autentimiskollektsioonil on juba kasutajaid', - authAccess: 'Praeguse kasutaja juurdepääs (õigused) selle kollektsiooni jaoks', + authAccess: 'Praeguse kasutaja õigused kõigi kollektsioonide ja globaalide jaoks', + docAccess: 'Praeguse kasutaja õigused selle dokumendi jaoks', + docAccessBody: + 'Dokumendi andmed, mille alusel õigusi kontrollitakse. Ilma nendeta kasutab Payload salvestatud dokumenti, kui see on olemas.', authUnlock: 'Avamise tulemus', authVerify: 'Kinnitamise tulemus', + apiKeyReveal: 'Dekrüpteeritud API-võti', versionList: 'Lehekülgedeks jaotatud versioonide loend', versionSingle: 'Üksik versioon', @@ -71,6 +87,14 @@ export const et: PluginDefaultTranslationsObject = { uploadBody: 'Faili üleslaadimiseks saada `multipart/form-data` (binaarne `file` osa pluss `_payload` osa JSON-stringitud väljadega) või `application/json` ainult väljadega, kui faili pole.', uploadPayloadField: 'JSON-stringitud {{schema}} väljad. Näide: `{\\"alt\\":\\"A caption\\"}`.', + fileServe: 'Fail', + filePartial: 'Osa failist `Range` päringu jaoks', + uploadInstructionsSummary: 'Hangi juhised faili üleslaadimiseks enne dokumendi salvestamist', + uploadInstructionsResult: + 'Kuhu saata faili baidid ja milline `file` väärtus saata loomise või uuendamise päringuga', + uploadStagePutSummary: 'Saada faili baidid ajutise üleslaadimise jaoks', + uploadStageDeleteSummary: 'Kustuta ajutine üleslaadimine', + uploadStageResult: 'Valmis, sisu puudub', error400: 'Valideerimis- või päringuviga (ValidationError, QueryError)', error401: 'Autentimata (AuthenticationError)', @@ -92,6 +116,8 @@ export const et: PluginDefaultTranslationsObject = { tagAuth: 'Autentimine', tagVersions: 'Versioonid', tagJobs: 'Tööd', + tagUploads: 'Üleslaadimised', + tagAccess: 'Juurdepääs', localizationHeading: 'Lokaliseerimine', localizationNote: diff --git a/src/translations/locales/fa.ts b/src/shared/translations/locales/fa.ts similarity index 75% rename from src/translations/locales/fa.ts rename to src/shared/translations/locales/fa.ts index c7ae80c..09c3397 100644 --- a/src/translations/locales/fa.ts +++ b/src/shared/translations/locales/fa.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const fa: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -18,6 +18,10 @@ export const fa: PluginDefaultTranslationsObject = { 'زبانی که باید بازگردانده شود، یا `all` برای همه زبان‌ها. به بخش بومی‌سازی (Localization) در توضیحات API مراجعه کنید.', paramFallbackLocale: 'زبانی که در صورت نبودن مقادیر محلی‌سازی‌شده به آن بازگشت داده شود، یا `none` برای غیرفعال کردن.', + paramValidateLocale: + 'زبان‌هایی که باید اعتبارسنجی شوند، یا `all` برای همه زبان‌ها. برای بیش از یک زبان، پارامتر را تکرار کنید.', + paramComputeHierarchyPaths: + 'برای محاسبه مسیرهای `{{slugPath}}` و `{{titlePath}}` مقدار true بدهید. انتخاب هر یک از این فیلدها هم آن‌ها را محاسبه می‌کند.', schemaSelect: 'فیلدهایی که باید بازگردانده شوند را انتخاب کنید، مثلاً `select[title]=true`. برای بازگرداندن همه، حذفش کنید.', @@ -27,6 +31,10 @@ export const fa: PluginDefaultTranslationsObject = { 'فیلتر `where` در Payload. ابتدا یک فیلد و سپس یک عملگر را تو در تو بنویسید: `where[field][equals]=value`. شرط‌ها را با آرایه‌های `and` / `or` ترکیب کنید، مثلاً `where[or][0][field][equals]=value`. هر فیلد فقط عملگرهای معتبر برای نوع خود را فهرست می‌کند.', schemaSupportedTimezones: 'منطقه‌های زمانی پشتیبانی‌شده با قالب IANA.', schemaPerLocale: 'مقادیر هر زبان، که هنگام `locale=all` بازگردانده می‌شوند.', + schemaHierarchySlugPath: + 'مسیر اسلاگ، مثلاً `parent/child`. هنگام خواندن با `computeHierarchyPaths=true` یا در صورت انتخاب محاسبه می‌شود. در `where` قابل استفاده نیست.', + schemaHierarchyTitlePath: + 'مسیر عنوان، مثلاً `Parent/Child`. هنگام خواندن با `computeHierarchyPaths=true` یا در صورت انتخاب محاسبه می‌شود. در `where` قابل استفاده نیست.', collectionList: 'فهرست صفحه‌بندی‌شده اسناد', collectionDoc: 'یک سند واحد', @@ -37,6 +45,10 @@ export const fa: PluginDefaultTranslationsObject = { collectionBulkDelete: 'نتیجه حذف گروهی', collectionCount: 'تعداد اسناد', collectionDuplicated: 'سند تکثیرشده', + validateResult: + 'نتیجه اعتبارسنجی. چیزی ذخیره نمی‌شود. مقادیر نامعتبر فیلدها به‌جای وضعیت خطا، `valid: false` را همراه با خطاها برمی‌گردانند.', + validateBody: + 'داده‌های سند برای اعتبارسنجی. در یک سند ذخیره‌شده یا سند سراسری (global)، داده‌ها روی آخرین پیش‌نویس ادغام می‌شوند، و اگر پیش‌نویسی نباشد روی سند ذخیره‌شده.', globalDoc: 'سند سراسری (global)', @@ -48,9 +60,13 @@ export const fa: PluginDefaultTranslationsObject = { authResetPassword: 'نتیجه بازنشانی رمز عبور', authFirstRegister: 'نخستین کاربر، که همراه با یک توکن احراز هویت ایجاد شد', authInit: 'اینکه آیا این مجموعه احراز هویت تاکنون کاربری دارد یا خیر', - authAccess: 'دسترسی (مجوزهای) کاربر فعلی برای این مجموعه', + authAccess: 'مجوزهای کاربر فعلی برای همه مجموعه‌ها و سراسری‌ها', + docAccess: 'مجوزهای کاربر فعلی برای این سند', + docAccessBody: + 'داده‌های سند برای بررسی دسترسی. بدون آن، Payload از سند ذخیره‌شده استفاده می‌کند، اگر وجود داشته باشد.', authUnlock: 'نتیجه باز کردن قفل', authVerify: 'نتیجه تأیید', + apiKeyReveal: 'کلید API رمزگشایی‌شده', versionList: 'فهرست صفحه‌بندی‌شده نسخه‌ها', versionSingle: 'یک نسخه واحد', @@ -72,6 +88,13 @@ export const fa: PluginDefaultTranslationsObject = { uploadBody: 'برای بارگذاری یک فایل، `multipart/form-data` ارسال کنید (یک بخش باینری `file` به‌علاوه یک بخش `_payload` حاوی فیلدهای به‌صورت JSON رشته‌شده)، یا وقتی فایلی وجود ندارد، `application/json` فقط با فیلدها ارسال کنید.', uploadPayloadField: 'فیلدهای {{schema}} به‌صورت JSON رشته‌شده. مثال: `{\\"alt\\":\\"A caption\\"}`.', + fileServe: 'فایل', + filePartial: 'بخشی از فایل، برای درخواست `Range`', + uploadInstructionsSummary: 'دریافت دستورالعمل بارگذاری فایل پیش از ذخیره سند', + uploadInstructionsResult: 'مقصد ارسال بایت‌های فایل و مقدار `file` که همراه درخواست ایجاد یا به‌روزرسانی ارسال می‌شود', + uploadStagePutSummary: 'ارسال بایت‌های فایل برای بارگذاری موقت', + uploadStageDeleteSummary: 'حذف بارگذاری موقت', + uploadStageResult: 'انجام شد، بدون محتوا', error400: 'خطای اعتبارسنجی یا پرس‌وجو (ValidationError, QueryError)', error401: 'احراز هویت نشده (AuthenticationError)', @@ -93,6 +116,8 @@ export const fa: PluginDefaultTranslationsObject = { tagAuth: 'احراز هویت', tagVersions: 'نسخه‌ها', tagJobs: 'کارها', + tagUploads: 'بارگذاری‌ها', + tagAccess: 'دسترسی', localizationHeading: 'بومی‌سازی', localizationNote: diff --git a/src/translations/locales/fr.ts b/src/shared/translations/locales/fr.ts similarity index 75% rename from src/translations/locales/fr.ts rename to src/shared/translations/locales/fr.ts index d20e2cf..dcb3524 100644 --- a/src/translations/locales/fr.ts +++ b/src/shared/translations/locales/fr.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const fr: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -18,6 +18,10 @@ export const fr: PluginDefaultTranslationsObject = { paramLocale: "Locale à renvoyer, ou `all` pour toutes les locales. Voir la section Localisation dans la description de l'API.", paramFallbackLocale: 'Locale de repli pour les valeurs localisées manquantes, ou `none` pour désactiver.', + paramValidateLocale: + "Locales à valider, ou `all` pour toutes les locales. Répétez le paramètre pour plus d'une locale.", + paramComputeHierarchyPaths: + 'Mettre à true pour calculer les chemins `{{slugPath}}` et `{{titlePath}}`. Sélectionner l’un de ces champs les calcule aussi.', schemaSelect: 'Choisissez les champs à renvoyer, par ex. `select[title]=true`. Omettez pour tout renvoyer.', schemaPopulate: 'Peuple les documents liés par collection, par ex. `populate[posts][title]=true`.', @@ -26,6 +30,10 @@ export const fr: PluginDefaultTranslationsObject = { 'Filtre `where` de Payload. Imbriquez un champ puis un opérateur : `where[field][equals]=value`. Combinez les clauses avec les tableaux `and` / `or`, par ex. `where[or][0][field][equals]=value`. Chaque champ ne liste que les opérateurs valides pour son type.', schemaSupportedTimezones: 'Fuseaux horaires pris en charge au format IANA.', schemaPerLocale: 'Valeurs par locale, renvoyées lorsque `locale=all`.', + schemaHierarchySlugPath: + 'Chemin de slugs, p. ex. `parent/child`. Calculé à la lecture avec `computeHierarchyPaths=true` ou s’il est sélectionné. Inutilisable dans `where`.', + schemaHierarchyTitlePath: + 'Chemin de titres, p. ex. `Parent/Child`. Calculé à la lecture avec `computeHierarchyPaths=true` ou s’il est sélectionné. Inutilisable dans `where`.', collectionList: 'Liste paginée de documents', collectionDoc: 'Un document unique', @@ -36,6 +44,10 @@ export const fr: PluginDefaultTranslationsObject = { collectionBulkDelete: 'Résultat de la suppression groupée', collectionCount: 'Nombre de documents', collectionDuplicated: 'Le document dupliqué', + validateResult: + "Résultat de la validation. Rien n'est enregistré. Les valeurs de champ invalides renvoient `valid: false` avec les erreurs, et non un statut d'erreur.", + validateBody: + "Données du document à valider. Sur un document enregistré ou un global, les données sont fusionnées sur le dernier brouillon, ou sur le document enregistré s'il n'y a pas de brouillon.", globalDoc: 'Le document global', @@ -47,9 +59,13 @@ export const fr: PluginDefaultTranslationsObject = { authResetPassword: 'Résultat de la réinitialisation du mot de passe', authFirstRegister: "Le premier utilisateur, créé avec un token d'authentification", authInit: "Indique si cette collection d'authentification possède déjà des utilisateurs", - authAccess: "Les accès (permissions) de l'utilisateur actuel pour cette collection", + authAccess: "Les permissions de l'utilisateur actuel pour toutes les collections et tous les globaux", + docAccess: "Les permissions de l'utilisateur actuel pour ce document", + docAccessBody: + "Données du document sur lesquelles vérifier l'accès. Sans elles, Payload utilise le document enregistré, s'il existe.", authUnlock: 'Résultat du déverrouillage', authVerify: 'Résultat de la vérification', + apiKeyReveal: "La clé d'API déchiffrée", versionList: 'Liste paginée de versions', versionSingle: 'Une version unique', @@ -71,6 +87,14 @@ export const fr: PluginDefaultTranslationsObject = { uploadBody: "Envoyez `multipart/form-data` pour téléverser un fichier (une partie binaire `file` plus une partie `_payload` contenant les champs au format JSON), ou `application/json` avec uniquement les champs en l'absence de fichier.", uploadPayloadField: 'Champs {{schema}} au format JSON. Exemple : `{"alt":"A caption"}`.', + fileServe: 'Le fichier', + filePartial: 'Une partie du fichier, pour une requête `Range`', + uploadInstructionsSummary: "Obtenir les instructions pour téléverser un fichier avant d'enregistrer le document", + uploadInstructionsResult: + 'Où envoyer les octets du fichier, et la valeur `file` à envoyer avec la requête de création ou de mise à jour', + uploadStagePutSummary: 'Envoyer les octets du fichier pour un téléversement temporaire', + uploadStageDeleteSummary: 'Supprimer un téléversement temporaire', + uploadStageResult: 'Terminé, aucun contenu', error400: 'Erreur de validation ou de requête (ValidationError, QueryError)', error401: 'Non authentifié (AuthenticationError)', @@ -92,6 +116,8 @@ export const fr: PluginDefaultTranslationsObject = { tagAuth: 'Authentification', tagVersions: 'Versions', tagJobs: 'Jobs', + tagUploads: 'Téléversements', + tagAccess: 'Accès', localizationHeading: 'Localisation', localizationNote: diff --git a/src/translations/locales/he.ts b/src/shared/translations/locales/he.ts similarity index 75% rename from src/translations/locales/he.ts rename to src/shared/translations/locales/he.ts index 084c491..3aa58f8 100644 --- a/src/translations/locales/he.ts +++ b/src/shared/translations/locales/he.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const he: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -15,6 +15,9 @@ export const he: PluginDefaultTranslationsObject = { paramFlattenLocales: 'עם `locale=all`, הגדר false כדי לשמור שדות מתורגמים כאובייקטים לכל לוקאל. ברירת המחדל true.', paramLocale: 'הלוקאל להחזרה, או `all` עבור כל הלוקאלים. ראה את סעיף הלוקליזציה בתיאור ה-API.', paramFallbackLocale: 'לוקאל לחזרה אחורה עבור ערכים מתורגמים חסרים, או `none` כדי להשבית.', + paramValidateLocale: 'לוקאלים לאימות, או `all` עבור כל הלוקאלים. חזור על הפרמטר עבור יותר מלוקאל אחד.', + paramComputeHierarchyPaths: + 'הגדירו true כדי לחשב את הנתיבים `{{slugPath}}` ו-`{{titlePath}}`. בחירה של אחד מהשדות מחשבת אותם גם כן.', schemaSelect: 'בחר אילו שדות להחזיר, למשל `select[title]=true`. השמט כדי להחזיר את הכל.', schemaPopulate: 'אכלוס מסמכים מקושרים לכל אוסף, למשל `populate[posts][title]=true`.', @@ -23,6 +26,10 @@ export const he: PluginDefaultTranslationsObject = { 'מסנן `where` של Payload. קנן שדה ואחריו אופרטור: `where[field][equals]=value`. שלב תנאים באמצעות המערכים `and` / `or`, למשל `where[or][0][field][equals]=value`. כל שדה מפרט רק את האופרטורים התקפים לסוג שלו.', schemaSupportedTimezones: 'אזורי זמן נתמכים בפורמט IANA.', schemaPerLocale: 'ערכים לכל לוקאל, מוחזרים כאשר `locale=all`.', + schemaHierarchySlugPath: + 'נתיב slug, למשל `parent/child`. מחושב בקריאה עם `computeHierarchyPaths=true` או כשהשדה נבחר. לא ניתן להשתמש בו ב-`where`.', + schemaHierarchyTitlePath: + 'נתיב כותרות, למשל `Parent/Child`. מחושב בקריאה עם `computeHierarchyPaths=true` או כשהשדה נבחר. לא ניתן להשתמש בו ב-`where`.', collectionList: 'רשימת מסמכים עם עימוד', collectionDoc: 'מסמך יחיד', @@ -33,6 +40,10 @@ export const he: PluginDefaultTranslationsObject = { collectionBulkDelete: 'תוצאת מחיקה מרובה', collectionCount: 'מספר מסמכים', collectionDuplicated: 'המסמך המשוכפל', + validateResult: + 'תוצאת האימות. דבר אינו נשמר. ערכי שדות לא תקינים מחזירים `valid: false` עם השגיאות, ולא סטטוס שגיאה.', + validateBody: + 'נתוני המסמך לאימות. במסמך שמור או במסמך גלובלי, הנתונים ממוזגים על הטיוטה האחרונה, או על המסמך השמור אם אין טיוטה.', globalDoc: 'המסמך הגלובלי', @@ -44,9 +55,12 @@ export const he: PluginDefaultTranslationsObject = { authResetPassword: 'תוצאת איפוס סיסמה', authFirstRegister: 'המשתמש הראשון, נוצר עם טוקן אימות', authInit: 'האם לאוסף אימות זה כבר יש משתמשים', - authAccess: 'הגישה (ההרשאות) של המשתמש הנוכחי לאוסף זה', + authAccess: 'ההרשאות של המשתמש הנוכחי לכל האוספים והגלובליים', + docAccess: 'ההרשאות של המשתמש הנוכחי למסמך זה', + docAccessBody: 'נתוני המסמך שמולם נבדקת הגישה. בלעדיהם Payload משתמש במסמך השמור, אם יש כזה.', authUnlock: 'תוצאת שחרור נעילה', authVerify: 'תוצאת אימות', + apiKeyReveal: 'מפתח ה-API המפוענח', versionList: 'רשימת גרסאות עם עימוד', versionSingle: 'גרסה יחידה', @@ -68,6 +82,13 @@ export const he: PluginDefaultTranslationsObject = { uploadBody: 'שלח `multipart/form-data` כדי להעלות קובץ (חלק `file` בינארי בתוספת חלק `_payload` עם השדות מקודדים כ-JSON), או `application/json` עם השדות בלבד כאשר אין קובץ.', uploadPayloadField: 'שדות {{schema}} מקודדים כ-JSON. דוגמה: `{"alt":"A caption"}`.', + fileServe: 'הקובץ', + filePartial: 'חלק מהקובץ, עבור בקשת `Range`', + uploadInstructionsSummary: 'קבלת הוראות להעלאת קובץ לפני שמירת המסמך', + uploadInstructionsResult: 'לאן לשלוח את בתי הקובץ, וערך `file` לשליחה עם בקשת היצירה או העדכון', + uploadStagePutSummary: 'שליחת בתי הקובץ להעלאה זמנית', + uploadStageDeleteSummary: 'מחיקת העלאה זמנית', + uploadStageResult: 'בוצע, ללא תוכן', error400: 'שגיאת אימות או שאילתה (ValidationError, QueryError)', error401: 'לא מאומת (AuthenticationError)', @@ -88,6 +109,8 @@ export const he: PluginDefaultTranslationsObject = { tagAuth: 'אימות', tagVersions: 'גרסאות', tagJobs: 'משימות', + tagUploads: 'העלאות', + tagAccess: 'גישה', localizationHeading: 'לוקליזציה', localizationNote: diff --git a/src/translations/locales/hr.ts b/src/shared/translations/locales/hr.ts similarity index 75% rename from src/translations/locales/hr.ts rename to src/shared/translations/locales/hr.ts index 0b0e6c9..0092561 100644 --- a/src/translations/locales/hr.ts +++ b/src/shared/translations/locales/hr.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const hr: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -18,6 +18,10 @@ export const hr: PluginDefaultTranslationsObject = { paramLocale: 'Lokalizacija koja se vraća ili `all` za svaku lokalizaciju. Pogledajte odjeljak Lokalizacija u opisu API-ja.', paramFallbackLocale: 'Lokalizacija na koju se vraća za vrijednosti koje nedostaju ili `none` za onemogućavanje.', + paramValidateLocale: + 'Lokalizacije za provjeru ili `all` za svaku lokalizaciju. Ponovite parametar za više od jedne lokalizacije.', + paramComputeHierarchyPaths: + 'Postavite true za izračun putanja `{{slugPath}}` i `{{titlePath}}`. Odabir bilo kojeg od tih polja također ih izračunava.', schemaSelect: 'Odaberite polja koja se vraćaju, npr. `select[title]=true`. Izostavite za vraćanje svih.', schemaPopulate: 'Popunite povezane dokumente po kolekciji, npr. `populate[posts][title]=true`.', @@ -26,6 +30,10 @@ export const hr: PluginDefaultTranslationsObject = { 'Payload `where` filtar. Ugnijezdite polje pa operator: `where[field][equals]=value`. Kombinirajte uvjete pomoću nizova `and` / `or`, npr. `where[or][0][field][equals]=value`. Svako polje navodi samo operatore valjane za svoj tip.', schemaSupportedTimezones: 'Podržane vremenske zone u IANA formatu.', schemaPerLocale: 'Vrijednosti po lokalizaciji, vraćaju se uz `locale=all`.', + schemaHierarchySlugPath: + 'Putanja slugova, npr. `parent/child`. Izračunava se pri čitanju s `computeHierarchyPaths=true` ili kad je odabrana. Ne može se koristiti u `where`.', + schemaHierarchyTitlePath: + 'Putanja naslova, npr. `Parent/Child`. Izračunava se pri čitanju s `computeHierarchyPaths=true` ili kad je odabrana. Ne može se koristiti u `where`.', collectionList: 'Paginirani popis dokumenata', collectionDoc: 'Jedan dokument', @@ -36,6 +44,10 @@ export const hr: PluginDefaultTranslationsObject = { collectionBulkDelete: 'Rezultat skupnog brisanja', collectionCount: 'Broj dokumenata', collectionDuplicated: 'Duplicirani dokument', + validateResult: + 'Rezultat provjere. Ništa se ne sprema. Nevažeće vrijednosti polja vraćaju `valid: false` s pogreškama, a ne status pogreške.', + validateBody: + 'Podaci dokumenta za provjeru. Na spremljenom dokumentu ili globalnom dokumentu podaci se spajaju preko najnovije skice, ili spremljenog dokumenta ako skice nema.', globalDoc: 'Globalni dokument', @@ -47,9 +59,13 @@ export const hr: PluginDefaultTranslationsObject = { authResetPassword: 'Rezultat poništavanja lozinke', authFirstRegister: 'Prvi korisnik, stvoren s tokenom za autentikaciju', authInit: 'Ima li ova autentikacijska kolekcija već korisnike', - authAccess: 'Pristup (dozvole) trenutnog korisnika za ovu kolekciju', + authAccess: 'Dozvole trenutnog korisnika za sve kolekcije i globale', + docAccess: 'Dozvole trenutnog korisnika za ovaj dokument', + docAccessBody: + 'Podaci dokumenta prema kojima se provjerava pristup. Bez njih Payload koristi spremljeni dokument, ako postoji.', authUnlock: 'Rezultat otključavanja', authVerify: 'Rezultat verifikacije', + apiKeyReveal: 'Dešifrirani API ključ', versionList: 'Paginirani popis verzija', versionSingle: 'Jedna verzija', @@ -71,6 +87,14 @@ export const hr: PluginDefaultTranslationsObject = { uploadBody: 'Pošaljite `multipart/form-data` za prijenos datoteke (binarni dio `file` plus dio `_payload` s poljima pretvorenima u JSON niz) ili `application/json` samo s poljima kada nema datoteke.', uploadPayloadField: 'Polja {{schema}} pretvorena u JSON niz. Primjer: `{\\"alt\\":\\"A caption\\"}`.', + fileServe: 'Datoteka', + filePartial: 'Dio datoteke za zahtjev `Range`', + uploadInstructionsSummary: 'Dohvati upute za prijenos datoteke prije spremanja dokumenta', + uploadInstructionsResult: + 'Kamo poslati bajtove datoteke i vrijednost `file` koja se šalje sa zahtjevom za stvaranje ili ažuriranje', + uploadStagePutSummary: 'Pošalji bajtove datoteke za privremeni prijenos', + uploadStageDeleteSummary: 'Izbriši privremeni prijenos', + uploadStageResult: 'Gotovo, bez sadržaja', error400: 'Pogreška valjanosti ili upita (ValidationError, QueryError)', error401: 'Nije autenticirano (AuthenticationError)', @@ -92,6 +116,8 @@ export const hr: PluginDefaultTranslationsObject = { tagAuth: 'Autentikacija', tagVersions: 'Verzije', tagJobs: 'Poslovi', + tagUploads: 'Prijenosi', + tagAccess: 'Pristup', localizationHeading: 'Lokalizacija', localizationNote: diff --git a/src/translations/locales/hu.ts b/src/shared/translations/locales/hu.ts similarity index 75% rename from src/translations/locales/hu.ts rename to src/shared/translations/locales/hu.ts index 2c30323..849e76f 100644 --- a/src/translations/locales/hu.ts +++ b/src/shared/translations/locales/hu.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const hu: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -16,6 +16,10 @@ export const hu: PluginDefaultTranslationsObject = { 'A `locale=all` esetén állítsd false értékre, hogy a lokalizált mezők lokálonkénti objektumokként maradjanak meg. Alapértelmezetten true.', paramLocale: 'A visszaadandó lokál, vagy `all` az összes lokálhoz. Lásd az API leírásban a Lokalizáció szakaszt.', paramFallbackLocale: 'A hiányzó lokalizált értékekhez használt tartalék lokál, vagy `none` a letiltáshoz.', + paramValidateLocale: + 'Az ellenőrzendő lokálok, vagy `all` az összes lokálhoz. Több lokálhoz ismételje meg a paramétert.', + paramComputeHierarchyPaths: + 'Állítsa true értékre a(z) `{{slugPath}}` és `{{titlePath}}` útvonalak kiszámításához. Bármelyik mező kiválasztása is kiszámítja őket.', schemaSelect: 'Válaszd ki a visszaadandó mezőket, pl. `select[title]=true`. Hagyd üresen az összes visszaadásához.', schemaPopulate: 'Kapcsolódó dokumentumok betöltése gyűjteményenként, pl. `populate[posts][title]=true`.', @@ -24,6 +28,10 @@ export const hu: PluginDefaultTranslationsObject = { 'Payload `where` szűrő. Ágyazz be egy mezőt, majd egy operátort: `where[field][equals]=value`. A feltételeket az `and` / `or` tömbökkel kombinálhatod, pl. `where[or][0][field][equals]=value`. Minden mezőnél csak az adott típushoz érvényes operátorok szerepelnek.', schemaSupportedTimezones: 'Támogatott időzónák IANA formátumban.', schemaPerLocale: 'Lokálonkénti értékek, amelyeket a `locale=all` esetén ad vissza.', + schemaHierarchySlugPath: + 'Slug útvonal, pl. `parent/child`. Olvasáskor számolódik `computeHierarchyPaths=true` esetén vagy ha ki van választva. A `where` feltételben nem használható.', + schemaHierarchyTitlePath: + 'Cím útvonal, pl. `Parent/Child`. Olvasáskor számolódik `computeHierarchyPaths=true` esetén vagy ha ki van választva. A `where` feltételben nem használható.', collectionList: 'Dokumentumok lapozott listája', collectionDoc: 'Egyetlen dokumentum', @@ -34,6 +42,10 @@ export const hu: PluginDefaultTranslationsObject = { collectionBulkDelete: 'Tömeges törlés eredménye', collectionCount: 'Dokumentumok száma', collectionDuplicated: 'A duplikált dokumentum', + validateResult: + 'Az ellenőrzés eredménye. Semmi sem kerül mentésre. Az érvénytelen mezőértékek `valid: false` értéket adnak vissza a hibákkal, nem hibaállapotot.', + validateBody: + 'Az ellenőrizendő dokumentumadatok. Mentett dokumentum vagy globális dokumentum esetén az adatok a legutóbbi vázlatra, vázlat hiányában a mentett dokumentumra egyesülnek.', globalDoc: 'A globális dokumentum', @@ -45,9 +57,13 @@ export const hu: PluginDefaultTranslationsObject = { authResetPassword: 'Jelszó-visszaállítás eredménye', authFirstRegister: 'Az első felhasználó, hitelesítési tokennel létrehozva', authInit: 'Hogy ennek a hitelesítési gyűjteménynek van-e már bármilyen felhasználója', - authAccess: 'Az aktuális felhasználó hozzáférése (jogosultságai) ehhez a gyűjteményhez', + authAccess: 'Az aktuális felhasználó jogosultságai az összes gyűjteményhez és globálishoz', + docAccess: 'Az aktuális felhasználó jogosultságai ehhez a dokumentumhoz', + docAccessBody: + 'Dokumentumadatok, amelyek alapján a hozzáférés ellenőrizve van. Nélküle a Payload a mentett dokumentumot használja, ha van.', authUnlock: 'Feloldás eredménye', authVerify: 'Ellenőrzés eredménye', + apiKeyReveal: 'A visszafejtett API-kulcs', versionList: 'Verziók lapozott listája', versionSingle: 'Egyetlen verzió', @@ -69,6 +85,14 @@ export const hu: PluginDefaultTranslationsObject = { uploadBody: 'Küldj `multipart/form-data` formátumot fájl feltöltéséhez (egy bináris `file` rész, valamint egy `_payload` rész a JSON-stringgé alakított mezőkkel), vagy `application/json` formátumot csak a mezőkkel, ha nincs fájl.', uploadPayloadField: 'JSON-stringgé alakított {{schema}} mezők. Példa: `{"alt":"A caption"}`.', + fileServe: 'A fájl', + filePartial: 'A fájl egy része `Range` kéréshez', + uploadInstructionsSummary: 'Utasítások lekérése fájl feltöltéséhez a dokumentum mentése előtt', + uploadInstructionsResult: + 'Hová kell küldeni a fájl bájtjait, és milyen `file` értéket kell küldeni a létrehozási vagy frissítési kéréssel', + uploadStagePutSummary: 'A fájl bájtjainak küldése ideiglenes feltöltéshez', + uploadStageDeleteSummary: 'Ideiglenes feltöltés törlése', + uploadStageResult: 'Kész, nincs tartalom', error400: 'Érvényesítési vagy lekérdezési hiba (ValidationError, QueryError)', error401: 'Nincs hitelesítve (AuthenticationError)', @@ -90,6 +114,8 @@ export const hu: PluginDefaultTranslationsObject = { tagAuth: 'Hitelesítés', tagVersions: 'Verziók', tagJobs: 'Feladatok', + tagUploads: 'Feltöltések', + tagAccess: 'Hozzáférés', localizationHeading: 'Lokalizáció', localizationNote: diff --git a/src/translations/locales/hy.ts b/src/shared/translations/locales/hy.ts similarity index 74% rename from src/translations/locales/hy.ts rename to src/shared/translations/locales/hy.ts index b8cf836..7ebd679 100644 --- a/src/translations/locales/hy.ts +++ b/src/shared/translations/locales/hy.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const hy: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -19,6 +19,10 @@ export const hy: PluginDefaultTranslationsObject = { 'Վերադարձվող locale-ը, կամ `all`՝ բոլոր locale-ների համար։ Տես API-ի նկարագրության Տեղայնացման բաժինը։', paramFallbackLocale: 'Locale, որին վերադառնալ բացակայող տեղայնացված արժեքների դեպքում, կամ `none`՝ անջատելու համար։', + paramValidateLocale: + 'Վավերացվող locale-ները, կամ `all`՝ բոլոր locale-ների համար։ Մեկից ավելի locale-ի համար կրկնեք պարամետրը։', + paramComputeHierarchyPaths: + 'Նշեք true՝ `{{slugPath}}` և `{{titlePath}}` ուղիները հաշվարկելու համար։ Դաշտերից որևէ մեկի ընտրությունը նույնպես հաշվարկում է դրանք։', schemaSelect: 'Ընտրեք վերադարձվող դաշտերը, օր. `select[title]=true`։ Բաց թողեք՝ բոլորը վերադարձնելու համար։', schemaPopulate: 'Ներառել կապակցված փաստաթղթերն ըստ կոլեկցիայի, օր. `populate[posts][title]=true`։', @@ -27,6 +31,10 @@ export const hy: PluginDefaultTranslationsObject = { 'Payload-ի `where` ֆիլտր։ Ներդրեք դաշտ, ապա օպերատոր. `where[field][equals]=value`։ Համատեղեք պայմանները `and` / `or` զանգվածներով, օր. `where[or][0][field][equals]=value`։ Յուրաքանչյուր դաշտ ցուցադրում է միայն իր տիպին վավեր օպերատորները։', schemaSupportedTimezones: 'Աջակցվող ժամային գոտիները IANA ձևաչափով։', schemaPerLocale: 'Ըստ-locale-ի արժեքներ, վերադարձվում են `locale=all`-ի դեպքում։', + schemaHierarchySlugPath: + 'Slug ուղի, օր.՝ `parent/child`։ Հաշվարկվում է կարդալիս `computeHierarchyPaths=true`-ով կամ ընտրելիս։ Չի կարող օգտագործվել `where`-ում։', + schemaHierarchyTitlePath: + 'Վերնագրերի ուղի, օր.՝ `Parent/Child`։ Հաշվարկվում է կարդալիս `computeHierarchyPaths=true`-ով կամ ընտրելիս։ Չի կարող օգտագործվել `where`-ում։', collectionList: 'Փաստաթղթերի էջավորված ցանկ', collectionDoc: 'Մեկ փաստաթուղթ', @@ -37,6 +45,10 @@ export const hy: PluginDefaultTranslationsObject = { collectionBulkDelete: 'Զանգվածային ջնջման արդյունք', collectionCount: 'Փաստաթղթերի քանակ', collectionDuplicated: 'Կրկնօրինակված փաստաթուղթը', + validateResult: + 'Վավերացման արդյունքը։ Ոչինչ չի պահպանվում։ Դաշտերի անվավեր արժեքների դեպքում վերադարձվում է `valid: false`՝ սխալներով, ոչ թե սխալի կարգավիճակ։', + validateBody: + 'Վավերացվող փաստաթղթի տվյալները։ Պահպանված փաստաթղթի կամ գլոբալ փաստաթղթի դեպքում տվյալները միավորվում են վերջին սևագրի վրա, իսկ եթե սևագիր չկա՝ պահպանված փաստաթղթի վրա։', globalDoc: 'Գլոբալ փաստաթուղթը', @@ -48,9 +60,13 @@ export const hy: PluginDefaultTranslationsObject = { authResetPassword: 'Գաղտնաբառի վերականգնման արդյունք', authFirstRegister: 'Առաջին օգտատերը, ստեղծված auth token-ով', authInit: 'Արդյոք այս auth կոլեկցիան արդեն ունի օգտատերեր', - authAccess: 'Ընթացիկ օգտատիրոջ մուտքի իրավունքները (թույլտվությունները) այս կոլեկցիայի համար', + authAccess: 'Ընթացիկ օգտատիրոջ թույլտվությունները բոլոր կոլեկցիաների և գլոբալների համար', + docAccess: 'Ընթացիկ օգտատիրոջ թույլտվությունները այս փաստաթղթի համար', + docAccessBody: + 'Փաստաթղթի տվյալներ, որոնց համեմատ ստուգվում է մուտքը։ Առանց դրանց Payload-ը օգտագործում է պահպանված փաստաթուղթը, եթե այն կա։', authUnlock: 'Ապակողպման արդյունք', authVerify: 'Հաստատման արդյունք', + apiKeyReveal: 'Վերծանված API բանալին', versionList: 'Տարբերակների էջավորված ցանկ', versionSingle: 'Մեկ տարբերակ', @@ -72,6 +88,14 @@ export const hy: PluginDefaultTranslationsObject = { uploadBody: 'Ֆայլ վերբեռնելու համար ուղարկեք `multipart/form-data` (երկուական `file` մաս և `_payload` մաս՝ JSON-ի տողի վերածված դաշտերով), կամ `application/json`՝ միայն դաշտերով, երբ ֆայլ չկա։', uploadPayloadField: 'JSON-ի տողի վերածված {{schema}} դաշտեր։ Օրինակ՝ `{"alt":"A caption"}`։', + fileServe: 'Ֆայլը', + filePartial: 'Ֆայլի մի մասը՝ `Range` հարցման համար', + uploadInstructionsSummary: 'Ստանալ ֆայլի վերբեռնման հրահանգներ մինչև փաստաթղթի պահպանումը', + uploadInstructionsResult: + 'Որտեղ ուղարկել ֆայլի բայթերը և `file` արժեքը, որն ուղարկվում է ստեղծման կամ թարմացման հարցման հետ', + uploadStagePutSummary: 'Ուղարկել ֆայլի բայթերը ժամանակավոր վերբեռնման համար', + uploadStageDeleteSummary: 'Ջնջել ժամանակավոր վերբեռնումը', + uploadStageResult: 'Պատրաստ է, առանց բովանդակության', error400: 'Վավերացման կամ հարցման սխալ (ValidationError, QueryError)', error401: 'Չնույնականացված (AuthenticationError)', @@ -93,6 +117,8 @@ export const hy: PluginDefaultTranslationsObject = { tagAuth: 'Նույնականացում', tagVersions: 'Տարբերակներ', tagJobs: 'Առաջադրանքներ', + tagUploads: 'Վերբեռնումներ', + tagAccess: 'Մուտք', localizationHeading: 'Տեղայնացում', localizationNote: diff --git a/src/translations/locales/id.ts b/src/shared/translations/locales/id.ts similarity index 75% rename from src/translations/locales/id.ts rename to src/shared/translations/locales/id.ts index 7a3ba53..1db1790 100644 --- a/src/translations/locales/id.ts +++ b/src/shared/translations/locales/id.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const id: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -18,6 +18,10 @@ export const id: PluginDefaultTranslationsObject = { paramLocale: 'Lokal yang akan dikembalikan, atau `all` untuk setiap lokal. Lihat bagian Localization pada deskripsi API.', paramFallbackLocale: 'Lokal cadangan untuk nilai terlokalisasi yang hilang, atau `none` untuk menonaktifkan.', + paramValidateLocale: + 'Lokal yang akan divalidasi, atau `all` untuk setiap lokal. Ulangi parameter untuk lebih dari satu lokal.', + paramComputeHierarchyPaths: + 'Atur ke true untuk menghitung jalur `{{slugPath}}` dan `{{titlePath}}`. Memilih salah satu field tersebut juga menghitungnya.', schemaSelect: 'Pilih field yang akan dikembalikan, mis. `select[title]=true`. Kosongkan untuk mengembalikan semua.', schemaPopulate: 'Mengisi dokumen terkait per koleksi, mis. `populate[posts][title]=true`.', @@ -26,6 +30,10 @@ export const id: PluginDefaultTranslationsObject = { 'Filter `where` Payload. Bersarangkan sebuah field lalu sebuah operator: `where[field][equals]=value`. Gabungkan klausa dengan array `and` / `or`, mis. `where[or][0][field][equals]=value`. Setiap field hanya mencantumkan operator yang valid untuk tipenya.', schemaSupportedTimezones: 'Zona waktu yang didukung dalam format IANA.', schemaPerLocale: 'Nilai per-lokal, dikembalikan saat `locale=all`.', + schemaHierarchySlugPath: + 'Jalur slug, mis. `parent/child`. Dihitung saat membaca dengan `computeHierarchyPaths=true` atau saat dipilih. Tidak dapat dipakai di `where`.', + schemaHierarchyTitlePath: + 'Jalur judul, mis. `Parent/Child`. Dihitung saat membaca dengan `computeHierarchyPaths=true` atau saat dipilih. Tidak dapat dipakai di `where`.', collectionList: 'Daftar dokumen berpaginasi', collectionDoc: 'Sebuah dokumen tunggal', @@ -36,6 +44,10 @@ export const id: PluginDefaultTranslationsObject = { collectionBulkDelete: 'Hasil penghapusan massal', collectionCount: 'Jumlah dokumen', collectionDuplicated: 'Dokumen hasil duplikasi', + validateResult: + 'Hasil validasi. Tidak ada yang disimpan. Nilai kolom yang tidak valid mengembalikan `valid: false` beserta kesalahannya, bukan status error.', + validateBody: + 'Data dokumen yang akan divalidasi. Pada dokumen yang tersimpan atau global, data digabungkan di atas draf terbaru, atau dokumen yang tersimpan jika tidak ada draf.', globalDoc: 'Dokumen global', @@ -47,9 +59,12 @@ export const id: PluginDefaultTranslationsObject = { authResetPassword: 'Hasil pengaturan ulang kata sandi', authFirstRegister: 'Pengguna pertama, dibuat dengan token autentikasi', authInit: 'Apakah koleksi autentikasi ini sudah memiliki pengguna', - authAccess: 'Akses (izin) pengguna saat ini untuk koleksi ini', + authAccess: 'Izin pengguna saat ini untuk semua koleksi dan global', + docAccess: 'Izin pengguna saat ini untuk dokumen ini', + docAccessBody: 'Data dokumen untuk memeriksa akses. Tanpanya, Payload memakai dokumen yang tersimpan, jika ada.', authUnlock: 'Hasil pembukaan kunci', authVerify: 'Hasil verifikasi', + apiKeyReveal: 'Kunci API yang sudah didekripsi', versionList: 'Daftar versi berpaginasi', versionSingle: 'Sebuah versi tunggal', @@ -71,6 +86,14 @@ export const id: PluginDefaultTranslationsObject = { uploadBody: 'Kirim `multipart/form-data` untuk mengunggah file (sebuah bagian biner `file` ditambah sebuah bagian `_payload` berisi field yang sudah di-JSON-stringify), atau `application/json` hanya dengan field saja jika tidak ada file.', uploadPayloadField: 'Field {{schema}} yang sudah di-JSON-stringify. Contoh: `{"alt":"A caption"}`.', + fileServe: 'File', + filePartial: 'Sebagian file, untuk permintaan `Range`', + uploadInstructionsSummary: 'Dapatkan instruksi untuk mengunggah file sebelum menyimpan dokumen', + uploadInstructionsResult: + 'Ke mana byte file dikirim, dan nilai `file` yang dikirim bersama permintaan buat atau perbarui', + uploadStagePutSummary: 'Kirim byte file untuk unggahan sementara', + uploadStageDeleteSummary: 'Hapus unggahan sementara', + uploadStageResult: 'Selesai, tanpa konten', error400: 'Galat validasi atau kueri (ValidationError, QueryError)', error401: 'Tidak terautentikasi (AuthenticationError)', @@ -92,6 +115,8 @@ export const id: PluginDefaultTranslationsObject = { tagAuth: 'Autentikasi', tagVersions: 'Versi', tagJobs: 'Job', + tagUploads: 'Unggahan', + tagAccess: 'Akses', localizationHeading: 'Localization', localizationNote: diff --git a/src/translations/locales/is.ts b/src/shared/translations/locales/is.ts similarity index 74% rename from src/translations/locales/is.ts rename to src/shared/translations/locales/is.ts index 2802c56..0bb4b85 100644 --- a/src/translations/locales/is.ts +++ b/src/shared/translations/locales/is.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const is: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -17,6 +17,10 @@ export const is: PluginDefaultTranslationsObject = { paramLocale: 'Tungumál til að skila, eða `all` fyrir öll tungumál. Sjá Staðfærsla-kaflann í API-lýsingunni.', paramFallbackLocale: 'Tungumál til að nota til vara fyrir staðfærð gildi sem vantar, eða `none` til að slökkva á því.', + paramValidateLocale: + 'Tungumál sem á að sannreyna, eða `all` fyrir öll tungumál. Endurtaktu færibreytuna fyrir fleiri en eitt tungumál.', + paramComputeHierarchyPaths: + 'Stilltu á true til að reikna slóðirnar `{{slugPath}}` og `{{titlePath}}`. Að velja annað hvort svæðið reiknar þær líka.', schemaSelect: 'Veldu reiti til að skila, t.d. `select[title]=true`. Slepptu til að skila öllum.', schemaPopulate: 'Birta tengd skjöl eftir safni, t.d. `populate[posts][title]=true`.', @@ -25,6 +29,10 @@ export const is: PluginDefaultTranslationsObject = { 'Payload `where`-sía. Hreiðraðu reit og svo virkja: `where[field][equals]=value`. Sameinaðu skilyrði með `and` / `or` fylkjunum, t.d. `where[or][0][field][equals]=value`. Hver reitur tilgreinir aðeins þá virkja sem gilda fyrir gerð hans.', schemaSupportedTimezones: 'Studd tímabelti á IANA-sniði.', schemaPerLocale: 'Gildi eftir tungumáli, skilað þegar `locale=all`.', + schemaHierarchySlugPath: + 'Slóð úr slug-gildum, t.d. `parent/child`. Reiknuð við lestur með `computeHierarchyPaths=true` eða þegar hún er valin. Ekki hægt að nota í `where`.', + schemaHierarchyTitlePath: + 'Slóð úr titlum, t.d. `Parent/Child`. Reiknuð við lestur með `computeHierarchyPaths=true` eða þegar hún er valin. Ekki hægt að nota í `where`.', collectionList: 'Síðuskiptur listi yfir skjöl', collectionDoc: 'Eitt skjal', @@ -35,6 +43,10 @@ export const is: PluginDefaultTranslationsObject = { collectionBulkDelete: 'Niðurstaða magneyðingar', collectionCount: 'Fjöldi skjala', collectionDuplicated: 'Afritaða skjalið', + validateResult: + 'Niðurstaða sannprófunar. Ekkert er vistað. Ógild reitagildi skila `valid: false` með villunum, ekki villustöðu.', + validateBody: + 'Skjalagögn sem á að sannreyna. Á vistuðu skjali eða algildu skjali eru gögnin sameinuð yfir nýjustu drög, eða vistaða skjalið ef engin drög eru til.', globalDoc: 'Algilda skjalið', @@ -46,9 +58,13 @@ export const is: PluginDefaultTranslationsObject = { authResetPassword: 'Niðurstaða endurstillingar lykilorðs', authFirstRegister: 'Fyrsti notandinn, stofnaður með auðkennistóka', authInit: 'Hvort þetta auðkenningarsafn hafi þegar einhverja notendur', - authAccess: 'Aðgangur (heimildir) núverandi notanda fyrir þetta safn', + authAccess: 'Heimildir núverandi notanda fyrir öll söfn og algild gögn', + docAccess: 'Heimildir núverandi notanda fyrir þetta skjal', + docAccessBody: + 'Skjalagögn sem aðgangur er athugaður út frá. Án þeirra notar Payload vistaða skjalið, ef það er til.', authUnlock: 'Niðurstaða aflæsingar', authVerify: 'Niðurstaða staðfestingar', + apiKeyReveal: 'Afkóðaði API-lykillinn', versionList: 'Síðuskiptur listi yfir útgáfur', versionSingle: 'Ein útgáfa', @@ -70,6 +86,14 @@ export const is: PluginDefaultTranslationsObject = { uploadBody: 'Sendu `multipart/form-data` til að hlaða upp skrá (tvíundarhluta `file` ásamt `_payload`-hluta með reitunum sem JSON-strengur), eða `application/json` með aðeins reitunum þegar engin skrá er til staðar.', uploadPayloadField: '{{schema}}-reitir sem JSON-strengur. Dæmi: `{"alt":"A caption"}`.', + fileServe: 'Skráin', + filePartial: 'Hluti skrárinnar, fyrir `Range`-beiðni', + uploadInstructionsSummary: 'Sækja leiðbeiningar til að hlaða upp skrá áður en skjalið er vistað', + uploadInstructionsResult: + 'Hvert á að senda bæti skrárinnar og `file`-gildið sem er sent með stofnunar- eða uppfærslubeiðninni', + uploadStagePutSummary: 'Senda bæti skrárinnar fyrir tímabundna upphleðslu', + uploadStageDeleteSummary: 'Eyða tímabundinni upphleðslu', + uploadStageResult: 'Lokið, ekkert efni', error400: 'Staðfestingar- eða fyrirspurnarvilla (ValidationError, QueryError)', error401: 'Ekki auðkenndur (AuthenticationError)', @@ -91,6 +115,8 @@ export const is: PluginDefaultTranslationsObject = { tagAuth: 'Auðkenning', tagVersions: 'Útgáfur', tagJobs: 'Verk', + tagUploads: 'Upphleðslur', + tagAccess: 'Aðgangur', localizationHeading: 'Staðfærsla', localizationNote: diff --git a/src/translations/locales/it.ts b/src/shared/translations/locales/it.ts similarity index 75% rename from src/translations/locales/it.ts rename to src/shared/translations/locales/it.ts index 0fd93b3..97421fa 100644 --- a/src/translations/locales/it.ts +++ b/src/shared/translations/locales/it.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const it: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -18,6 +18,10 @@ export const it: PluginDefaultTranslationsObject = { paramLocale: "Lingua da restituire, oppure `all` per tutte le lingue. Vedere la sezione Localizzazione nella descrizione dell'API.", paramFallbackLocale: 'Lingua di ripiego per i valori localizzati mancanti, oppure `none` per disabilitarla.', + paramValidateLocale: + 'Lingue da validare, oppure `all` per tutte le lingue. Ripetere il parametro per più di una lingua.', + paramComputeHierarchyPaths: + 'Imposta true per calcolare i percorsi `{{slugPath}}` e `{{titlePath}}`. Anche selezionare uno dei due campi li calcola.', schemaSelect: 'Scegliere i campi da restituire, ad es. `select[title]=true`. Omettere per restituirli tutti.', schemaPopulate: 'Popola i documenti correlati per ciascuna collezione, ad es. `populate[posts][title]=true`.', @@ -26,6 +30,10 @@ export const it: PluginDefaultTranslationsObject = { 'Filtro `where` di Payload. Annidare un campo e poi un operatore: `where[field][equals]=value`. Combinare le clausole con gli array `and` / `or`, ad es. `where[or][0][field][equals]=value`. Ogni campo elenca solo gli operatori validi per il suo tipo.', schemaSupportedTimezones: 'Fusi orari supportati in formato IANA.', schemaPerLocale: 'Valori per singola lingua, restituiti quando `locale=all`.', + schemaHierarchySlugPath: + 'Percorso di slug, es. `parent/child`. Calcolato in lettura con `computeHierarchyPaths=true` o quando selezionato. Non utilizzabile in `where`.', + schemaHierarchyTitlePath: + 'Percorso di titoli, es. `Parent/Child`. Calcolato in lettura con `computeHierarchyPaths=true` o quando selezionato. Non utilizzabile in `where`.', collectionList: 'Elenco paginato di documenti', collectionDoc: 'Un singolo documento', @@ -36,6 +44,10 @@ export const it: PluginDefaultTranslationsObject = { collectionBulkDelete: "Risultato dell'eliminazione di massa", collectionCount: 'Conteggio dei documenti', collectionDuplicated: 'Il documento duplicato', + validateResult: + 'Risultato della validazione. Non viene salvato nulla. I valori di campo non validi restituiscono `valid: false` con gli errori, non uno stato di errore.', + validateBody: + "Dati del documento da validare. Su un documento salvato o un global, i dati vengono uniti all'ultima bozza, oppure al documento salvato se non c'è una bozza.", globalDoc: 'Il documento globale', @@ -47,9 +59,13 @@ export const it: PluginDefaultTranslationsObject = { authResetPassword: 'Risultato della reimpostazione della password', authFirstRegister: 'Il primo utente, creato con un token di autenticazione', authInit: 'Indica se questa collezione di autenticazione ha già degli utenti', - authAccess: "L'accesso (i permessi) dell'utente corrente per questa collezione", + authAccess: "I permessi dell'utente corrente per tutte le collezioni e i globali", + docAccess: "I permessi dell'utente corrente per questo documento", + docAccessBody: + "Dati del documento su cui verificare l'accesso. Senza, Payload usa il documento salvato, se esiste.", authUnlock: 'Risultato dello sblocco', authVerify: 'Risultato della verifica', + apiKeyReveal: 'La chiave API decifrata', versionList: 'Elenco paginato di versioni', versionSingle: 'Una singola versione', @@ -71,6 +87,14 @@ export const it: PluginDefaultTranslationsObject = { uploadBody: "Inviare `multipart/form-data` per caricare un file (una parte binaria `file` più una parte `_payload` con i campi in formato JSON), oppure `application/json` con i soli campi quando non c'è alcun file.", uploadPayloadField: 'Campi {{schema}} in formato JSON. Esempio: `{"alt":"A caption"}`.', + fileServe: 'Il file', + filePartial: 'Una parte del file, per una richiesta `Range`', + uploadInstructionsSummary: 'Ottiene le istruzioni per caricare un file prima di salvare il documento', + uploadInstructionsResult: + 'Dove inviare i byte del file e il valore `file` da inviare con la richiesta di creazione o aggiornamento', + uploadStagePutSummary: 'Invia i byte del file per un caricamento temporaneo', + uploadStageDeleteSummary: 'Elimina un caricamento temporaneo', + uploadStageResult: 'Fatto, nessun contenuto', error400: 'Errore di validazione o di query (ValidationError, QueryError)', error401: 'Non autenticato (AuthenticationError)', @@ -92,6 +116,8 @@ export const it: PluginDefaultTranslationsObject = { tagAuth: 'Autenticazione', tagVersions: 'Versioni', tagJobs: 'Job', + tagUploads: 'Caricamenti', + tagAccess: 'Accesso', localizationHeading: 'Localizzazione', localizationNote: diff --git a/src/translations/locales/ja.ts b/src/shared/translations/locales/ja.ts similarity index 75% rename from src/translations/locales/ja.ts rename to src/shared/translations/locales/ja.ts index 2b043c3..fbdd097 100644 --- a/src/translations/locales/ja.ts +++ b/src/shared/translations/locales/ja.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const ja: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -18,6 +18,10 @@ export const ja: PluginDefaultTranslationsObject = { '返すロケール。すべてのロケールを取得するには `all` を指定します。API 説明のローカライズのセクションを参照してください。', paramFallbackLocale: 'ローカライズされた値が欠落している場合にフォールバックするロケール。無効にするには `none` を指定します。', + paramValidateLocale: + '検証するロケール。すべてのロケールを対象にするには `all` を指定します。複数のロケールを指定するにはパラメーターを繰り返します。', + paramComputeHierarchyPaths: + 'true にすると `{{slugPath}}` と `{{titlePath}}` のパスを計算します。どちらかのフィールドを選択した場合も計算されます。', schemaSelect: '返すフィールドを選択します。例: `select[title]=true`。省略するとすべて返します。', schemaPopulate: 'コレクションごとに関連ドキュメントを展開します。例: `populate[posts][title]=true`。', @@ -26,6 +30,10 @@ export const ja: PluginDefaultTranslationsObject = { 'Payload の `where` フィルター。フィールドの次に演算子をネストします: `where[field][equals]=value`。`and` / `or` 配列で条件を組み合わせます。例: `where[or][0][field][equals]=value`。各フィールドにはその型で有効な演算子のみが列挙されます。', schemaSupportedTimezones: 'IANA 形式でサポートされているタイムゾーン。', schemaPerLocale: '`locale=all` のときに返される、ロケールごとの値。', + schemaHierarchySlugPath: + 'スラッグのパス(例: `parent/child`)。`computeHierarchyPaths=true` を付けて読み取るか、選択したときに計算されます。`where` では使えません。', + schemaHierarchyTitlePath: + 'タイトルのパス(例: `Parent/Child`)。`computeHierarchyPaths=true` を付けて読み取るか、選択したときに計算されます。`where` では使えません。', collectionList: 'ドキュメントのページネーション付き一覧', collectionDoc: '単一のドキュメント', @@ -36,6 +44,10 @@ export const ja: PluginDefaultTranslationsObject = { collectionBulkDelete: '一括削除の結果', collectionCount: 'ドキュメント数', collectionDuplicated: '複製されたドキュメント', + validateResult: + '検証結果。何も保存されません。無効なフィールド値は、エラーステータスではなく、エラー内容を含む `valid: false` を返します。', + validateBody: + '検証するドキュメントデータ。保存済みのドキュメントまたはグローバルでは、データは最新の下書きに、下書きがなければ保存済みのドキュメントにマージされます。', globalDoc: 'グローバルドキュメント', @@ -47,9 +59,13 @@ export const ja: PluginDefaultTranslationsObject = { authResetPassword: 'パスワードリセットの結果', authFirstRegister: '認証トークン付きで作成された最初のユーザー', authInit: 'この認証コレクションにすでにユーザーが存在するかどうか', - authAccess: 'このコレクションに対する現在のユーザーのアクセス権 (パーミッション)', + authAccess: 'すべてのコレクションとグローバルに対する現在のユーザーのパーミッション', + docAccess: 'このドキュメントに対する現在のユーザーのパーミッション', + docAccessBody: + 'アクセスを確認するドキュメントデータ。省略すると、Payload は保存済みのドキュメントがあればそれを使います。', authUnlock: 'ロック解除の結果', authVerify: '検証結果', + apiKeyReveal: '復号された API キー', versionList: 'バージョンのページネーション付き一覧', versionSingle: '単一のバージョン', @@ -71,6 +87,13 @@ export const ja: PluginDefaultTranslationsObject = { uploadBody: 'ファイルをアップロードするには `multipart/form-data` を送信します (バイナリの `file` パートと、JSON 文字列化したフィールドを含む `_payload` パート)。ファイルがない場合は、フィールドのみを含む `application/json` を送信します。', uploadPayloadField: 'JSON 文字列化した {{schema}} フィールド。例: `{"alt":"A caption"}`。', + fileServe: 'ファイル', + filePartial: '`Range` リクエストに対するファイルの一部', + uploadInstructionsSummary: 'ドキュメントを保存する前にファイルをアップロードする手順を取得します', + uploadInstructionsResult: 'ファイルのバイトの送信先と、作成または更新リクエストで送信する `file` の値', + uploadStagePutSummary: '一時アップロードにファイルのバイトを送信します', + uploadStageDeleteSummary: '一時アップロードを削除します', + uploadStageResult: '完了 (コンテンツなし)', error400: '検証エラーまたはクエリエラー (ValidationError, QueryError)', error401: '認証されていません (AuthenticationError)', @@ -92,6 +115,8 @@ export const ja: PluginDefaultTranslationsObject = { tagAuth: '認証', tagVersions: 'バージョン', tagJobs: 'ジョブ', + tagUploads: 'アップロード', + tagAccess: 'アクセス', localizationHeading: 'ローカライズ', localizationNote: diff --git a/src/translations/locales/ko.ts b/src/shared/translations/locales/ko.ts similarity index 75% rename from src/translations/locales/ko.ts rename to src/shared/translations/locales/ko.ts index d9a5af9..b10d0f3 100644 --- a/src/translations/locales/ko.ts +++ b/src/shared/translations/locales/ko.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const ko: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -17,6 +17,10 @@ export const ko: PluginDefaultTranslationsObject = { paramLocale: '반환할 로케일이며, 모든 로케일을 받으려면 `all`을 사용합니다. API 설명의 Localization 섹션을 참고하세요.', paramFallbackLocale: '현지화된 값이 없을 때 대체할 로케일이며, 비활성화하려면 `none`을 사용합니다.', + paramValidateLocale: + '검증할 로케일이며, 모든 로케일을 검증하려면 `all`을 사용합니다. 로케일이 둘 이상이면 파라미터를 반복합니다.', + paramComputeHierarchyPaths: + 'true로 설정하면 `{{slugPath}}` 및 `{{titlePath}}` 경로를 계산합니다. 두 필드 중 하나를 선택해도 계산됩니다.', schemaSelect: '반환할 필드를 선택합니다(예: `select[title]=true`). 생략하면 모두 반환합니다.', schemaPopulate: '컬렉션별로 관련 문서를 채웁니다(예: `populate[posts][title]=true`).', @@ -25,6 +29,10 @@ export const ko: PluginDefaultTranslationsObject = { 'Payload `where` 필터입니다. 필드 다음에 연산자를 중첩합니다: `where[field][equals]=value`. `and` / `or` 배열로 절을 결합합니다(예: `where[or][0][field][equals]=value`). 각 필드는 해당 타입에 유효한 연산자만 나열합니다.', schemaSupportedTimezones: 'IANA 형식으로 지원되는 시간대입니다.', schemaPerLocale: '`locale=all`일 때 반환되는 로케일별 값입니다.', + schemaHierarchySlugPath: + '슬러그 경로(예: `parent/child`). `computeHierarchyPaths=true`로 읽거나 선택하면 계산됩니다. `where`에서는 사용할 수 없습니다.', + schemaHierarchyTitlePath: + '제목 경로(예: `Parent/Child`). `computeHierarchyPaths=true`로 읽거나 선택하면 계산됩니다. `where`에서는 사용할 수 없습니다.', collectionList: '페이지네이션된 문서 목록', collectionDoc: '단일 문서', @@ -35,6 +43,10 @@ export const ko: PluginDefaultTranslationsObject = { collectionBulkDelete: '일괄 삭제 결과', collectionCount: '문서 개수', collectionDuplicated: '복제된 문서', + validateResult: + '검증 결과. 아무것도 저장되지 않습니다. 유효하지 않은 필드 값은 오류 상태가 아니라 오류 내용과 함께 `valid: false`를 반환합니다.', + validateBody: + '검증할 문서 데이터. 저장된 문서나 글로벌에서는 데이터가 최신 초안 위에, 초안이 없으면 저장된 문서 위에 병합됩니다.', globalDoc: '글로벌 문서', @@ -46,9 +58,12 @@ export const ko: PluginDefaultTranslationsObject = { authResetPassword: '비밀번호 재설정 결과', authFirstRegister: '인증 토큰과 함께 생성된 첫 번째 사용자', authInit: '이 인증 컬렉션에 사용자가 있는지 여부', - authAccess: '이 컬렉션에 대한 현재 사용자의 접근 권한(권한)', + authAccess: '모든 컬렉션과 글로벌에 대한 현재 사용자의 권한', + docAccess: '이 문서에 대한 현재 사용자의 권한', + docAccessBody: '접근 권한을 확인할 문서 데이터. 없으면 Payload는 저장된 문서가 있을 경우 그것을 사용합니다.', authUnlock: '잠금 해제 결과', authVerify: '인증 확인 결과', + apiKeyReveal: '복호화된 API 키', versionList: '페이지네이션된 버전 목록', versionSingle: '단일 버전', @@ -70,6 +85,13 @@ export const ko: PluginDefaultTranslationsObject = { uploadBody: '파일을 업로드하려면 `multipart/form-data`를 전송하고(바이너리 `file` 파트와 JSON 문자열로 변환된 필드가 담긴 `_payload` 파트), 파일이 없으면 필드만 담은 `application/json`을 전송합니다.', uploadPayloadField: 'JSON 문자열로 변환된 {{schema}} 필드입니다. 예: `{"alt":"A caption"}`.', + fileServe: '파일', + filePartial: '`Range` 요청에 대한 파일의 일부', + uploadInstructionsSummary: '문서를 저장하기 전에 파일 업로드 안내를 받습니다', + uploadInstructionsResult: '파일 바이트를 보낼 위치와 생성 또는 수정 요청에 함께 보낼 `file` 값', + uploadStagePutSummary: '임시 업로드에 파일 바이트를 보냅니다', + uploadStageDeleteSummary: '임시 업로드를 삭제합니다', + uploadStageResult: '완료, 콘텐츠 없음', error400: '유효성 검사 또는 쿼리 오류 (ValidationError, QueryError)', error401: '인증되지 않음 (AuthenticationError)', @@ -91,6 +113,8 @@ export const ko: PluginDefaultTranslationsObject = { tagAuth: '인증', tagVersions: '버전', tagJobs: '작업', + tagUploads: '업로드', + tagAccess: '접근', localizationHeading: '현지화', localizationNote: diff --git a/src/translations/locales/lt.ts b/src/shared/translations/locales/lt.ts similarity index 76% rename from src/translations/locales/lt.ts rename to src/shared/translations/locales/lt.ts index 10c4019..2c3219d 100644 --- a/src/translations/locales/lt.ts +++ b/src/shared/translations/locales/lt.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const lt: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -17,6 +17,9 @@ export const lt: PluginDefaultTranslationsObject = { 'Su `locale=all` nustatykite false, kad lokalizuoti laukai liktų kaip atskirų lokalių objektai. Numatytoji reikšmė true.', paramLocale: 'Lokalė, kurią grąžinti, arba `all` visoms lokalėms. Žr. lokalizacijos skyrių API aprašyme.', paramFallbackLocale: 'Atsarginė lokalė trūkstamoms lokalizuotoms reikšmėms, arba `none` norint išjungti.', + paramValidateLocale: 'Tikrintinos lokalės arba `all` visoms lokalėms. Kelioms lokalėms kartokite parametrą.', + paramComputeHierarchyPaths: + 'Nustatykite true, kad būtų apskaičiuoti keliai `{{slugPath}}` ir `{{titlePath}}`. Pasirinkus bet kurį iš šių laukų jie taip pat apskaičiuojami.', schemaSelect: 'Pasirinkite grąžintinus laukus, pvz. `select[title]=true`. Praleiskite, kad būtų grąžinti visi.', schemaPopulate: 'Užpildykite susijusius dokumentus pagal kolekciją, pvz. `populate[posts][title]=true`.', @@ -25,6 +28,10 @@ export const lt: PluginDefaultTranslationsObject = { 'Payload `where` filtras. Įdėkite lauką, tada operatorių: `where[field][equals]=value`. Sąlygas derinkite naudodami `and` / `or` masyvus, pvz. `where[or][0][field][equals]=value`. Kiekvienas laukas nurodo tik jo tipui tinkamus operatorius.', schemaSupportedTimezones: 'Palaikomos laiko juostos IANA formatu.', schemaPerLocale: 'Atskirų lokalių reikšmės, grąžinamos kai naudojama `locale=all`.', + schemaHierarchySlugPath: + 'Slug kelias, pvz., `parent/child`. Apskaičiuojamas skaitant su `computeHierarchyPaths=true` arba kai pasirinktas. Negalima naudoti `where`.', + schemaHierarchyTitlePath: + 'Pavadinimų kelias, pvz., `Parent/Child`. Apskaičiuojamas skaitant su `computeHierarchyPaths=true` arba kai pasirinktas. Negalima naudoti `where`.', collectionList: 'Puslapiuojamas dokumentų sąrašas', collectionDoc: 'Vienas dokumentas', @@ -35,6 +42,10 @@ export const lt: PluginDefaultTranslationsObject = { collectionBulkDelete: 'Masinio ištrynimo rezultatas', collectionCount: 'Dokumentų skaičius', collectionDuplicated: 'Dublikuotas dokumentas', + validateResult: + 'Patikros rezultatas. Niekas neišsaugoma. Netinkamos laukų reikšmės grąžina `valid: false` su klaidomis, o ne klaidos būseną.', + validateBody: + 'Tikrintini dokumento duomenys. Išsaugotame dokumente ar globaliame dokumente duomenys sujungiami su naujausiu juodraščiu, o jei juodraščio nėra – su išsaugotu dokumentu.', globalDoc: 'Globalus dokumentas', @@ -46,9 +57,13 @@ export const lt: PluginDefaultTranslationsObject = { authResetPassword: 'Slaptažodžio atstatymo rezultatas', authFirstRegister: 'Pirmasis naudotojas, sukurtas su autentifikacijos tokenu', authInit: 'Ar ši autentifikacijos kolekcija jau turi naudotojų', - authAccess: 'Dabartinio naudotojo prieiga (teisės) prie šios kolekcijos', + authAccess: 'Dabartinio naudotojo teisės visoms kolekcijoms ir globaliems objektams', + docAccess: 'Dabartinio naudotojo teisės šiam dokumentui', + docAccessBody: + 'Dokumento duomenys, pagal kuriuos tikrinama prieiga. Be jų Payload naudoja išsaugotą dokumentą, jei toks yra.', authUnlock: 'Atrakinimo rezultatas', authVerify: 'Patvirtinimo rezultatas', + apiKeyReveal: 'Iššifruotas API raktas', versionList: 'Puslapiuojamas versijų sąrašas', versionSingle: 'Viena versija', @@ -70,6 +85,14 @@ export const lt: PluginDefaultTranslationsObject = { uploadBody: 'Siųskite `multipart/form-data`, kad įkeltumėte failą (dvejetainė `file` dalis ir `_payload` dalis su JSON eilute pavaizduotais laukais), arba `application/json` tik su laukais, kai failo nėra.', uploadPayloadField: 'JSON eilute pavaizduoti {{schema}} laukai. Pavyzdys: `{"alt":"A caption"}`.', + fileServe: 'Failas', + filePartial: 'Failo dalis `Range` užklausai', + uploadInstructionsSummary: 'Gauti failo įkėlimo instrukcijas prieš išsaugant dokumentą', + uploadInstructionsResult: + 'Kur siųsti failo baitus ir kokią `file` reikšmę siųsti su kūrimo arba atnaujinimo užklausa', + uploadStagePutSummary: 'Siųsti failo baitus laikinam įkėlimui', + uploadStageDeleteSummary: 'Ištrinti laikiną įkėlimą', + uploadStageResult: 'Atlikta, be turinio', error400: 'Patvirtinimo arba užklausos klaida (ValidationError, QueryError)', error401: 'Neautentifikuota (AuthenticationError)', @@ -91,6 +114,8 @@ export const lt: PluginDefaultTranslationsObject = { tagAuth: 'Autentifikacija', tagVersions: 'Versijos', tagJobs: 'Užduotys', + tagUploads: 'Įkėlimai', + tagAccess: 'Prieiga', localizationHeading: 'Lokalizacija', localizationNote: diff --git a/src/translations/locales/lv.ts b/src/shared/translations/locales/lv.ts similarity index 76% rename from src/translations/locales/lv.ts rename to src/shared/translations/locales/lv.ts index 3158c88..7b4034f 100644 --- a/src/translations/locales/lv.ts +++ b/src/shared/translations/locales/lv.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const lv: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -18,6 +18,9 @@ export const lv: PluginDefaultTranslationsObject = { paramLocale: 'Atgriežamā lokāle vai `all`, lai iegūtu visas lokāles. Skatiet sadaļu Lokalizācija API aprakstā.', paramFallbackLocale: 'Lokāle, uz kuru atkāpties trūkstošu lokalizētu vērtību gadījumā, vai `none`, lai to atspējotu.', + paramValidateLocale: 'Pārbaudāmās lokāles vai `all` visām lokālēm. Vairākām lokālēm atkārtojiet parametru.', + paramComputeHierarchyPaths: + 'Iestatiet true, lai aprēķinātu ceļus `{{slugPath}}` un `{{titlePath}}`. Jebkura no šiem laukiem atlase arī tos aprēķina.', schemaSelect: 'Izvēlieties atgriežamos laukus, piemēram, `select[title]=true`. Izlaidiet, lai atgrieztu visus.', schemaPopulate: 'Aizpildiet saistītos dokumentus pa kolekcijām, piemēram, `populate[posts][title]=true`.', @@ -26,6 +29,10 @@ export const lv: PluginDefaultTranslationsObject = { 'Payload `where` filtrs. Ietveriet lauku un pēc tam operatoru: `where[field][equals]=value`. Apvienojiet nosacījumus ar `and` / `or` masīviem, piemēram, `where[or][0][field][equals]=value`. Katrs lauks norāda tikai tā tipam derīgos operatorus.', schemaSupportedTimezones: 'Atbalstītās laika joslas IANA formātā.', schemaPerLocale: 'Atsevišķu lokāļu vērtības, kas tiek atgrieztas, kad izmantots `locale=all`.', + schemaHierarchySlugPath: + 'Slug ceļš, piem., `parent/child`. Aprēķina, lasot ar `computeHierarchyPaths=true` vai kad atlasīts. Nevar izmantot `where`.', + schemaHierarchyTitlePath: + 'Virsrakstu ceļš, piem., `Parent/Child`. Aprēķina, lasot ar `computeHierarchyPaths=true` vai kad atlasīts. Nevar izmantot `where`.', collectionList: 'Lapotais dokumentu saraksts', collectionDoc: 'Atsevišķs dokuments', @@ -36,6 +43,10 @@ export const lv: PluginDefaultTranslationsObject = { collectionBulkDelete: 'Masveida dzēšanas rezultāts', collectionCount: 'Dokumentu skaits', collectionDuplicated: 'Dublētais dokuments', + validateResult: + 'Pārbaudes rezultāts. Nekas netiek saglabāts. Nederīgas lauku vērtības atgriež `valid: false` ar kļūdām, nevis kļūdas statusu.', + validateBody: + 'Pārbaudāmie dokumenta dati. Saglabātam dokumentam vai globālajam dokumentam dati tiek apvienoti ar jaunāko melnrakstu, bet, ja melnraksta nav, ar saglabāto dokumentu.', globalDoc: 'Globālais dokuments', @@ -47,9 +58,13 @@ export const lv: PluginDefaultTranslationsObject = { authResetPassword: 'Paroles atiestatīšanas rezultāts', authFirstRegister: 'Pirmais lietotājs, izveidots ar autentifikācijas talonu', authInit: 'Vai šajā autentifikācijas kolekcijā jau ir kādi lietotāji', - authAccess: 'Pašreizējā lietotāja piekļuve (atļaujas) šai kolekcijai', + authAccess: 'Pašreizējā lietotāja atļaujas visām kolekcijām un globālajiem objektiem', + docAccess: 'Pašreizējā lietotāja atļaujas šim dokumentam', + docAccessBody: + 'Dokumenta dati, pret kuriem pārbauda piekļuvi. Bez tiem Payload izmanto saglabāto dokumentu, ja tāds ir.', authUnlock: 'Atbloķēšanas rezultāts', authVerify: 'Verifikācijas rezultāts', + apiKeyReveal: 'Atšifrētā API atslēga', versionList: 'Lapotais versiju saraksts', versionSingle: 'Atsevišķa versija', @@ -71,6 +86,14 @@ export const lv: PluginDefaultTranslationsObject = { uploadBody: 'Sūtiet `multipart/form-data`, lai augšupielādētu failu (binārā `file` daļa un `_payload` daļa ar JSON virknē pārveidotajiem laukiem), vai `application/json` tikai ar laukiem, ja faila nav.', uploadPayloadField: 'JSON virknē pārveidotie {{schema}} lauki. Piemērs: `{"alt":"A caption"}`.', + fileServe: 'Fails', + filePartial: 'Faila daļa `Range` pieprasījumam', + uploadInstructionsSummary: 'Saņemt norādījumus faila augšupielādei pirms dokumenta saglabāšanas', + uploadInstructionsResult: + 'Kur sūtīt faila baitus un kāda `file` vērtība jāsūta kopā ar izveides vai atjaunināšanas pieprasījumu', + uploadStagePutSummary: 'Sūtīt faila baitus pagaidu augšupielādei', + uploadStageDeleteSummary: 'Dzēst pagaidu augšupielādi', + uploadStageResult: 'Gatavs, bez satura', error400: 'Validācijas vai vaicājuma kļūda (ValidationError, QueryError)', error401: 'Nav autentificēts (AuthenticationError)', @@ -92,6 +115,8 @@ export const lv: PluginDefaultTranslationsObject = { tagAuth: 'Autentifikācija', tagVersions: 'Versijas', tagJobs: 'Darbi', + tagUploads: 'Augšupielādes', + tagAccess: 'Piekļuve', localizationHeading: 'Lokalizācija', localizationNote: diff --git a/src/translations/locales/my.ts b/src/shared/translations/locales/my.ts similarity index 75% rename from src/translations/locales/my.ts rename to src/shared/translations/locales/my.ts index 200a96e..0377768 100644 --- a/src/translations/locales/my.ts +++ b/src/shared/translations/locales/my.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const my: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -16,6 +16,9 @@ export const my: PluginDefaultTranslationsObject = { '`locale=all` ဖြင့်အသုံးပြုသောအခါ ဒေသန္တရပြုလုပ်ထားသော အကွက်များကို တစ်ဒေသစီ object အဖြစ် ဆက်ထားရန် false သတ်မှတ်ပါ။ မူရင်းတန်ဖိုးမှာ true ဖြစ်သည်။', paramLocale: 'ပြန်ပေးရမည့် ဒေသ၊ သို့မဟုတ် ဒေသအားလုံးအတွက် `all`။ API ဖော်ပြချက်ရှိ Localization အပိုင်းကို ကြည့်ပါ။', paramFallbackLocale: 'ပျောက်ဆုံးနေသော ဒေသန္တရတန်ဖိုးများအတွက် အစားထိုးပြန်လှည့်မည့် ဒေသ၊ သို့မဟုတ် ပိတ်ရန် `none`။', + paramValidateLocale: 'အတည်ပြုစစ်ဆေးမည့် ဒေသများ၊ သို့မဟုတ် ဒေသအားလုံးအတွက် `all`။ ဒေသတစ်ခုထက်ပိုလျှင် parameter ကို ထပ်ရေးပါ။', + paramComputeHierarchyPaths: + '`{{slugPath}}` နှင့် `{{titlePath}}` လမ်းကြောင်းများကို တွက်ချက်ရန် true သတ်မှတ်ပါ။ ထို field တစ်ခုခုကို ရွေးလျှင်လည်း တွက်ချက်သည်။', schemaSelect: 'ပြန်ပေးမည့် အကွက်များကို ရွေးချယ်ပါ၊ ဥပမာ `select[title]=true`။ အားလုံးပြန်ပေးစေရန် ချန်လှပ်ထားပါ။', schemaPopulate: 'collection အလိုက် ဆက်စပ်စာရွက်စာတမ်းများကို ဖြည့်သွင်းသည်၊ ဥပမာ `populate[posts][title]=true`။', @@ -24,6 +27,10 @@ export const my: PluginDefaultTranslationsObject = { 'Payload ၏ `where` စစ်ထုတ်ချက်။ အကွက်တစ်ခုပြီး operator တစ်ခုကို အလွှာဆင့်ထည့်ပါ: `where[field][equals]=value`။ စကားစုများကို `and` / `or` array များဖြင့် ပေါင်းစပ်ပါ၊ ဥပမာ `where[or][0][field][equals]=value`။ အကွက်တစ်ခုစီသည် ၎င်း၏အမျိုးအစားအတွက် မှန်ကန်သော operator များကိုသာ ဖော်ပြသည်။', schemaSupportedTimezones: 'IANA ပုံစံဖြင့် ပံ့ပိုးထားသော အချိန်ဇုန်များ။', schemaPerLocale: '`locale=all` သုံးသောအခါ ပြန်ပေးသော တစ်ဒေသစီ တန်ဖိုးများ။', + schemaHierarchySlugPath: + 'Slug လမ်းကြောင်း၊ ဥပမာ `parent/child`။ `computeHierarchyPaths=true` ဖြင့် ဖတ်သောအခါ သို့မဟုတ် ရွေးထားသောအခါ တွက်ချက်သည်။ `where` တွင် သုံး၍မရပါ။', + schemaHierarchyTitlePath: + 'ခေါင်းစဉ် လမ်းကြောင်း၊ ဥပမာ `Parent/Child`။ `computeHierarchyPaths=true` ဖြင့် ဖတ်သောအခါ သို့မဟုတ် ရွေးထားသောအခါ တွက်ချက်သည်။ `where` တွင် သုံး၍မရပါ။', collectionList: 'စာရွက်စာတမ်းများ၏ စာမျက်နှာခွဲ စာရင်း', collectionDoc: 'စာရွက်စာတမ်းတစ်ခု', @@ -34,6 +41,10 @@ export const my: PluginDefaultTranslationsObject = { collectionBulkDelete: 'အစုလိုက် ဖျက်မှု ရလဒ်', collectionCount: 'စာရွက်စာတမ်း အရေအတွက်', collectionDuplicated: 'ပွားယူထားသော စာရွက်စာတမ်း', + validateResult: + 'အတည်ပြုစစ်ဆေးမှု ရလဒ်။ မည်သည့်အရာမျှ မသိမ်းပါ။ မှားယွင်းသော ကွင်းဒေတာတန်ဖိုးများသည် error status မဟုတ်ဘဲ အမှားများနှင့်အတူ `valid: false` ကို ပြန်ပေးသည်။', + validateBody: + 'အတည်ပြုစစ်ဆေးမည့် စာရွက်စာတမ်းဒေတာ။ သိမ်းထားသော စာရွက်စာတမ်း သို့မဟုတ် global တွင် ဒေတာကို နောက်ဆုံး မူကြမ်းပေါ်တွင် ပေါင်းစပ်ပြီး မူကြမ်းမရှိလျှင် သိမ်းထားသော စာရွက်စာတမ်းပေါ်တွင် ပေါင်းစပ်သည်။', globalDoc: 'global စာရွက်စာတမ်း', @@ -45,9 +56,12 @@ export const my: PluginDefaultTranslationsObject = { authResetPassword: 'စကားဝှက် ပြန်လည်သတ်မှတ်မှု ရလဒ်', authFirstRegister: 'auth token ဖြင့် ဖန်တီးထားသော ပထမဆုံး အသုံးပြုသူ', authInit: 'ဤ auth collection တွင် အသုံးပြုသူ ရှိပြီးဖြစ်မဖြစ်', - authAccess: 'ဤ collection အတွက် လက်ရှိအသုံးပြုသူ၏ ဝင်ရောက်ခွင့် (ခွင့်ပြုချက်များ)', + authAccess: 'collections နှင့် globals အားလုံးအတွက် လက်ရှိအသုံးပြုသူ၏ ခွင့်ပြုချက်များ', + docAccess: 'ဤစာရွက်စာတမ်းအတွက် လက်ရှိအသုံးပြုသူ၏ ခွင့်ပြုချက်များ', + docAccessBody: 'ဝင်ရောက်ခွင့်ကို စစ်ဆေးမည့် စာရွက်စာတမ်းဒေတာ။ မပါပါက Payload သည် သိမ်းထားသော စာရွက်စာတမ်းရှိလျှင် ၎င်းကို အသုံးပြုသည်။', authUnlock: 'သော့ဖွင့်မှု ရလဒ်', authVerify: 'အတည်ပြုမှု ရလဒ်', + apiKeyReveal: 'ကုဒ်ဖြည်ထားသော API key', versionList: 'ဗားရှင်းများ၏ စာမျက်နှာခွဲ စာရင်း', versionSingle: 'ဗားရှင်းတစ်ခု', @@ -69,6 +83,13 @@ export const my: PluginDefaultTranslationsObject = { uploadBody: 'ဖိုင်တစ်ခု အပ်လုဒ်လုပ်ရန် `multipart/form-data` ပေးပို့ပါ (binary `file` အပိုင်းနှင့် JSON-stringified အကွက်များပါသော `_payload` အပိုင်း)၊ သို့မဟုတ် ဖိုင်မရှိသည့်အခါ အကွက်များသာပါသော `application/json` ကို ပေးပို့ပါ။', uploadPayloadField: 'JSON-stringified {{schema}} အကွက်များ။ ဥပမာ: `{"alt":"A caption"}`။', + fileServe: 'ဖိုင်', + filePartial: '`Range` request အတွက် ဖိုင်၏ အစိတ်အပိုင်း', + uploadInstructionsSummary: 'စာရွက်စာတမ်းကို မသိမ်းမီ ဖိုင်အပ်လုဒ်လုပ်ရန် ညွှန်ကြားချက်များ ရယူသည်', + uploadInstructionsResult: 'ဖိုင် bytes ပို့ရမည့်နေရာနှင့် create သို့မဟုတ် update request နှင့်အတူ ပို့ရမည့် `file` တန်ဖိုး', + uploadStagePutSummary: 'ယာယီအပ်လုဒ်အတွက် ဖိုင် bytes ပို့သည်', + uploadStageDeleteSummary: 'ယာယီအပ်လုဒ်ကို ဖျက်သည်', + uploadStageResult: 'ပြီးပါပြီ၊ အကြောင်းအရာ မရှိပါ', error400: 'အတည်ပြုမှု သို့မဟုတ် query အမှား (ValidationError, QueryError)', error401: 'အထောက်အထား မစိစစ်ရသေးပါ (AuthenticationError)', @@ -90,6 +111,8 @@ export const my: PluginDefaultTranslationsObject = { tagAuth: 'Auth', tagVersions: 'Versions', tagJobs: 'Jobs', + tagUploads: 'Uploads', + tagAccess: 'Access', localizationHeading: 'Localization', localizationNote: diff --git a/src/translations/locales/nb.ts b/src/shared/translations/locales/nb.ts similarity index 75% rename from src/translations/locales/nb.ts rename to src/shared/translations/locales/nb.ts index cb47942..b4fd28a 100644 --- a/src/translations/locales/nb.ts +++ b/src/shared/translations/locales/nb.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const nb: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -17,6 +17,10 @@ export const nb: PluginDefaultTranslationsObject = { paramLocale: 'Locale som skal returneres, eller `all` for alle locales. Se Lokalisering-seksjonen i API-beskrivelsen.', paramFallbackLocale: 'Locale å falle tilbake til for manglende lokaliserte verdier, eller `none` for å deaktivere.', + paramValidateLocale: + 'Locales som skal valideres, eller `all` for alle locales. Gjenta parameteren for mer enn én locale.', + paramComputeHierarchyPaths: + 'Sett til true for å beregne stiene `{{slugPath}}` og `{{titlePath}}`. Å velge ett av feltene beregner dem også.', schemaSelect: 'Velg felt som skal returneres, f.eks. `select[title]=true`. Utelat for å returnere alle.', schemaPopulate: 'Populer relaterte dokumenter per samling, f.eks. `populate[posts][title]=true`.', @@ -25,6 +29,10 @@ export const nb: PluginDefaultTranslationsObject = { 'Payload `where`-filter. Nest et felt og deretter en operator: `where[field][equals]=value`. Kombiner betingelser med `and`- / `or`-arrayene, f.eks. `where[or][0][field][equals]=value`. Hvert felt lister bare operatorene som er gyldige for typen sin.', schemaSupportedTimezones: 'Støttede tidssoner i IANA-format.', schemaPerLocale: 'Verdier per locale, returnert når `locale=all`.', + schemaHierarchySlugPath: + 'Sti av slugs, f.eks. `parent/child`. Beregnes ved lesing med `computeHierarchyPaths=true` eller når feltet velges. Kan ikke brukes i `where`.', + schemaHierarchyTitlePath: + 'Sti av titler, f.eks. `Parent/Child`. Beregnes ved lesing med `computeHierarchyPaths=true` eller når feltet velges. Kan ikke brukes i `where`.', collectionList: 'Paginert liste over dokumenter', collectionDoc: 'Et enkelt dokument', @@ -35,6 +43,10 @@ export const nb: PluginDefaultTranslationsObject = { collectionBulkDelete: 'Resultat av massesletting', collectionCount: 'Antall dokumenter', collectionDuplicated: 'Det dupliserte dokumentet', + validateResult: + 'Valideringsresultat. Ingenting lagres. Ugyldige feltverdier returnerer `valid: false` med feilene, ikke en feilstatus.', + validateBody: + 'Dokumentdata som skal valideres. På et lagret dokument eller en global flettes dataene oppå det siste utkastet, eller det lagrede dokumentet hvis det ikke finnes noe utkast.', globalDoc: 'Det globale dokumentet', @@ -46,9 +58,13 @@ export const nb: PluginDefaultTranslationsObject = { authResetPassword: 'Resultat av passordtilbakestilling', authFirstRegister: 'Den første brukeren, opprettet med et auth-token', authInit: 'Om denne auth-samlingen har noen brukere ennå', - authAccess: 'Den nåværende brukerens tilgang (tillatelser) for denne samlingen', + authAccess: 'Den nåværende brukerens tillatelser for alle samlinger og globaler', + docAccess: 'Den nåværende brukerens tillatelser for dette dokumentet', + docAccessBody: + 'Dokumentdata som tilgangen sjekkes mot. Uten dem bruker Payload det lagrede dokumentet, hvis det finnes.', authUnlock: 'Resultat av opplåsing', authVerify: 'Verifiseringsresultat', + apiKeyReveal: 'Den dekrypterte API-nøkkelen', versionList: 'Paginert liste over versjoner', versionSingle: 'En enkelt versjon', @@ -70,6 +86,14 @@ export const nb: PluginDefaultTranslationsObject = { uploadBody: 'Send `multipart/form-data` for å laste opp en fil (en binær `file`-del pluss en `_payload`-del med de JSON-strengkodede feltene), eller `application/json` med bare feltene når det ikke finnes noen fil.', uploadPayloadField: 'JSON-strengkodede {{schema}}-felt. Eksempel: `{"alt":"A caption"}`.', + fileServe: 'Filen', + filePartial: 'En del av filen ved en `Range`-forespørsel', + uploadInstructionsSummary: 'Hent instruksjoner for å laste opp en fil før dokumentet lagres', + uploadInstructionsResult: + 'Hvor filens byte skal sendes, og `file`-verdien som skal sendes med opprett- eller oppdater-forespørselen', + uploadStagePutSummary: 'Send filens byte for en midlertidig opplasting', + uploadStageDeleteSummary: 'Slett en midlertidig opplasting', + uploadStageResult: 'Ferdig, uten innhold', error400: 'Validerings- eller spørringsfeil (ValidationError, QueryError)', error401: 'Ikke autentisert (AuthenticationError)', @@ -91,6 +115,8 @@ export const nb: PluginDefaultTranslationsObject = { tagAuth: 'Autentisering', tagVersions: 'Versjoner', tagJobs: 'Jobber', + tagUploads: 'Opplastinger', + tagAccess: 'Tilgang', localizationHeading: 'Lokalisering', localizationNote: diff --git a/src/translations/locales/nl.ts b/src/shared/translations/locales/nl.ts similarity index 74% rename from src/translations/locales/nl.ts rename to src/shared/translations/locales/nl.ts index 4b2a014..4284d17 100644 --- a/src/translations/locales/nl.ts +++ b/src/shared/translations/locales/nl.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const nl: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -18,6 +18,10 @@ export const nl: PluginDefaultTranslationsObject = { 'De locale die wordt geretourneerd, of `all` voor elke locale. Zie het gedeelte Lokalisatie in de API-beschrijving.', paramFallbackLocale: 'De locale waarop wordt teruggevallen voor ontbrekende gelokaliseerde waarden, of `none` om uit te schakelen.', + paramValidateLocale: + 'Te valideren locales, of `all` voor elke locale. Herhaal de parameter voor meer dan één locale.', + paramComputeHierarchyPaths: + 'Zet op true om de paden `{{slugPath}}` en `{{titlePath}}` te berekenen. Een van beide velden selecteren berekent ze ook.', schemaSelect: 'Kies welke velden worden geretourneerd, bijv. `select[title]=true`. Laat weg om alle velden te retourneren.', @@ -27,6 +31,10 @@ export const nl: PluginDefaultTranslationsObject = { 'Payload `where`-filter. Nest eerst een veld en daarna een operator: `where[field][equals]=value`. Combineer clausules met de arrays `and` / `or`, bijv. `where[or][0][field][equals]=value`. Elk veld toont alleen de operators die geldig zijn voor het bijbehorende type.', schemaSupportedTimezones: 'Ondersteunde tijdzones in IANA-formaat.', schemaPerLocale: 'Waarden per locale, geretourneerd wanneer `locale=all`.', + schemaHierarchySlugPath: + 'Slug-pad, bijv. `parent/child`. Berekend bij lezen met `computeHierarchyPaths=true` of wanneer geselecteerd. Niet bruikbaar in `where`.', + schemaHierarchyTitlePath: + 'Titelpad, bijv. `Parent/Child`. Berekend bij lezen met `computeHierarchyPaths=true` of wanneer geselecteerd. Niet bruikbaar in `where`.', collectionList: 'Gepagineerde lijst met documenten', collectionDoc: 'Eén enkel document', @@ -37,6 +45,10 @@ export const nl: PluginDefaultTranslationsObject = { collectionBulkDelete: 'Resultaat van bulkverwijdering', collectionCount: 'Aantal documenten', collectionDuplicated: 'Het gedupliceerde document', + validateResult: + 'Validatieresultaat. Er wordt niets opgeslagen. Ongeldige veldwaarden geven `valid: false` met de fouten terug, geen foutstatus.', + validateBody: + 'Te valideren documentgegevens. Bij een opgeslagen document of een global worden de gegevens samengevoegd over het laatste concept, of over het opgeslagen document als er geen concept is.', globalDoc: 'Het globale document', @@ -48,9 +60,13 @@ export const nl: PluginDefaultTranslationsObject = { authResetPassword: 'Resultaat van wachtwoordherstel', authFirstRegister: 'De eerste gebruiker, aangemaakt met een auth-token', authInit: 'Of deze auth-collectie al gebruikers heeft', - authAccess: 'De toegang (rechten) van de huidige gebruiker voor deze collectie', + authAccess: 'De rechten van de huidige gebruiker voor alle collecties en globals', + docAccess: 'De rechten van de huidige gebruiker voor dit document', + docAccessBody: + 'Documentgegevens waartegen toegang wordt gecontroleerd. Zonder gegevens gebruikt Payload het opgeslagen document, als dat er is.', authUnlock: 'Resultaat van ontgrendeling', authVerify: 'Verificatieresultaat', + apiKeyReveal: 'De ontsleutelde API-sleutel', versionList: 'Gepagineerde lijst met versies', versionSingle: 'Eén enkele versie', @@ -72,6 +88,14 @@ export const nl: PluginDefaultTranslationsObject = { uploadBody: 'Verzend `multipart/form-data` om een bestand te uploaden (een binair `file`-deel plus een `_payload`-deel met de velden als JSON-string), of `application/json` met alleen de velden wanneer er geen bestand is.', uploadPayloadField: '{{schema}}-velden als JSON-string. Voorbeeld: `{"alt":"A caption"}`.', + fileServe: 'Het bestand', + filePartial: 'Een deel van het bestand, voor een `Range`-verzoek', + uploadInstructionsSummary: 'Instructies ophalen om een bestand te uploaden voordat het document wordt opgeslagen', + uploadInstructionsResult: + 'Waar de bytes van het bestand naartoe moeten, en de `file`-waarde die met het aanmaak- of bijwerkverzoek mee moet', + uploadStagePutSummary: 'De bytes van het bestand verzenden voor een tijdelijke upload', + uploadStageDeleteSummary: 'Een tijdelijke upload verwijderen', + uploadStageResult: 'Klaar, geen inhoud', error400: 'Validatie- of queryfout (ValidationError, QueryError)', error401: 'Niet geverifieerd (AuthenticationError)', @@ -93,6 +117,8 @@ export const nl: PluginDefaultTranslationsObject = { tagAuth: 'Auth', tagVersions: 'Versies', tagJobs: 'Taken', + tagUploads: 'Uploads', + tagAccess: 'Toegang', localizationHeading: 'Lokalisatie', localizationNote: diff --git a/src/translations/locales/pl.ts b/src/shared/translations/locales/pl.ts similarity index 75% rename from src/translations/locales/pl.ts rename to src/shared/translations/locales/pl.ts index ba8ef26..6ceda1f 100644 --- a/src/translations/locales/pl.ts +++ b/src/shared/translations/locales/pl.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const pl: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -17,6 +17,10 @@ export const pl: PluginDefaultTranslationsObject = { 'Przy `locale=all` ustaw false, aby zachować pola zlokalizowane jako obiekty z podziałem na języki. Domyślnie true.', paramLocale: 'Język do zwrócenia lub `all`, aby zwrócić wszystkie języki. Zobacz sekcję Lokalizacja w opisie API.', paramFallbackLocale: 'Język zastępczy dla brakujących wartości zlokalizowanych lub `none`, aby wyłączyć.', + paramValidateLocale: + 'Języki do sprawdzenia lub `all` dla wszystkich języków. Dla więcej niż jednego języka powtórz parametr.', + paramComputeHierarchyPaths: + 'Ustaw true, aby obliczyć ścieżki `{{slugPath}}` i `{{titlePath}}`. Wybranie któregokolwiek z tych pól również je oblicza.', schemaSelect: 'Wybierz pola do zwrócenia, np. `select[title]=true`. Pomiń, aby zwrócić wszystkie.', schemaPopulate: 'Wypełnij powiązane dokumenty dla każdej kolekcji, np. `populate[posts][title]=true`.', @@ -25,6 +29,10 @@ export const pl: PluginDefaultTranslationsObject = { 'Filtr `where` Payload. Zagnieźdź pole, a następnie operator: `where[field][equals]=value`. Łącz warunki za pomocą tablic `and` / `or`, np. `where[or][0][field][equals]=value`. Każde pole wymienia tylko operatory właściwe dla jego typu.', schemaSupportedTimezones: 'Obsługiwane strefy czasowe w formacie IANA.', schemaPerLocale: 'Wartości dla poszczególnych języków, zwracane przy `locale=all`.', + schemaHierarchySlugPath: + 'Ścieżka slugów, np. `parent/child`. Obliczana przy odczycie z `computeHierarchyPaths=true` lub po wybraniu. Nie można jej użyć w `where`.', + schemaHierarchyTitlePath: + 'Ścieżka tytułów, np. `Parent/Child`. Obliczana przy odczycie z `computeHierarchyPaths=true` lub po wybraniu. Nie można jej użyć w `where`.', collectionList: 'Stronicowana lista dokumentów', collectionDoc: 'Pojedynczy dokument', @@ -35,6 +43,10 @@ export const pl: PluginDefaultTranslationsObject = { collectionBulkDelete: 'Wynik usuwania zbiorczego', collectionCount: 'Liczba dokumentów', collectionDuplicated: 'Zduplikowany dokument', + validateResult: + 'Wynik walidacji. Nic nie jest zapisywane. Nieprawidłowe wartości pól zwracają `valid: false` wraz z błędami, a nie status błędu.', + validateBody: + 'Dane dokumentu do walidacji. W zapisanym dokumencie lub dokumencie globalnym dane są scalane z najnowszą wersją roboczą, a gdy jej nie ma, z zapisanym dokumentem.', globalDoc: 'Dokument globalny', @@ -46,9 +58,13 @@ export const pl: PluginDefaultTranslationsObject = { authResetPassword: 'Wynik resetowania hasła', authFirstRegister: 'Pierwszy użytkownik utworzony wraz z tokenem uwierzytelniającym', authInit: 'Czy ta kolekcja uwierzytelniania ma już jakichkolwiek użytkowników', - authAccess: 'Uprawnienia (dostęp) bieżącego użytkownika do tej kolekcji', + authAccess: 'Uprawnienia bieżącego użytkownika do wszystkich kolekcji i globalnych', + docAccess: 'Uprawnienia bieżącego użytkownika do tego dokumentu', + docAccessBody: + 'Dane dokumentu, względem których sprawdzany jest dostęp. Bez nich Payload używa zapisanego dokumentu, jeśli istnieje.', authUnlock: 'Wynik odblokowania', authVerify: 'Wynik weryfikacji', + apiKeyReveal: 'Odszyfrowany klucz API', versionList: 'Stronicowana lista wersji', versionSingle: 'Pojedyncza wersja', @@ -70,6 +86,14 @@ export const pl: PluginDefaultTranslationsObject = { uploadBody: 'Wyślij `multipart/form-data`, aby przesłać plik (część binarna `file` oraz część `_payload` z polami w postaci ciągu JSON), albo `application/json` z samymi polami, gdy nie ma pliku.', uploadPayloadField: 'Pola {{schema}} w postaci ciągu JSON. Przykład: `{\\"alt\\":\\"A caption\\"}`.', + fileServe: 'Plik', + filePartial: 'Część pliku dla żądania `Range`', + uploadInstructionsSummary: 'Pobierz instrukcje przesłania pliku przed zapisaniem dokumentu', + uploadInstructionsResult: + 'Dokąd wysłać bajty pliku i jaką wartość `file` wysłać z żądaniem utworzenia lub aktualizacji', + uploadStagePutSummary: 'Wyślij bajty pliku do tymczasowego przesyłania', + uploadStageDeleteSummary: 'Usuń tymczasowe przesyłanie', + uploadStageResult: 'Gotowe, bez treści', error400: 'Błąd walidacji lub zapytania (ValidationError, QueryError)', error401: 'Brak uwierzytelnienia (AuthenticationError)', @@ -91,6 +115,8 @@ export const pl: PluginDefaultTranslationsObject = { tagAuth: 'Uwierzytelnianie', tagVersions: 'Wersje', tagJobs: 'Zadania', + tagUploads: 'Przesyłanie plików', + tagAccess: 'Dostęp', localizationHeading: 'Lokalizacja', localizationNote: diff --git a/src/translations/locales/pt.ts b/src/shared/translations/locales/pt.ts similarity index 75% rename from src/translations/locales/pt.ts rename to src/shared/translations/locales/pt.ts index d845ad7..3e41e64 100644 --- a/src/translations/locales/pt.ts +++ b/src/shared/translations/locales/pt.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const pt: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -18,6 +18,10 @@ export const pt: PluginDefaultTranslationsObject = { paramLocale: 'Locale a retornar, ou `all` para todos os locales. Consulte a seção de Localização na descrição da API.', paramFallbackLocale: 'Locale alternativo para valores localizados ausentes, ou `none` para desativar.', + paramValidateLocale: + 'Locales a validar, ou `all` para todos os locales. Repita o parâmetro para mais de um locale.', + paramComputeHierarchyPaths: + 'Defina como true para calcular os caminhos `{{slugPath}}` e `{{titlePath}}`. Selecionar qualquer um dos campos também os calcula.', schemaSelect: 'Escolha os campos a retornar, por exemplo `select[title]=true`. Omita para retornar todos.', schemaPopulate: 'Popule documentos relacionados por coleção, por exemplo `populate[posts][title]=true`.', @@ -26,6 +30,10 @@ export const pt: PluginDefaultTranslationsObject = { 'Filtro `where` do Payload. Aninhe um campo e depois um operador: `where[field][equals]=value`. Combine cláusulas com os arrays `and` / `or`, por exemplo `where[or][0][field][equals]=value`. Cada campo lista apenas os operadores válidos para seu tipo.', schemaSupportedTimezones: 'Fusos horários suportados no formato IANA.', schemaPerLocale: 'Valores por locale, retornados quando `locale=all`.', + schemaHierarchySlugPath: + 'Caminho de slugs, ex. `parent/child`. Calculado na leitura com `computeHierarchyPaths=true` ou quando selecionado. Não pode ser usado em `where`.', + schemaHierarchyTitlePath: + 'Caminho de títulos, ex. `Parent/Child`. Calculado na leitura com `computeHierarchyPaths=true` ou quando selecionado. Não pode ser usado em `where`.', collectionList: 'Lista paginada de documentos', collectionDoc: 'Um único documento', @@ -36,6 +44,10 @@ export const pt: PluginDefaultTranslationsObject = { collectionBulkDelete: 'Resultado da exclusão em massa', collectionCount: 'Contagem de documentos', collectionDuplicated: 'O documento duplicado', + validateResult: + 'Resultado da validação. Nada é salvo. Valores de campo inválidos retornam `valid: false` com os erros, não um status de erro.', + validateBody: + 'Dados do documento a validar. Em um documento salvo ou em um global, os dados são mesclados sobre o rascunho mais recente, ou sobre o documento salvo se não houver rascunho.', globalDoc: 'O documento global', @@ -47,9 +59,13 @@ export const pt: PluginDefaultTranslationsObject = { authResetPassword: 'Resultado da redefinição de senha', authFirstRegister: 'O primeiro usuário, criado com um token de autenticação', authInit: 'Se esta coleção de autenticação já possui algum usuário', - authAccess: 'O acesso (permissões) do usuário atual para esta coleção', + authAccess: 'As permissões do usuário atual para todas as coleções e globais', + docAccess: 'As permissões do usuário atual para este documento', + docAccessBody: + 'Dados do documento usados para verificar o acesso. Sem eles, o Payload usa o documento salvo, se existir.', authUnlock: 'Resultado do desbloqueio', authVerify: 'Resultado da verificação', + apiKeyReveal: 'A chave de API descriptografada', versionList: 'Lista paginada de versões', versionSingle: 'Uma única versão', @@ -71,6 +87,14 @@ export const pt: PluginDefaultTranslationsObject = { uploadBody: 'Envie `multipart/form-data` para fazer upload de um arquivo (uma parte binária `file` mais uma parte `_payload` com os campos em JSON stringificado), ou `application/json` apenas com os campos quando não houver arquivo.', uploadPayloadField: 'Campos {{schema}} em JSON stringificado. Exemplo: `{"alt":"A caption"}`.', + fileServe: 'O arquivo', + filePartial: 'Uma parte do arquivo, para uma requisição `Range`', + uploadInstructionsSummary: 'Obter instruções para enviar um arquivo antes de salvar o documento', + uploadInstructionsResult: + 'Para onde enviar os bytes do arquivo e o valor `file` a enviar com a requisição de criação ou atualização', + uploadStagePutSummary: 'Enviar os bytes do arquivo para um upload temporário', + uploadStageDeleteSummary: 'Excluir um upload temporário', + uploadStageResult: 'Concluído, sem conteúdo', error400: 'Erro de validação ou de consulta (ValidationError, QueryError)', error401: 'Não autenticado (AuthenticationError)', @@ -92,6 +116,8 @@ export const pt: PluginDefaultTranslationsObject = { tagAuth: 'Autenticação', tagVersions: 'Versões', tagJobs: 'Tarefas', + tagUploads: 'Uploads', + tagAccess: 'Acesso', localizationHeading: 'Localização', localizationNote: diff --git a/src/translations/locales/ro.ts b/src/shared/translations/locales/ro.ts similarity index 74% rename from src/translations/locales/ro.ts rename to src/shared/translations/locales/ro.ts index 9397bf9..2a1e53e 100644 --- a/src/translations/locales/ro.ts +++ b/src/shared/translations/locales/ro.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const ro: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -18,6 +18,10 @@ export const ro: PluginDefaultTranslationsObject = { paramLocale: 'Locale-ul de returnat sau `all` pentru toate locale-urile. Consultați secțiunea Localizare din descrierea API.', paramFallbackLocale: 'Locale-ul de rezervă pentru valorile localizate lipsă sau `none` pentru a dezactiva.', + paramValidateLocale: + 'Locale-uri de validat sau `all` pentru toate locale-urile. Repetați parametrul pentru mai mult de un locale.', + paramComputeHierarchyPaths: + 'Setați true pentru a calcula căile `{{slugPath}}` și `{{titlePath}}`. Selectarea oricăruia dintre câmpuri le calculează și ea.', schemaSelect: 'Alegeți câmpurile de returnat, de ex. `select[title]=true`. Omiteți pentru a le returna pe toate.', schemaPopulate: 'Populați documentele asociate per colecție, de ex. `populate[posts][title]=true`.', @@ -26,6 +30,10 @@ export const ro: PluginDefaultTranslationsObject = { 'Filtru `where` Payload. Imbricați un câmp, apoi un operator: `where[field][equals]=value`. Combinați clauzele cu tablourile `and` / `or`, de ex. `where[or][0][field][equals]=value`. Fiecare câmp listează doar operatorii valizi pentru tipul său.', schemaSupportedTimezones: 'Fusuri orare acceptate în format IANA.', schemaPerLocale: 'Valori per-locale, returnate când `locale=all`.', + schemaHierarchySlugPath: + 'Cale din sluguri, de ex. `parent/child`. Calculată la citire cu `computeHierarchyPaths=true` sau când este selectată. Nu poate fi folosită în `where`.', + schemaHierarchyTitlePath: + 'Cale din titluri, de ex. `Parent/Child`. Calculată la citire cu `computeHierarchyPaths=true` sau când este selectată. Nu poate fi folosită în `where`.', collectionList: 'Listă paginată de documente', collectionDoc: 'Un singur document', @@ -36,6 +44,10 @@ export const ro: PluginDefaultTranslationsObject = { collectionBulkDelete: 'Rezultatul ștergerii în masă', collectionCount: 'Numărul de documente', collectionDuplicated: 'Documentul duplicat', + validateResult: + 'Rezultatul validării. Nu se salvează nimic. Valorile invalide ale câmpurilor returnează `valid: false` cu erorile, nu un status de eroare.', + validateBody: + 'Datele documentului de validat. Pe un document salvat sau un global, datele sunt îmbinate peste cea mai recentă ciornă sau peste documentul salvat, dacă nu există ciornă.', globalDoc: 'Documentul global', @@ -47,9 +59,13 @@ export const ro: PluginDefaultTranslationsObject = { authResetPassword: 'Rezultatul resetării parolei', authFirstRegister: 'Primul utilizator, creat cu un token de autentificare', authInit: 'Dacă această colecție de autentificare are deja utilizatori', - authAccess: 'Accesul (permisiunile) utilizatorului curent pentru această colecție', + authAccess: 'Permisiunile utilizatorului curent pentru toate colecțiile și globalele', + docAccess: 'Permisiunile utilizatorului curent pentru acest document', + docAccessBody: + 'Datele documentului față de care se verifică accesul. Fără ele, Payload folosește documentul salvat, dacă există.', authUnlock: 'Rezultatul deblocării', authVerify: 'Rezultatul verificării', + apiKeyReveal: 'Cheia API decriptată', versionList: 'Listă paginată de versiuni', versionSingle: 'O singură versiune', @@ -71,6 +87,14 @@ export const ro: PluginDefaultTranslationsObject = { uploadBody: 'Trimiteți `multipart/form-data` pentru a încărca un fișier (o parte binară `file` plus o parte `_payload` cu câmpurile serializate în JSON) sau `application/json` doar cu câmpurile când nu există fișier.', uploadPayloadField: 'Câmpuri {{schema}} serializate în JSON. Exemplu: `{\\"alt\\":\\"A caption\\"}`.', + fileServe: 'Fișierul', + filePartial: 'O parte din fișier, pentru o cerere `Range`', + uploadInstructionsSummary: 'Obține instrucțiuni pentru încărcarea unui fișier înainte de salvarea documentului', + uploadInstructionsResult: + 'Unde se trimit octeții fișierului și valoarea `file` de trimis cu cererea de creare sau actualizare', + uploadStagePutSummary: 'Trimite octeții fișierului pentru o încărcare temporară', + uploadStageDeleteSummary: 'Șterge o încărcare temporară', + uploadStageResult: 'Gata, fără conținut', error400: 'Eroare de validare sau de interogare (ValidationError, QueryError)', error401: 'Neautentificat (AuthenticationError)', @@ -92,6 +116,8 @@ export const ro: PluginDefaultTranslationsObject = { tagAuth: 'Autentificare', tagVersions: 'Versiuni', tagJobs: 'Joburi', + tagUploads: 'Încărcări', + tagAccess: 'Acces', localizationHeading: 'Localizare', localizationNote: diff --git a/src/translations/locales/rs.ts b/src/shared/translations/locales/rs.ts similarity index 75% rename from src/translations/locales/rs.ts rename to src/shared/translations/locales/rs.ts index fb55712..3de82bd 100644 --- a/src/translations/locales/rs.ts +++ b/src/shared/translations/locales/rs.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const rs: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -16,6 +16,10 @@ export const rs: PluginDefaultTranslationsObject = { 'Уз `locale=all`, поставите на false да би се локализована поља задржала као објекти по локалитету. Подразумевано true.', paramLocale: 'Локалитет који се враћа, или `all` за све локалитете. Погледајте одељак Локализација у опису API-ја.', paramFallbackLocale: 'Резервни локалитет за недостајуће локализоване вредности, или `none` за онемогућавање.', + paramValidateLocale: + 'Локалитети за проверу, или `all` за све локалитете. Поновите параметар за више од једног локалитета.', + paramComputeHierarchyPaths: + 'Поставите true да бисте израчунали путање `{{slugPath}}` и `{{titlePath}}`. Избор било ког од тих поља их такође израчунава.', schemaSelect: 'Изаберите поља која се враћају, нпр. `select[title]=true`. Изоставите да би се вратила сва.', schemaPopulate: 'Попуњавање повезаних докумената по колекцији, нпр. `populate[posts][title]=true`.', @@ -24,6 +28,10 @@ export const rs: PluginDefaultTranslationsObject = { 'Payload `where` филтер. Угнездите поље, па оператор: `where[field][equals]=value`. Комбинујте клаузуле помоћу низова `and` / `or`, нпр. `where[or][0][field][equals]=value`. Свако поље наводи само операторе важеће за његов тип.', schemaSupportedTimezones: 'Подржане временске зоне у IANA формату.', schemaPerLocale: 'Вредности по локалитету, враћају се када је `locale=all`.', + schemaHierarchySlugPath: + 'Путања slug вредности, нпр. `parent/child`. Израчунава се при читању са `computeHierarchyPaths=true` или када је изабрана. Не може се користити у `where`.', + schemaHierarchyTitlePath: + 'Путања наслова, нпр. `Parent/Child`. Израчунава се при читању са `computeHierarchyPaths=true` или када је изабрана. Не може се користити у `where`.', collectionList: 'Страничена листа докумената', collectionDoc: 'Један документ', @@ -34,6 +42,10 @@ export const rs: PluginDefaultTranslationsObject = { collectionBulkDelete: 'Резултат групног брисања', collectionCount: 'Број докумената', collectionDuplicated: 'Дуплирани документ', + validateResult: + 'Резултат провере. Ништа се не чува. Неважеће вредности поља враћају `valid: false` са грешкама, а не статус грешке.', + validateBody: + 'Подаци документа за проверу. На сачуваном документу или глобалном документу подаци се спајају преко најновијег нацрта, или сачуваног документа ако нацрта нема.', globalDoc: 'Глобални документ', @@ -45,9 +57,13 @@ export const rs: PluginDefaultTranslationsObject = { authResetPassword: 'Резултат ресетовања лозинке', authFirstRegister: 'Први корисник, креиран са токеном за аутентификацију', authInit: 'Да ли ова колекција за аутентификацију већ има кориснике', - authAccess: 'Приступ (дозволе) тренутног корисника за ову колекцију', + authAccess: 'Дозволе тренутног корисника за све колекције и глобале', + docAccess: 'Дозволе тренутног корисника за овај документ', + docAccessBody: + 'Подаци документа према којима се проверава приступ. Без њих Payload користи сачувани документ, ако постоји.', authUnlock: 'Резултат откључавања', authVerify: 'Резултат верификације', + apiKeyReveal: 'Дешифровани API кључ', versionList: 'Страничена листа верзија', versionSingle: 'Једна верзија', @@ -69,6 +85,14 @@ export const rs: PluginDefaultTranslationsObject = { uploadBody: 'Пошаљите `multipart/form-data` да отпремите датотеку (бинарни `file` део плус `_payload` део са пољима у JSON стринг формату), или `application/json` само са пољима када нема датотеке.', uploadPayloadField: 'Поља {{schema}} у JSON стринг формату. Пример: `{"alt":"A caption"}`.', + fileServe: 'Датотека', + filePartial: 'Део датотеке за `Range` захтев', + uploadInstructionsSummary: 'Преузимање упутстава за отпремање датотеке пре чувања документа', + uploadInstructionsResult: + 'Где послати бајтове датотеке и вредност `file` која се шаље уз захтев за креирање или ажурирање', + uploadStagePutSummary: 'Слање бајтова датотеке за привремено отпремање', + uploadStageDeleteSummary: 'Брисање привременог отпремања', + uploadStageResult: 'Готово, без садржаја', error400: 'Грешка валидације или упита (ValidationError, QueryError)', error401: 'Није аутентификован (AuthenticationError)', @@ -90,6 +114,8 @@ export const rs: PluginDefaultTranslationsObject = { tagAuth: 'Аутентификација', tagVersions: 'Верзије', tagJobs: 'Послови', + tagUploads: 'Отпремања', + tagAccess: 'Приступ', localizationHeading: 'Локализација', localizationNote: diff --git a/src/translations/locales/rsLatin.ts b/src/shared/translations/locales/rsLatin.ts similarity index 75% rename from src/translations/locales/rsLatin.ts rename to src/shared/translations/locales/rsLatin.ts index b5016a9..a6684a9 100644 --- a/src/translations/locales/rsLatin.ts +++ b/src/shared/translations/locales/rsLatin.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const rsLatin: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -19,6 +19,10 @@ export const rsLatin: PluginDefaultTranslationsObject = { 'Lokalitet koji se vraća, ili `all` za sve lokalitete. Pogledajte odeljak Lokalizacija u opisu API-ja.', paramFallbackLocale: 'Lokalitet na koji se vraća za nedostajuće lokalizovane vrednosti, ili `none` za onemogućavanje.', + paramValidateLocale: + 'Lokaliteti za proveru, ili `all` za sve lokalitete. Ponovite parametar za više od jednog lokaliteta.', + paramComputeHierarchyPaths: + 'Postavite true da biste izračunali putanje `{{slugPath}}` i `{{titlePath}}`. Izbor bilo kog od tih polja ih takođe izračunava.', schemaSelect: 'Izaberite polja koja se vraćaju, npr. `select[title]=true`. Izostavite da biste vratili sva.', schemaPopulate: 'Popunite povezane dokumente po kolekciji, npr. `populate[posts][title]=true`.', @@ -27,6 +31,10 @@ export const rsLatin: PluginDefaultTranslationsObject = { 'Payload `where` filter. Ugnezdite polje pa operator: `where[field][equals]=value`. Kombinujte uslove pomoću nizova `and` / `or`, npr. `where[or][0][field][equals]=value`. Svako polje navodi samo operatore koji važe za njegov tip.', schemaSupportedTimezones: 'Podržane vremenske zone u IANA formatu.', schemaPerLocale: 'Vrednosti po lokalitetu, vraćaju se kada je `locale=all`.', + schemaHierarchySlugPath: + 'Putanja slug vrednosti, npr. `parent/child`. Izračunava se pri čitanju sa `computeHierarchyPaths=true` ili kada je izabrana. Ne može se koristiti u `where`.', + schemaHierarchyTitlePath: + 'Putanja naslova, npr. `Parent/Child`. Izračunava se pri čitanju sa `computeHierarchyPaths=true` ili kada je izabrana. Ne može se koristiti u `where`.', collectionList: 'Stranicama podeljena lista dokumenata', collectionDoc: 'Jedan dokument', @@ -37,6 +45,10 @@ export const rsLatin: PluginDefaultTranslationsObject = { collectionBulkDelete: 'Rezultat grupnog brisanja', collectionCount: 'Broj dokumenata', collectionDuplicated: 'Duplirani dokument', + validateResult: + 'Rezultat provere. Ništa se ne čuva. Nevažeće vrednosti polja vraćaju `valid: false` sa greškama, a ne status greške.', + validateBody: + 'Podaci dokumenta za proveru. Na sačuvanom dokumentu ili globalnom dokumentu podaci se spajaju preko najnovijeg nacrta, ili sačuvanog dokumenta ako nacrta nema.', globalDoc: 'Globalni dokument', @@ -48,9 +60,13 @@ export const rsLatin: PluginDefaultTranslationsObject = { authResetPassword: 'Rezultat resetovanja lozinke', authFirstRegister: 'Prvi korisnik, kreiran sa tokenom za autentifikaciju', authInit: 'Da li ova kolekcija za autentifikaciju već ima korisnike', - authAccess: 'Pristup (dozvole) trenutnog korisnika za ovu kolekciju', + authAccess: 'Dozvole trenutnog korisnika za sve kolekcije i globale', + docAccess: 'Dozvole trenutnog korisnika za ovaj dokument', + docAccessBody: + 'Podaci dokumenta prema kojima se proverava pristup. Bez njih Payload koristi sačuvani dokument, ako postoji.', authUnlock: 'Rezultat otključavanja', authVerify: 'Rezultat verifikacije', + apiKeyReveal: 'Dešifrovani API ključ', versionList: 'Stranicama podeljena lista verzija', versionSingle: 'Jedna verzija', @@ -72,6 +88,14 @@ export const rsLatin: PluginDefaultTranslationsObject = { uploadBody: 'Pošaljite `multipart/form-data` za otpremanje datoteke (binarni `file` deo plus `_payload` deo sa poljima pretvorenim u JSON string), ili `application/json` samo sa poljima kada nema datoteke.', uploadPayloadField: '{{schema}} polja pretvorena u JSON string. Primer: `{"alt":"A caption"}`.', + fileServe: 'Datoteka', + filePartial: 'Deo datoteke za `Range` zahtev', + uploadInstructionsSummary: 'Preuzimanje uputstava za otpremanje datoteke pre čuvanja dokumenta', + uploadInstructionsResult: + 'Gde poslati bajtove datoteke i vrednost `file` koja se šalje uz zahtev za kreiranje ili ažuriranje', + uploadStagePutSummary: 'Slanje bajtova datoteke za privremeno otpremanje', + uploadStageDeleteSummary: 'Brisanje privremenog otpremanja', + uploadStageResult: 'Gotovo, bez sadržaja', error400: 'Greška u validaciji ili upitu (ValidationError, QueryError)', error401: 'Niste autentifikovani (AuthenticationError)', @@ -93,6 +117,8 @@ export const rsLatin: PluginDefaultTranslationsObject = { tagAuth: 'Autentifikacija', tagVersions: 'Verzije', tagJobs: 'Poslovi', + tagUploads: 'Otpremanja', + tagAccess: 'Pristup', localizationHeading: 'Lokalizacija', localizationNote: diff --git a/src/translations/locales/ru.ts b/src/shared/translations/locales/ru.ts similarity index 74% rename from src/translations/locales/ru.ts rename to src/shared/translations/locales/ru.ts index 27e136c..8919dbb 100644 --- a/src/translations/locales/ru.ts +++ b/src/shared/translations/locales/ru.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const ru: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -16,6 +16,9 @@ export const ru: PluginDefaultTranslationsObject = { 'При `locale=all` установите false, чтобы локализованные поля оставались объектами по локалям. По умолчанию true.', paramLocale: 'Локаль для возврата или `all` для всех локалей. См. раздел «Локализация» в описании API.', paramFallbackLocale: 'Локаль для подстановки при отсутствии локализованных значений или `none`, чтобы отключить.', + paramValidateLocale: 'Локали для проверки или `all` для всех локалей. Для нескольких локалей повторите параметр.', + paramComputeHierarchyPaths: + 'Передайте true, чтобы вычислить пути `{{slugPath}}` и `{{titlePath}}`. Выбор любого из этих полей тоже их вычисляет.', schemaSelect: 'Выберите возвращаемые поля, например `select[title]=true`. Опустите, чтобы вернуть все.', schemaPopulate: 'Подгрузка связанных документов по коллекциям, например `populate[posts][title]=true`.', @@ -24,6 +27,10 @@ export const ru: PluginDefaultTranslationsObject = { 'Фильтр Payload `where`. Вложите поле, затем оператор: `where[field][equals]=value`. Объединяйте условия с помощью массивов `and` / `or`, например `where[or][0][field][equals]=value`. Для каждого поля перечислены только операторы, допустимые для его типа.', schemaSupportedTimezones: 'Поддерживаемые часовые пояса в формате IANA.', schemaPerLocale: 'Значения по локалям, возвращаемые при `locale=all`.', + schemaHierarchySlugPath: + 'Путь из slug, например `parent/child`. Вычисляется при чтении с `computeHierarchyPaths=true` или если поле выбрано. Нельзя использовать в `where`.', + schemaHierarchyTitlePath: + 'Путь из заголовков, например `Parent/Child`. Вычисляется при чтении с `computeHierarchyPaths=true` или если поле выбрано. Нельзя использовать в `where`.', collectionList: 'Постраничный список документов', collectionDoc: 'Один документ', @@ -34,6 +41,10 @@ export const ru: PluginDefaultTranslationsObject = { collectionBulkDelete: 'Результат массового удаления', collectionCount: 'Количество документов', collectionDuplicated: 'Дублированный документ', + validateResult: + 'Результат проверки. Ничего не сохраняется. Недопустимые значения полей возвращают `valid: false` с ошибками, а не статус ошибки.', + validateBody: + 'Данные документа для проверки. Для сохранённого документа или глобального документа данные накладываются на последний черновик, а если черновика нет, на сохранённый документ.', globalDoc: 'Глобальный документ', @@ -45,9 +56,13 @@ export const ru: PluginDefaultTranslationsObject = { authResetPassword: 'Результат сброса пароля', authFirstRegister: 'Первый пользователь, созданный с токеном аутентификации', authInit: 'Есть ли уже пользователи в этой коллекции аутентификации', - authAccess: 'Доступ (права) текущего пользователя к этой коллекции', + authAccess: 'Права текущего пользователя для всех коллекций и глобальных данных', + docAccess: 'Права текущего пользователя для этого документа', + docAccessBody: + 'Данные документа, по которым проверяется доступ. Без них Payload использует сохранённый документ, если он есть.', authUnlock: 'Результат разблокировки', authVerify: 'Результат подтверждения', + apiKeyReveal: 'Расшифрованный API-ключ', versionList: 'Постраничный список версий', versionSingle: 'Одна версия', @@ -69,6 +84,14 @@ export const ru: PluginDefaultTranslationsObject = { uploadBody: 'Отправьте `multipart/form-data` для загрузки файла (бинарная часть `file` плюс часть `_payload` с полями в виде JSON-строки) или `application/json` только с полями, если файла нет.', uploadPayloadField: 'Поля {{schema}} в виде JSON-строки. Пример: `{"alt":"A caption"}`.', + fileServe: 'Файл', + filePartial: 'Часть файла для запроса с `Range`', + uploadInstructionsSummary: 'Получить инструкции для загрузки файла до сохранения документа', + uploadInstructionsResult: + 'Куда отправить байты файла и какое значение `file` передать в запросе на создание или обновление', + uploadStagePutSummary: 'Отправить байты файла для временной загрузки', + uploadStageDeleteSummary: 'Удалить временную загрузку', + uploadStageResult: 'Готово, без содержимого', error400: 'Ошибка валидации или запроса (ValidationError, QueryError)', error401: 'Не аутентифицирован (AuthenticationError)', @@ -90,6 +113,8 @@ export const ru: PluginDefaultTranslationsObject = { tagAuth: 'Аутентификация', tagVersions: 'Версии', tagJobs: 'Задачи', + tagUploads: 'Загрузки', + tagAccess: 'Доступ', localizationHeading: 'Локализация', localizationNote: diff --git a/src/translations/locales/sk.ts b/src/shared/translations/locales/sk.ts similarity index 76% rename from src/translations/locales/sk.ts rename to src/shared/translations/locales/sk.ts index 0955189..5ddd3f3 100644 --- a/src/translations/locales/sk.ts +++ b/src/shared/translations/locales/sk.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const sk: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -19,6 +19,9 @@ export const sk: PluginDefaultTranslationsObject = { 'Lokalita, ktorá sa má vrátiť, alebo `all` pre všetky lokality. Pozri sekciu Lokalizácia v popise API.', paramFallbackLocale: 'Lokalita, na ktorú sa použije záloha pri chýbajúcich lokalizovaných hodnotách, alebo `none` pre vypnutie.', + paramValidateLocale: 'Lokality na overenie, alebo `all` pre všetky lokality. Pre viac lokalít parameter zopakujte.', + paramComputeHierarchyPaths: + 'Nastavte true na výpočet ciest `{{slugPath}}` a `{{titlePath}}`. Výber ktoréhokoľvek z týchto polí ich vypočíta tiež.', schemaSelect: 'Vyberte polia, ktoré sa majú vrátiť, napr. `select[title]=true`. Vynechajte pre vrátenie všetkých.', schemaPopulate: 'Naplniť súvisiace dokumenty podľa kolekcie, napr. `populate[posts][title]=true`.', @@ -28,6 +31,10 @@ export const sk: PluginDefaultTranslationsObject = { 'Filter `where` v Payloade. Vnorte pole a potom operátor: `where[field][equals]=value`. Klauzuly kombinujte pomocou polí `and` / `or`, napr. `where[or][0][field][equals]=value`. Každé pole uvádza iba operátory platné pre jeho typ.', schemaSupportedTimezones: 'Podporované časové pásma vo formáte IANA.', schemaPerLocale: 'Hodnoty podľa jednotlivých lokalít, vrátené pri `locale=all`.', + schemaHierarchySlugPath: + 'Cesta zo slugov, napr. `parent/child`. Počíta sa pri čítaní s `computeHierarchyPaths=true` alebo pri výbere. Nedá sa použiť vo `where`.', + schemaHierarchyTitlePath: + 'Cesta z názvov, napr. `Parent/Child`. Počíta sa pri čítaní s `computeHierarchyPaths=true` alebo pri výbere. Nedá sa použiť vo `where`.', collectionList: 'Stránkovaný zoznam dokumentov', collectionDoc: 'Jeden dokument', @@ -38,6 +45,10 @@ export const sk: PluginDefaultTranslationsObject = { collectionBulkDelete: 'Výsledok hromadného odstránenia', collectionCount: 'Počet dokumentov', collectionDuplicated: 'Duplikovaný dokument', + validateResult: + 'Výsledok overenia. Nič sa neukladá. Neplatné hodnoty polí vrátia `valid: false` s chybami, nie chybový stav.', + validateBody: + 'Údaje dokumentu na overenie. Pri uloženom dokumente alebo globálnom dokumente sa údaje zlúčia s najnovším konceptom, prípadne s uloženým dokumentom, ak koncept neexistuje.', globalDoc: 'Globálny dokument', @@ -49,9 +60,13 @@ export const sk: PluginDefaultTranslationsObject = { authResetPassword: 'Výsledok obnovenia hesla', authFirstRegister: 'Prvý používateľ vytvorený s autentifikačným tokenom', authInit: 'Či táto autentifikačná kolekcia už má nejakých používateľov', - authAccess: 'Prístup (oprávnenia) aktuálneho používateľa pre túto kolekciu', + authAccess: 'Oprávnenia aktuálneho používateľa pre všetky kolekcie a globály', + docAccess: 'Oprávnenia aktuálneho používateľa pre tento dokument', + docAccessBody: + 'Údaje dokumentu, voči ktorým sa overuje prístup. Bez nich Payload použije uložený dokument, ak existuje.', authUnlock: 'Výsledok odomknutia', authVerify: 'Výsledok overenia', + apiKeyReveal: 'Dešifrovaný API kľúč', versionList: 'Stránkovaný zoznam verzií', versionSingle: 'Jedna verzia', @@ -73,6 +88,14 @@ export const sk: PluginDefaultTranslationsObject = { uploadBody: 'Pre nahranie súboru odošlite `multipart/form-data` (binárnu časť `file` plus časť `_payload` s poliami serializovanými do JSON), alebo `application/json` len s poliami, ak súbor nie je.', uploadPayloadField: 'Polia {{schema}} serializované do JSON. Príklad: `{\\"alt\\":\\"A caption\\"}`.', + fileServe: 'Súbor', + filePartial: 'Časť súboru pre požiadavku `Range`', + uploadInstructionsSummary: 'Získať pokyny na nahranie súboru pred uložením dokumentu', + uploadInstructionsResult: + 'Kam poslať bajty súboru a hodnota `file`, ktorá sa pošle s požiadavkou na vytvorenie alebo úpravu', + uploadStagePutSummary: 'Odoslať bajty súboru pre dočasné nahranie', + uploadStageDeleteSummary: 'Odstrániť dočasné nahranie', + uploadStageResult: 'Hotovo, bez obsahu', error400: 'Chyba validácie alebo dotazu (ValidationError, QueryError)', error401: 'Neprihlásený (AuthenticationError)', @@ -94,6 +117,8 @@ export const sk: PluginDefaultTranslationsObject = { tagAuth: 'Autentifikácia', tagVersions: 'Verzie', tagJobs: 'Úlohy', + tagUploads: 'Nahrávanie', + tagAccess: 'Prístup', localizationHeading: 'Lokalizácia', localizationNote: diff --git a/src/translations/locales/sl.ts b/src/shared/translations/locales/sl.ts similarity index 75% rename from src/translations/locales/sl.ts rename to src/shared/translations/locales/sl.ts index 728f24a..823957a 100644 --- a/src/translations/locales/sl.ts +++ b/src/shared/translations/locales/sl.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const sl: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -18,6 +18,9 @@ export const sl: PluginDefaultTranslationsObject = { paramLocale: 'Jezik, ki naj se vrne, ali `all` za vse jezike. Glejte razdelek Lokalizacija v opisu API-ja.', paramFallbackLocale: 'Jezik, na katerega naj se zateče pri manjkajočih lokaliziranih vrednostih, ali `none` za onemogočenje.', + paramValidateLocale: 'Jeziki za preverjanje ali `all` za vse jezike. Za več jezikov parameter ponovite.', + paramComputeHierarchyPaths: + 'Nastavite true za izračun poti `{{slugPath}}` in `{{titlePath}}`. Izbira katerega koli od teh polj ju prav tako izračuna.', schemaSelect: 'Izberite polja, ki naj se vrnejo, npr. `select[title]=true`. Izpustite za vrnitev vseh.', schemaPopulate: 'Napolnite povezane dokumente po posamezni zbirki, npr. `populate[posts][title]=true`.', @@ -26,6 +29,10 @@ export const sl: PluginDefaultTranslationsObject = { 'Payloadov filter `where`. Ugnezdite polje, nato operator: `where[field][equals]=value`. Stavke združite s polji `and` / `or`, npr. `where[or][0][field][equals]=value`. Vsako polje navaja le operatorje, veljavne za njegov tip.', schemaSupportedTimezones: 'Podprti časovni pasovi v zapisu IANA.', schemaPerLocale: 'Vrednosti po posameznem jeziku, vrnjene pri `locale=all`.', + schemaHierarchySlugPath: + 'Pot iz slugov, npr. `parent/child`. Izračuna se pri branju z `computeHierarchyPaths=true` ali ko je izbrana. Ni je mogoče uporabiti v `where`.', + schemaHierarchyTitlePath: + 'Pot iz naslovov, npr. `Parent/Child`. Izračuna se pri branju z `computeHierarchyPaths=true` ali ko je izbrana. Ni je mogoče uporabiti v `where`.', collectionList: 'Stranjen seznam dokumentov', collectionDoc: 'Posamezen dokument', @@ -36,6 +43,10 @@ export const sl: PluginDefaultTranslationsObject = { collectionBulkDelete: 'Rezultat množičnega brisanja', collectionCount: 'Število dokumentov', collectionDuplicated: 'Podvojeni dokument', + validateResult: + 'Rezultat preverjanja. Nič se ne shrani. Neveljavne vrednosti polj vrnejo `valid: false` z napakami, ne statusa napake.', + validateBody: + 'Podatki dokumenta za preverjanje. Pri shranjenem dokumentu ali globalnem dokumentu se podatki združijo z zadnjim osnutkom ali s shranjenim dokumentom, če osnutka ni.', globalDoc: 'Globalni dokument', @@ -47,9 +58,13 @@ export const sl: PluginDefaultTranslationsObject = { authResetPassword: 'Rezultat ponastavitve gesla', authFirstRegister: 'Prvi uporabnik, ustvarjen z žetonom za overjanje', authInit: 'Ali ta zbirka za overjanje že vsebuje kakšne uporabnike', - authAccess: 'Dostop (dovoljenja) trenutnega uporabnika za to zbirko', + authAccess: 'Dovoljenja trenutnega uporabnika za vse zbirke in globalne nastavitve', + docAccess: 'Dovoljenja trenutnega uporabnika za ta dokument', + docAccessBody: + 'Podatki dokumenta, glede na katere se preveri dostop. Brez njih Payload uporabi shranjeni dokument, če obstaja.', authUnlock: 'Rezultat odklepanja', authVerify: 'Rezultat preverjanja', + apiKeyReveal: 'Dešifrirani ključ API', versionList: 'Stranjen seznam različic', versionSingle: 'Posamezna različica', @@ -71,6 +86,14 @@ export const sl: PluginDefaultTranslationsObject = { uploadBody: 'Za nalaganje datoteke pošljite `multipart/form-data` (binarni del `file` ter del `_payload` s polji, pretvorjenimi v niz JSON) ali `application/json` zgolj s polji, kadar datoteke ni.', uploadPayloadField: 'Polja {{schema}}, pretvorjena v niz JSON. Primer: `{\\"alt\\":\\"A caption\\"}`.', + fileServe: 'Datoteka', + filePartial: 'Del datoteke za zahtevo `Range`', + uploadInstructionsSummary: 'Pridobi navodila za nalaganje datoteke pred shranjevanjem dokumenta', + uploadInstructionsResult: + 'Kam poslati bajte datoteke in vrednost `file`, ki jo pošljete z zahtevo za ustvarjanje ali posodobitev', + uploadStagePutSummary: 'Pošlji bajte datoteke za začasno nalaganje', + uploadStageDeleteSummary: 'Izbriši začasno nalaganje', + uploadStageResult: 'Končano, brez vsebine', error400: 'Napaka pri preverjanju ali poizvedbi (ValidationError, QueryError)', error401: 'Niste overjeni (AuthenticationError)', @@ -92,6 +115,8 @@ export const sl: PluginDefaultTranslationsObject = { tagAuth: 'Overjanje', tagVersions: 'Različice', tagJobs: 'Opravila', + tagUploads: 'Nalaganja', + tagAccess: 'Dostop', localizationHeading: 'Lokalizacija', localizationNote: diff --git a/src/translations/locales/sv.ts b/src/shared/translations/locales/sv.ts similarity index 74% rename from src/translations/locales/sv.ts rename to src/shared/translations/locales/sv.ts index 3de7549..e8862b5 100644 --- a/src/translations/locales/sv.ts +++ b/src/shared/translations/locales/sv.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const sv: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -18,6 +18,10 @@ export const sv: PluginDefaultTranslationsObject = { paramLocale: 'Språk att returnera, eller `all` för alla språk. Se avsnittet Lokalisering i API-beskrivningen.', paramFallbackLocale: 'Språk att falla tillbaka på för saknade lokaliserade värden, eller `none` för att inaktivera.', + paramValidateLocale: + 'Språk som ska valideras, eller `all` för alla språk. Upprepa parametern för fler än ett språk.', + paramComputeHierarchyPaths: + 'Ange true för att beräkna sökvägarna `{{slugPath}}` och `{{titlePath}}`. Att välja något av fälten beräknar dem också.', schemaSelect: 'Välj fält att returnera, t.ex. `select[title]=true`. Utelämna för att returnera alla.', schemaPopulate: 'Populera relaterade dokument per samling, t.ex. `populate[posts][title]=true`.', @@ -26,6 +30,10 @@ export const sv: PluginDefaultTranslationsObject = { 'Payload `where`-filter. Nästla ett fält och sedan en operator: `where[field][equals]=value`. Kombinera villkor med arrayerna `and` / `or`, t.ex. `where[or][0][field][equals]=value`. Varje fält listar endast de operatorer som är giltiga för dess typ.', schemaSupportedTimezones: 'Tidszoner som stöds, i IANA-format.', schemaPerLocale: 'Värden per språk, returneras när `locale=all`.', + schemaHierarchySlugPath: + 'Sökväg av slugs, t.ex. `parent/child`. Beräknas vid läsning med `computeHierarchyPaths=true` eller när fältet väljs. Kan inte användas i `where`.', + schemaHierarchyTitlePath: + 'Sökväg av titlar, t.ex. `Parent/Child`. Beräknas vid läsning med `computeHierarchyPaths=true` eller när fältet väljs. Kan inte användas i `where`.', collectionList: 'Paginerad lista över dokument', collectionDoc: 'Ett enskilt dokument', @@ -36,6 +44,10 @@ export const sv: PluginDefaultTranslationsObject = { collectionBulkDelete: 'Resultat av massborttagning', collectionCount: 'Antal dokument', collectionDuplicated: 'Det duplicerade dokumentet', + validateResult: + 'Valideringsresultat. Inget sparas. Ogiltiga fältvärden returnerar `valid: false` med felen, inte en felstatus.', + validateBody: + 'Dokumentdata som ska valideras. På ett sparat dokument eller en global slås datan samman över det senaste utkastet, eller det sparade dokumentet om det inte finns något utkast.', globalDoc: 'Det globala dokumentet', @@ -47,9 +59,13 @@ export const sv: PluginDefaultTranslationsObject = { authResetPassword: 'Resultat av lösenordsåterställning', authFirstRegister: 'Den första användaren, skapad med en autentiseringstoken', authInit: 'Huruvida den här autentiseringssamlingen har några användare ännu', - authAccess: 'Den aktuella användarens åtkomst (behörigheter) för den här samlingen', + authAccess: 'Den aktuella användarens behörigheter för alla samlingar och globaler', + docAccess: 'Den aktuella användarens behörigheter för detta dokument', + docAccessBody: + 'Dokumentdata som åtkomsten kontrolleras mot. Utan dem använder Payload det sparade dokumentet, om det finns.', authUnlock: 'Resultat av upplåsning', authVerify: 'Verifieringsresultat', + apiKeyReveal: 'Den dekrypterade API-nyckeln', versionList: 'Paginerad lista över versioner', versionSingle: 'En enskild version', @@ -71,6 +87,14 @@ export const sv: PluginDefaultTranslationsObject = { uploadBody: 'Skicka `multipart/form-data` för att ladda upp en fil (en binär `file`-del plus en `_payload`-del med de JSON-strängifierade fälten), eller `application/json` med enbart fälten när det inte finns någon fil.', uploadPayloadField: 'JSON-strängifierade {{schema}}-fält. Exempel: `{\\"alt\\":\\"A caption\\"}`.', + fileServe: 'Filen', + filePartial: 'En del av filen vid en `Range`-begäran', + uploadInstructionsSummary: 'Hämta instruktioner för att ladda upp en fil innan dokumentet sparas', + uploadInstructionsResult: + 'Vart filens byte ska skickas och vilket `file`-värde som ska skickas med skapa- eller uppdatera-begäran', + uploadStagePutSummary: 'Skicka filens byte för en tillfällig uppladdning', + uploadStageDeleteSummary: 'Ta bort en tillfällig uppladdning', + uploadStageResult: 'Klart, inget innehåll', error400: 'Validerings- eller frågefel (ValidationError, QueryError)', error401: 'Inte autentiserad (AuthenticationError)', @@ -92,6 +116,8 @@ export const sv: PluginDefaultTranslationsObject = { tagAuth: 'Autentisering', tagVersions: 'Versioner', tagJobs: 'Jobb', + tagUploads: 'Uppladdningar', + tagAccess: 'Åtkomst', localizationHeading: 'Lokalisering', localizationNote: diff --git a/src/translations/locales/ta.ts b/src/shared/translations/locales/ta.ts similarity index 73% rename from src/translations/locales/ta.ts rename to src/shared/translations/locales/ta.ts index 193a42f..014a2d3 100644 --- a/src/translations/locales/ta.ts +++ b/src/shared/translations/locales/ta.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const ta: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -17,6 +17,10 @@ export const ta: PluginDefaultTranslationsObject = { paramLocale: 'திருப்பி அளிக்க வேண்டிய மொழியிடம், அல்லது ஒவ்வொரு மொழியிடத்திற்கும் `all`. API விளக்கத்தில் உள்ளூராக்கம் (Localization) பகுதியைப் பார்க்கவும்.', paramFallbackLocale: 'விடுபட்ட உள்ளூராக்கப்பட்ட மதிப்புகளுக்குப் பின்வாங்கப் பயன்படும் மொழியிடம், அல்லது முடக்க `none`.', + paramValidateLocale: + 'சரிபார்க்க வேண்டிய மொழியிடங்கள், அல்லது ஒவ்வொரு மொழியிடத்திற்கும் `all`. ஒன்றுக்கு மேற்பட்ட மொழியிடங்களுக்கு அளவுருவை மீண்டும் குறிப்பிடவும்.', + paramComputeHierarchyPaths: + '`{{slugPath}}` மற்றும் `{{titlePath}}` பாதைகளைக் கணக்கிட true என அமைக்கவும். இந்தப் புலங்களில் ஒன்றைத் தேர்ந்தெடுத்தாலும் அவை கணக்கிடப்படும்.', schemaSelect: 'திருப்பி அளிக்க வேண்டிய புலங்களைத் தேர்வுசெய்யவும், எ.கா. `select[title]=true`. அனைத்தையும் திருப்பி அளிக்க இதைத் தவிர்க்கவும்.', @@ -26,6 +30,10 @@ export const ta: PluginDefaultTranslationsObject = { 'Payload `where` வடிகட்டி. ஒரு புலத்தை, பின் ஒரு செயற்குறியை உள்ளமைக்கவும்: `where[field][equals]=value`. `and` / `or` வரிசைகளுடன் விதிகளை இணைக்கவும், எ.கா. `where[or][0][field][equals]=value`. ஒவ்வொரு புலமும் அதன் வகைக்குப் பொருந்தும் செயற்குறிகளை மட்டுமே பட்டியலிடுகிறது.', schemaSupportedTimezones: 'IANA வடிவத்தில் ஆதரிக்கப்படும் நேர மண்டலங்கள்.', schemaPerLocale: '`locale=all` எனும்போது திருப்பி அளிக்கப்படும், ஒவ்வொரு மொழியிடத்திற்குமான மதிப்புகள்.', + schemaHierarchySlugPath: + 'ஸ்லக் பாதை, எ.கா. `parent/child`. `computeHierarchyPaths=true` உடன் படிக்கும்போது அல்லது தேர்ந்தெடுக்கும்போது கணக்கிடப்படும். `where`-இல் பயன்படுத்த முடியாது.', + schemaHierarchyTitlePath: + 'தலைப்புப் பாதை, எ.கா. `Parent/Child`. `computeHierarchyPaths=true` உடன் படிக்கும்போது அல்லது தேர்ந்தெடுக்கும்போது கணக்கிடப்படும். `where`-இல் பயன்படுத்த முடியாது.', collectionList: 'ஆவணங்களின் பக்கமிடப்பட்ட பட்டியல்', collectionDoc: 'ஒரு ஆவணம்', @@ -36,6 +44,10 @@ export const ta: PluginDefaultTranslationsObject = { collectionBulkDelete: 'மொத்த நீக்கம் முடிவு', collectionCount: 'ஆவண எண்ணிக்கை', collectionDuplicated: 'நகலெடுக்கப்பட்ட ஆவணம்', + validateResult: + 'சரிபார்ப்பு முடிவு. எதுவும் சேமிக்கப்படாது. தவறான புல மதிப்புகள் பிழை நிலைக்குப் பதிலாக பிழைகளுடன் `valid: false` என்பதைத் திருப்பி அளிக்கும்.', + validateBody: + 'சரிபார்க்க வேண்டிய ஆவணத் தரவு. சேமிக்கப்பட்ட ஆவணம் அல்லது உலகளாவிய ஆவணத்தில், தரவு சமீபத்திய வரைவின் மேல் இணைக்கப்படும்; வரைவு இல்லையெனில் சேமிக்கப்பட்ட ஆவணத்தின் மேல் இணைக்கப்படும்.', globalDoc: 'உலகளாவிய ஆவணம்', @@ -47,9 +59,12 @@ export const ta: PluginDefaultTranslationsObject = { authResetPassword: 'கடவுச்சொல் மீட்டமைப்பு முடிவு', authFirstRegister: 'அங்கீகார டோக்கனுடன் உருவாக்கப்பட்ட முதல் பயனர்', authInit: 'இந்த அங்கீகாரத் தொகுப்பில் இதுவரை ஏதேனும் பயனர்கள் உள்ளனரா என்பது', - authAccess: 'இந்தத் தொகுப்பிற்கான தற்போதைய பயனரின் அணுகல் (அனுமதிகள்)', + authAccess: 'அனைத்து தொகுப்புகள் மற்றும் உலகளாவியவைகளுக்கான தற்போதைய பயனரின் அனுமதிகள்', + docAccess: 'இந்த ஆவணத்துக்கான தற்போதைய பயனரின் அனுமதிகள்', + docAccessBody: 'அணுகலைச் சரிபார்க்க வேண்டிய ஆவணத் தரவு. இல்லையெனில், சேமிக்கப்பட்ட ஆவணம் இருந்தால் Payload அதைப் பயன்படுத்தும்.', authUnlock: 'திறப்பு முடிவு', authVerify: 'சரிபார்ப்பு முடிவு', + apiKeyReveal: 'மறைகுறியாக்கம் நீக்கப்பட்ட API விசை', versionList: 'பதிப்புகளின் பக்கமிடப்பட்ட பட்டியல்', versionSingle: 'ஒரு பதிப்பு', @@ -71,6 +86,14 @@ export const ta: PluginDefaultTranslationsObject = { uploadBody: 'ஒரு கோப்பைப் பதிவேற்ற `multipart/form-data` ஐ அனுப்பவும் (ஒரு பைனரி `file` பகுதி மற்றும் JSON-stringified புலங்களைக் கொண்ட `_payload` பகுதி), அல்லது கோப்பு இல்லாதபோது புலங்களை மட்டும் கொண்ட `application/json` ஐ அனுப்பவும்.', uploadPayloadField: 'JSON-stringified {{schema}} புலங்கள். எடுத்துக்காட்டு: `{"alt":"A caption"}`.', + fileServe: 'கோப்பு', + filePartial: '`Range` கோரிக்கைக்கான கோப்பின் ஒரு பகுதி', + uploadInstructionsSummary: 'ஆவணத்தைச் சேமிக்கும் முன் கோப்பைப் பதிவேற்றுவதற்கான வழிமுறைகளைப் பெறவும்', + uploadInstructionsResult: + 'கோப்பின் பைட்டுகளை எங்கு அனுப்ப வேண்டும் மற்றும் உருவாக்கு அல்லது புதுப்பி கோரிக்கையுடன் அனுப்ப வேண்டிய `file` மதிப்பு', + uploadStagePutSummary: 'தற்காலிகப் பதிவேற்றத்திற்குக் கோப்பின் பைட்டுகளை அனுப்பவும்', + uploadStageDeleteSummary: 'தற்காலிகப் பதிவேற்றத்தை நீக்கவும்', + uploadStageResult: 'முடிந்தது, உள்ளடக்கம் இல்லை', error400: 'சரிபார்ப்பு அல்லது வினவல் பிழை (ValidationError, QueryError)', error401: 'அங்கீகரிக்கப்படவில்லை (AuthenticationError)', @@ -92,6 +115,8 @@ export const ta: PluginDefaultTranslationsObject = { tagAuth: 'அங்கீகாரம்', tagVersions: 'பதிப்புகள்', tagJobs: 'பணிகள்', + tagUploads: 'பதிவேற்றங்கள்', + tagAccess: 'அணுகல்', localizationHeading: 'உள்ளூராக்கம்', localizationNote: diff --git a/src/translations/locales/th.ts b/src/shared/translations/locales/th.ts similarity index 75% rename from src/translations/locales/th.ts rename to src/shared/translations/locales/th.ts index f6c5338..5ae41de 100644 --- a/src/translations/locales/th.ts +++ b/src/shared/translations/locales/th.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const th: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -16,6 +16,9 @@ export const th: PluginDefaultTranslationsObject = { 'เมื่อใช้ `locale=all` ให้ตั้งค่าเป็น false เพื่อเก็บฟิลด์ที่แปลภาษาไว้เป็นออบเจ็กต์แยกตามแต่ละโลแคล ค่าเริ่มต้นคือ true', paramLocale: 'โลแคลที่จะคืนค่า หรือ `all` เพื่อรับทุกโลแคล ดูที่หัวข้อ Localization ในคำอธิบาย API', paramFallbackLocale: 'โลแคลที่จะใช้แทนเมื่อไม่มีค่าที่แปลภาษาไว้ หรือ `none` เพื่อปิดการใช้งาน', + paramValidateLocale: 'โลแคลที่จะตรวจสอบ หรือ `all` เพื่อรับทุกโลแคล ระบุพารามิเตอร์ซ้ำหากมีมากกว่าหนึ่งโลแคล', + paramComputeHierarchyPaths: + 'ตั้งเป็น true เพื่อคำนวณเส้นทาง `{{slugPath}}` และ `{{titlePath}}` การเลือกฟิลด์ใดฟิลด์หนึ่งก็คำนวณเช่นกัน', schemaSelect: 'เลือกฟิลด์ที่จะคืนค่า เช่น `select[title]=true` หากไม่ระบุจะคืนค่าทั้งหมด', schemaPopulate: 'ดึงข้อมูลเอกสารที่เกี่ยวข้องในแต่ละคอลเลกชัน เช่น `populate[posts][title]=true`', @@ -24,6 +27,10 @@ export const th: PluginDefaultTranslationsObject = { 'ตัวกรอง `where` ของ Payload ซ้อนฟิลด์แล้วตามด้วยตัวดำเนินการ: `where[field][equals]=value` รวมหลายเงื่อนไขด้วยอาเรย์ `and` / `or` เช่น `where[or][0][field][equals]=value` แต่ละฟิลด์จะแสดงเฉพาะตัวดำเนินการที่ใช้ได้กับชนิดของฟิลด์นั้น', schemaSupportedTimezones: 'เขตเวลาที่รองรับในรูปแบบ IANA', schemaPerLocale: 'ค่าแยกตามแต่ละโลแคล จะคืนค่าเมื่อใช้ `locale=all`', + schemaHierarchySlugPath: + 'เส้นทางสลัก เช่น `parent/child` คำนวณเมื่ออ่านพร้อม `computeHierarchyPaths=true` หรือเมื่อถูกเลือก ใช้ใน `where` ไม่ได้', + schemaHierarchyTitlePath: + 'เส้นทางชื่อเรื่อง เช่น `Parent/Child` คำนวณเมื่ออ่านพร้อม `computeHierarchyPaths=true` หรือเมื่อถูกเลือก ใช้ใน `where` ไม่ได้', collectionList: 'รายการเอกสารแบบแบ่งหน้า', collectionDoc: 'เอกสารหนึ่งรายการ', @@ -34,6 +41,9 @@ export const th: PluginDefaultTranslationsObject = { collectionBulkDelete: 'ผลการลบแบบกลุ่ม', collectionCount: 'จำนวนเอกสาร', collectionDuplicated: 'เอกสารที่ทำสำเนา', + validateResult: 'ผลการตรวจสอบ ไม่มีการบันทึกใดๆ ค่าของฟิลด์ที่ไม่ถูกต้องจะคืนค่า `valid: false` พร้อมข้อผิดพลาด ไม่ใช่สถานะข้อผิดพลาด', + validateBody: + 'ข้อมูลเอกสารที่จะตรวจสอบ สำหรับเอกสารที่บันทึกไว้หรือเอกสาร global ข้อมูลจะถูกรวมทับแบบร่างล่าสุด หรือเอกสารที่บันทึกไว้หากไม่มีแบบร่าง', globalDoc: 'เอกสาร global', @@ -45,9 +55,12 @@ export const th: PluginDefaultTranslationsObject = { authResetPassword: 'ผลการรีเซ็ตรหัสผ่าน', authFirstRegister: 'ผู้ใช้คนแรกที่สร้างขึ้นพร้อมกับโทเค็นการยืนยันตัวตน', authInit: 'ระบุว่าคอลเลกชันการยืนยันตัวตนนี้มีผู้ใช้อยู่แล้วหรือยัง', - authAccess: 'สิทธิ์การเข้าถึง (permissions) ของผู้ใช้ปัจจุบันสำหรับคอลเลกชันนี้', + authAccess: 'สิทธิ์ของผู้ใช้ปัจจุบันสำหรับทุกคอลเลกชันและ Globals', + docAccess: 'สิทธิ์ของผู้ใช้ปัจจุบันสำหรับเอกสารนี้', + docAccessBody: 'ข้อมูลเอกสารที่ใช้ตรวจสอบสิทธิ์ หากไม่ระบุ Payload จะใช้เอกสารที่บันทึกไว้ ถ้ามี', authUnlock: 'ผลการปลดล็อก', authVerify: 'ผลการยืนยัน', + apiKeyReveal: 'API key ที่ถอดรหัสแล้ว', versionList: 'รายการเวอร์ชันแบบแบ่งหน้า', versionSingle: 'เวอร์ชันหนึ่งรายการ', @@ -69,6 +82,13 @@ export const th: PluginDefaultTranslationsObject = { uploadBody: 'ส่ง `multipart/form-data` เพื่ออัปโหลดไฟล์ (ส่วน `file` ที่เป็นไบนารี พร้อมส่วน `_payload` ที่มีฟิลด์ในรูปแบบ JSON string) หรือส่ง `application/json` พร้อมเฉพาะฟิลด์เมื่อไม่มีไฟล์', uploadPayloadField: 'ฟิลด์ {{schema}} ในรูปแบบ JSON string ตัวอย่าง: `{"alt":"A caption"}`', + fileServe: 'ไฟล์', + filePartial: 'ส่วนหนึ่งของไฟล์ สำหรับคำขอ `Range`', + uploadInstructionsSummary: 'รับคำแนะนำในการอัปโหลดไฟล์ก่อนบันทึกเอกสาร', + uploadInstructionsResult: 'ปลายทางสำหรับส่งไบต์ของไฟล์ และค่า `file` ที่ต้องส่งไปกับคำขอสร้างหรืออัปเดต', + uploadStagePutSummary: 'ส่งไบต์ของไฟล์สำหรับการอัปโหลดชั่วคราว', + uploadStageDeleteSummary: 'ลบการอัปโหลดชั่วคราว', + uploadStageResult: 'เสร็จแล้ว ไม่มีเนื้อหา', error400: 'ข้อผิดพลาดในการตรวจสอบความถูกต้องหรือการค้นหา (ValidationError, QueryError)', error401: 'ยังไม่ได้ยืนยันตัวตน (AuthenticationError)', @@ -89,6 +109,8 @@ export const th: PluginDefaultTranslationsObject = { tagAuth: 'การยืนยันตัวตน', tagVersions: 'เวอร์ชัน', tagJobs: 'งาน', + tagUploads: 'การอัปโหลด', + tagAccess: 'การเข้าถึง', localizationHeading: 'การแปลภาษา', localizationNote: diff --git a/src/translations/locales/tr.ts b/src/shared/translations/locales/tr.ts similarity index 75% rename from src/translations/locales/tr.ts rename to src/shared/translations/locales/tr.ts index 82ac751..4b7d2d1 100644 --- a/src/translations/locales/tr.ts +++ b/src/shared/translations/locales/tr.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const tr: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -18,6 +18,10 @@ export const tr: PluginDefaultTranslationsObject = { 'Döndürülecek yerel ayar veya her yerel ayar için `all`. API açıklamasındaki Yerelleştirme bölümüne bakın.', paramFallbackLocale: 'Eksik yerelleştirilmiş değerler için geri dönülecek yerel ayar veya devre dışı bırakmak için `none`.', + paramValidateLocale: + 'Doğrulanacak yerel ayarlar veya her yerel ayar için `all`. Birden fazla yerel ayar için parametreyi tekrarlayın.', + paramComputeHierarchyPaths: + '`{{slugPath}}` ve `{{titlePath}}` yollarını hesaplamak için true yapın. Bu alanlardan birini seçmek de bunları hesaplar.', schemaSelect: 'Döndürülecek alanları seçin, örn. `select[title]=true`. Tümünü döndürmek için boş bırakın.', schemaPopulate: 'İlişkili dokümanları koleksiyon başına doldurun, örn. `populate[posts][title]=true`.', @@ -26,6 +30,10 @@ export const tr: PluginDefaultTranslationsObject = { 'Payload `where` filtresi. Önce bir alanı, sonra bir operatörü yuvalayın: `where[field][equals]=value`. Yan tümceleri `and` / `or` dizileriyle birleştirin, örn. `where[or][0][field][equals]=value`. Her alan yalnızca kendi türü için geçerli operatörleri listeler.', schemaSupportedTimezones: 'IANA formatında desteklenen saat dilimleri.', schemaPerLocale: '`locale=all` kullanıldığında döndürülen yerel başına değerler.', + schemaHierarchySlugPath: + 'Slug yolu, örn. `parent/child`. Okurken `computeHierarchyPaths=true` ile ya da seçildiğinde hesaplanır. `where` içinde kullanılamaz.', + schemaHierarchyTitlePath: + 'Başlık yolu, örn. `Parent/Child`. Okurken `computeHierarchyPaths=true` ile ya da seçildiğinde hesaplanır. `where` içinde kullanılamaz.', collectionList: 'Sayfalandırılmış doküman listesi', collectionDoc: 'Tek bir doküman', @@ -36,6 +44,10 @@ export const tr: PluginDefaultTranslationsObject = { collectionBulkDelete: 'Toplu silme sonucu', collectionCount: 'Doküman sayısı', collectionDuplicated: 'Çoğaltılan doküman', + validateResult: + 'Doğrulama sonucu. Hiçbir şey kaydedilmez. Geçersiz alan değerleri hata durumu değil, hatalarla birlikte `valid: false` döndürür.', + validateBody: + 'Doğrulanacak doküman verisi. Kayıtlı bir dokümanda veya genel dokümanda veriler en son taslağın üzerine, taslak yoksa kayıtlı dokümanın üzerine birleştirilir.', globalDoc: 'Genel doküman', @@ -47,9 +59,12 @@ export const tr: PluginDefaultTranslationsObject = { authResetPassword: 'Parola sıfırlama sonucu', authFirstRegister: 'Bir auth token ile oluşturulan ilk kullanıcı', authInit: 'Bu auth koleksiyonunda henüz kullanıcı olup olmadığı', - authAccess: 'Geçerli kullanıcının bu koleksiyon için erişimi (izinleri)', + authAccess: 'Geçerli kullanıcının tüm koleksiyonlar ve globaller için izinleri', + docAccess: 'Geçerli kullanıcının bu doküman için izinleri', + docAccessBody: 'Erişimin kontrol edileceği doküman verisi. Yoksa Payload, varsa kayıtlı dokümanı kullanır.', authUnlock: 'Kilit açma sonucu', authVerify: 'Doğrulama sonucu', + apiKeyReveal: 'Şifresi çözülmüş API anahtarı', versionList: 'Sayfalandırılmış sürüm listesi', versionSingle: 'Tek bir sürüm', @@ -71,6 +86,14 @@ export const tr: PluginDefaultTranslationsObject = { uploadBody: 'Bir dosya yüklemek için `multipart/form-data` gönderin (ikili bir `file` bölümü ile JSON olarak dizgeleştirilmiş alanları içeren bir `_payload` bölümü) veya dosya yoksa yalnızca alanlarla `application/json` gönderin.', uploadPayloadField: 'JSON olarak dizgeleştirilmiş {{schema}} alanları. Örnek: `{\\"alt\\":\\"A caption\\"}`.', + fileServe: 'Dosya', + filePartial: '`Range` isteği için dosyanın bir bölümü', + uploadInstructionsSummary: 'Dokümanı kaydetmeden önce dosya yükleme talimatlarını alın', + uploadInstructionsResult: + 'Dosya baytlarının nereye gönderileceği ve oluşturma ya da güncelleme isteğiyle gönderilecek `file` değeri', + uploadStagePutSummary: 'Geçici yükleme için dosya baytlarını gönderin', + uploadStageDeleteSummary: 'Geçici yüklemeyi silin', + uploadStageResult: 'Tamamlandı, içerik yok', error400: 'Doğrulama veya sorgu hatası (ValidationError, QueryError)', error401: 'Kimlik doğrulanmadı (AuthenticationError)', @@ -92,6 +115,8 @@ export const tr: PluginDefaultTranslationsObject = { tagAuth: 'Kimlik Doğrulama', tagVersions: 'Sürümler', tagJobs: 'İşler', + tagUploads: 'Yüklemeler', + tagAccess: 'Erişim', localizationHeading: 'Yerelleştirme', localizationNote: diff --git a/src/translations/locales/uk.ts b/src/shared/translations/locales/uk.ts similarity index 74% rename from src/translations/locales/uk.ts rename to src/shared/translations/locales/uk.ts index 679c783..68753c9 100644 --- a/src/translations/locales/uk.ts +++ b/src/shared/translations/locales/uk.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const uk: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -16,6 +16,9 @@ export const uk: PluginDefaultTranslationsObject = { 'З `locale=all` встановіть false, щоб зберегти локалізовані поля як об’єкти за локалями. За замовчуванням true.', paramLocale: 'Локаль для повернення або `all` для всіх локалей. Див. розділ Localization в описі API.', paramFallbackLocale: 'Локаль для підстановки відсутніх локалізованих значень або `none`, щоб вимкнути.', + paramValidateLocale: 'Локалі для перевірки або `all` для всіх локалей. Для кількох локалей повторіть параметр.', + paramComputeHierarchyPaths: + 'Передайте true, щоб обчислити шляхи `{{slugPath}}` і `{{titlePath}}`. Вибір будь-якого з цих полів теж їх обчислює.', schemaSelect: 'Виберіть поля для повернення, напр. `select[title]=true`. Пропустіть, щоб повернути всі.', schemaPopulate: 'Заповнюйте пов’язані документи для кожної колекції, напр. `populate[posts][title]=true`.', @@ -24,6 +27,10 @@ export const uk: PluginDefaultTranslationsObject = { 'Фільтр Payload `where`. Вкладіть поле, а потім оператор: `where[field][equals]=value`. Комбінуйте умови за допомогою масивів `and` / `or`, напр. `where[or][0][field][equals]=value`. Кожне поле перелічує лише оператори, дійсні для його типу.', schemaSupportedTimezones: 'Підтримувані часові пояси у форматі IANA.', schemaPerLocale: 'Значення для кожної локалі, що повертаються, коли `locale=all`.', + schemaHierarchySlugPath: + 'Шлях зі slug, наприклад `parent/child`. Обчислюється під час читання з `computeHierarchyPaths=true` або якщо поле вибрано. Не можна використовувати в `where`.', + schemaHierarchyTitlePath: + 'Шлях із заголовків, наприклад `Parent/Child`. Обчислюється під час читання з `computeHierarchyPaths=true` або якщо поле вибрано. Не можна використовувати в `where`.', collectionList: 'Посторінковий список документів', collectionDoc: 'Окремий документ', @@ -34,6 +41,10 @@ export const uk: PluginDefaultTranslationsObject = { collectionBulkDelete: 'Результат масового видалення', collectionCount: 'Кількість документів', collectionDuplicated: 'Дубльований документ', + validateResult: + 'Результат перевірки. Нічого не зберігається. Недійсні значення полів повертають `valid: false` з помилками, а не статус помилки.', + validateBody: + 'Дані документа для перевірки. Для збереженого документа або глобального документа дані накладаються на останню чернетку, а якщо чернетки немає, на збережений документ.', globalDoc: 'Глобальний документ', @@ -45,9 +56,13 @@ export const uk: PluginDefaultTranslationsObject = { authResetPassword: 'Результат скидання пароля', authFirstRegister: 'Перший користувач, створений із токеном автентифікації', authInit: 'Чи має ця колекція автентифікації хоча б одного користувача', - authAccess: 'Доступ (дозволи) поточного користувача до цієї колекції', + authAccess: 'Дозволи поточного користувача для всіх колекцій і глобальних', + docAccess: 'Дозволи поточного користувача для цього документа', + docAccessBody: + 'Дані документа, за якими перевіряється доступ. Без них Payload використовує збережений документ, якщо він є.', authUnlock: 'Результат розблокування', authVerify: 'Результат підтвердження', + apiKeyReveal: 'Розшифрований API-ключ', versionList: 'Посторінковий список версій', versionSingle: 'Окрема версія', @@ -69,6 +84,14 @@ export const uk: PluginDefaultTranslationsObject = { uploadBody: 'Надішліть `multipart/form-data`, щоб завантажити файл (двійкова частина `file` плюс частина `_payload` з полями у вигляді JSON-рядка), або `application/json` лише з полями, коли файлу немає.', uploadPayloadField: 'Поля {{schema}} у вигляді JSON-рядка. Приклад: `{"alt":"A caption"}`.', + fileServe: 'Файл', + filePartial: 'Частина файлу для запиту з `Range`', + uploadInstructionsSummary: 'Отримати інструкції для завантаження файлу перед збереженням документа', + uploadInstructionsResult: + 'Куди надіслати байти файлу і яке значення `file` передати в запиті на створення чи оновлення', + uploadStagePutSummary: 'Надіслати байти файлу для тимчасового завантаження', + uploadStageDeleteSummary: 'Видалити тимчасове завантаження', + uploadStageResult: 'Готово, без вмісту', error400: 'Помилка валідації або запиту (ValidationError, QueryError)', error401: 'Не автентифіковано (AuthenticationError)', @@ -90,6 +113,8 @@ export const uk: PluginDefaultTranslationsObject = { tagAuth: 'Автентифікація', tagVersions: 'Версії', tagJobs: 'Завдання', + tagUploads: 'Завантаження', + tagAccess: 'Доступ', localizationHeading: 'Локалізація', localizationNote: diff --git a/src/translations/locales/vi.ts b/src/shared/translations/locales/vi.ts similarity index 75% rename from src/translations/locales/vi.ts rename to src/shared/translations/locales/vi.ts index 07d4b68..6fae4c5 100644 --- a/src/translations/locales/vi.ts +++ b/src/shared/translations/locales/vi.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const vi: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -16,6 +16,10 @@ export const vi: PluginDefaultTranslationsObject = { 'Với `locale=all`, đặt false để giữ các trường đa ngôn ngữ dưới dạng đối tượng theo từng ngôn ngữ. Mặc định là true.', paramLocale: 'Ngôn ngữ cần trả về, hoặc `all` để lấy mọi ngôn ngữ. Xem phần Đa ngôn ngữ trong phần mô tả API.', paramFallbackLocale: 'Ngôn ngữ dự phòng cho các giá trị đa ngôn ngữ bị thiếu, hoặc `none` để tắt.', + paramValidateLocale: + 'Các ngôn ngữ cần xác thực, hoặc `all` để lấy mọi ngôn ngữ. Lặp lại tham số nếu có nhiều ngôn ngữ.', + paramComputeHierarchyPaths: + 'Đặt true để tính các đường dẫn `{{slugPath}}` và `{{titlePath}}`. Chọn một trong hai trường cũng sẽ tính chúng.', schemaSelect: 'Chọn các trường cần trả về, ví dụ `select[title]=true`. Bỏ trống để trả về tất cả.', schemaPopulate: 'Điền dữ liệu các tài liệu liên quan theo từng collection, ví dụ `populate[posts][title]=true`.', @@ -24,6 +28,10 @@ export const vi: PluginDefaultTranslationsObject = { 'Bộ lọc `where` của Payload. Lồng một trường rồi đến một toán tử: `where[field][equals]=value`. Kết hợp các mệnh đề bằng mảng `and` / `or`, ví dụ `where[or][0][field][equals]=value`. Mỗi trường chỉ liệt kê các toán tử hợp lệ với kiểu của nó.', schemaSupportedTimezones: 'Các múi giờ được hỗ trợ theo định dạng IANA.', schemaPerLocale: 'Giá trị theo từng ngôn ngữ, được trả về khi dùng `locale=all`.', + schemaHierarchySlugPath: + 'Đường dẫn slug, ví dụ `parent/child`. Được tính khi đọc với `computeHierarchyPaths=true` hoặc khi được chọn. Không dùng được trong `where`.', + schemaHierarchyTitlePath: + 'Đường dẫn tiêu đề, ví dụ `Parent/Child`. Được tính khi đọc với `computeHierarchyPaths=true` hoặc khi được chọn. Không dùng được trong `where`.', collectionList: 'Danh sách tài liệu được phân trang', collectionDoc: 'Một tài liệu đơn lẻ', @@ -34,6 +42,10 @@ export const vi: PluginDefaultTranslationsObject = { collectionBulkDelete: 'Kết quả xóa hàng loạt', collectionCount: 'Số lượng tài liệu', collectionDuplicated: 'Tài liệu đã được nhân bản', + validateResult: + 'Kết quả xác thực. Không có gì được lưu. Giá trị trường không hợp lệ trả về `valid: false` kèm các lỗi, không phải trạng thái lỗi.', + validateBody: + 'Dữ liệu tài liệu cần xác thực. Với tài liệu đã lưu hoặc tài liệu global, dữ liệu được gộp lên bản nháp mới nhất, hoặc tài liệu đã lưu nếu không có bản nháp.', globalDoc: 'Tài liệu global', @@ -45,9 +57,13 @@ export const vi: PluginDefaultTranslationsObject = { authResetPassword: 'Kết quả đặt lại mật khẩu', authFirstRegister: 'Người dùng đầu tiên, được tạo cùng với một token xác thực', authInit: 'Cho biết collection xác thực này đã có người dùng nào hay chưa', - authAccess: 'Quyền truy cập (permissions) của người dùng hiện tại đối với collection này', + authAccess: 'Quyền của người dùng hiện tại đối với mọi collection và global', + docAccess: 'Quyền của người dùng hiện tại đối với tài liệu này', + docAccessBody: + 'Dữ liệu tài liệu dùng để kiểm tra quyền truy cập. Nếu không có, Payload dùng tài liệu đã lưu, nếu có.', authUnlock: 'Kết quả mở khóa', authVerify: 'Kết quả xác minh', + apiKeyReveal: 'API key đã được giải mã', versionList: 'Danh sách phiên bản được phân trang', versionSingle: 'Một phiên bản đơn lẻ', @@ -69,6 +85,13 @@ export const vi: PluginDefaultTranslationsObject = { uploadBody: 'Gửi `multipart/form-data` để tải lên một tệp (một phần `file` nhị phân cùng với một phần `_payload` chứa các trường đã được chuyển thành chuỗi JSON), hoặc gửi `application/json` chỉ với các trường khi không có tệp.', uploadPayloadField: 'Các trường {{schema}} đã được chuyển thành chuỗi JSON. Ví dụ: `{"alt":"A caption"}`.', + fileServe: 'Tệp', + filePartial: 'Một phần của tệp, cho yêu cầu `Range`', + uploadInstructionsSummary: 'Lấy hướng dẫn tải tệp lên trước khi lưu tài liệu', + uploadInstructionsResult: 'Nơi gửi các byte của tệp, và giá trị `file` cần gửi kèm yêu cầu tạo hoặc cập nhật', + uploadStagePutSummary: 'Gửi các byte của tệp cho một lần tải lên tạm thời', + uploadStageDeleteSummary: 'Xóa một lần tải lên tạm thời', + uploadStageResult: 'Xong, không có nội dung', error400: 'Lỗi xác thực hoặc lỗi truy vấn (ValidationError, QueryError)', error401: 'Chưa được xác thực (AuthenticationError)', @@ -90,6 +113,8 @@ export const vi: PluginDefaultTranslationsObject = { tagAuth: 'Xác thực', tagVersions: 'Phiên bản', tagJobs: 'Jobs', + tagUploads: 'Tải lên', + tagAccess: 'Truy cập', localizationHeading: 'Đa ngôn ngữ', localizationNote: diff --git a/src/translations/locales/zh.ts b/src/shared/translations/locales/zh.ts similarity index 75% rename from src/translations/locales/zh.ts rename to src/shared/translations/locales/zh.ts index 959817e..b872511 100644 --- a/src/translations/locales/zh.ts +++ b/src/shared/translations/locales/zh.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const zh: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -15,6 +15,9 @@ export const zh: PluginDefaultTranslationsObject = { paramFlattenLocales: '在使用 `locale=all` 时,设为 false 可将本地化字段保留为按语言区域划分的对象。默认值为 true。', paramLocale: '要返回的语言区域,或使用 `all` 返回所有语言区域。参见 API 说明中的本地化(Localization)部分。', paramFallbackLocale: '缺少本地化值时回退到的语言区域,或使用 `none` 禁用回退。', + paramValidateLocale: '要验证的语言区域,或使用 `all` 验证所有语言区域。多个语言区域请重复该参数。', + paramComputeHierarchyPaths: + '设为 true 以计算 `{{slugPath}}` 和 `{{titlePath}}` 路径。选择其中任一字段也会计算它们。', schemaSelect: '选择要返回的字段,例如 `select[title]=true`。省略则返回所有字段。', schemaPopulate: '按集合填充关联文档,例如 `populate[posts][title]=true`。', @@ -23,6 +26,10 @@ export const zh: PluginDefaultTranslationsObject = { 'Payload 的 `where` 过滤器。先嵌套字段,再嵌套运算符:`where[field][equals]=value`。使用 `and` / `or` 数组组合多个条件,例如 `where[or][0][field][equals]=value`。每个字段仅列出对其类型有效的运算符。', schemaSupportedTimezones: 'IANA 格式的受支持时区。', schemaPerLocale: '按语言区域划分的值,在使用 `locale=all` 时返回。', + schemaHierarchySlugPath: + 'slug 路径,例如 `parent/child`。在读取时带上 `computeHierarchyPaths=true` 或选择该字段时计算。不能用于 `where`。', + schemaHierarchyTitlePath: + '标题路径,例如 `Parent/Child`。在读取时带上 `computeHierarchyPaths=true` 或选择该字段时计算。不能用于 `where`。', collectionList: '文档的分页列表', collectionDoc: '单个文档', @@ -33,6 +40,9 @@ export const zh: PluginDefaultTranslationsObject = { collectionBulkDelete: '批量删除结果', collectionCount: '文档数量', collectionDuplicated: '复制出的文档', + validateResult: '验证结果。不会保存任何内容。无效的字段值会返回带有错误信息的 `valid: false`,而不是错误状态。', + validateBody: + '要验证的文档数据。对于已保存的文档或全局文档,数据会合并到最新草稿上;没有草稿时合并到已保存的文档上。', globalDoc: '该全局文档', @@ -44,9 +54,12 @@ export const zh: PluginDefaultTranslationsObject = { authResetPassword: '密码重置结果', authFirstRegister: '第一个用户,已创建并附带认证令牌', authInit: '该认证集合中是否已存在任何用户', - authAccess: '当前用户对该集合的访问权限(permissions)', + authAccess: '当前用户对所有集合和全局的权限', + docAccess: '当前用户对此文档的权限', + docAccessBody: '用于检查访问权限的文档数据。省略时,Payload 使用已保存的文档(如果有)。', authUnlock: '解锁结果', authVerify: '验证结果', + apiKeyReveal: '解密后的 API 密钥', versionList: '版本的分页列表', versionSingle: '单个版本', @@ -68,6 +81,13 @@ export const zh: PluginDefaultTranslationsObject = { uploadBody: '发送 `multipart/form-data` 以上传文件(一个二进制 `file` 部分,加上一个包含 JSON 字符串化字段的 `_payload` 部分),或在没有文件时仅发送字段并使用 `application/json`。', uploadPayloadField: 'JSON 字符串化的 {{schema}} 字段。示例:`{"alt":"A caption"}`。', + fileServe: '文件', + filePartial: '`Range` 请求返回的部分文件', + uploadInstructionsSummary: '在保存文档前获取上传文件的说明', + uploadInstructionsResult: '文件字节的发送位置,以及在创建或更新请求中发送的 `file` 值', + uploadStagePutSummary: '为临时上传发送文件字节', + uploadStageDeleteSummary: '删除临时上传', + uploadStageResult: '完成,无内容', error400: '验证或查询错误(ValidationError、QueryError)', error401: '未认证(AuthenticationError)', @@ -88,6 +108,8 @@ export const zh: PluginDefaultTranslationsObject = { tagAuth: '认证', tagVersions: '版本', tagJobs: '作业', + tagUploads: '上传', + tagAccess: '访问', localizationHeading: '本地化', localizationNote: diff --git a/src/translations/locales/zhTw.ts b/src/shared/translations/locales/zhTw.ts similarity index 75% rename from src/translations/locales/zhTw.ts rename to src/shared/translations/locales/zhTw.ts index 789c13d..69ecbf0 100644 --- a/src/translations/locales/zhTw.ts +++ b/src/shared/translations/locales/zhTw.ts @@ -1,4 +1,4 @@ -import type { PluginDefaultTranslationsObject } from '../types.js' +import type { PluginDefaultTranslationsObject } from '@/shared/translations/types.js' export const zhTw: PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -15,6 +15,9 @@ export const zhTw: PluginDefaultTranslationsObject = { paramFlattenLocales: '搭配 `locale=all` 使用時,設為 false 可將本地化欄位保留為各語系物件。預設為 true。', paramLocale: '要回傳的語系,或使用 `all` 取得所有語系。請參閱 API 說明中的「本地化」一節。', paramFallbackLocale: '當本地化值缺失時用以回退的語系,或使用 `none` 停用。', + paramValidateLocale: '要驗證的語系,或使用 `all` 驗證所有語系。多個語系請重複該參數。', + paramComputeHierarchyPaths: + '設為 true 以計算 `{{slugPath}}` 與 `{{titlePath}}` 路徑。選擇其中任一欄位也會計算它們。', schemaSelect: '選擇要回傳的欄位,例如 `select[title]=true`。省略則回傳全部。', schemaPopulate: '依集合填充關聯文件,例如 `populate[posts][title]=true`。', @@ -23,6 +26,10 @@ export const zhTw: PluginDefaultTranslationsObject = { 'Payload 的 `where` 篩選器。先巢狀欄位再接運算子:`where[field][equals]=value`。使用 `and` / `or` 陣列組合子句,例如 `where[or][0][field][equals]=value`。每個欄位僅列出其型別適用的運算子。', schemaSupportedTimezones: '採用 IANA 格式的支援時區。', schemaPerLocale: '各語系的值,於 `locale=all` 時回傳。', + schemaHierarchySlugPath: + 'slug 路徑,例如 `parent/child`。在讀取時帶上 `computeHierarchyPaths=true` 或選擇該欄位時計算。不能用於 `where`。', + schemaHierarchyTitlePath: + '標題路徑,例如 `Parent/Child`。在讀取時帶上 `computeHierarchyPaths=true` 或選擇該欄位時計算。不能用於 `where`。', collectionList: '分頁的文件清單', collectionDoc: '單一文件', @@ -33,6 +40,9 @@ export const zhTw: PluginDefaultTranslationsObject = { collectionBulkDelete: '批次刪除結果', collectionCount: '文件數量', collectionDuplicated: '已複製的文件', + validateResult: '驗證結果。不會儲存任何內容。無效的欄位值會回傳含錯誤資訊的 `valid: false`,而不是錯誤狀態。', + validateBody: + '要驗證的文件資料。對於已儲存的文件或全域文件,資料會合併到最新草稿上;沒有草稿時合併到已儲存的文件上。', globalDoc: '全域文件', @@ -44,9 +54,12 @@ export const zhTw: PluginDefaultTranslationsObject = { authResetPassword: '密碼重設結果', authFirstRegister: '第一位使用者,建立時附帶驗證權杖', authInit: '此驗證集合是否已有任何使用者', - authAccess: '目前使用者對此集合的存取(權限)', + authAccess: '目前使用者對所有集合與全域的權限', + docAccess: '目前使用者對此文件的權限', + docAccessBody: '用於檢查存取權限的文件資料。省略時,Payload 會使用已儲存的文件(如果有)。', authUnlock: '解鎖結果', authVerify: '驗證結果', + apiKeyReveal: '解密後的 API 金鑰', versionList: '分頁的版本清單', versionSingle: '單一版本', @@ -68,6 +81,13 @@ export const zhTw: PluginDefaultTranslationsObject = { uploadBody: '傳送 `multipart/form-data` 以上傳檔案(一個二進位的 `file` 部分,加上一個含 JSON 字串化欄位的 `_payload` 部分),或在沒有檔案時僅以 `application/json` 傳送欄位。', uploadPayloadField: 'JSON 字串化的 {{schema}} 欄位。範例:`{"alt":"A caption"}`。', + fileServe: '檔案', + filePartial: '`Range` 請求傳回的部分檔案', + uploadInstructionsSummary: '在儲存文件前取得上傳檔案的說明', + uploadInstructionsResult: '檔案位元組的傳送位置,以及在建立或更新請求中傳送的 `file` 值', + uploadStagePutSummary: '為暫存上傳傳送檔案位元組', + uploadStageDeleteSummary: '刪除暫存上傳', + uploadStageResult: '完成,無內容', error400: '驗證或查詢錯誤 (ValidationError, QueryError)', error401: '未驗證 (AuthenticationError)', @@ -88,6 +108,8 @@ export const zhTw: PluginDefaultTranslationsObject = { tagAuth: '驗證', tagVersions: '版本', tagJobs: '工作', + tagUploads: '上傳', + tagAccess: '存取', localizationHeading: '本地化', localizationNote: diff --git a/src/translations/types.ts b/src/shared/translations/types.ts similarity index 81% rename from src/translations/types.ts rename to src/shared/translations/types.ts index ac198fb..f2fcb3b 100644 --- a/src/translations/types.ts +++ b/src/shared/translations/types.ts @@ -1,6 +1,6 @@ import type { DefaultTranslationKeys, NestedKeysStripped, TFunction } from '@payloadcms/translations' -import type { en } from './locales/en.js' +import type { en } from '@/shared/translations/locales/en.js' export type PluginDefaultTranslationsObject = { '@seshuk/payload-plugin-openapi': { @@ -17,6 +17,8 @@ export type PluginDefaultTranslationsObject = { paramFlattenLocales: string paramLocale: string paramFallbackLocale: string + paramValidateLocale: string + paramComputeHierarchyPaths: string schemaSelect: string schemaPopulate: string @@ -24,6 +26,8 @@ export type PluginDefaultTranslationsObject = { schemaWhere: string schemaSupportedTimezones: string schemaPerLocale: string + schemaHierarchySlugPath: string + schemaHierarchyTitlePath: string collectionList: string collectionDoc: string @@ -34,6 +38,8 @@ export type PluginDefaultTranslationsObject = { collectionBulkDelete: string collectionCount: string collectionDuplicated: string + validateResult: string + validateBody: string globalDoc: string @@ -46,8 +52,11 @@ export type PluginDefaultTranslationsObject = { authFirstRegister: string authInit: string authAccess: string + docAccess: string + docAccessBody: string authUnlock: string authVerify: string + apiKeyReveal: string versionList: string versionSingle: string @@ -68,6 +77,13 @@ export type PluginDefaultTranslationsObject = { uploadFile: string uploadBody: string uploadPayloadField: string + fileServe: string + filePartial: string + uploadInstructionsSummary: string + uploadInstructionsResult: string + uploadStagePutSummary: string + uploadStageDeleteSummary: string + uploadStageResult: string error400: string error401: string @@ -87,6 +103,8 @@ export type PluginDefaultTranslationsObject = { tagAuth: string tagVersions: string tagJobs: string + tagUploads: string + tagAccess: string localizationHeading: string localizationNote: string diff --git a/src/shared/types/index.ts b/src/shared/types/index.ts new file mode 100644 index 0000000..8a8cc6c --- /dev/null +++ b/src/shared/types/index.ts @@ -0,0 +1,432 @@ +import type { I18n } from '@payloadcms/translations' +import type { + ComponentsObject, + Document, + InfoObject, + PathsObject, + SecurityRequirementObject, + ServerObject, + TagObject, +} from '@scalar/openapi-types/3.2' +import type { BasePayload, PayloadRequest, SanitizedCollectionConfig, SanitizedGlobalConfig } from 'payload' + +// Shared primitives + +export type OpenApiVersion = '3.0' | '3.1' | '3.2' + +export type EntityKind = 'collection' | 'global' + +export type OperationKind = EntityKind | 'custom' | 'jobs' | 'system' + +export type HttpMethod = 'get' | 'post' | 'patch' | 'put' | 'delete' + +export type IDType = 'text' | 'number' + +export type Entity = SanitizedCollectionConfig | SanitizedGlobalConfig + +export type EntityOperation = 'read' | 'create' | 'update' | 'delete' | 'validate' + +export type SecurityMarking = 'public' | 'secured' + +/** + * Marking for a collection or global, set in its `custom.openapi.security`: + * one marking for every operation, or one per operation. Operations you leave + * out keep the marking the access probe found. `read` also marks the version + * reads. `validate` falls back to `update` when you leave it out. + */ +export type EntitySecurityOverride = SecurityMarking | Partial> + +/** + * Text that follows the request language: a string, a map of language to + * string (`{ en: '…', de: '…' }`), or a function that gets `{ t, i18n }`. + */ +export type LocalizedText = + | string + | Record + | ((args: { t: (key: string, vars?: Record) => string; i18n: I18n }) => string) + +/** `custom.openapi` on a collection or global. */ +export interface EntityOpenApiMeta { + /** Overrides the public or secured marking. See {@link EntitySecurityOverride}. */ + security?: EntitySecurityOverride +} + +/** + * `custom.openapi` on a field: a partial Schema Object deep-merged on top of + * the inferred one. `description`, `title` and `summary` follow the + * request language. + */ +export interface FieldOpenApiMeta { + description?: LocalizedText + title?: LocalizedText + summary?: LocalizedText + [keyword: string]: unknown +} + +declare module 'payload' { + interface CollectionCustom { + openapi?: EntityOpenApiMeta + } + interface GlobalCustom { + openapi?: EntityOpenApiMeta + } + interface FieldCustom { + openapi?: FieldOpenApiMeta + } +} + +// Plugin options + +/** + * The OpenAPI `info` object. `title` and `version` are required. `description` + * can follow the request language, see {@link LocalizedText}. + */ +export type OpenApiInfo = Omit & { description?: LocalizedText } + +/** + * Base URLs for the spec's `servers`: a fixed list, or a function that gets + * the request. + */ +export type ServersOption = + | ServerObject[] + | ((args: { req: PayloadRequest }) => ServerObject[] | Promise) + +/** + * Interactive "Authorize" login for the docs UI. When on, the plugin adds a + * token endpoint and a security scheme so Scalar/Swagger can log in with a + * username and password instead of pasting a token. Off by default. + * - `true` — on, at the default path, against the first auth collection. + * - `{ path, collection }` — on, with a custom token endpoint path or auth collection. + */ +export type InteractiveAuthOption = + | boolean + | { + /** + * Token endpoint path, relative to the API route (`/openapi-auth` → `/api/openapi-auth`). + * @default '/openapi-auth' + */ + path?: string + /** + * Auth collection the login runs against. + * @default the first collection with `auth` enabled, or `'users'` + */ + collection?: string + } + +/** + * Gate for a plugin route. Return `true` to serve the request; anything else + * answers 403. + */ +export type AccessOption = (args: { req: PayloadRequest }) => boolean | Promise + +/** Match an entity by slug (any kind), a {kind,slug} pair, or a slug pattern. */ +export type EntityMatcher = string | RegExp | { kind: EntityKind; slug: string } + +/** + * Drops generated operations. Within one rule, every set field must match (AND); + * an operation is removed if any rule matches it. Fields: + * - `method` — HTTP method(s); omit to match any. + * - `slug` — collection or global slug, exact string or RegExp; omit to match any. + * A rule with `slug` never matches custom, jobs or system operations. + * - `kind` — restrict to one operation group; omit to match all. + * - `path` — RegExp tested against the final route path (e.g. `/api/posts/{id}`). + */ +export interface OperationRule { + method?: HttpMethod | HttpMethod[] + slug?: string | RegExp + kind?: OperationKind + path?: RegExp +} + +/** Context passed to `excludeOperations` functions and `security` for each operation. */ +export interface OperationContext { + method: HttpMethod + path: string + /** Collection or global slug. Unset for custom, jobs and system operations. */ + slug?: string + kind: OperationKind +} + +/** A rule, or a function that drops the operation when it returns (or resolves to) `true`. */ +export type OperationFilter = OperationRule | ((ctx: OperationContext) => boolean | Promise) + +type SecurityDecision = SecurityMarking | SecurityRequirementObject[] | undefined + +/** + * Decides the security marking of one operation. `'public'` removes it, + * `'secured'` sets the default requirement, an array sets that exact + * requirement, and `undefined` keeps `detected`. + */ +export type SecurityOption = ( + ctx: OperationContext & { detected: SecurityMarking }, +) => SecurityDecision | Promise + +/** + * Controls what appears in the spec, in one flat object: which entities are + * documented, which Payload-internal collections to show, which extra endpoint + * groups to generate, and which operations to drop. + */ +export interface FilterOptions { + /** + * Allowlist of entities; when non-empty, only matching entities are documented. + * @default [] + */ + include?: EntityMatcher[] + /** + * Entities to omit entirely (schemas + paths). + * @default [] + */ + exclude?: EntityMatcher[] + /** + * Include collections flagged `hidden`/`admin.hidden`. + * @default false + */ + includeHidden?: boolean + /** + * Include Payload-internal collections and globals (payload-jobs, payload-preferences, payload-jobs-stats, ...). + * @default false + */ + includeSystem?: boolean + /** + * Document endpoints carrying `custom.openapi` metadata. + * @default true + */ + includeCustom?: boolean + /** + * Document auth operation endpoints (login/logout/me/...). + * @default true + */ + includeAuth?: boolean + /** + * Document admin/bootstrap auth endpoints (/init, first-register, root /access) and the document access endpoints (/{slug}/access/{id}, /globals/{slug}/access). + * @default false + */ + includeAdminAuth?: boolean + /** + * Document version operation endpoints (/versions, /versions/{id}). + * @default true + */ + includeVersions?: boolean + /** + * Document jobs endpoints (/payload-jobs/run, /handle-schedules). + * @default true + */ + includeJobs?: boolean + /** + * Drops operations from every group: collection, global, custom, jobs and + * system. An operation is removed when any rule matches it or any function + * returns `true`. + * @default [] + */ + excludeOperations?: OperationFilter[] +} + +export interface OpenApiExtension { + /** Paths deep-merged into the generated ones. */ + paths?: PathsObject + /** Components deep-merged into the generated ones. */ + components?: ComponentsObject + /** Tags appended to the list; a tag whose name already exists is skipped. */ + tags?: TagObject[] + /** + * Runs on the finished document and returns the document to use. Gets one + * object: `doc` plus the {@link ExtensionContext}, e.g. `({ doc, payload }) => doc`. + * It can be `async`. If it throws, the spec request fails: the plugin never + * serves a document that skipped a transform. + */ + transform?: (args: ExtensionContext) => Document | Promise +} + +/** What `extensions[].transform` gets: the finished document and the build context. */ +export type ExtensionContext = BuildContext & { doc: Document; payload: BasePayload; options: ResolvedOptions } + +export type OpenApiPluginOptions = { + /** The document's `info` object. `title` and `version` are required. See {@link OpenApiInfo}. */ + info: OpenApiInfo + /** + * OpenAPI version to serve. The document is always built as 3.2; `3.1` and + * `3.0` each run a downconversion pass on the result. + * @default '3.2' + */ + openapiVersion?: OpenApiVersion + /** + * Where the spec is served, relative to the Payload API route (`routes.api`, + * `/api` by default). So the `/openapi.json` default is served at + * `/api/openapi.json`. + * @default '/openapi.json' + */ + path?: string + /** + * Set false to turn the plugin off. It then adds no endpoints, no CLI command + * and no translations. The plugin adds no collections, so the schema stays the same. + * @default true + */ + enabled?: boolean + /** + * Mount the runtime spec (and interactive-auth) endpoints. Set false to + * register only the `openapi:generate` CLI command and skip serving over + * HTTP — generate the spec to a file and host it however you like (static + * asset, CDN, committed to the repo). Point the docs UI plugins' `specURL` + * at wherever the file ends up. + * @default true + */ + serve?: boolean + /** + * Who may read the spec endpoint. Runs on every request. + * @default open to everyone + */ + access?: AccessOption + /** + * The spec's `servers`. A function runs on every request. See {@link ServersOption}. + * @default `[{ url: serverURL }]` when the Payload `serverURL` is set, otherwise `[]` (clients use the spec's own origin) + */ + servers?: ServersOption + /** + * Hosts the spec may echo back as its server URL. When the request `Host` + * header (`host` or `host:port`) is on this list, `servers` is + * `https://` (`http` for localhost and 127.*). The `Host` header comes from + * the client, so the plugin never uses it unless you list it here. A + * `servers` option wins over this list. + * @default [] + */ + trustedHosts?: string[] + /** What appears in the spec. See {@link FilterOptions}. */ + filters?: FilterOptions + /** + * Interactive docs-UI login. See {@link InteractiveAuthOption}. + * @default false + */ + interactiveAuth?: InteractiveAuthOption + /** + * Emit an OpenAPI 3.2 nested tag hierarchy (nav groups via `kind`/`parent`, + * plus per-entity auth/version sub-tags). Off by default: Scalar and Swagger UI + * don't render 3.2 nested tags yet and would show a confusing flat list of group + * names. When off, `doc.tags` is a flat list of per-entity tags with their + * descriptions. + * @default false + */ + nestedTags?: boolean + /** + * Override the security marking per operation. Runs last, after + * `custom.openapi.security` and the access probe, across every operation + * group. See {@link SecurityOption}. + */ + security?: SecurityOption + /** + * Cache the built document for the life of the process. The Payload config is + * static after boot, so the spec is the same every time apart from + * `servers`, which is set per request. Set false in dev to rebuild on + * every request, e.g. `cache: process.env.NODE_ENV === 'production'`. + * @default true + */ + cache?: boolean + /** + * Extra paths, components and tags merged into the document, plus a final `transform`. + * @default [] + */ + extensions?: OpenApiExtension[] + /** + * Anonymous usage telemetry: plugin, Payload and Node versions and the features in use. On by default. + * It never sends secrets, IP addresses, keys, URLs, paths, hostnames or collection names. + * The first run logs a notice. + * + * Off when `payload.config.telemetry` is `false`, when `DO_NOT_TRACK` or `OPENAPI_TELEMETRY_DISABLED` + * is set, in CI or with `NODE_ENV=test`. Set `false` to turn it off. Pass `{ url }` to use your own collector. + * + * @see https://payload-plugin-openapi.seshuk.im/v1/configuration/telemetry + * @default true + */ + telemetry?: + | boolean + | { + /** + * Collector URL that receives the telemetry report. + * @default the plugin's public collector + */ + url?: string + } +} + +export interface UiPluginOptions { + /** + * Where the docs UI is served, relative to the Payload API route + * (`routes.api`, `/api` by default). So the `/docs` default is served at + * `/api/docs`. + * @default '/docs' + */ + path?: string + /** + * URL the UI fetches the OpenAPI document from. Any URL reference works: + * absolute, root-relative, or relative to the docs page. + * @default the `openapi()` spec route, `/openapi.json` unless its `path` is set + */ + specURL?: string + /** + * Where the UI's assets load from: the script URL for Scalar, the folder with + * `swagger-ui-bundle.js` and `swagger-ui.css` for Swagger UI. Set it to + * self-host the assets. The default URL is pinned to one version and loads + * with Subresource Integrity; a custom URL loads without it. + * @default the pinned jsDelivr URL + */ + cdnBase?: string + /** + * Extra UI config, merged into the underlying library's init options (Scalar's + * `createApiReference` config or Swagger UI's `SwaggerUIBundle`). Must be + * JSON-serializable, functions aren't supported here. + * @default {} + */ + configuration?: Record + /** + * Who may open the docs page. Runs on every request. + * @default open to everyone + */ + access?: AccessOption + /** + * Set false to skip mounting the docs UI. + * @default true + */ + enabled?: boolean +} + +// Resolved options + +export interface ResolvedFilters { + include: EntityMatcher[] + exclude: EntityMatcher[] + includeHidden: boolean + includeSystem: boolean + includeCustom: boolean + includeAuth: boolean + includeAdminAuth: boolean + includeVersions: boolean + includeJobs: boolean + excludeOperations: OperationFilter[] +} + +export interface ResolvedOptions { + info: OpenApiInfo + openapiVersion: OpenApiVersion + path: string + serve: boolean + access?: AccessOption + servers?: ServersOption + trustedHosts: string[] + filters: ResolvedFilters + interactiveAuth: { enabled: boolean; path: string; collection?: string } + nestedTags: boolean + security?: SecurityOption + cache: boolean + extensions: OpenApiExtension[] +} + +// Build context + +export interface BuildContext { + defaultIDType: IDType + locales: string[] + apiRoute: string + /** Languages the docs are available in (project `i18n.supportedLanguages`). */ + docLanguages: string[] + /** Request i18n: resolves Payload entity descriptions and, via {@link makeT}, plugin strings. */ + i18n: I18n +} diff --git a/src/utils.ts b/src/shared/utils.ts similarity index 100% rename from src/utils.ts rename to src/shared/utils.ts diff --git a/src/spec/security.ts b/src/spec/security.ts deleted file mode 100644 index 58d45bd..0000000 --- a/src/spec/security.ts +++ /dev/null @@ -1,124 +0,0 @@ -import type { Access } from 'payload' -import type { PathsObject, SecurityRequirementObject } from '@scalar/openapi-types/3.2' - -import type { - Entity, - EntityKind, - EntityOperation, - EntitySecurityOverride, - HttpMethod, - OperationContext, -} from '../types.js' -import { SECURITY_SCHEME_NAME } from './components.js' - -const HTTP_METHODS: HttpMethod[] = ['get', 'post', 'patch', 'put', 'delete'] - -const securedRequirement = (): SecurityRequirementObject[] => [{ [SECURITY_SCHEME_NAME]: [] }] - -export type EntitySecurity = Partial> - -const withTimeout = async (promise: Promise, timeoutMs: number): Promise => { - let timer: ReturnType | undefined - const guard = new Promise((resolve) => { - timer = setTimeout(() => resolve(false), timeoutMs) - }) - try { - // Both handlers attached, so a rejected or never-settling fn leaks no unhandled rejection. - return await Promise.race([ - promise.then( - (value) => value === true, - () => false, - ), - guard, - ]) - } finally { - if (timer) clearTimeout(timer) - } -} - -// Probe an access fn as an anonymous request: public only if it settles on `true`; -// a throw, a `Where`, a timeout, or a `req.payload` (DB) read all fall back to secured. -export const evaluateAccess = async ( - access: Access | undefined, - opts: { locale?: string; timeoutMs?: number } = {}, -): Promise => { - if (!access) return false - const { locale, timeoutMs = 250 } = opts - const payloadTrap = new Proxy( - {}, - { - get() { - throw new Error('req.payload accessed during build') - }, - }, - ) - const req = { user: null, headers: new Headers(), context: {}, locale, payload: payloadTrap } - try { - const result = access({ req } as never) - if (result && typeof (result as Promise).then === 'function') { - return await withTimeout(result as Promise, timeoutMs) - } - return result === true - } catch { - return false - } -} - -const readSecurityOverride = (entity: Entity): EntitySecurityOverride | undefined => { - const meta = (entity as { custom?: { openapi?: { security?: unknown } } }).custom?.openapi?.security - if (typeof meta === 'boolean') return meta - if (meta && typeof meta === 'object') return meta as EntitySecurityOverride - return undefined -} - -const overrideFor = (override: EntitySecurityOverride | undefined, op: EntityOperation): boolean | undefined => { - if (override === undefined) return undefined - if (typeof override === 'boolean') return override - return override[op] -} - -export const resolveEntitySecurity = async ({ - entity, - operations, - locale, -}: { - entity: Entity - operations: EntityOperation[] - locale?: string -}): Promise => { - const override = readSecurityOverride(entity) - const access = entity.access as Partial> | undefined - const out: EntitySecurity = {} - for (const op of operations) { - const declared = overrideFor(override, op) - const isPublic = declared ?? (await evaluateAccess(access?.[op], { locale })) - out[op] = isPublic ? undefined : securedRequirement() - } - return out -} - -export const applySecurityWhen = ({ - paths, - slug, - kind, - securityWhen, -}: { - paths: PathsObject - slug: string - kind: EntityKind - securityWhen?: (ctx: OperationContext) => boolean | undefined -}): PathsObject => { - if (!securityWhen) return paths - for (const [path, item] of Object.entries(paths)) { - if (!item) continue - const ops = item as Record - for (const method of HTTP_METHODS) { - const op = ops[method] - if (!op) continue - const decision = securityWhen({ method, path, slug, kind }) - if (decision === true) delete op.security - else if (decision === false) op.security = securedRequirement() - } - } - return paths -} diff --git a/src/translations/index.ts b/src/translations/index.ts deleted file mode 100644 index 6026215..0000000 --- a/src/translations/index.ts +++ /dev/null @@ -1,103 +0,0 @@ -import type { AcceptedLanguages, I18n } from '@payloadcms/translations' - -import type { PluginDefaultTranslationsObject, Translate } from './types.js' - -import { PLUGIN_NAME } from '../constants.js' -import { ar } from './locales/ar.js' -import { az } from './locales/az.js' -import { bg } from './locales/bg.js' -import { bnBd } from './locales/bnBd.js' -import { bnIn } from './locales/bnIn.js' -import { ca } from './locales/ca.js' -import { cs } from './locales/cs.js' -import { da } from './locales/da.js' -import { de } from './locales/de.js' -import { en } from './locales/en.js' -import { es } from './locales/es.js' -import { et } from './locales/et.js' -import { fa } from './locales/fa.js' -import { fr } from './locales/fr.js' -import { he } from './locales/he.js' -import { hr } from './locales/hr.js' -import { hu } from './locales/hu.js' -import { hy } from './locales/hy.js' -import { id } from './locales/id.js' -import { is } from './locales/is.js' -import { it } from './locales/it.js' -import { ja } from './locales/ja.js' -import { ko } from './locales/ko.js' -import { lt } from './locales/lt.js' -import { lv } from './locales/lv.js' -import { my } from './locales/my.js' -import { nb } from './locales/nb.js' -import { nl } from './locales/nl.js' -import { pl } from './locales/pl.js' -import { pt } from './locales/pt.js' -import { ro } from './locales/ro.js' -import { rs } from './locales/rs.js' -import { rsLatin } from './locales/rsLatin.js' -import { ru } from './locales/ru.js' -import { sk } from './locales/sk.js' -import { sl } from './locales/sl.js' -import { sv } from './locales/sv.js' -import { ta } from './locales/ta.js' -import { th } from './locales/th.js' -import { tr } from './locales/tr.js' -import { uk } from './locales/uk.js' -import { vi } from './locales/vi.js' -import { zh } from './locales/zh.js' -import { zhTw } from './locales/zhTw.js' - -export const makeT = (i18n: Pick): Translate => { - const t = i18n.t as ((key: string, vars?: Record) => string) | undefined - return (key, vars) => (t ? t(`${PLUGIN_NAME}:${key}`, vars) : key) -} - -export const translations: { - [key in AcceptedLanguages]?: PluginDefaultTranslationsObject -} = { - ar, - az, - bg, - 'bn-BD': bnBd, - 'bn-IN': bnIn, - ca, - cs, - da, - de, - en, - es, - et, - fa, - fr, - he, - hr, - hu, - hy, - id, - is, - it, - ja, - ko, - lt, - lv, - my, - nb, - nl, - pl, - pt, - ro, - rs, - 'rs-latin': rsLatin, - ru, - sk, - sl, - sv, - ta, - th, - tr, - uk, - vi, - zh, - 'zh-TW': zhTw, -} diff --git a/src/types.ts b/src/types.ts deleted file mode 100644 index 4b2a065..0000000 --- a/src/types.ts +++ /dev/null @@ -1,212 +0,0 @@ -import type { SanitizedCollectionConfig, SanitizedGlobalConfig } from 'payload' -import type { ComponentsObject, Document, PathsObject, TagObject } from '@scalar/openapi-types/3.2' - -import type { I18n } from '@payloadcms/translations' - -// Shared primitives - -export type OpenApiVersion = '3.0' | '3.1' | '3.2' - -export type EntityKind = 'collection' | 'global' - -export type HttpMethod = 'get' | 'post' | 'patch' | 'put' | 'delete' - -export type IDType = 'text' | 'number' - -export type Entity = SanitizedCollectionConfig | SanitizedGlobalConfig - -export type EntityOperation = 'read' | 'create' | 'update' | 'delete' - -export type EntitySecurityOverride = boolean | Partial> - -// Plugin options - -export interface OpenApiMetadata { - title: string - version: string - description?: string -} - -/** - * Interactive "Authorize" login for the docs UI. When on, the plugin adds a - * token endpoint and a security scheme so Scalar/Swagger can log in with a - * username and password instead of pasting a token. Off by default. The - * endpoint path is relative to the API route (`/openapi-auth` → `/api/openapi-auth`). - * - `true` — on, at the default endpoint (`/openapi-auth`). - * - `{ endpoint }` — on, at a custom token endpoint path. - */ -export type InteractiveAuthOption = boolean | { endpoint?: string } - -/** Match an entity by slug (any kind), a {kind,slug} pair, or a slug pattern. */ -export type EntityMatcher = string | RegExp | { kind: EntityKind; slug: string } - -/** - * Drops generated operations. Within one rule, every set field must match (AND); - * an operation is removed if any rule matches it. Fields: - * - `method` — HTTP method(s); omit to match any. - * - `slug` — entity slug, exact string or RegExp; omit to match any. - * - `kind` — restrict to collections or globals; omit to match both. - * - `path` — RegExp tested against the final route path (e.g. `/api/posts/{id}`). - */ -export interface OperationRule { - method?: HttpMethod | HttpMethod[] - slug?: string | RegExp - kind?: EntityKind - path?: RegExp -} - -/** Context passed to {@link FilterOptions.excludeWhen} for each operation. */ -export interface OperationContext { - method: HttpMethod - path: string - slug: string - kind: EntityKind -} - -/** - * Controls what appears in the spec, in one flat object: which entities are - * documented, which Payload-internal collections to show, which extra endpoint - * groups to generate, and which operations to drop. - */ -export interface FilterOptions { - /** Allowlist of entities; when non-empty, only matching entities are documented. */ - include?: EntityMatcher[] - /** Entities to omit entirely (schemas + paths). */ - exclude?: EntityMatcher[] - /** Include collections flagged `hidden`/`admin.hidden`. Default false. */ - includeHidden?: boolean - /** Include Payload-internal collections (payload-jobs, payload-preferences, ...). Default false. */ - includeSystem?: boolean - /** Document endpoints carrying `custom.openapi` metadata. Default true. */ - includeCustom?: boolean - /** Document auth operation endpoints (login/logout/me/...). Default true. */ - includeAuth?: boolean - /** Document admin/bootstrap auth endpoints (/init, /access, first-register). Default false. */ - includeAdminAuth?: boolean - /** Document version operation endpoints (/versions, /versions/{id}). Default true. */ - includeVersions?: boolean - /** Document jobs endpoints (/payload-jobs/run, /handle-schedules). Default true. */ - includeJobs?: boolean - /** Per-operation removal rules. */ - excludeOperations?: OperationRule[] - /** Escape hatch: return true to drop an operation. Runs after excludeOperations. */ - excludeWhen?: (ctx: OperationContext) => boolean -} - -export interface OpenApiExtension { - paths?: PathsObject - components?: ComponentsObject - tags?: TagObject[] - transform?: (doc: Document, ctx: BuildContext) => Document -} - -export type OpenApiPluginOptions = { - metadata: OpenApiMetadata - /** - * OpenAPI version to serve. The document is always built as 3.2; `3.1` and - * `3.0` each run a downconversion pass on the result. Default `3.2`. - */ - openapiVersion?: OpenApiVersion - /** - * Where the spec is served, relative to the Payload API route (`routes.api`, - * `/api` by default). So the `/openapi.json` default is served at - * `/api/openapi.json`. Default `/openapi.json`. - */ - specEndpoint?: string - enabled?: boolean - /** - * Mount the runtime spec (and interactive-auth) endpoints. Default true. Set - * false to register only the `openapi:generate` CLI bin and skip serving over - * HTTP — generate the spec to a file and host it however you like (static - * asset, CDN, committed to the repo). The docs UI plugins (`scalar`/`swaggerUi`) - * are unaffected; point their `specEndpoint` at wherever the file ends up. - */ - serve?: boolean - filters?: FilterOptions - /** Interactive docs-UI login. See {@link InteractiveAuthOption}. Default false. */ - interactiveAuth?: InteractiveAuthOption - /** - * Emit an OpenAPI 3.2 nested tag hierarchy (nav groups via `kind`/`parent`, - * plus per-entity auth/version sub-tags). Off by default: Scalar and Swagger UI - * don't render 3.2 nested tags yet and would show a confusing flat list of group - * names. When off, `doc.tags` is a flat list of per-entity tags with their - * descriptions. - */ - nestedTags?: boolean - /** - * Override the security marking per operation: `true` marks it public, `false` - * secured (`PayloadToken`), `undefined` keeps the detected marking. Runs last, - * after `custom.openapi.security` and the probe, across every operation group. - */ - securityWhen?: (ctx: OperationContext) => boolean | undefined - /** - * Cache the built document for the life of the process. The Payload config is - * static after boot, so the spec is the same every time apart from the server - * URL, which is filled in fresh per response. Default true; set false in dev to - * rebuild on every request, e.g. `cache: process.env.NODE_ENV === 'production'`. - */ - cache?: boolean - extensions?: OpenApiExtension[] -} - -export interface UiPluginOptions { - /** - * Where the docs UI is served, relative to the Payload API route - * (`routes.api`, `/api` by default). So the `/docs` default is served at - * `/api/docs`. Default `/docs`. - */ - path?: string - /** Absolute URL the UI fetches the OpenAPI document from. Default `/openapi.json`. */ - specEndpoint?: string - /** CDN base for the UI's assets. Default is the official jsDelivr package. */ - cdnBase?: string - /** - * Extra UI config, merged into the underlying library's init options (Scalar's - * `createApiReference` config or Swagger UI's `SwaggerUIBundle`). Must be - * JSON-serializable, functions aren't supported here. - */ - configuration?: Record - /** Set false to skip mounting the docs UI. */ - enabled?: boolean -} - -// Resolved options - -export interface ResolvedFilters { - include: EntityMatcher[] - exclude: EntityMatcher[] - includeHidden: boolean - includeSystem: boolean - includeCustom: boolean - includeAuth: boolean - includeAdminAuth: boolean - includeVersions: boolean - includeJobs: boolean - excludeOperations: OperationRule[] - excludeWhen?: (ctx: OperationContext) => boolean -} - -export interface ResolvedOptions { - metadata: OpenApiMetadata - openapiVersion: OpenApiVersion - specEndpoint: string - serve: boolean - filters: ResolvedFilters - interactiveAuth: { enabled: boolean; endpoint: string } - nestedTags: boolean - securityWhen?: (ctx: OperationContext) => boolean | undefined - cache: boolean - extensions: OpenApiExtension[] -} - -// Build context - -export interface BuildContext { - defaultIDType: IDType - locales: string[] - apiRoute: string - /** Languages the docs are available in (project `i18n.supportedLanguages`). */ - docLanguages: string[] - /** Request i18n: resolves Payload entity descriptions and, via {@link makeT}, plugin strings. */ - i18n: I18n -} diff --git a/src/ui/createUiPlugin.ts b/src/ui/createUiPlugin.ts deleted file mode 100644 index 06a1ecc..0000000 --- a/src/ui/createUiPlugin.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type { Config, Plugin } from 'payload' -import type { UiPluginOptions } from '../types.js' - -export interface UiRenderArgs { - specUrl: string - cdnBase: string - title: string - configuration: Record -} - -export interface UiPluginSpec { - defaultDocsUrl: string - defaultCdnBase: string - render: (args: UiRenderArgs) => string -} - -export const createUiPlugin = - (spec: UiPluginSpec) => - (options: UiPluginOptions = {}): Plugin => - (config: Config): Config => { - if (options.enabled === false) return config - - const docsPath = options.path ?? spec.defaultDocsUrl - const specUrl = options.specEndpoint ?? `${config.routes?.api ?? '/api'}/openapi.json` - const cdnBase = options.cdnBase ?? spec.defaultCdnBase - const html = spec.render({ - specUrl, - cdnBase, - title: 'API Reference', - configuration: options.configuration ?? {}, - }) - - return { - ...config, - endpoints: [ - ...(config.endpoints ?? []), - { - path: docsPath, - method: 'get', - handler: () => new Response(html, { headers: { 'content-type': 'text/html; charset=utf-8' } }), - }, - ], - } - } diff --git a/src/ui/scalar.ts b/src/ui/scalar.ts deleted file mode 100644 index 7e0801e..0000000 --- a/src/ui/scalar.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { createUiPlugin } from './createUiPlugin.js' -import { escapeHtml, htmlPage, jsonForScript, specUrlExpr } from './html.js' - -export const scalar = createUiPlugin({ - defaultDocsUrl: '/docs', - defaultCdnBase: 'https://cdn.jsdelivr.net/npm/@scalar/api-reference', - render: ({ specUrl, cdnBase, title, configuration }) => - htmlPage({ - title, - head: '', - body: - `
` + - `` + - ``, - }), -}) diff --git a/src/ui/swagger.ts b/src/ui/swagger.ts deleted file mode 100644 index 52e9220..0000000 --- a/src/ui/swagger.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { createUiPlugin } from './createUiPlugin.js' -import { escapeHtml, htmlPage, jsonForScript, specUrlExpr } from './html.js' - -export const swaggerUi = createUiPlugin({ - defaultDocsUrl: '/docs', - defaultCdnBase: 'https://cdn.jsdelivr.net/npm/swagger-ui-dist', - render: ({ specUrl, cdnBase, title, configuration }) => - htmlPage({ - title, - head: ``, - body: - `
` + - `` + - ``, - }), -}) diff --git a/tests/app/(payload)/admin/importMap.js b/tests/app/(payload)/admin/importMap.js index af86423..2efdd32 100644 --- a/tests/app/(payload)/admin/importMap.js +++ b/tests/app/(payload)/admin/importMap.js @@ -1,6 +1,16 @@ -import { CollectionCards as CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1 } from '@payloadcms/next/rsc' +import { CollectionCards as CollectionCards_ab83ff7e88da8d3530831f296ec4756a } from '@payloadcms/ui/rsc' +import { CollectionQueryWidget as CollectionQueryWidget_ab83ff7e88da8d3530831f296ec4756a } from '@payloadcms/ui/rsc' +import { QueryPresetsWhereField as QueryPresetsWhereField_3817bf644402e67bfe6577f60ef982de } from '@payloadcms/ui' +import { CollectionQuerySortField as CollectionQuerySortField_3817bf644402e67bfe6577f60ef982de } from '@payloadcms/ui' +import { RecentlyViewedWidget as RecentlyViewedWidget_ab83ff7e88da8d3530831f296ec4756a } from '@payloadcms/ui/rsc' +import { RecentlyViewedCollectionsField as RecentlyViewedCollectionsField_3817bf644402e67bfe6577f60ef982de } from '@payloadcms/ui' /** @type import('payload').ImportMap */ export const importMap = { - "@payloadcms/next/rsc#CollectionCards": CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1 + "@payloadcms/ui/rsc#CollectionCards": CollectionCards_ab83ff7e88da8d3530831f296ec4756a, + "@payloadcms/ui/rsc#CollectionQueryWidget": CollectionQueryWidget_ab83ff7e88da8d3530831f296ec4756a, + "@payloadcms/ui#QueryPresetsWhereField": QueryPresetsWhereField_3817bf644402e67bfe6577f60ef982de, + "@payloadcms/ui#CollectionQuerySortField": CollectionQuerySortField_3817bf644402e67bfe6577f60ef982de, + "@payloadcms/ui/rsc#RecentlyViewedWidget": RecentlyViewedWidget_ab83ff7e88da8d3530831f296ec4756a, + "@payloadcms/ui#RecentlyViewedCollectionsField": RecentlyViewedCollectionsField_3817bf644402e67bfe6577f60ef982de } diff --git a/tests/collections/Folders.ts b/tests/collections/Folders.ts new file mode 100644 index 0000000..454f427 --- /dev/null +++ b/tests/collections/Folders.ts @@ -0,0 +1,8 @@ +import type { CollectionConfig } from 'payload' + +export const Folders: CollectionConfig = { + slug: 'folders', + admin: { useAsTitle: 'name' }, + folders: { collectionSpecific: true, joinField: { name: 'children' } }, + fields: [{ name: 'name', type: 'text', required: true }], +} diff --git a/tests/collections/Media.ts b/tests/collections/Media.ts index 007a4bb..39b5ed4 100644 --- a/tests/collections/Media.ts +++ b/tests/collections/Media.ts @@ -1,10 +1,10 @@ +import { createFolderField } from 'payload' import type { CollectionConfig } from 'payload' export const Media: CollectionConfig = { slug: 'media', upload: { mimeTypes: ['image/*'], - imageSizes: [{ name: 'thumbnail', width: 400, height: 300 }], }, - fields: [{ name: 'alt', type: 'text' }], + fields: [{ name: 'alt', type: 'text' }, createFolderField({ relationTo: 'folders' })], } diff --git a/tests/collections/Posts.ts b/tests/collections/Posts.ts index f8be009..679c8f9 100644 --- a/tests/collections/Posts.ts +++ b/tests/collections/Posts.ts @@ -3,7 +3,7 @@ import type { CollectionConfig } from 'payload' export const Posts: CollectionConfig = { slug: 'posts', admin: { description: 'Blog posts and articles' }, - versions: { drafts: { localizeStatus: true }, maxPerDoc: 10 }, + versions: { drafts: true, maxPerDoc: 10 }, fields: [ { name: 'title', type: 'text', required: true, localized: true }, { @@ -42,8 +42,7 @@ export const Posts: CollectionConfig = { { name: 'callouts', type: 'blocks', - blockReferences: ['callToAction'], - blocks: [], + blocks: ['callToAction'], }, { name: 'slug', type: 'text', unique: true }, { name: 'legacyField', type: 'text', custom: { openapi: { deprecated: true } } }, @@ -123,7 +122,7 @@ export const Posts: CollectionConfig = { type: 'array', fields: [ { type: 'row', fields: [{ name: 'tags', type: 'relationship', relationTo: 'tags', hasMany: true }] }, - { name: 'content', type: 'blocks', blockReferences: ['mediaBlock', 'callToAction'], blocks: [] }, + { name: 'content', type: 'blocks', blocks: ['mediaBlock', 'callToAction'] }, ], }, ], diff --git a/tests/collections/Tags.ts b/tests/collections/Tags.ts index caf292c..7c567bd 100644 --- a/tests/collections/Tags.ts +++ b/tests/collections/Tags.ts @@ -4,6 +4,7 @@ export const Tags: CollectionConfig = { slug: 'tags', admin: { useAsTitle: 'name' }, lockDocuments: false, + access: { read: () => true }, fields: [ { name: 'name', type: 'text', required: true }, { name: 'color', type: 'text' }, diff --git a/tests/collections/Users.ts b/tests/collections/Users.ts index f2cf8c5..d889659 100644 --- a/tests/collections/Users.ts +++ b/tests/collections/Users.ts @@ -3,6 +3,7 @@ import type { CollectionConfig } from 'payload' export const Users: CollectionConfig = { slug: 'users', auth: true, + versions: false, fields: [ { name: 'name', type: 'text' }, { diff --git a/tests/collections/index.ts b/tests/collections/index.ts index 78bce72..74fa60c 100644 --- a/tests/collections/index.ts +++ b/tests/collections/index.ts @@ -1,3 +1,4 @@ +export { Folders } from './Folders.js' export { Media } from './Media.js' export { Posts } from './Posts.js' export { Tags } from './Tags.js' diff --git a/tests/helpers.ts b/tests/helpers/context.ts similarity index 71% rename from tests/helpers.ts rename to tests/helpers/context.ts index 58e8c61..a212a17 100644 --- a/tests/helpers.ts +++ b/tests/helpers/context.ts @@ -1,10 +1,11 @@ -import { resolveOptions } from '../src/options.js' -import type { BuildContext } from '../src/types.js' -import type { Translate } from '../src/translations/types.js' -import { en } from '../src/translations/locales/en.js' -import { PLUGIN_NAME as NS } from '../src/constants.js' import { I18n, importDateFNSLocale } from '@payloadcms/translations' +import { resolveOptions } from '@/server/options/resolveOptions.js' +import { PLUGIN_NAME as NS } from '@/shared/constants.js' +import { en } from '@/shared/translations/locales/en.js' +import type { Translate } from '@/shared/translations/types.js' +import type { BuildContext } from '@/shared/types/index.js' + export const t: Translate = (key, vars) => { let str = en[NS][key] for (const [k, v] of Object.entries(vars ?? {})) { @@ -37,12 +38,12 @@ export const ctx: BuildContext = { } export const baseInput = (overrides: Record = {}) => ({ - options: resolveOptions({ metadata: { title: 'Test API', version: '1.0.0' } }), + options: resolveOptions({ info: { title: 'Test API', version: '1.0.0' } }), servers: [{ url: 'http://localhost:3000' }], collections: [], globals: [], config: {} as never, ctx, - logger: { warn() {} } as never, + payload: { logger: { warn() {} } } as never, ...overrides, }) diff --git a/tests/int.spec.ts b/tests/integration/spec.int.spec.ts similarity index 75% rename from tests/int.spec.ts rename to tests/integration/spec.int.spec.ts index e651c26..3892876 100644 --- a/tests/int.spec.ts +++ b/tests/integration/spec.int.spec.ts @@ -1,5 +1,3 @@ -import { getPayload } from 'payload' -import type { Access, SanitizedCollectionConfig } from 'payload' import type { Document, ParameterObject, @@ -9,25 +7,28 @@ import type { SchemaObject, TagObject, } from '@scalar/openapi-types/3.2' +import { getPayload } from 'payload' +import type { Access, SanitizedCollectionConfig } from 'payload' import { beforeAll, describe, expect, it } from 'vitest' -import { buildDocument } from '../src/spec/buildDocument.js' -import { toOpenApi30, toOpenApi31 } from '../src/spec/downconvert.js' -import { specHandler } from '../src/endpoints/spec.js' -import { resolveOptions } from '../src/options.js' -import type { BuildContext, FilterOptions } from '../src/types.js' -import { i18nStub } from './helpers.js' -import configPromise from './payload.config.js' +import { specHandler } from '@/server/endpoints/spec.js' +import { resolveOptions } from '@/server/options/resolveOptions.js' +import { buildDocument } from '@/server/spec/buildDocument.js' +import { toOpenApi30, toOpenApi31 } from '@/server/spec/downconvert.js' +import type { BuildContext, FilterOptions } from '@/shared/types/index.js' + +import { i18nStub } from '../helpers/context.js' +import configPromise from '../payload.config.js' let doc: Document let nestedDoc: Document -let idType: 'string' | 'integer' +let adminDoc: Document +let idType: 'string' | 'number' beforeAll(async () => { - process.env.USE_MEMORY_DB = '1' const config = await configPromise const payload = await getPayload({ config }) - idType = payload.db.defaultIDType === 'number' ? 'integer' : 'string' + idType = payload.db.defaultIDType === 'number' ? 'number' : 'string' const ctx: BuildContext = { defaultIDType: payload.db.defaultIDType, @@ -38,22 +39,31 @@ beforeAll(async () => { } const collections = Object.values(payload.collections).map((c) => c.config) doc = await buildDocument({ - options: resolveOptions({ metadata: { title: 'T', version: '1.0.0' } }), + options: resolveOptions({ info: { title: 'T', version: '1.0.0' } }), servers: [{ url: 'http://localhost' }], collections, globals: payload.config.globals, config: payload.config, ctx, - logger: payload.logger, + payload, + }) + adminDoc = await buildDocument({ + options: resolveOptions({ info: { title: 'T', version: '1.0.0' }, filters: { includeAdminAuth: true } }), + servers: [{ url: 'http://localhost' }], + collections, + globals: payload.config.globals, + config: payload.config, + ctx, + payload, }) nestedDoc = await buildDocument({ - options: resolveOptions({ metadata: { title: 'T', version: '1.0.0' }, nestedTags: true }), + options: resolveOptions({ info: { title: 'T', version: '1.0.0' }, nestedTags: true }), servers: [{ url: 'http://localhost' }], collections, globals: payload.config.globals, config: payload.config, ctx, - logger: payload.logger, + payload, }) }, 60_000) @@ -62,13 +72,20 @@ const schema = (name: string) => doc.components?.schemas?.[name] as SchemaObject describe('generated document', () => { describe('document validity', () => { it.each([ - ['3.2', (d: typeof doc) => d], + ['3.2', (d: Document) => d], ['3.1', toOpenApi31], ['3.0', toOpenApi30], ])('validates as a well-formed OpenAPI %s document', async (_version, convert) => { const { Validator } = await import('@seriousme/openapi-schema-validator') - const result = await new Validator().validate(convert(structuredClone(doc))) - expect(result.valid).toBe(true) + for (const d of [doc, nestedDoc, adminDoc]) { + const result = await new Validator().validate(convert(structuredClone(d))) + expect(result.errors).toBeUndefined() + expect(result.valid).toBe(true) + } + }) + + it('drops empty `required` arrays in 3.0, which needs at least one item', () => { + expect(JSON.stringify(toOpenApi30(structuredClone(doc)))).not.toContain('"required":[]') }) it('leaves no broken `$ref` to a lowercased slug', () => { @@ -88,26 +105,26 @@ describe('generated document', () => { it('adds a localization note to `info.description` when locales are configured', async () => { const payload = await getPayload({ config: await configPromise }) const localized = await buildDocument({ - options: resolveOptions({ metadata: { title: 'T', version: '1.0.0', description: 'Base.' } }), + options: resolveOptions({ info: { title: 'T', version: '1.0.0', description: 'Base.' } }), servers: [{ url: 'http://localhost' }], collections: [], globals: [], config: payload.config, ctx: { defaultIDType: 'text', locales: ['en', 'de'], apiRoute: '/api', docLanguages: ['en'], i18n: i18nStub }, - logger: payload.logger, + payload, }) expect(localized.info.description).toContain('Base.') expect(localized.info.description).toContain('locale=all') expect(localized.info.description).toContain('`en`') const noLocale = await buildDocument({ - options: resolveOptions({ metadata: { title: 'T', version: '1.0.0' } }), + options: resolveOptions({ info: { title: 'T', version: '1.0.0' } }), servers: [{ url: 'http://localhost' }], collections: [], globals: [], config: payload.config, ctx: { defaultIDType: 'text', locales: [], apiRoute: '/api', docLanguages: ['en'], i18n: i18nStub }, - logger: payload.logger, + payload, }) expect(noLocale.info.description ?? '').not.toContain('locale=all') }) @@ -164,7 +181,7 @@ describe('generated document', () => { it('writes relationships as ids in create bodies', () => { const author = schema('PostsCreate').properties?.author as SchemaObject - expect(author.type).toEqual([idType, 'null']) + expect(author).toEqual({ type: [idType, 'null'] }) const tags = schema('PostsCreate').properties?.tags as SchemaObject expect(tags.type).toEqual(['array', 'null']) expect((tags.items as SchemaObject).type).toBe(idType) @@ -183,7 +200,7 @@ describe('generated document', () => { it('writes an upload field as an id in create bodies', () => { const featured = schema('PostsCreate').properties?.featuredImage as SchemaObject - expect(featured.type).toEqual([idType, 'null']) + expect(featured).toEqual({ type: [idType, 'null'] }) }) it('writes relationships nested in groups, named tabs, arrays and blocks as ids', () => { @@ -201,6 +218,7 @@ describe('generated document', () => { expect(row.tags).toEqual({ type: ['array', 'null'], items: { type: idType } }) const blocks = ((row.content as SchemaObject).items as SchemaObject).oneOf as SchemaObject[] expect(blocks[0]?.properties?.media).toEqual({ type: idType }) + expect(blocks[1]).toEqual({ $ref: '#/components/schemas/BlockCallToAction' }) const layout = ((create.layout as SchemaObject).items as SchemaObject).oneOf as SchemaObject[] const gallery = layout.find((b) => (b.properties?.blockType as SchemaObject | undefined)?.const === 'gallery') @@ -211,15 +229,26 @@ describe('generated document', () => { it('keeps the populated document in nested read shapes', () => { const read = schema('Posts').properties ?? {} const hero = (read.hero as SchemaObject).properties ?? {} - expect((hero.image as SchemaObject).oneOf?.[1]).toEqual({ $ref: '#/components/schemas/Media' }) - const related = ((read.related as SchemaObject).items as SchemaObject).oneOf as SchemaObject[] - const value = related[0]?.properties?.value as SchemaObject - expect(value.oneOf?.[1]).toEqual({ $ref: '#/components/schemas/Posts' }) + const image = hero.image as SchemaObject + expect(image.oneOf?.[1]).toEqual({ $ref: '#/components/schemas/Media' }) const row = ((read.sections as SchemaObject).items as SchemaObject).properties ?? {} const blocks = ((row.content as SchemaObject).items as SchemaObject).oneOf as SchemaObject[] expect(blocks[0]).toEqual({ $ref: '#/components/schemas/BlockMedia' }) - const media = schema('BlockMedia').properties?.media as SchemaObject - expect(media.oneOf?.[1]).toEqual({ $ref: '#/components/schemas/Media' }) + }) + + it('leaves join fields and timestamps out of create bodies', () => { + expect(schema('UsersCreate').properties?.posts).toBeUndefined() + expect(schema('PostsCreate').properties?.createdAt).toBeUndefined() + expect(schema('PostsCreate').properties?.updatedAt).toBeUndefined() + }) + + it('leaves `createdBy` and `updatedBy` out of write bodies and keeps them on read', () => { + for (const field of ['createdBy', 'updatedBy']) { + expect(schema('Posts').properties?.[field]).toBeDefined() + expect(schema('PostsCreate').properties?.[field]).toBeUndefined() + expect(schema('PostsUpdate').properties?.[field]).toBeUndefined() + expect(schema('GlobalSettingsUpdate').properties?.[field]).toBeUndefined() + } }) }) @@ -319,6 +348,17 @@ describe('generated document', () => { expect(mpSchema.required).toBeUndefined() expect(body.required).toBe(false) }) + + it('documents the file and upload instructions endpoints for upload collections', () => { + expect(doc.paths['/api/media/file/{filename}']?.get).toBeDefined() + expect(doc.paths['/api/users/file/{filename}']).toBeUndefined() + const body = doc.paths['/api/upload-instructions']?.post?.requestBody as RequestBodyObject + const bodySchema = body.content['application/json'].schema as SchemaObject + const slug = bodySchema.properties?.collectionSlug as SchemaObject + expect(slug.enum).toEqual(['media']) + expect(doc.paths['/api/upload-instructions/{uploadId}']?.put).toBeDefined() + expect(doc.paths['/api/upload-instructions/{uploadId}']?.delete).toBeDefined() + }) }) describe('query params', () => { @@ -418,6 +458,63 @@ describe('generated document', () => { }) }) + describe('hierarchy', () => { + const paramNames = (op?: { parameters?: unknown[] }) => + ((op?.parameters ?? []) as ParameterObject[]).map((p) => p.name) + + it('documents a folders collection with the standard collection endpoints only', () => { + const folderPaths = Object.keys(doc.paths).filter((p) => p.startsWith('/api/folders')) + expect(folderPaths.toSorted()).toEqual([ + '/api/folders', + '/api/folders/count', + '/api/folders/validate', + '/api/folders/versions', + '/api/folders/versions/{id}', + '/api/folders/{id}', + '/api/folders/{id}/duplicate', + '/api/folders/{id}/validate', + ]) + expect(Object.keys(doc.paths).some((p) => p.includes('payload-folders'))).toBe(false) + }) + + it('adds the parent, scope and join fields to the folder read schema', () => { + const props = schema('Folders').properties ?? {} + expect(props._h_folders).toBeDefined() + expect((props.hierarchyType as SchemaObject).items).toEqual({ type: 'string', enum: ['media'] }) + const children = (props.children as SchemaObject).properties?.docs as SchemaObject + for (const branch of ((children.items as SchemaObject).oneOf ?? []) as SchemaObject[]) { + expect(branch.required).toEqual(['relationTo', 'value']) + } + }) + + it('marks the computed path fields read-only and keeps them out of bodies and `where`', () => { + for (const name of ['_h_slugPath', '_h_titlePath']) { + const field = schema('Folders').properties?.[name] as SchemaObject + expect(field.readOnly).toBe(true) + expect(field.description).toContain('computeHierarchyPaths=true') + expect(schema('FoldersCreate').properties?.[name]).toBeUndefined() + expect(schema('FoldersUpdate').properties?.[name]).toBeUndefined() + expect(schema('FoldersQueryOperations').properties?.[name]).toBeUndefined() + expect(schema('FoldersSelect').properties?.[name]).toEqual({ type: 'boolean' }) + } + expect(schema('FoldersQueryOperations').properties?._h_folders).toBeDefined() + }) + + it('lists `computeHierarchyPaths` on the folder reads only', () => { + expect(paramNames(doc.paths['/api/folders']?.get)).toContain('computeHierarchyPaths') + expect(paramNames(doc.paths['/api/folders/{id}']?.get)).toContain('computeHierarchyPaths') + expect(paramNames(doc.paths['/api/folders']?.post)).not.toContain('computeHierarchyPaths') + expect(paramNames(doc.paths['/api/folders/{id}']?.patch)).not.toContain('computeHierarchyPaths') + expect(paramNames(doc.paths['/api/media']?.get)).not.toContain('computeHierarchyPaths') + }) + + it('adds the folder field to a related collection', () => { + expect(schema('Media').properties?._h_folders).toBeDefined() + expect(schema('MediaCreate').properties?._h_folders).toMatchObject({ type: [idType, 'null'] }) + expect(schema('MediaQueryOperations').properties?._h_folders).toBeDefined() + }) + }) + describe('paths/auth', () => { it('documents the auth endpoints for auth collections', () => { expect(doc.paths['/api/users/login']?.post).toBeDefined() @@ -432,26 +529,15 @@ describe('generated document', () => { it('omits the admin and bootstrap auth endpoints by default', () => { expect(doc.paths['/api/users/first-register']).toBeUndefined() expect(doc.paths['/api/users/init']).toBeUndefined() + expect(doc.paths['/api/access']).toBeUndefined() expect(doc.paths['/api/users/access']).toBeUndefined() }) - it('documents the admin and bootstrap auth endpoints when `adminAuthEndpoints` is on', async () => { - const payload = await getPayload({ config: await configPromise }) - const adminDoc = await buildDocument({ - options: resolveOptions({ - metadata: { title: 'T', version: '1.0.0' }, - filters: { includeAdminAuth: true }, - }), - servers: [{ url: 'http://localhost' }], - collections: Object.values(payload.collections).map((c) => c.config), - globals: [], - config: payload.config, - ctx: { defaultIDType: 'text', locales: [], apiRoute: '/api', docLanguages: ['en'], i18n: i18nStub }, - logger: payload.logger, - }) + it('documents the admin and bootstrap auth endpoints when `adminAuthEndpoints` is on', () => { expect(adminDoc.paths['/api/users/first-register']?.post).toBeDefined() expect(adminDoc.paths['/api/users/init']?.get).toBeDefined() - expect(adminDoc.paths['/api/users/access']?.get).toBeDefined() + expect(adminDoc.paths['/api/access']?.get).toBeDefined() + expect(adminDoc.paths['/api/users/access']?.get).toBeUndefined() }) it('does not document auth endpoints for non-auth collections', () => { @@ -459,6 +545,45 @@ describe('generated document', () => { }) }) + describe('paths/access', () => { + it('documents the per-document access endpoints only with `includeAdminAuth`', () => { + expect(doc.paths['/api/posts/access/{id}']).toBeUndefined() + expect(doc.paths['/api/globals/settings/access']).toBeUndefined() + + const props = (op?: { responses?: Record }) => { + const ok = op?.responses?.['200'] as ResponseObject + const body = ok.content?.['application/json']?.schema as SchemaObject + return Object.keys(body.properties ?? {}) + } + const byId = adminDoc.paths['/api/posts/access/{id}']?.post + expect(byId?.operationId).toBe('accessPostsById') + expect(byId?.security).toBeUndefined() + const requestBody = byId?.requestBody as RequestBodyObject + expect(requestBody.content?.['application/json']?.schema).toEqual({ + $ref: '#/components/schemas/PostsUpdate', + }) + expect(props(byId)).toEqual(['create', 'read', 'update', 'delete', 'validate', 'readVersions', 'fields']) + expect(byId?.responses?.['404']).toBeDefined() + expect(adminDoc.paths['/api/posts/access']?.post?.operationId).toBe('accessPosts') + expect(props(adminDoc.paths['/api/users/access/{id}']?.post)).toEqual([ + 'create', + 'read', + 'update', + 'delete', + 'validate', + 'unlock', + 'fields', + ]) + expect(props(adminDoc.paths['/api/globals/settings/access']?.post)).toEqual([ + 'read', + 'update', + 'validate', + 'readVersions', + 'fields', + ]) + }) + }) + describe('paths/versions', () => { it('documents the version endpoints for versioned collections', () => { expect(doc.paths['/api/posts/versions']?.get).toBeDefined() @@ -599,7 +724,7 @@ describe('generated document', () => { } const serveSpec = async (url: string): Promise => { const payload = await getPayload({ config: await configPromise }) - const handler = specHandler(resolveOptions({ metadata: { title: 'T', version: '1.0.0' }, cache: false })) + const handler = specHandler(resolveOptions({ info: { title: 'T', version: '1.0.0' }, cache: false })) const res = await handler({ url, payload, headers: new Headers(), i18n: i18nStub } as never) return (await res.json()) as Document } @@ -648,13 +773,13 @@ describe('filters', () => { const build = async (filters: Partial): Promise => { const payload = await getPayload({ config: await configPromise }) return buildDocument({ - options: resolveOptions({ metadata: { title: 'T', version: '1.0.0' }, filters }), + options: resolveOptions({ info: { title: 'T', version: '1.0.0' }, filters }), servers: [{ url: 'http://localhost' }], collections: Object.values(payload.collections).map((c) => c.config), globals: payload.config.globals, config: payload.config, ctx: { defaultIDType: 'text', locales: [], apiRoute: '/api', docLanguages: ['en'], i18n: i18nStub }, - logger: payload.logger, + payload, }) } @@ -689,7 +814,7 @@ describe('security marking', () => { const buildWithReadAccess = async ( read: Access, - options = resolveOptions({ metadata: { title: 'T', version: '1.0.0' } }), + options = resolveOptions({ info: { title: 'T', version: '1.0.0' } }), ): Promise => { const payload = await getPayload({ config: await configPromise }) const base = Object.values(payload.collections).find((c) => c.config.slug === 'tags')!.config @@ -701,7 +826,7 @@ describe('security marking', () => { globals: [], config: payload.config, ctx: { defaultIDType: 'text', locales: [], apiRoute: '/api', docLanguages: ['en'], i18n: i18nStub }, - logger: payload.logger, + payload, }) } @@ -714,6 +839,25 @@ describe('security marking', () => { expect(d.paths?.['/api/tags']?.get?.security).toEqual(secured) }) + it('marks a public read from the collection config as public after Payload sanitizes it', () => { + expect(doc.paths['/api/tags']?.get?.security).toBeUndefined() + expect(doc.paths['/api/tags/{id}']?.get?.security).toBeUndefined() + expect(doc.paths['/api/tags']?.post?.security).toEqual(secured) + expect(doc.paths['/api/tags/{id}/validate']?.post?.security).toEqual(secured) + }) + + it('marks version endpoints from `readVersions` and `update`, not as public', () => { + expect(doc.paths['/api/posts/versions']?.get?.security).toEqual(secured) + expect(doc.paths['/api/posts/versions/{id}']?.get?.security).toEqual(secured) + expect(doc.paths['/api/posts/versions/{id}']?.post?.security).toEqual(secured) + expect(doc.paths['/api/globals/settings/versions']?.get?.security).toEqual(secured) + }) + + it('marks the jobs endpoints as secured by default', () => { + expect(doc.paths['/api/payload-jobs/run']?.get?.security).toEqual(secured) + expect(doc.paths['/api/payload-jobs/handle-schedules']?.get?.security).toEqual(secured) + }) + it('marks a plain public read function as public, leaving other operations secured', async () => { const d = await buildWithReadAccess(() => true) expect(d.paths?.['/api/tags']?.get?.security).toBeUndefined() @@ -721,12 +865,12 @@ describe('security marking', () => { expect(d.paths?.['/api/tags/{id}']?.delete?.security).toEqual(secured) }) - it('lets securityWhen override the marking per operation in both directions', async () => { + it('lets `security` override the marking per operation in both directions', async () => { const options = resolveOptions({ - metadata: { title: 'T', version: '1.0.0' }, - securityWhen: ({ method }) => { - if (method === 'get') return false - if (method === 'post') return true + info: { title: 'T', version: '1.0.0' }, + security: ({ method }) => { + if (method === 'get') return 'secured' + if (method === 'post') return 'public' return undefined }, }) diff --git a/tests/next-env.d.ts b/tests/next-env.d.ts index c4b7818..a419cbe 100644 --- a/tests/next-env.d.ts +++ b/tests/next-env.d.ts @@ -1,6 +1,7 @@ /// /// import "./.next/dev/types/routes.d.ts"; +import "./.next/dev/types/root-params.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/tests/next.config.mjs b/tests/next.config.mjs index 0693ca5..ecf0c35 100644 --- a/tests/next.config.mjs +++ b/tests/next.config.mjs @@ -10,7 +10,7 @@ const nextConfig = { } return webpackConfig }, - serverExternalPackages: ['mongodb-memory-server'], + serverExternalPackages: ['@seshuk/payload-plugin-tooling', 'mongodb-memory-server'], } export default withPayload(nextConfig, { devBundleServerPackages: false }) diff --git a/tests/payload.config.ts b/tests/payload.config.ts index 02328dc..060908e 100644 --- a/tests/payload.config.ts +++ b/tests/payload.config.ts @@ -1,28 +1,28 @@ -import { sqliteAdapter } from '@payloadcms/db-sqlite' +import path from 'node:path' +import { fileURLToPath } from 'node:url' + import { de } from '@payloadcms/translations/languages/de' import { en } from '@payloadcms/translations/languages/en' import { fr } from '@payloadcms/translations/languages/fr' -import path from 'node:path' -import { fileURLToPath } from 'node:url' +import { testDatabase } from '@seshuk/payload-plugin-tooling/test-database' import { buildConfig } from 'payload' -import { openapi, scalar, swaggerUi } from '../src/index.js' +import { openapi, scalar, swaggerUi } from '@/index.js' + import { CallToAction } from './blocks/CallToAction.js' import { MediaBlock } from './blocks/MediaBlock.js' -import { Media, Posts, Tags, Users } from './collections/index.js' +import { Folders, Media, Posts, Tags, Users } from './collections/index.js' import { Settings } from './globals/Settings.js' const dirname = path.dirname(fileURLToPath(import.meta.url)) const devUser = { email: 'dev@example.com', password: 'dev' } -const buildConfigAsync = async () => { - const useMemory = process.env.NODE_ENV === 'test' || process.env.USE_MEMORY_DB === '1' - - return buildConfig({ +const buildConfigAsync = async () => + buildConfig({ secret: process.env.PAYLOAD_SECRET || 'dev-secret', + telemetry: false, typescript: { autoGenerate: false }, - experimental: { localizeStatus: true }, admin: { user: 'users', importMap: { baseDir: path.resolve(dirname) } }, i18n: { supportedLanguages: { en, de, fr }, @@ -31,11 +31,7 @@ const buildConfigAsync = async () => { locales: ['en', 'de', 'fr'], defaultLocale: 'en', }, - db: sqliteAdapter({ - client: { - url: useMemory ? ':memory:' : process.env.DATABASE_URI || `file:${path.resolve(dirname, 'payload.db')}`, - }, - }), + db: await testDatabase(), blocks: [CallToAction, MediaBlock], jobs: { tasks: [ @@ -47,11 +43,11 @@ const buildConfigAsync = async () => { }, ], }, - collections: [Users, Posts, Media, Tags], + collections: [Users, Posts, Media, Tags, Folders], globals: [Settings], plugins: [ openapi({ - metadata: { title: 'Dev API', version: '1.0.0' }, + info: { title: 'Dev API', version: '1.0.0' }, filters: { includeAdminAuth: true }, interactiveAuth: true, cache: process.env.NODE_ENV === 'production', @@ -62,13 +58,13 @@ const buildConfigAsync = async () => { onInit: async (payload) => { const existing = await payload.find({ collection: 'users', + overrideAccess: true, where: { email: { equals: devUser.email } }, }) if (existing.docs.length === 0) { - await payload.create({ collection: 'users', data: devUser }) + await payload.create({ collection: 'users', data: devUser, overrideAccess: true }) } }, }) -} export default buildConfigAsync() diff --git a/tests/tsconfig.json b/tests/tsconfig.json index 171eaa3..82a5b59 100644 --- a/tests/tsconfig.json +++ b/tests/tsconfig.json @@ -19,7 +19,8 @@ } ], "paths": { - "@payload-config": ["./payload.config.ts"] + "@payload-config": ["./payload.config.ts"], + "@/*": ["../src/*"] } }, "include": ["**/*.ts", "**/*.tsx", "next-env.d.ts", ".next/types/**/*.ts", ".next/dev/types/**/*.ts"], diff --git a/tests/unit/hierarchy.spec.ts b/tests/unit/hierarchy.spec.ts new file mode 100644 index 0000000..bd7b945 --- /dev/null +++ b/tests/unit/hierarchy.spec.ts @@ -0,0 +1,61 @@ +import type { ParameterObject, PathsObject, SchemaObject } from '@scalar/openapi-types/3.2' +import type { SanitizedCollectionConfig } from 'payload' +import { describe, expect, it } from 'vitest' + +import { applyHierarchy } from '@/server/spec/hierarchy.js' + +import { ctx } from '../helpers/context.js' + +const text: SchemaObject = { type: 'string' } + +const setup = (hierarchy: SanitizedCollectionConfig['hierarchy']) => { + const paths: PathsObject = { + '/api/folders': { get: { parameters: [] }, post: { parameters: [] } }, + '/api/folders/{id}': { get: { parameters: [] }, patch: { parameters: [] } }, + } + const props = () => ({ name: text, crumbs: text, titles: text }) + const schemas: Record = { + Folders: { type: 'object', properties: props() }, + FoldersCreate: { type: 'object', properties: props() }, + FoldersUpdate: { type: 'object', properties: props() }, + FoldersQueryOperations: { type: 'object', properties: props() }, + FoldersSelect: { type: 'object', properties: props() }, + } + const collection = { slug: 'folders', hierarchy } as unknown as SanitizedCollectionConfig + applyHierarchy({ collection, base: 'Folders', paths, schemas, ctx }) + return { paths, schemas } +} + +const names = (op: unknown) => ((op as { parameters: ParameterObject[] }).parameters ?? []).map((p) => p.name) + +describe('applyHierarchy', () => { + it('uses the configured path field names', () => { + const { paths, schemas } = setup({ + slugPathFieldName: 'crumbs', + titlePathFieldName: 'titles', + } as SanitizedCollectionConfig['hierarchy']) + + for (const name of ['crumbs', 'titles']) { + expect(schemas.Folders.properties?.[name]).toMatchObject({ type: 'string', readOnly: true }) + expect(schemas.FoldersCreate.properties?.[name]).toBeUndefined() + expect(schemas.FoldersUpdate.properties?.[name]).toBeUndefined() + expect(schemas.FoldersQueryOperations.properties?.[name]).toBeUndefined() + expect(schemas.FoldersSelect.properties?.[name]).toEqual(text) + } + expect(schemas.FoldersCreate.properties?.name).toEqual(text) + + const param = paths['/api/folders']?.get?.parameters?.[0] as ParameterObject + expect(param).toMatchObject({ name: 'computeHierarchyPaths', in: 'query', schema: { type: 'boolean' } }) + expect(param.description).toContain('`crumbs`') + expect(names(paths['/api/folders/{id}']?.get)).toEqual(['computeHierarchyPaths']) + expect(names(paths['/api/folders']?.post)).toEqual([]) + expect(names(paths['/api/folders/{id}']?.patch)).toEqual([]) + }) + + it('leaves a collection without hierarchy untouched', () => { + const { paths, schemas } = setup(false) + expect(schemas.Folders.properties?.crumbs).toEqual(text) + expect(schemas.FoldersCreate.properties?.crumbs).toEqual(text) + expect(names(paths['/api/folders']?.get)).toEqual([]) + }) +}) diff --git a/tests/unit.spec.ts b/tests/unit/index.spec.ts similarity index 50% rename from tests/unit.spec.ts rename to tests/unit/index.spec.ts index 3a369d4..5b5fad1 100644 --- a/tests/unit.spec.ts +++ b/tests/unit/index.spec.ts @@ -1,24 +1,32 @@ +import type { I18n } from '@payloadcms/translations' +import type { Document, PathsObject, RequestBodyObject, SchemaObject } from '@scalar/openapi-types/3.2' import { Validator } from '@seriousme/openapi-schema-validator' import type { + CollectionConfig, Config, Endpoint, Field, + GlobalConfig, PayloadRequest, Plugin, SanitizedCollectionConfig, SanitizedConfig, SanitizedGlobalConfig, + TextField, } from 'payload' import { flattenAllFields } from 'payload' -import type { I18n } from '@payloadcms/translations' -import { describe, expect, it } from 'vitest' - -import { resolveOptions } from '../src/options.js' -import { PLUGIN_NAME } from '../src/constants.js' -import { buildDocument } from '../src/spec/buildDocument.js' -import { flattenFields } from '../src/spec/fields.js' -import { filterOperations, shouldIncludeCollection, shouldIncludeGlobal } from '../src/spec/filters.js' -import { rewriteRefs, schemaName } from '../src/spec/names.js' +import { describe, expect, expectTypeOf, it } from 'vitest' + +import { resolveOptions } from '@/server/options/resolveOptions.js' +import { buildDocument } from '@/server/spec/buildDocument.js' +import { flattenFields } from '@/server/spec/fields.js' +import { filterOperations, shouldIncludeCollection, shouldIncludeGlobal } from '@/server/spec/filters.js' +import { rewriteRefs, schemaName } from '@/server/spec/names.js' +import { buildAuthPaths } from '@/server/spec/paths/auth.js' +import { buildCollectionPaths } from '@/server/spec/paths/collections.js' +import { buildCustomEndpointPaths } from '@/server/spec/paths/custom.js' +import { buildSystemPaths } from '@/server/spec/paths/system.js' +import { applySecurity, evaluateAccess, resolveEntitySecurity } from '@/server/spec/security.js' import { NAV_COLLECTIONS, NAV_GLOBALS, @@ -27,15 +35,12 @@ import { buildTagHierarchy, entityTagName, versionsTagName, -} from '../src/spec/tags.js' -import type { Translate } from '../src/translations/types.js' -import { buildCustomEndpointPaths } from '../src/spec/paths/custom.js' -import { buildCollectionPaths } from '../src/spec/paths/collections.js' -import { buildAuthPaths } from '../src/spec/paths/auth.js' -import { applySecurityWhen, evaluateAccess, resolveEntitySecurity } from '../src/spec/security.js' -import type { Document, PathsObject, RequestBodyObject, SchemaObject } from '@scalar/openapi-types/3.2' -import type { OpenApiExtension, ResolvedFilters } from '../src/types.js' -import { baseInput, ctx, i18nStub, t } from './helpers.js' +} from '@/server/spec/tags.js' +import { PLUGIN_NAME } from '@/shared/constants.js' +import type { Translate } from '@/shared/translations/types.js' +import type { EntityOpenApiMeta, FieldOpenApiMeta, OpenApiExtension, ResolvedFilters } from '@/shared/types/index.js' + +import { baseInput, ctx, i18nStub, t } from '../helpers/context.js' describe('spec/names', () => { describe('schemaName', () => { @@ -58,6 +63,20 @@ describe('spec/names', () => { const input = { $ref: '#/components/schemas/X' } expect(rewriteRefs(input)).toEqual(input) }) + + it('inlines collected `$defs` but keeps refs to config-level blocks', () => { + const defs = new Map([ + ['Hero', { type: 'object', properties: { blockType: { const: 'hero' } } }], + ['CallToAction', { type: 'object' }], + ]) + const input = { oneOf: [{ $ref: '#/$defs/Hero' }, { $ref: '#/$defs/CallToAction' }] } + expect(rewriteRefs(input, new Set(['callToAction']), defs)).toEqual({ + oneOf: [ + { type: 'object', properties: { blockType: { const: 'hero' } } }, + { $ref: '#/components/schemas/BlockCallToAction' }, + ], + }) + }) }) }) @@ -115,6 +134,17 @@ describe('spec/filters', () => { expect(shouldIncludeCollection(coll('posts'), f())).toBe(true) }) + it('hides the `payload-kv`, `payload-llm-instructions` and `payload-query-presets` collections unless `includeSystem` is set', () => { + expect(shouldIncludeCollection(coll('payload-kv'), f())).toBe(false) + expect(shouldIncludeCollection(coll('payload-query-presets'), f())).toBe(false) + expect(shouldIncludeCollection(coll('payload-llm-instructions'), f())).toBe(false) + expect(shouldIncludeCollection(coll('payload-kv'), f({ includeSystem: true }))).toBe(true) + }) + + it('documents a `payload-folders` collection, which Payload 4 no longer creates', () => { + expect(shouldIncludeCollection(coll('payload-folders'), f())).toBe(true) + }) + it('hides hidden collections unless `includeHidden` is set', () => { const hidden = coll('drafts', { admin: { hidden: true } }) expect(shouldIncludeCollection(hidden, f())).toBe(false) @@ -129,12 +159,6 @@ describe('spec/filters', () => { expect(shouldIncludeCollection(hiddenFromAnon, f({ includeHidden: true }))).toBe(true) }) - it('hides the `payload-kv` and `payload-query-presets` collections unless `includeSystem` is set', () => { - expect(shouldIncludeCollection(coll('payload-kv'), f())).toBe(false) - expect(shouldIncludeCollection(coll('payload-query-presets'), f())).toBe(false) - expect(shouldIncludeCollection(coll('payload-kv'), f({ includeSystem: true }))).toBe(true) - }) - it('keeps a collection whose `admin.hidden` function throws', () => { const thrower = coll('drafts', { admin: { @@ -162,6 +186,12 @@ describe('spec/filters', () => { ) expect(shouldIncludeCollection(coll('posts'), filters)).toBe(true) }) + + it('hides the `payload-jobs-stats` global unless `includeSystem` is on', () => { + const stats = coll('payload-jobs-stats') as unknown as Parameters[0] + expect(shouldIncludeGlobal(stats, f())).toBe(false) + expect(shouldIncludeGlobal(stats, f({ includeSystem: true }))).toBe(true) + }) }) describe('filterOperations', () => { @@ -170,8 +200,8 @@ describe('spec/filters', () => { '/api/posts/{id}': { get: {}, patch: {}, delete: {} }, }) - it('drops a method across all paths and removes the emptied paths', () => { - const out = filterOperations({ + it('drops a method across all paths and removes the emptied paths', async () => { + const out = await filterOperations({ paths: paths(), slug: 'posts', kind: 'collection', @@ -182,8 +212,8 @@ describe('spec/filters', () => { expect(out['/api/posts']?.get).toBeDefined() }) - it('targets a single entity by slug', () => { - const out = filterOperations({ + it('targets a single entity by slug', async () => { + const out = await filterOperations({ paths: paths(), slug: 'tags', kind: 'collection', @@ -192,8 +222,8 @@ describe('spec/filters', () => { expect(out['/api/posts']?.post).toBeDefined() }) - it('matches a path by regex to tell list from by-id', () => { - const out = filterOperations({ + it('matches a path by regex to tell list from by-id', async () => { + const out = await filterOperations({ paths: paths(), slug: 'posts', kind: 'collection', @@ -203,8 +233,8 @@ describe('spec/filters', () => { expect(out['/api/posts/{id}']?.get).toBeUndefined() }) - it('removes a path entirely when all of its methods are excluded', () => { - const out = filterOperations({ + it('removes a path entirely when all of its methods are excluded', async () => { + const out = await filterOperations({ paths: paths(), slug: 'posts', kind: 'collection', @@ -214,8 +244,8 @@ describe('spec/filters', () => { expect(out['/api/posts']).toBeDefined() }) - it('respects `kind` so globals and collections do not collide', () => { - const out = filterOperations({ + it('respects `kind` so globals and collections do not collide', async () => { + const out = await filterOperations({ paths: paths(), slug: 'posts', kind: 'collection', @@ -224,16 +254,41 @@ describe('spec/filters', () => { expect(out['/api/posts']?.get).toBeDefined() }) - it('applies the `excludeWhen` predicate', () => { - const out = filterOperations({ + it('applies a function entry next to the rules', async () => { + const out = await filterOperations({ paths: paths(), slug: 'posts', kind: 'collection', - filters: f({ excludeWhen: ({ method }) => method === 'patch' }), + filters: f({ excludeOperations: [{ method: 'delete' }, ({ method }) => method === 'patch'] }), }) expect(out['/api/posts']?.patch).toBeUndefined() + expect(out['/api/posts']?.delete).toBeUndefined() expect(out['/api/posts']?.get).toBeDefined() }) + + it('awaits an async function and excludes only on `true`', async () => { + const out = await filterOperations({ + paths: paths(), + slug: 'posts', + kind: 'collection', + filters: f({ + excludeOperations: [async ({ method }) => method === 'patch', (() => 'yes') as unknown as () => boolean], + }), + }) + expect(out['/api/posts']?.patch).toBeUndefined() + expect(out['/api/posts']?.get).toBeDefined() + expect(out['/api/posts/{id}']?.delete).toBeDefined() + }) + + it('never matches a `slug` rule against an operation without a slug', async () => { + const out = await filterOperations({ + paths: paths(), + kind: 'custom', + filters: f({ excludeOperations: [{ slug: /.*/ }, { kind: 'custom', method: 'delete' }] }), + }) + expect(out['/api/posts']?.get).toBeDefined() + expect(out['/api/posts']?.delete).toBeUndefined() + }) }) }) @@ -249,7 +304,7 @@ describe('spec/tags', () => { const tags = buildTagHierarchy({ collections: [{ base: 'Posts', hasAuth: false, hasVersions: true, description: 'Blog posts' }], globals: [{ base: 'GlobalSettings', hasVersions: false }], - hasJobs: true, + systemTags: ['Jobs'], t: tt, nested: true, }) @@ -268,12 +323,28 @@ describe('spec/tags', () => { expect(byName['Jobs']?.parent).toBe(NAV_SYSTEM) }) + it('puts the Uploads and Access tags under the System group', () => { + const tt = ((key: string) => key) as Translate + const tags = buildTagHierarchy({ + collections: [], + globals: [], + systemTags: ['Uploads', 'Access'], + t: tt, + nested: true, + }) + const byName = Object.fromEntries(tags.map((tag) => [tag.name, tag])) + expect(byName['Uploads']?.parent).toBe(NAV_SYSTEM) + expect(byName['Uploads']?.summary).toBe('tagUploads') + expect(byName['Access']?.parent).toBe(NAV_SYSTEM) + expect(byName['Jobs']).toBeUndefined() + }) + it('builds a flat list of entity tags with descriptions when not nested', () => { const tt = ((key: string) => key) as Translate const tags = buildTagHierarchy({ collections: [{ base: 'Posts', hasAuth: true, hasVersions: true, description: 'Blog posts' }], globals: [{ base: 'GlobalSettings', hasVersions: true, description: 'Site config' }], - hasJobs: true, + systemTags: ['Jobs'], t: tt, nested: false, }) @@ -309,10 +380,10 @@ describe('spec/buildDocument', () => { const doc = await buildDocument( baseInput({ options: resolveOptions({ - metadata: { title: 'T', version: '1.0.0' }, + info: { title: 'T', version: '1.0.0' }, extensions: [ { paths: { '/api/custom': { get: { responses: { '200': { description: 'ok' } } } } } }, - { transform: (d) => ({ ...d, info: { ...d.info, title: 'Transformed' } }) }, + { transform: ({ doc: d }) => ({ ...d, info: { ...d.info, title: 'Transformed' } }) }, ], }), }), @@ -321,11 +392,11 @@ describe('spec/buildDocument', () => { expect(doc.info.title).toBe('Transformed') }) - it('keeps building when an extension throws', async () => { - const doc = await buildDocument( + it('fails closed when an extension `transform` throws', async () => { + const build = buildDocument( baseInput({ options: resolveOptions({ - metadata: { title: 'T', version: '1.0.0' }, + info: { title: 'T', version: '1.0.0' }, extensions: [ { transform: () => { @@ -336,15 +407,197 @@ describe('spec/buildDocument', () => { }), }), ) - expect(doc.info.title).toBe('T') + await expect(build).rejects.toThrow('boom') + }) + + it('fails closed when an extension `transform` returns the context, not the document', async () => { + const build = buildDocument( + baseInput({ + options: resolveOptions({ + info: { title: 'T', version: '1.0.0' }, + extensions: [{ transform: (d) => ({ ...d }) as never }], + }), + }), + ) + await expect(build).rejects.toThrow('extensions[].transform must return the document') + }) + + it('awaits an async `transform` and passes it `payload` and the resolved options', async () => { + let seen: { payload: unknown; title: unknown; locales: unknown } | undefined + const input = baseInput({ + options: resolveOptions({ + info: { title: 'T', version: '1.0.0' }, + extensions: [ + { + transform: async ({ doc: d, payload, options, locales }) => { + seen = { payload, title: options.info.title, locales } + return { ...d, info: { ...d.info, title: 'Async' } } + }, + }, + ], + }), + }) + const doc = await buildDocument(input) + expect(doc.info.title).toBe('Async') + expect(seen).toEqual({ payload: input.payload, title: 'T', locales: input.ctx.locales }) + }) + + it('copies every `info` field and resolves a localized `info.description`', async () => { + const info = { + title: 'T', + version: '1.0.0', + summary: 'Short', + termsOfService: 'https://example.com/terms', + contact: { name: 'Team', email: 'team@example.com' }, + license: { name: 'MIT', identifier: 'MIT' }, + } + const doc = await buildDocument( + baseInput({ options: resolveOptions({ info: { ...info, description: { en: 'English', de: 'Deutsch' } } }) }), + ) + expect(doc.info).toMatchObject(info) + expect(doc.info.description).toMatch(/^English\n\n## /) + const fromFn = await buildDocument( + baseInput({ options: resolveOptions({ info: { ...info, description: ({ i18n }) => `Lang ${i18n.language}` } }) }), + ) + expect(fromFn.info.description).toMatch(/^Lang en\n/) + const result = await new Validator().validate(structuredClone(doc)) + expect(result.valid).toBe(true) + }) + + it('drops `info.summary` and `license.identifier` from a 3.0 document', async () => { + const { toOpenApi30 } = await import('@/server/spec/downconvert.js') + const doc = await buildDocument( + baseInput({ + options: resolveOptions({ + info: { title: 'T', version: '1.0.0', summary: 'Short', license: { name: 'MIT', identifier: 'MIT' } }, + }), + }), + ) + const converted = toOpenApi30(doc) + expect(converted.info.summary).toBeUndefined() + expect(converted.info.license).toEqual({ name: 'MIT' }) + expect(doc.info.summary).toBe('Short') + }) + + describe('interactive auth', () => { + const securedPaths = { + endpoints: [ + { + path: '/private', + method: 'get', + handler: () => new Response(), + custom: { openapi: { responses: { '200': { description: 'ok' } }, security: [{ PayloadToken: [] }] } }, + }, + ], + } as unknown as SanitizedConfig + const build = (over: Partial[0]>) => + buildDocument( + baseInput({ + config: securedPaths, + options: resolveOptions({ info: { title: 'T', version: '1.0.0' }, ...over }), + }), + ) + + it('references `PayloadLogin` from secured operations so "Try it" sends the token', async () => { + const doc = await build({ interactiveAuth: true }) + expect(doc.components?.securitySchemes?.PayloadLogin).toBeDefined() + expect(doc.paths?.['/api/private']?.get?.security).toEqual([{ PayloadToken: [] }, { PayloadLogin: [] }]) + const result = await new Validator().validate(structuredClone(doc)) + expect(result.valid).toBe(true) + }) + + it('advertises no `tokenUrl` when `serve` is false', async () => { + const doc = await build({ interactiveAuth: true, serve: false }) + expect(doc.components?.securitySchemes?.PayloadLogin).toBeUndefined() + expect(doc.paths?.['/api/private']?.get?.security).toEqual([{ PayloadToken: [] }]) + }) + + it("does not change the user's endpoint metadata", async () => { + await build({ interactiveAuth: true }) + const meta = (securedPaths.endpoints as unknown as Array<{ custom: { openapi: { security: unknown } } }>)[0] + expect(meta?.custom.openapi.security).toEqual([{ PayloadToken: [] }]) + }) + }) + + describe('custom, jobs and system operations', () => { + const config = { + endpoints: [ + { + path: '/health', + method: 'get', + handler: () => new Response(), + custom: { openapi: { responses: { '200': { description: 'ok' } } } }, + }, + ], + jobs: { tasks: [{}] }, + } as unknown as SanitizedConfig + const upload = coll('media', { upload: {} as never, fields: [] }) + + it('applies `excludeOperations` to every group', async () => { + const doc = await buildDocument( + baseInput({ + config, + collections: [upload], + options: resolveOptions({ + info: { title: 'T', version: '1.0.0' }, + filters: { + excludeOperations: [ + { kind: 'custom' }, + ({ kind }) => kind === 'jobs', + { kind: 'system', method: 'delete' }, + ], + }, + }), + }), + ) + expect(doc.paths?.['/api/health']).toBeUndefined() + expect(doc.paths?.['/api/payload-jobs/run']).toBeUndefined() + expect(doc.paths?.['/api/upload-instructions/{uploadId}']?.delete).toBeUndefined() + expect(doc.paths?.['/api/upload-instructions/{uploadId}']?.put).toBeDefined() + }) + + it('passes custom, jobs and system operations to `security`', async () => { + const kinds = new Set() + const doc = await buildDocument( + baseInput({ + config, + collections: [upload], + options: resolveOptions({ + info: { title: 'T', version: '1.0.0' }, + security: ({ kind }) => { + kinds.add(kind) + return kind === 'custom' ? 'secured' : undefined + }, + }), + }), + ) + expect([...kinds].toSorted()).toEqual(['custom', 'jobs', 'system']) + expect(doc.paths?.['/api/health']?.get?.security).toEqual([{ PayloadToken: [] }]) + }) + + it('marks jobs secured unless `jobs.access.run` is public', async () => { + const secured = await buildDocument(baseInput({ config })) + expect(secured.paths?.['/api/payload-jobs/run']?.get?.security).toEqual([{ PayloadToken: [] }]) + expect(secured.paths?.['/api/payload-jobs/handle-schedules']?.get?.security).toEqual([{ PayloadToken: [] }]) + const open = await buildDocument( + baseInput({ config: { ...config, jobs: { tasks: [{}], access: { run: () => true } } } }), + ) + expect(open.paths?.['/api/payload-jobs/run']?.get?.security).toBeUndefined() + }) }) }) describe('spec/paths/jobs', () => { describe('buildJobsPaths', () => { it('adds the run endpoint only when tasks or workflows are set up', async () => { - const { buildJobsPaths } = await import('../src/spec/paths/jobs.js') - const jobsCtx = { defaultIDType: 'text' as const, locales: [], apiRoute: '/api', i18n: i18nStub } + const { buildJobsPaths } = await import('@/server/spec/paths/jobs.js') + const jobsCtx = { + defaultIDType: 'text' as const, + locales: [], + apiRoute: '/api', + docLanguages: ['en'], + i18n: i18nStub, + } const cfg = (jobs: SanitizedConfig['jobs']): SanitizedConfig => ({ jobs }) as SanitizedConfig expect(buildJobsPaths({ config: cfg({} as SanitizedConfig['jobs']), ctx: jobsCtx })).toEqual({}) const withJobs = buildJobsPaths({ @@ -356,6 +609,91 @@ describe('spec/paths/jobs', () => { }) }) +describe('spec/paths/system', () => { + describe('buildSystemPaths', () => { + it('adds the upload instructions endpoints only when an upload collection exists', () => { + expect(buildSystemPaths({ ctx, access: false, uploadSlugs: [] })).toEqual({}) + const paths = buildSystemPaths({ ctx, access: false, uploadSlugs: ['media'] }) + expect(paths['/api/upload-instructions']?.post?.operationId).toBe('getUploadInstructions') + expect(paths['/api/upload-instructions/{uploadId}']?.put?.responses?.['204']).toBeDefined() + expect(paths['/api/upload-instructions/{uploadId}']?.delete?.responses?.['204']).toBeDefined() + expect(paths['/api/access']).toBeUndefined() + }) + + it('marks the staged upload `PUT` and `DELETE` as secured', () => { + const paths = buildSystemPaths({ ctx, access: false, uploadSlugs: ['media'] }) + expect(paths['/api/upload-instructions/{uploadId}']?.put?.security).toEqual([{ PayloadToken: [] }]) + expect(paths['/api/upload-instructions/{uploadId}']?.delete?.security).toEqual([{ PayloadToken: [] }]) + }) + + it('adds the root access endpoint only when `access` is on', () => { + expect(buildSystemPaths({ ctx, access: true, uploadSlugs: [] })['/api/access']?.get).toBeDefined() + }) + }) +}) + +describe('spec/paths/auth', () => { + const users = (auth: Record): SanitizedCollectionConfig => + ({ slug: 'users', fields: [], auth }) as unknown as SanitizedCollectionConfig + const build = (auth: Record): PathsObject => + buildAuthPaths({ collection: users(auth), ctx, includeAdmin: true, nestedTags: true }) + + const body = (paths: PathsObject, route: string): SchemaObject => { + const requestBody = paths[`/api/users/${route}`]?.post?.requestBody as RequestBodyObject + return requestBody.content['application/json'].schema as SchemaObject + } + + it('asks for `email` in login, forgot-password and unlock bodies by default', () => { + const paths = build({}) + for (const route of ['login', 'forgot-password', 'unlock']) { + expect(Object.keys(body(paths, route).properties ?? {})).not.toContain('username') + expect(body(paths, route).required).toContain('email') + } + expect(body(paths, 'login').required).toEqual(['email', 'password']) + }) + + it('asks for `username` only when `loginWithUsername` does not allow email login', () => { + for (const loginWithUsername of [true, { allowEmailLogin: false, requireEmail: true }]) { + const paths = build({ loginWithUsername }) + for (const route of ['login', 'forgot-password', 'unlock']) { + expect(Object.keys(body(paths, route).properties ?? {})).not.toContain('email') + expect(body(paths, route).required).toContain('username') + } + } + }) + + it('accepts `email` or `username` when `allowEmailLogin` is on', () => { + const paths = build({ loginWithUsername: { allowEmailLogin: true, requireEmail: false } }) + for (const route of ['login', 'forgot-password', 'unlock']) { + const schema = body(paths, route) + expect(Object.keys(schema.properties ?? {})).toEqual(expect.arrayContaining(['email', 'username'])) + expect(schema.anyOf).toEqual([{ required: ['email'] }, { required: ['username'] }]) + } + expect(body(paths, 'login').required).toEqual(['password']) + expect(body(paths, 'unlock').required).toBeUndefined() + }) + + it('documents unlock for every auth collection, without `maxLoginAttempts`', () => { + expect(build({})['/api/users/unlock']?.post).toBeDefined() + }) + + it('adds the API key reveal endpoint only when `useAPIKey.reveal` is on', () => { + expect(build({ useAPIKey: true })['/api/users/{id}/api-key/reveal']).toBeUndefined() + expect(build({ useAPIKey: { reveal: true } })['/api/users/{id}/api-key/reveal']?.post).toBeDefined() + }) + + it('keeps me, logout, refresh-token and init but drops login routes when `disableLocalStrategy` is set', () => { + const paths = build({ disableLocalStrategy: true }) + expect(paths['/api/users/me']?.get).toBeDefined() + expect(paths['/api/users/logout']?.post).toBeDefined() + expect(paths['/api/users/refresh-token']?.post).toBeDefined() + expect(paths['/api/users/init']?.get).toBeDefined() + expect(paths['/api/users/login']).toBeUndefined() + expect(paths['/api/users/forgot-password']).toBeUndefined() + expect(paths['/api/users/first-register']).toBeUndefined() + }) +}) + describe('spec/paths/custom', () => { describe('buildCustomEndpointPaths', () => { it('keeps only endpoints with `custom.openapi` and turns params into placeholders', () => { @@ -384,8 +722,8 @@ describe('spec/paths/custom', () => { } as unknown as SanitizedCollectionConfig const paths = buildCustomEndpointPaths({ config, collections: [collection], globals: [], ctx }) - expect(paths['/health']).toBeDefined() - expect(paths['/secret']).toBeUndefined() + expect(paths['/api/health']).toBeDefined() + expect(paths['/api/secret']).toBeUndefined() expect(paths['/api/posts/{id}/tracking']).toBeDefined() }) }) @@ -402,6 +740,21 @@ describe('spec/paths/collections', () => { expect(paths['/api/posts/{id}/duplicate']?.post).toBeDefined() }) + it('lists `limit` and `sort` on bulk update only', () => { + const paths = buildCollectionPaths({ collection: postsColl(), ctx }) + const names = (op?: { parameters?: unknown[] }) => ((op?.parameters ?? []) as { name: string }[]).map((p) => p.name) + expect(names(paths['/api/posts']?.patch)).toEqual(expect.arrayContaining(['limit', 'sort'])) + expect(names(paths['/api/posts']?.patch)).not.toContain('page') + expect(names(paths['/api/posts']?.delete)).not.toContain('limit') + expect(names(paths['/api/posts']?.delete)).not.toContain('sort') + }) + + it('documents the file endpoint only for upload collections', () => { + expect(buildCollectionPaths({ collection: postsColl(), ctx })['/api/posts/file/{filename}']).toBeUndefined() + const paths = buildCollectionPaths({ collection: postsColl({ upload: {} as never }), ctx }) + expect(paths['/api/posts/file/{filename}']?.get?.responses?.['206']).toBeDefined() + }) + it('drops bulk update/delete when `disableBulkEdit` is set, keeping by-id ops', () => { const paths = buildCollectionPaths({ collection: postsColl({ disableBulkEdit: true }), ctx }) expect(paths['/api/posts']?.patch).toBeUndefined() @@ -417,18 +770,9 @@ describe('spec/paths/collections', () => { expect(paths['/api/posts/{id}']?.get).toBeDefined() }) - it('lists `limit` and `sort` on bulk update only', () => { - const paths = buildCollectionPaths({ collection: postsColl(), ctx }) - const names = (op?: { parameters?: unknown[] }) => ((op?.parameters ?? []) as { name: string }[]).map((p) => p.name) - expect(names(paths['/api/posts']?.patch)).toEqual(expect.arrayContaining(['limit', 'sort'])) - expect(names(paths['/api/posts']?.patch)).not.toContain('page') - expect(names(paths['/api/posts']?.delete)).not.toContain('limit') - expect(names(paths['/api/posts']?.delete)).not.toContain('sort') - }) - it('describes the responses as Payload sends them', async () => { - const { buildGlobalPaths } = await import('../src/spec/paths/globals.js') - const { buildVersionPaths } = await import('../src/spec/paths/versions.js') + const { buildGlobalPaths } = await import('@/server/spec/paths/globals.js') + const { buildVersionPaths } = await import('@/server/spec/paths/versions.js') const body = (op?: { responses?: Record }, code = '200') => { const response = op?.responses?.[code] as { content: Record } | undefined return response?.content['application/json']?.schema @@ -458,52 +802,85 @@ describe('spec/paths/collections', () => { expect(restore()?.allOf?.[0]).toEqual(doc) expect(restore(true)?.properties).toEqual({ message: { type: 'string' }, doc }) }) -}) -describe('spec/paths/auth', () => { - const users = (auth: Record): SanitizedCollectionConfig => - ({ slug: 'users', fields: [], auth }) as unknown as SanitizedCollectionConfig - - const build = (auth: Record): PathsObject => - buildAuthPaths({ collection: users(auth), ctx, includeAdmin: true, nestedTags: true }) + it('adds the document access endpoints with the operations Payload checks', async () => { + const { buildGlobalPaths } = await import('@/server/spec/paths/globals.js') + const operations = (op?: { responses?: Record }) => { + const ok = op?.responses?.['200'] as { content: Record } + return Object.keys(ok.content['application/json']?.schema.properties ?? {}) + } + expect(buildCollectionPaths({ collection: postsColl(), ctx })['/api/posts/access/{id}']).toBeUndefined() + + const plain = buildCollectionPaths({ collection: postsColl(), ctx, access: true }) + expect(operations(plain['/api/posts/access']?.post)).toEqual([ + 'create', + 'read', + 'update', + 'delete', + 'validate', + 'fields', + ]) + expect(plain['/api/posts/access/{id}']?.post?.operationId).toBe('accessPostsById') - const body = (paths: PathsObject, route: string): SchemaObject => { - const requestBody = paths[`/api/users/${route}`]?.post?.requestBody as RequestBodyObject - return requestBody.content['application/json'].schema as SchemaObject - } + const users = buildCollectionPaths({ + collection: postsColl({ auth: { maxLoginAttempts: 5 } as never, versions: { drafts: false } as never }), + ctx, + access: true, + }) + expect(operations(users['/api/posts/access/{id}']?.post)).toEqual([ + 'create', + 'read', + 'update', + 'delete', + 'validate', + 'unlock', + 'readVersions', + 'fields', + ]) - it('asks for `email` in login, forgot-password and unlock bodies by default', () => { - const paths = build({}) - for (const route of ['login', 'forgot-password', 'unlock']) { - expect(Object.keys(body(paths, route).properties ?? {})).not.toContain('username') - expect(body(paths, route).required).toContain('email') - } - expect(body(paths, 'login').required).toEqual(['email', 'password']) + const global = { slug: 'site', fields: [], versions: false } as unknown as SanitizedGlobalConfig + expect(buildGlobalPaths({ global, ctx })['/api/globals/site/access']).toBeUndefined() + expect(operations(buildGlobalPaths({ global, ctx, access: true })['/api/globals/site/access']?.post)).toEqual([ + 'read', + 'update', + 'validate', + 'fields', + ]) }) - it('asks for `username` only when `loginWithUsername` does not allow email login', () => { - for (const loginWithUsername of [true, { allowEmailLogin: false, requireEmail: true }]) { - const paths = build({ loginWithUsername }) - for (const route of ['login', 'forgot-password', 'unlock']) { - expect(Object.keys(body(paths, route).properties ?? {})).not.toContain('email') - expect(body(paths, route).required).toContain('username') - } - } - }) + it('adds the validate endpoints with a locale list and the validation result', async () => { + const { buildGlobalPaths } = await import('@/server/spec/paths/globals.js') + const paths = buildCollectionPaths({ collection: postsColl(), ctx }) + const create = paths['/api/posts/validate']?.post + const byId = paths['/api/posts/{id}/validate']?.post + expect(create?.operationId).toBe('validatePosts') + expect(create?.requestBody).toMatchObject({ + required: true, + content: { 'application/json': { schema: { $ref: '#/components/schemas/PostsCreate' } } }, + }) + expect(byId?.operationId).toBe('validatePostsById') + expect(byId?.requestBody).not.toHaveProperty('required') + expect(byId?.requestBody).toMatchObject({ + content: { 'application/json': { schema: { $ref: '#/components/schemas/PostsUpdate' } } }, + }) + expect(paths['/api/posts/{id}/validate']?.parameters?.[0]).toMatchObject({ name: 'id', in: 'path' }) + expect(create?.responses?.['200']).toMatchObject({ + content: { 'application/json': { schema: { required: ['valid', 'errors'] } } }, + }) + expect( + buildCollectionPaths({ collection: postsColl(), ctx: { ...ctx, locales: [] } })['/api/posts/validate']?.post + ?.parameters, + ).toEqual([]) - it('accepts `email` or `username` when `allowEmailLogin` is on', () => { - const paths = build({ loginWithUsername: { allowEmailLogin: true, requireEmail: false } }) - for (const route of ['login', 'forgot-password', 'unlock']) { - const schema = body(paths, route) - expect(Object.keys(schema.properties ?? {})).toEqual(expect.arrayContaining(['email', 'username'])) - expect(schema.anyOf).toEqual([{ required: ['email'] }, { required: ['username'] }]) - } - expect(body(paths, 'login').required).toEqual(['password']) - expect(body(paths, 'unlock').required).toBeUndefined() - }) + const localized = buildCollectionPaths({ collection: postsColl(), ctx: { ...ctx, locales: ['en', 'de'] } }) + expect(localized['/api/posts/validate']?.post?.parameters).toMatchObject([ + { name: 'locale', in: 'query', explode: true, schema: { type: 'array', items: { enum: ['en', 'de', 'all'] } } }, + ]) - it('documents unlock for every auth collection, without `maxLoginAttempts`', () => { - expect(build({})['/api/users/unlock']?.post).toBeDefined() + const global = { slug: 'site', fields: [] } as unknown as SanitizedGlobalConfig + expect(buildGlobalPaths({ global, ctx })['/api/globals/site/validate']?.post?.operationId).toBe( + 'validateGlobalSite', + ) }) }) @@ -555,6 +932,22 @@ describe('spec/security', () => { expect(await evaluateAccess(access as never)).toBe(false) }) + it('reads `baseAccess` from the config the way Payload wraps access', async () => { + const wrapped = async ({ req }: { req: { payload: { config: { baseAccess?: { read?: () => boolean } } } } }) => { + const base = req.payload.config.baseAccess?.read + return base ? base() : true + } + expect(await evaluateAccess(wrapped as never)).toBe(true) + expect(await evaluateAccess(wrapped as never, { baseAccess: { read: () => true } as never })).toBe(true) + expect(await evaluateAccess(wrapped as never, { baseAccess: { read: () => false } as never })).toBe(false) + }) + + it('treats access that reads other config keys as secured', async () => { + const access = ({ req }: { req: { payload: { config: { custom?: { private?: boolean } } } } }) => + !req.payload.config.custom?.private + expect(await evaluateAccess(access as never)).toBe(false) + }) + it('treats a never-settling promise as secured once the timeout elapses', async () => { const access = () => new Promise(() => {}) expect(await evaluateAccess(access as never, { timeoutMs: 10 })).toBe(false) @@ -572,21 +965,32 @@ describe('spec/security', () => { expect(sec.create).toEqual(secured) }) - it('lets a boolean custom.openapi.security override the probe for all operations', async () => { - const entity = { - slug: 'posts', - access: { read: () => true }, - custom: { openapi: { security: false } }, - } as unknown as SanitizedCollectionConfig - const sec = await resolveEntitySecurity({ entity, operations: ['read'] }) - expect(sec.read).toEqual(secured) + it("lets 'public' or 'secured' in custom.openapi.security override the probe for all operations", async () => { + const entity = (security: string, allowed: boolean) => + ({ + slug: 'posts', + access: { read: () => allowed, create: () => allowed }, + custom: { openapi: { security } }, + }) as unknown as SanitizedCollectionConfig + const secured_ = await resolveEntitySecurity({ entity: entity('secured', true), operations: ['read', 'create'] }) + expect(secured_).toEqual({ read: secured, create: secured }) + const open = await resolveEntitySecurity({ entity: entity('public', false), operations: ['read', 'create'] }) + expect(open).toEqual({ read: undefined, create: undefined }) + }) + + it('throws on the removed true/false form, also inside a per-operation object', async () => { + const message = `[${PLUGIN_NAME}] custom.openapi.security true/false on "posts" was removed, use 'public' or 'secured'` + for (const security of [false, true, { read: true }, { read: 'public', create: false }]) { + const entity = { slug: 'posts', custom: { openapi: { security } } } as unknown as SanitizedCollectionConfig + await expect(resolveEntitySecurity({ entity, operations: ['read'] })).rejects.toThrow(message) + } }) it('lets a per-operation override win over the probe in both directions', async () => { const entity = { slug: 'posts', access: { read: () => false, create: () => true }, - custom: { openapi: { security: { read: true, create: false } } }, + custom: { openapi: { security: { read: 'public', create: 'secured' } } }, } as unknown as SanitizedCollectionConfig const sec = await resolveEntitySecurity({ entity, operations: ['read', 'create'] }) expect(sec.read).toBeUndefined() @@ -597,7 +1001,7 @@ describe('spec/security', () => { const entity = { slug: 'posts', access: { read: () => true, create: () => true }, - custom: { openapi: { security: { read: false } } }, + custom: { openapi: { security: { read: 'secured' } } }, } as unknown as SanitizedCollectionConfig const sec = await resolveEntitySecurity({ entity, operations: ['read', 'create'] }) expect(sec.read).toEqual(secured) @@ -605,36 +1009,76 @@ describe('spec/security', () => { }) }) - describe('applySecurityWhen', () => { + it('types `custom.openapi` on collections, globals and fields', () => { + expectTypeOf['openapi']>().toEqualTypeOf() + expectTypeOf['openapi']>().toEqualTypeOf() + expectTypeOf['openapi']>().toEqualTypeOf() + expectTypeOf<{ security: 'public' }>().toExtend() + expectTypeOf<{ security: { read: 'public'; update: 'secured' } }>().toExtend() + expectTypeOf<{ security: true }>().not.toExtend() + }) + + describe('applySecurity', () => { const paths = (): PathsObject => ({ '/api/posts': { get: { security: undefined }, post: { security: [{ PayloadToken: [] }] } }, }) + const withLogin = [{ PayloadToken: [] }, { PayloadLogin: [] }] - it('is a no-op when securityWhen is not set', () => { + it('is a no-op when neither `security` nor login is set', async () => { const p = paths() - expect(applySecurityWhen({ paths: p, slug: 'posts', kind: 'collection' })).toBe(p) + expect(await applySecurity({ paths: p, slug: 'posts', kind: 'collection' })).toBe(p) }) - it('opens an operation when the predicate returns true', () => { - const out = applySecurityWhen({ paths: paths(), slug: 'posts', kind: 'collection', securityWhen: () => true }) + it("opens an operation on 'public'", async () => { + const out = await applySecurity({ paths: paths(), slug: 'posts', kind: 'collection', security: () => 'public' }) expect(out['/api/posts']?.post?.security).toBeUndefined() }) - it('closes an operation when the predicate returns false', () => { - const out = applySecurityWhen({ paths: paths(), slug: 'posts', kind: 'collection', securityWhen: () => false }) + it("closes an operation on 'secured'", async () => { + const out = await applySecurity({ paths: paths(), slug: 'posts', kind: 'collection', security: () => 'secured' }) expect(out['/api/posts']?.get?.security).toEqual(secured) }) - it('leaves an operation untouched when the predicate returns undefined', () => { - const out = applySecurityWhen({ + it('keeps the detected marking on undefined and passes it as `detected`', async () => { + const seen: string[] = [] + const out = await applySecurity({ paths: paths(), slug: 'posts', kind: 'collection', - securityWhen: () => undefined, + security: ({ method, detected }) => { + seen.push(`${method}:${detected}`) + return undefined + }, }) + expect(seen).toEqual(['get:public', 'post:secured']) expect(out['/api/posts']?.get?.security).toBeUndefined() expect(out['/api/posts']?.post?.security).toEqual(secured) }) + + it('sets a returned requirement array as is and awaits a Promise', async () => { + const apiKey = [{ ApiKey: [] }] + const out = await applySecurity({ + paths: paths(), + slug: 'posts', + kind: 'collection', + security: async ({ method }) => (method === 'get' ? apiKey : 'public'), + }) + expect(out['/api/posts']?.get?.security).toEqual(apiKey) + expect(out['/api/posts']?.post?.security).toBeUndefined() + }) + + it('adds the `PayloadLogin` requirement next to `PayloadToken` when login is on', async () => { + const out = await applySecurity({ paths: paths(), slug: 'posts', kind: 'collection', login: true }) + expect(out['/api/posts']?.post?.security).toEqual(withLogin) + expect(out['/api/posts']?.get?.security).toBeUndefined() + const closed = await applySecurity({ + paths: paths(), + kind: 'custom', + login: true, + security: () => 'secured', + }) + expect(closed['/api/posts']?.get?.security).toEqual(withLogin) + }) }) describe('per-operation marking in path builders', () => { @@ -653,8 +1097,28 @@ describe('spec/security', () => { expect(paths['/api/posts']?.post?.security).toEqual(secured) }) + it('marks version reads from `readVersions` and restore from `update`', async () => { + const { buildVersionPaths } = await import('@/server/spec/paths/versions.js') + const entity = { + slug: 'posts', + fields: [], + versions: {}, + access: { read: () => true, readVersions: async () => true, update: () => false }, + } as unknown as SanitizedCollectionConfig + const security = await resolveEntitySecurity({ entity, operations: ['readVersions', 'update'] }) + const paths = buildVersionPaths({ entity, pathBase: '/api/posts', ctx, nestedTags: false, security }) + expect(paths['/api/posts/versions']?.get?.security).toBeUndefined() + expect(paths['/api/posts/versions/{id}']?.get?.security).toBeUndefined() + expect(paths['/api/posts/versions/{id}']?.post?.security).toEqual(secured) + const closed = await resolveEntitySecurity({ + entity: { ...entity, access: {} } as SanitizedCollectionConfig, + operations: ['readVersions'], + }) + expect(closed.readVersions).toEqual(secured) + }) + it('marks global get/post from read/update security', async () => { - const { buildGlobalPaths } = await import('../src/spec/paths/globals.js') + const { buildGlobalPaths } = await import('@/server/spec/paths/globals.js') const global = { slug: 'settings', fields: [], @@ -665,12 +1129,38 @@ describe('spec/security', () => { expect(paths['/api/globals/settings']?.get?.security).toBeUndefined() expect(paths['/api/globals/settings']?.post?.security).toEqual(secured) }) + + it('marks validate from `validate` access, its own override and the `update` override', async () => { + const { buildGlobalPaths } = await import('@/server/spec/paths/globals.js') + const global = { + slug: 'settings', + fields: [], + access: { update: () => false, validate: () => true }, + } as unknown as SanitizedGlobalConfig + const probed = await resolveEntitySecurity({ entity: global, operations: ['update', 'validate'] }) + const paths = buildGlobalPaths({ global, ctx, security: probed }) + expect(paths['/api/globals/settings']?.post?.security).toEqual(secured) + expect(paths['/api/globals/settings/validate']?.post?.security).toBeUndefined() + const overridden = await resolveEntitySecurity({ + entity: { ...global, custom: { openapi: { security: { update: 'secured' } } } } as SanitizedGlobalConfig, + operations: ['validate'], + }) + expect(overridden.validate).toEqual(secured) + const ownKey = await resolveEntitySecurity({ + entity: { + ...global, + custom: { openapi: { security: { update: 'secured', validate: 'public' } } }, + } as SanitizedGlobalConfig, + operations: ['update', 'validate'], + }) + expect(ownKey).toEqual({ update: secured, validate: undefined }) + }) }) }) describe('spec/entitySchemas', () => { it('writes relationships as ids at any depth and keeps the populated shape on read', async () => { - const { buildEntitySchemas } = await import('../src/spec/entitySchemas.js') + const { buildEntitySchemas } = await import('@/server/spec/entitySchemas.js') const fields = [ { name: 'cover', type: 'upload', relationTo: 'media' }, { name: 'meta', type: 'group', fields: [{ name: 'image', type: 'upload', relationTo: 'media' }] }, @@ -697,38 +1187,72 @@ describe('spec/entitySchemas', () => { const config = { collections: [collection('media'), collection('tags')], blocks: [] } as unknown as SanitizedConfig const { read, create, update } = await buildEntitySchemas({ entity, config, ctx }) - const id = { type: 'string' } - const optionalId = { type: ['string', 'null'] } + const id = { type: ['string', 'null'] } const props = create.properties ?? {} - expect(props.cover).toEqual(optionalId) - expect((props.meta as SchemaObject).properties?.image).toEqual(optionalId) + expect(props.cover).toEqual(id) + expect((props.meta as SchemaObject).properties?.image).toEqual(id) const row = ((props.rows as SchemaObject).items as SchemaObject).properties ?? {} - expect(row.tags).toEqual({ type: ['array', 'null'], items: id }) + expect(row.tags).toEqual({ type: ['array', 'null'], items: { type: 'string' } }) const link = (row.link as SchemaObject).oneOf as SchemaObject[] expect(link.map((b) => [b.properties?.relationTo, b.properties?.value])).toEqual([ - [{ const: 'media' }, id], - [{ const: 'tags' }, id], + [{ const: 'media' }, { type: 'string' }], + [{ const: 'tags' }, { type: 'string' }], ]) const quote = ((props.content as SchemaObject).items as SchemaObject).oneOf?.[0] as SchemaObject - expect(quote.properties?.source).toEqual(optionalId) + expect(quote.properties?.source).toEqual(id) expect(update.properties?.meta).toEqual(props.meta) const readMeta = read.properties?.meta as SchemaObject const readImage = readMeta.properties?.image as SchemaObject expect(readImage.oneOf?.[1]).toEqual({ $ref: '#/components/schemas/Media' }) }) + + it('leaves fields whose write access is always false out of the matching write body', async () => { + const { buildEntitySchemas } = await import('@/server/spec/entitySchemas.js') + const fields = [ + { name: 'stamp', type: 'text', access: { create: () => false, update: () => false } }, + { name: 'lockedLater', type: 'text', access: { update: () => false } }, + { + name: 'maybe', + type: 'text', + access: { create: (args?: { req?: { user?: { admin?: boolean } } }) => args?.req?.user?.admin === true }, + }, + { + name: 'adminOnly', + type: 'text', + access: { create: ({ req }: { req: { user: unknown } }) => Boolean(req.user) }, + }, + { type: 'row', fields: [{ name: 'nested', type: 'text', access: { create: () => false } }] }, + ] as Field[] + const entity = { + slug: 'posts', + fields, + flattenedFields: flattenAllFields({ fields }), + } as unknown as SanitizedCollectionConfig + const config = { collections: [entity], blocks: [] } as unknown as SanitizedConfig + const { read, create, update } = await buildEntitySchemas({ entity, config, ctx }) + expect(Object.keys(read.properties ?? {})).toEqual( + expect.arrayContaining(['stamp', 'lockedLater', 'adminOnly', 'nested']), + ) + expect(Object.keys(create.properties ?? {})).toEqual(expect.arrayContaining(['lockedLater', 'adminOnly', 'maybe'])) + expect(create.properties).not.toHaveProperty('stamp') + expect(create.properties).not.toHaveProperty('nested') + expect(update.properties).not.toHaveProperty('stamp') + expect(update.properties).not.toHaveProperty('lockedLater') + expect(update.properties).toHaveProperty('adminOnly') + }) }) describe('spec/params', () => { it('uses `ctx.t` for descriptions so a custom translation flows through', async () => { - const { buildSelectSchema } = await import('../src/spec/params.js') + const { buildSelectSchema } = await import('@/server/spec/params.js') const custom = { ...ctx, i18n: { ...ctx.i18n, t: (() => 'ÜBERSETZT') as unknown as I18n['t'] } } const schema = buildSelectSchema({ fields: [{ name: 'title', type: 'text' }] as Field[], ctx: custom }) expect(schema.description).toBe('ÜBERSETZT') }) it('adds draft, locale and lock params by entity config and operation', async () => { - const { writeParams } = await import('../src/spec/params.js') + const { writeParams } = await import('@/server/spec/params.js') const refs = { select: true, populate: false, joins: false } const entity = (versions: unknown, lockDocuments?: false) => ({ slug: 'x', fields: [], versions, lockDocuments }) as unknown as SanitizedCollectionConfig @@ -770,7 +1294,7 @@ type Loose = Record describe('spec/downconvert', () => { describe('toOpenApi30', () => { it('rewrites the version and nullable type arrays, leaving non-null types alone', async () => { - const { toOpenApi30 } = await import('../src/spec/downconvert.js') + const { toOpenApi30 } = await import('@/server/spec/downconvert.js') const doc: Document = { openapi: '3.1.2', info: { title: 'T', version: '1' }, @@ -798,7 +1322,7 @@ describe('spec/downconvert', () => { }) it('converts content and example keywords and drops the ones 3.0 rejects', async () => { - const { toOpenApi30 } = await import('../src/spec/downconvert.js') + const { toOpenApi30 } = await import('@/server/spec/downconvert.js') const doc: Document = { openapi: '3.1.2', info: { title: 'T', version: '1' }, @@ -831,7 +1355,7 @@ describe('spec/downconvert', () => { }) it('leaves a 3.0 examples map untouched when it is already an object', async () => { - const { toOpenApi30 } = await import('../src/spec/downconvert.js') + const { toOpenApi30 } = await import('@/server/spec/downconvert.js') const doc: Document = { openapi: '3.1.2', info: { title: 'T', version: '1' }, @@ -859,11 +1383,9 @@ describe('spec/downconvert', () => { sample: { value: { ok: true } }, }) }) - }) - describe('toOpenApi30 required', () => { it('drops an empty `required` array and keeps a filled one', async () => { - const { toOpenApi30 } = await import('../src/spec/downconvert.js') + const { toOpenApi30 } = await import('@/server/spec/downconvert.js') const doc: Document = { openapi: '3.2.0', info: { title: 'T', version: '1' }, @@ -881,7 +1403,7 @@ describe('spec/downconvert', () => { }) it('turns several types into `anyOf` and keeps `null` in each branch', async () => { - const { toOpenApi30 } = await import('../src/spec/downconvert.js') + const { toOpenApi30 } = await import('@/server/spec/downconvert.js') const doc: Document = { openapi: '3.2.0', info: { title: 'T', version: '1' }, @@ -907,7 +1429,7 @@ describe('spec/downconvert', () => { describe('toOpenApi31', () => { it('rewrites the `openapi` version field to 3.1.2', async () => { - const { toOpenApi31 } = await import('../src/spec/downconvert.js') + const { toOpenApi31 } = await import('@/server/spec/downconvert.js') const doc: Document = { openapi: '3.2.0', info: { title: 'Test', version: '1.0' }, paths: {} } const result = toOpenApi31(doc) expect(result.openapi).toBe('3.1.2') @@ -915,13 +1437,13 @@ describe('spec/downconvert', () => { }) it('returns a new document instead of mutating the input', async () => { - const { toOpenApi31 } = await import('../src/spec/downconvert.js') + const { toOpenApi31 } = await import('@/server/spec/downconvert.js') const doc: Document = { openapi: '3.2.0', info: { title: 'T', version: '1' }, paths: {} } expect(toOpenApi31(doc)).not.toBe(doc) }) it('strips the 3.2-only `kind`, `parent` and `summary` fields from tags', async () => { - const { toOpenApi31 } = await import('../src/spec/downconvert.js') + const { toOpenApi31 } = await import('@/server/spec/downconvert.js') const doc: Document = { openapi: '3.2.0', info: { title: 'T', version: '1' }, @@ -963,41 +1485,90 @@ describe('endpoints/spec', () => { }, }) as unknown as PayloadRequest - const optionsBase = () => resolveOptions({ metadata: { title: 'T', version: '1.0.0' } }) + const optionsBase = () => resolveOptions({ info: { title: 'T', version: '1.0.0' } }) + const serversFor = async (options: ReturnType, host: string, serverURL?: string) => { + const { specHandler } = await import('@/server/endpoints/spec.js') + const req = makeReq(host) + Object.assign(req.payload.config, { serverURL }) + return (await (await specHandler(options)(req)).json()).servers + } - it('injects a fresh server URL per request from the cached doc', async () => { - const { specHandler } = await import('../src/endpoints/spec.js') - const handler = specHandler(optionsBase()) - const a = await (await handler(makeReq('localhost:3000'))).json() - const b = await (await handler(makeReq('api.example.com'))).json() - expect(a.servers[0].url).toBe('http://localhost:3000') - expect(b.servers[0].url).toBe('https://api.example.com') + it('never reads servers from an untrusted Host header', async () => { + expect(await serversFor(optionsBase(), 'evil.example.com')).toEqual([]) + expect(await serversFor(optionsBase(), 'evil.example.com', 'https://api.example.com')).toEqual([ + { url: 'https://api.example.com' }, + ]) + }) + + it('uses the Host header only when it is in `trustedHosts`', async () => { + const options = resolveOptions({ + info: { title: 'T', version: '1.0.0' }, + trustedHosts: ['API.example.com', 'localhost:3000'], + }) + expect(await serversFor(options, 'api.example.com')).toEqual([{ url: 'https://api.example.com' }]) + expect(await serversFor(options, 'localhost:3000')).toEqual([{ url: 'http://localhost:3000' }]) + expect(await serversFor(options, 'evil.example.com', 'https://api.example.com')).toEqual([ + { url: 'https://api.example.com' }, + ]) + }) + + it('prefers a `servers` array over `trustedHosts` and `serverURL`', async () => { + const servers = [{ url: 'https://a.example.com', description: 'A' }] + const options = resolveOptions({ + info: { title: 'T', version: '1.0.0' }, + servers, + trustedHosts: ['b.example.com'], + }) + expect(await serversFor(options, 'b.example.com', 'https://c.example.com')).toEqual(servers) + }) + + it('calls a `servers` function per request with `req` and keeps the cached doc', async () => { + const options = resolveOptions({ + info: { title: 'T', version: '1.0.0' }, + servers: async ({ req }) => [{ url: `https://${req.headers.get('host')}` }], + }) + const { specHandler } = await import('@/server/endpoints/spec.js') + const handler = specHandler(options) + const a = await (await handler(makeReq('a.example.com'))).json() + const b = await (await handler(makeReq('b.example.com'))).json() + expect(a.servers).toEqual([{ url: 'https://a.example.com' }]) + expect(b.servers).toEqual([{ url: 'https://b.example.com' }]) expect({ ...a, servers: [] }).toEqual({ ...b, servers: [] }) }) + it('answers 403 when `access` denies the request', async () => { + const { specHandler } = await import('@/server/endpoints/spec.js') + const handler = specHandler( + resolveOptions({ info: { title: 'T', version: '1.0.0' }, access: async ({ req }) => Boolean(req.user) }), + ) + await expect(handler(makeReq('localhost:3000'))).rejects.toMatchObject({ status: 403 }) + const req = Object.assign(makeReq('localhost:3000'), { user: { id: 1 } }) + expect((await handler(req)).status).toBe(200) + }) + it('rebuilds on every request when the cache is off', async () => { - const { specHandler } = await import('../src/endpoints/spec.js') - const handler = specHandler(resolveOptions({ metadata: { title: 'T', version: '1.0.0' }, cache: false })) + const { specHandler } = await import('@/server/endpoints/spec.js') + const handler = specHandler(resolveOptions({ info: { title: 'T', version: '1.0.0' }, cache: false })) const res = await (await handler(makeReq('localhost:3000'))).json() expect(res.openapi).toBe('3.2.0') }) it('serves 3.0 when `openapiVersion` is `3.0`', async () => { - const { specHandler } = await import('../src/endpoints/spec.js') - const handler = specHandler(resolveOptions({ metadata: { title: 'T', version: '1.0.0' }, openapiVersion: '3.0' })) + const { specHandler } = await import('@/server/endpoints/spec.js') + const handler = specHandler(resolveOptions({ info: { title: 'T', version: '1.0.0' }, openapiVersion: '3.0' })) const res = await (await handler(makeReq('localhost:3000'))).json() expect(res.openapi).toBe('3.0.4') }) it('serves 3.1 when `openapiVersion` is `3.1`', async () => { - const { specHandler } = await import('../src/endpoints/spec.js') - const handler = specHandler(resolveOptions({ metadata: { title: 'T', version: '1.0.0' }, openapiVersion: '3.1' })) + const { specHandler } = await import('@/server/endpoints/spec.js') + const handler = specHandler(resolveOptions({ info: { title: 'T', version: '1.0.0' }, openapiVersion: '3.1' })) const res = await (await handler(makeReq('localhost:3000'))).json() expect(res.openapi).toBe('3.1.2') }) it('defaults to 3.2 when `openapiVersion` is not set', async () => { - const { specHandler } = await import('../src/endpoints/spec.js') + const { specHandler } = await import('@/server/endpoints/spec.js') const handler = specHandler(optionsBase()) const res = await (await handler(makeReq('localhost:3000'))).json() expect(res.openapi).toBe('3.2.0') @@ -1006,15 +1577,47 @@ describe('endpoints/spec', () => { }) describe('ui/html', () => { - const renderHtml = async (plugin: Plugin): Promise => { + const uiRequest = (custom: Record = {}, user: unknown = null) => + ({ + user, + t: (k: string) => k, + payload: { config: { routes: { api: '/api' }, custom } }, + }) as unknown as PayloadRequest + const renderHtml = async (plugin: Plugin, req = uiRequest()): Promise => { const out = await plugin({ routes: { api: '/api' } } as unknown as Config) const endpoint = (out.endpoints ?? []).at(-1) as Endpoint - const handler = endpoint.handler as () => Response | Promise - return (await handler()).text() + return (await endpoint.handler(req)).text() } + it('loads the spec from the `openapi()` route, including a custom `path`', async () => { + const { scalar } = await import('@/server/ui/scalar.js') + expect(await renderHtml(scalar())).toContain('"/api/openapi.json"') + const resolved = resolveOptions({ info: { title: 'Shop API', version: '1.0.0' }, path: '/spec.json' }) + const html = await renderHtml(scalar(), uiRequest({ [PLUGIN_NAME]: resolved })) + expect(html).toContain('"/api/spec.json"') + expect(html).toContain('Shop API') + }) + + it('uses `specURL` when set', async () => { + const { swaggerUi } = await import('@/server/ui/swagger.js') + expect(await renderHtml(swaggerUi({ specURL: '/static/openapi.json' }))).toContain('"/static/openapi.json"') + }) + + it('answers 403 when `access` denies the request', async () => { + const { scalar } = await import('@/server/ui/scalar.js') + const plugin = scalar({ access: ({ req }) => Boolean(req.user) }) + await expect(renderHtml(plugin)).rejects.toMatchObject({ status: 403 }) + expect(await renderHtml(plugin, uiRequest({}, { id: 1 }))).toContain('Scalar.createApiReference') + }) + + it('throws on the removed `specEndpoint`', async () => { + const { scalar } = await import('@/server/ui/scalar.js') + const plugin = scalar({ specEndpoint: '/x' } as never) + expect(() => plugin({} as Config)).toThrow(`[${PLUGIN_NAME}] specEndpoint was renamed to specURL`) + }) + it('passes Scalar configuration into the init call', async () => { - const { scalar } = await import('../src/ui/scalar.js') + const { scalar } = await import('@/server/ui/scalar.js') const html = await renderHtml(scalar({ configuration: { theme: 'purple', hideModels: true } })) expect(html).toContain('Scalar.createApiReference') expect(html).toContain('"theme":"purple"') @@ -1022,22 +1625,42 @@ describe('ui/html', () => { }) it('passes Swagger UI configuration into `SwaggerUIBundle`', async () => { - const { swaggerUi } = await import('../src/ui/swagger.js') + const { swaggerUi } = await import('@/server/ui/swagger.js') const html = await renderHtml(swaggerUi({ configuration: { docExpansion: 'none' } })) expect(html).toContain('SwaggerUIBundle') expect(html).toContain('"docExpansion":"none"') }) it('escapes `<` in configuration so a string value cannot break out of the script', async () => { - const { swaggerUi } = await import('../src/ui/swagger.js') + const { swaggerUi } = await import('@/server/ui/swagger.js') const html = await renderHtml(swaggerUi({ configuration: { x: '' } })) expect(html).not.toContain('"') expect(html).toContain('\\u003c/script>') }) + it('loads pinned CDN assets with SRI, and a custom `cdnBase` without it', async () => { + const { scalar } = await import('@/server/ui/scalar.js') + const { swaggerUi } = await import('@/server/ui/swagger.js') + const scalarHtml = await renderHtml(scalar()) + expect(scalarHtml).toMatch( + /