diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d1824e5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +node_modules/ + +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +.DS_Store +Thumbs.db + +.env +.env.* +!.env.example \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index a2d4104..a8876b8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,9 +1,12 @@ { "yaml.customTags": ["!include scalar"], "yaml.schemas": { - "schemas/api-config/config.json": "demo-api/config.y*ml", - "schemas/api-config/collection.json": "demo-api/collections/*.y*ml", - "schemas/api-config/field.json": "demo-api/collections/fields/*.y*ml" + "schemas/config/project.schema.json": "demo-api/config.y*ml", + "schemas/config/collection.schema.json": "demo-api/collections/*.y*ml", + "schemas/config/field.schema.json": "demo-api/collections/fields/*.y*ml", + "schemas/config/field-list.schema.json": "demo-api/collections/fieldLists/*.y*ml", + "schemas/config/job.schema.json": "demo-api/jobs/*.y*ml", + "schemas/config/asset.schema.json": "demo-api/assets/*.y*ml" }, "editor.formatOnSave": true, "editor.tabCompletion": "on", diff --git a/demo-api/actions/demo-action.yml b/demo-api/actions/demo-action.yml new file mode 100644 index 0000000..65945ec --- /dev/null +++ b/demo-api/actions/demo-action.yml @@ -0,0 +1,56 @@ +# yaml-language-server: $schema=../../schemas/config/action.schema.json +name: demo-action + +meta: + label: { de: Demo Aktion, en: Demo Action } + methods: + get: + query: + title: { de: Demo Query, en: Demo Query } + fields: + - name: verbose + type: boolean + execution: + kind: sync + post: + data: + title: { de: Demo Input, en: Demo Input } + fields: + - name: dryRun + type: boolean + execution: + kind: sse + cancellable: true + output: + title: { de: Demo Ergebnis, en: Demo Result } + mapping: + summary: + fields: + - source: result.status + type: string + meta: + label: { de: Status, en: Status } + +permissions: + public: + methods: + get: true + post: false + user: + methods: + get: true + post: false + admin: + methods: + get: true + post: true + +hooks: + get: + handle: + type: javascript + file: hooks/demo-action/get_handle.js + post: + handle: + type: javascript + file: hooks/demo-action/post_handle.js diff --git a/demo-api/collections/global/test-permissions.yml b/demo-api/collections/global/test-permissions.yml index 1d6e6d2..b748142 100644 --- a/demo-api/collections/global/test-permissions.yml +++ b/demo-api/collections/global/test-permissions.yml @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=../../../schemas/api-config/permissions.json +# yaml-language-server: $schema=../../../schemas/config/permissions.schema.json public: methods: diff --git a/demo-api/collections/mycol.yml b/demo-api/collections/mycol.yml index 453a917..7aaa9e0 100644 --- a/demo-api/collections/mycol.yml +++ b/demo-api/collections/mycol.yml @@ -3,11 +3,7 @@ uploadPath: ../media/mycol meta: label: { de: mycol, en: mycol } - rowIdentTpl: { twig: "{{ title }}" } - views: - - type: table - columns: - - title + preview: testfield permissions: !include global/test-permissions.yml @@ -39,8 +35,7 @@ fields: label: de: test2 en: test2 - widget: "contentbuilder" - baseHref: /mycol + widget: "textarea" - name: testfield3 type: string @@ -50,3 +45,86 @@ fields: en: test3 widget: "select" choices: [] + + - name: code + type: string + validator: + pattern: "[A-Z]{3}-[0-9]{2}" + meta: + label: + de: Code + en: Code + widget: "text" + + - name: status + type: string + validator: + in: [draft, published] + meta: + label: + de: Status + en: Status + widget: "text" + + - name: contactEmail + type: string + validator: + format: email + meta: + label: + de: E-Mail + en: Email + widget: "text" + + - name: shortCode + type: string + validator: + minLength: 3 + maxLength: 5 + meta: + label: + de: Kurzcode + en: Short code + widget: "text" + + - name: rating + type: number + validator: + min: 1 + max: 10 + meta: + label: + de: Bewertung + en: Rating + widget: "text" + + - name: eventDate + type: date + validator: + minDate: "2024-01-01" + maxDate: "2026-12-31" + meta: + label: + de: Eventdatum + en: Event date + widget: "date" + + - name: heroImage + type: file + validator: + minFileSize: "10KB" + maxFileSize: "2MB" + accept: ["image/jpeg", "image/png", "image/webp"] + image: + minWidth: 1200 + maxWidth: 3200 + minHeight: 600 + maxHeight: 2400 + + - name: attachments + type: file[] + validator: + minItems: 1 + maxItems: 3 + maxFileSize: "5MB" + accept: ["application/pdf"] diff --git a/demo-api/config.yml b/demo-api/config.yml index 189f09b..d87d423 100644 --- a/demo-api/config.yml +++ b/demo-api/config.yml @@ -9,6 +9,9 @@ meta: collections: - !include collections/mycol.yml +actions: + - !include actions/demo-action.yml + assets: - name: _dist_ path: ../frontend/_dist_ diff --git a/index.d.ts b/index.d.ts index 2597ca5..2b947b0 100644 --- a/index.d.ts +++ b/index.d.ts @@ -90,6 +90,22 @@ declare global { k?: number topK?: number weights?: { [source: string]: number } + /** Fusion strategy: "rrf" (default), "score_rrf", "score_sum", "rank_score", "hybrid" */ + strategy?: "rrf" | "score_rrf" | "score_sum" | "rank_score" | "hybrid" + /** Score normalization: "none" (default), "minmax", "zscore" */ + normalize?: "none" | "minmax" | "zscore" + /** Blend factor for "hybrid" strategy: 0.0 = pure rank, 1.0 = pure score */ + scoreWeight?: number + /** Per-source overrides for fine-grained fusion tuning */ + sourceConfig?: { [source: string]: SourceFusionConfig } + } + + /** Per-source overrides for rank fusion */ + export interface SourceFusionConfig { + /** Override global scoreWeight for this source */ + scoreWeight?: number + /** Score multiplier for this source (default 1.0) */ + boost?: number } export interface VectorConfig { @@ -101,6 +117,35 @@ declare global { chunkOverlap?: number } + export interface SearchExplainSource { + mode?: string + matched?: boolean + rank?: number + weight?: number + score?: number + /** Normalized score (only present when normalize is active) */ + normalizedScore?: number + contribution?: number + details?: SearchExplanation + } + + export interface SearchExplanation { + algorithm?: string + /** Fusion strategy used ("score_rrf", "hybrid", etc.). Omitted when default "rrf". */ + strategy?: string + /** Normalization mode used ("minmax", "zscore"). Omitted when "none". */ + normalize?: string + k?: number + sources?: { [source: string]: SearchExplainSource } + } + + export interface SearchMeta { + score?: number + mode?: string + /** Present when the request uses qExplain=1 or qExplain=true. */ + details?: SearchExplanation + } + /** Search configuration for a collection */ export interface SearchConfig { /** Search config name (used in query parameter qName) */ @@ -132,6 +177,8 @@ declare global { vector?: VectorConfig /** Reciprocal rank fusion configuration */ rrf?: RrfConfig + /** Execute synchronously (false) or via background queue (true). Vector mode defaults to true. */ + async?: boolean /** Auto-regenerate enrichment data when the config changes */ autoRegenerate?: boolean } @@ -204,15 +251,24 @@ declare global { export type ChoiceValue = string | number | boolean | null + export type ChoiceStyle = Record + export interface ChoiceItem { id?: ChoiceValue value?: ChoiceValue name?: LocalizedText label?: LocalizedText + description?: LocalizedText badgeVariant?: "success" | "error" | "warning" | "info" | "neutral" chipVariant?: "success" | "error" | "warning" | "info" | "neutral" - badgeStyle?: Record - chipStyle?: Record + /** + * Flat inline CSS properties for badge/chip rendering. + * Supports camelCase, kebab-case, and CSS custom properties. + * `bg` and `text` remain aliases for `background` and `color`. + */ + badgeStyle?: ChoiceStyle + /** Alias for `badgeStyle`. */ + chipStyle?: ChoiceStyle } export type EvalStringConfig = { @@ -334,7 +390,9 @@ declare global { /** Action endpoint descriptor as returned by context.api() for _actions resources */ export interface ActionInfo { name: string + permissions?: { [role: string]: PermissionSet } meta?: ActionMeta + audit?: AuditCollectionConfig [key: string]: any } @@ -366,21 +424,76 @@ declare global { export interface ActionExecutionMeta { kind?: "sync" | "sse" | string - progress?: boolean cancellable?: boolean [key: string]: any } + export interface ActionMappedField extends Omit< + CollectionFieldInfo, + "name" + > { + /** Dot-path in action payload/result/progress. Preferred over `name` for mapped output configs. */ + source?: string + /** Legacy fallback for path-based mappings. */ + name?: string + } + + export interface ActionOutputProgressMapping { + indexField?: string + countField?: string + statusField?: string | string[] + messageField?: string + fields?: ActionMappedField[] + } + + export interface ActionOutputMapping { + summary?: { + fields?: ActionMappedField[] + } + details?: { + fields?: ActionMappedField[] + } + progress?: ActionOutputProgressMapping + } + export interface ActionOutputMeta { display?: "summary" | "detail" | "raw" | "download" | "none" | string title?: LocalizedText fields?: CollectionFieldInfo[] + mapping?: ActionOutputMapping [key: string]: any } export type MethodPermission = boolean | { allow: boolean; bulk?: boolean } export type SimpleMethodPermission = boolean | { allow: boolean } + export interface AllowedMethodPermission { + allow: boolean + bulk?: boolean + } + + export interface CollectionYourPermissions { + get?: AllowedMethodPermission + post?: AllowedMethodPermission + put?: AllowedMethodPermission + delete?: AllowedMethodPermission + readonlyFields?: string[] + hiddenFields?: string[] + } + + export interface ActionYourPermissions { + get?: { allow: boolean } + post?: { allow: boolean } + } + + export interface ProjectYourPermissions { + _actions?: { [actionName: string]: ActionYourPermissions } + [collectionName: string]: + | CollectionYourPermissions + | { [actionName: string]: ActionYourPermissions } + | undefined + } + export interface PermissionSet { methods?: { get?: SimpleMethodPermission @@ -444,7 +557,7 @@ declare global { users?: string[] configFile?: string api?: ApiInfo - yourPermissions?: { [key: string]: any } + yourPermissions?: ProjectYourPermissions [key: string]: any } @@ -1095,12 +1208,19 @@ declare global { } interface CharsetPackage { + /** + * decode byte-oriented input using the given encoding + * + * supported encodings: utf-8, iso-8859-1 + */ + decode(input: string | Int8Array | number[], encoding: string): string + /** * convert iso8859 to utf8 * - * @param iso8859 iso string + * @param iso8859 iso string or bytes */ - iso8859ToUtf8(iso8859: string): string + iso8859ToUtf8(iso8859: string | Int8Array | number[]): string } interface ImagePackage { @@ -1525,10 +1645,7 @@ declare global { } export interface HookContext - extends GetHookData, - GetHookGetOnlyData, - PostHookData, - JobData { + extends GetHookData, GetHookGetOnlyData, PostHookData, JobData { request(): { method: string remoteAddr: string @@ -1541,7 +1658,7 @@ declare global { header(h: string): string body(): string /** - * bodyBytes return []byte go slice of post body for later use pe. in iso8859ToUtf8 + * bodyBytes returns the raw request body bytes for later decoding via charset.decode */ bodyBytes(): Int8Array } diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..a64aa3c --- /dev/null +++ b/package-lock.json @@ -0,0 +1,350 @@ +{ + "name": "tibi-types", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "tibi-types", + "version": "0.0.1", + "license": "MIT", + "devDependencies": { + "ajv": "^8.20.0", + "ajv-formats": "^3.0.1", + "glob": "^11.1.0", + "js-yaml": "^4.1.1" + } + }, + "node_modules/@isaacs/cliui": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz", + "integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.2.3.tgz", + "integrity": "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^9.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", + "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + } + } +} diff --git a/package.json b/package.json index 9ff0f7f..9511c79 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,17 @@ { "name": "tibi-types", "version": "0.0.1", - "description": "tibi javascript types and json schemas", + "description": "tibi hook types and validated config schemas", "types": "index.d.ts", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "validate:schemas": "node scripts/validate-schemas.mjs", + "test": "npm run validate:schemas" + }, + "devDependencies": { + "ajv": "^8.20.0", + "ajv-formats": "^3.0.1", + "glob": "^11.1.0", + "js-yaml": "^4.1.1" }, "repository": { "type": "git", diff --git a/schemas/api-config/assets.json b/schemas/api-config/assets.json deleted file mode 100644 index dc88afa..0000000 --- a/schemas/api-config/assets.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "JSON Schema tibi-server assets configuration", - "description": "tibi-server assets linter", - "type": "object", - "additionalProperties": false, - "patternProperties": { - "^x\\-": { - "description": "template property" - } - }, - "properties": { - "name": { - "type": "string", - "description": "name, used in url ../NAMESPACE/_/assets/NAME" - }, - "path": { - "type": "string", - "description": "path relative to config.yml" - } - }, - "required": ["name", "path"] -} diff --git a/schemas/api-config/collection.json b/schemas/api-config/collection.json deleted file mode 100644 index 3198525..0000000 --- a/schemas/api-config/collection.json +++ /dev/null @@ -1,389 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "JSON Schema tibi-server collection configuration", - "description": "tibi-server collection configuration", - "type": "object", - "additionalProperties": false, - "patternProperties": { - "^x\\-": { - "description": "template property" - } - }, - "properties": { - "name": { - "type": "string", - "description": "collection name, part of api path" - }, - "defaultLanguage": { - "type": "string", - "description": "default language for database text index" - }, - "uploadPath": { - "type": "string", - "description": "relative to config.yml, path for file uploads" - }, - "upload": { - "type": "object", - "description": "collection-specific upload defaults and restrictions", - "additionalProperties": false, - "properties": { - "maxBodySize": { - "type": "string", - "description": "request body size limit override for this collection (e.g. '10MB')" - }, - "allowedMimeTypes": { - "type": "array", - "description": "allowed MIME types for file uploads in this collection; empty means all types are allowed", - "items": { - "type": "string" - } - } - } - }, - "meta": { - "oneOf": [ - { - "$comment": "for include tag", - "type": "string" - }, - { - "type": "object", - "description": "meta object used for admin ui configuration", - "additionalProperties": true, - "allOf": [ - { - "$ref": "collectionNavigation.json" - } - ], - "properties": { - "rowIdentTpl": { - "description": "template which evaluates to short string to identify entry in pe. select boxes", - "$ref": "definitions.json#/definitions/evalExpressions" - }, - "subNavigation": { - "oneOf": [ - { - "$comment": "for include tag", - "type": "string" - }, - { - "type": "array", - "description": "sub navigation of collection", - "items": { - "oneOf": [ - { - "$comment": "for include tag", - "type": "string" - }, - { - "$ref": "collectionNavigation.json" - } - ] - } - } - ] - }, - "pagebuilder": { - "type": "object", - "description": "Collection-level pagebuilder defaults. These serve as fallbacks when field-level pagebuilder config omits them.", - "additionalProperties": false, - "properties": { - "blockTypeField": { - "type": "string", - "description": "Name of the sub-field that holds the block type identifier. Default: 'blockType'. Used as fallback for all pagebuilder fields in this collection." - }, - "blockRegistry": { - "type": "object", - "description": "Block registry configuration.", - "additionalProperties": false, - "properties": { - "file": { - "type": "string", - "description": "URL to an ES module that default-exports a BlockRegistry. Used as fallback for all pagebuilder fields in this collection." - } - } - } - } - } - } - } - ] - }, - "projections": { - "oneOf": [ - { - "$comment": "for include tag", - "type": "string" - }, - { - "$ref": "projections.json" - } - ] - }, - "permissions": { - "oneOf": [ - { - "$comment": "for include tag", - "type": "string" - }, - { - "$ref": "permissions.json" - } - ] - }, - "hooks": { - "oneOf": [ - { - "$comment": "for include tag", - "type": "string" - }, - { - "$ref": "hooks.json" - } - ] - }, - "imageFilter": { - "oneOf": [ - { - "$comment": "for include tag", - "type": "string" - }, - { - "$ref": "imageFilter.json" - } - ] - }, - "fields": { - "oneOf": [ - { - "$comment": "for include tag", - "type": "string" - }, - { - "type": "array", - "description": "fields of collection", - "items": { - "oneOf": [ - { - "$comment": "for include tag", - "type": "string" - }, - { - "$ref": "field.json" - } - ] - } - } - ] - }, - "indexes": { - "oneOf": [ - { - "$comment": "for include tag", - "type": "string" - }, - { - "type": "array", - "description": "indexes of collection", - "items": { - "oneOf": [ - { - "$comment": "for include tag", - "type": "string" - }, - { - "$ref": "index.json" - } - ] - } - } - ] - }, - "bulk": { - "type": "object", - "description": "bulk operation configuration", - "additionalProperties": false, - "properties": { - "optimize": { - "type": "object", - "description": "enable optimised single-DB-call bulk path without requiring a hook file", - "additionalProperties": false, - "properties": { - "create": { - "type": "boolean", - "description": "POST with JSON array uses optimised InsertMany path" - }, - "update": { - "type": "boolean", - "description": "PUT without ID uses optimised path" - }, - "delete": { - "type": "boolean", - "description": "DELETE without ID uses optimised path" - } - } - } - } - }, - "cors": { - "type": "object", - "description": "cors configuration", - "additionalProperties": false, - "properties": { - "merge": { - "type": "boolean", - "description": "merge with global and project cors configuration" - }, - "allowOrigins": { - "type": "array", - "description": "list of allowed origins", - "items": { - "type": "string" - } - }, - "allowMethods": { - "type": "array", - "description": "list of allowed methods", - "items": { - "type": "string" - } - }, - "allowHeaders": { - "type": "array", - "description": "list of allowed headers", - "items": { - "type": "string" - } - }, - "allowCredentials": { - "type": "boolean", - "description": "allow credentials" - }, - "exposeHeaders": { - "type": "array", - "description": "list of exposed headers", - "items": { - "type": "string" - } - }, - "maxAge": { - "type": "integer", - "description": "max age in seconds" - } - } - }, - "audit": { - "type": "object", - "description": "audit logging configuration for this collection", - "additionalProperties": false, - "properties": { - "enabled": { - "type": "boolean", - "description": "enable audit logging for this collection" - }, - "actions": { - "type": "array", - "description": "list of actions to audit (create, update, delete, bulkCreate, bulkUpdate, bulkDelete, get)", - "items": { - "type": "string", - "enum": [ - "create", - "update", - "delete", - "bulkCreate", - "bulkUpdate", - "bulkDelete", - "get" - ] - } - } - } - }, - "queryLimits": { - "type": "object", - "description": "query limit configuration for GET requests", - "additionalProperties": false, - "properties": { - "defaultLimit": { - "type": "integer", - "description": "default limit for GET queries if client doesn't specify one" - }, - "maxLimit": { - "type": "integer", - "description": "maximum allowed limit for GET queries" - } - } - }, - "readonlyFields": { - "type": "array", - "description": "fields that are read-only at collection level", - "items": { - "type": "string" - } - }, - "hiddenFields": { - "type": "array", - "description": "fields that are hidden at collection level", - "items": { - "type": "string" - } - }, - "search": { - "type": "array", - "description": "search configurations for the collection", - "items": { - "type": "object", - "additionalProperties": false, - "properties": { - "name": { - "type": "string", - "description": "search config name (used in query parameter qName)" - }, - "mode": { - "type": "string", - "description": "search mode", - "enum": [ - "text", - "regex", - "eval", - "filter" - ] - }, - "fields": { - "type": "array", - "description": "fields to search in (for text/regex mode)", - "items": { - "type": "string" - } - }, - "meta": { - "type": "object", - "description": "meta information", - "additionalProperties": true - }, - "eval": { - "type": "string", - "description": "JS eval expression (for eval mode)" - }, - "filter": { - "type": "object", - "description": "MongoDB filter template (for filter mode)", - "additionalProperties": true - } - }, - "required": [ - "name", - "mode" - ] - } - }, - "strictFields": { - "type": "boolean", - "description": "reject unknown fields not defined in the fields array" - } - }, - "required": [ - "name", - "permissions" - ] -} \ No newline at end of file diff --git a/schemas/api-config/collectionNavigation.json b/schemas/api-config/collectionNavigation.json deleted file mode 100644 index ee64d51..0000000 --- a/schemas/api-config/collectionNavigation.json +++ /dev/null @@ -1,363 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "JSON Schema tibi-server collection meta navigation configuration", - "description": "tibi-server collection navigation linter", - "type": "object", - "additionalProperties": true, - "patternProperties": { - "^x\\-": { - "description": "template property" - } - }, - "required": [], - "properties": { - "name": { - "type": "string", - "description": "name of navigation" - }, - "label": { - "$ref": "definitions.json#/definitions/i18nString" - }, - "defaultImageFilter": { - "type": "string", - "description": "image filter name used for image previews" - }, - "viewHint": { - "description": "default collection view hint or structured special view config", - "oneOf": [ - { - "type": "string", - "enum": [ - "table", - "cards", - "media" - ] - }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "navigation": { - "type": "object", - "additionalProperties": false, - "properties": { - "nodesField": { - "type": "string", - "description": "root field containing the recursive node array" - }, - "declaredTrees": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "properties": { - "label": { - "$ref": "definitions.json#/definitions/i18nString" - }, - "singleton": { - "type": "object", - "description": "root-level field values that identify one declared tree slot", - "additionalProperties": true - }, - "maxLevel": { - "type": "number" - } - }, - "required": [ - "singleton" - ] - } - } - } - } - }, - "required": [ - "navigation" - ] - } - ] - }, - "muiIcon": { - "type": "string", - "description": "material ui icon name" - }, - "multiupload": { - "type": "object", - "description": "Configuration for multi-upload behavior.", - "properties": { - "fields": { - "type": "array", - "description": "Fields editable in the modal. If not set, all fields will be selected automatically.", - "items": { - "type": "object", - "properties": { - "source": { - "type": "string", - "description": "Name of the field." - } - }, - "required": [ - "source" - ] - } - }, - "prefilledFields": { - "type": "array", - "description": "Fields that should have default values. They won't be visible to the user but just an informational text for which fields will receive a default value.", - "items": { - "type": "object", - "properties": { - "source": { - "type": "string", - "description": "Name of the field." - }, - "defaultValue": { - "type": "object", - "properties": { - "eval": { - "type": "string", - "description": "JavaScript evaluation string to determine the default value." - } - }, - "required": [ - "eval" - ] - } - }, - "required": [ - "source", - "defaultValue" - ] - } - } - }, - "required": [ - "fields" - ] - }, - "defaultSort": { - "type": "object", - "description": "default sort in admin ui lists", - "additionalProperties": false, - "properties": { - "field": { - "$ref": "definitions.json#/definitions/fieldSource" - }, - "order": { - "description": "sort order", - "enum": [ - "ASC", - "DESC", - "MANUALLY" - ] - } - } - }, - "defaultCallback": { - "description": "default action on entry, defaults to edit - can be one of: edit, view, Object with js eval", - "oneOf": [ - { - "type": "string", - "enum": [ - "edit", - "view" - ] - }, - { - "type": "object", - "properties": { - "eval": { - "type": "string", - "description": "js eval string of (entry) => {}" - } - }, - "additionalProperties": false - } - ] - }, - "filter": { - "type": "object", - "description": "filter for prefiltering collection view" - }, - "views": { - "type": "array", - "description": "list views", - "items": { - "oneOf": [ - { - "type": "string", - "description": "YAML !include reference to an external view definition file" - }, - { - "type": "object", - "properties": { - "mediaQuery": { - "type": "string", - "description": "css media query to select this view in ui" - }, - "selectionPriority": { - "type": "number", - "description": "priority for selection based on media query" - }, - "fileDropArea": { - "type": "object", - "properties": { - "label": { - "$ref": "definitions.json#/definitions/i18nString" - }, - "helperText": { - "$ref": "definitions.json#/definitions/i18nString" - }, - "targetField": { - "type": "string" - }, - "pageAsDropArea": { - "type": "boolean" - } - } - } - }, - "oneOf": [ - { - "properties": { - "type": { - "enum": [ - "simpleList", - "dashboardSimpleList" - ] - }, - "primaryText": { - "oneOf": [ - { - "$ref": "definitions.json#/definitions/fieldSource" - }, - { - "$ref": "definitions.json#/definitions/evalExpressions" - } - ] - }, - "secondaryText": { - "oneOf": [ - { - "$ref": "definitions.json#/definitions/fieldSource" - }, - { - "$ref": "definitions.json#/definitions/evalExpressions" - } - ] - }, - "tertiaryText": { - "oneOf": [ - { - "$ref": "definitions.json#/definitions/fieldSource" - }, - { - "$ref": "definitions.json#/definitions/evalExpressions" - } - ] - } - }, - "required": [ - "type", - "primaryText" - ] - }, - { - "properties": { - "type": { - "enum": [ - "table", - "dashboardTable" - ] - }, - "columns": { - "type": "array", - "items": { - "oneOf": [ - { - "anyOf": [ - { - "$ref": "definitions.json#/definitions/fieldSource" - }, - { - "type": "object", - "properties": { - "label": { - "$ref": "definitions.json#/definitions/i18nString" - }, - "renderValue": { - "description": "render value based on entry ($ variable)", - "$ref": "definitions.json#/definitions/evalObjectWithRaw" - } - } - } - ] - }, - { - "allOf": [ - { - "$ref": "definitions.json#/definitions/evalExpressions" - }, - { - "properties": { - "label": { - "$ref": "definitions.json#/definitions/i18nString" - } - } - } - ] - } - ] - }, - "minItems": 1 - } - }, - "required": [ - "type", - "columns" - ] - }, - { - "properties": { - "type": { - "const": "cardList" - }, - "fields": { - "type": "array", - "items": { - "type": "object", - "properties": { - "source": { - "type": "string" - }, - "type": { - "type": "string" - }, - "label": { - "type": "string" - }, - "filter": { - "type": "boolean" - } - }, - "required": [ - "source" - ] - } - } - }, - "required": [ - "type", - "fields" - ] - } - ], - "required": [ - "type" - ] - } - ] - } - } - } -} \ No newline at end of file diff --git a/schemas/api-config/config.json b/schemas/api-config/config.json deleted file mode 100644 index 4fbb86a..0000000 --- a/schemas/api-config/config.json +++ /dev/null @@ -1,229 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "JSON Schema tibi-server config file", - "description": "tibi-server config.yml linter", - "type": "object", - "additionalProperties": false, - "patternProperties": { - "^x\\-": { - "description": "template property" - } - }, - "properties": { - "namespace": { - "type": "string", - "description": "suffix for mongo db database and part of api url, can be overwritten in database project settings" - }, - "meta": { - "type": "object", - "description": "meta object used for admin ui configuration", - "additionalProperties": true, - "properties": { - "dashboard": { - "$ref": "dashboard.json#/properties/dashboard" - }, - "imageUrl": { - "description": "project's teaser image url shown in admin ui", - "oneOf": [ - { - "type": "string" - }, - { - "$ref": "definitions.json#/definitions/evalObject" - } - ] - } - } - }, - "upload": { - "type": "object", - "description": "project-wide upload defaults and restrictions", - "additionalProperties": false, - "properties": { - "maxBodySize": { - "type": "string", - "description": "default request body size limit for collections in this project (e.g. '50MB')" - }, - "allowedMimeTypes": { - "type": "array", - "description": "project-wide allowed MIME types for file uploads; empty means all types are allowed", - "items": { - "type": "string" - } - } - } - }, - "collections": { - "type": "array", - "description": "list of collections in this project", - "items": { - "oneOf": [ - { - "$comment": "for include tag", - "type": "string" - }, - { - "$ref": "collection.json" - } - ] - } - }, - "jobs": { - "type": "array", - "description": "list of jobs in this project", - "items": { - "oneOf": [ - { - "$comment": "for include tag", - "type": "string" - }, - { - "$ref": "job.json" - } - ] - } - }, - "assets": { - "type": "array", - "description": "list of assets in this project", - "items": { - "oneOf": [ - { - "$comment": "for include tag", - "type": "string" - }, - { - "$ref": "assets.json" - } - ] - } - }, - "cors": { - "type": "object", - "description": "cors configuration", - "additionalProperties": false, - "properties": { - "merge": { - "type": "boolean", - "description": "merge with global cors configuration" - }, - "allowOrigins": { - "type": "array", - "description": "list of allowed origins", - "items": { - "type": "string" - } - }, - "allowMethods": { - "type": "array", - "description": "list of allowed methods", - "items": { - "type": "string" - } - }, - "allowHeaders": { - "type": "array", - "description": "list of allowed headers", - "items": { - "type": "string" - } - }, - "allowCredentials": { - "type": "boolean", - "description": "allow credentials" - }, - "exposeHeaders": { - "type": "array", - "description": "list of exposed headers", - "items": { - "type": "string" - } - }, - "maxAge": { - "type": "integer", - "description": "max age in seconds" - } - } - }, - "admin": { - "type": "object", - "description": "admin configuration", - "additionalProperties": false, - "properties": { - "tokens": { - "type": "array", - "description": "list of admin tokens", - "items": { - "type": "object", - "properties": { - "token": { - "type": "string", - "description": "admin token" - }, - "allowReload": { - "type": "boolean", - "description": "allow reload" - } - } - } - } - } - }, - "hook": { - "type": "object", - "description": "project-level hook configuration (cache and ratelimit)", - "additionalProperties": false, - "properties": { - "cache": { - "type": "object", - "description": "hook cache configuration", - "additionalProperties": false, - "properties": { - "maxEntries": { - "type": "integer", - "description": "maximum number of cache entries" - }, - "defaultTTL": { - "type": "string", - "description": "default time-to-live (e.g. '5m', '1h')" - } - } - }, - "ratelimit": { - "type": "object", - "description": "hook ratelimit configuration", - "additionalProperties": false, - "properties": { - "backoffInitialDelay": { - "type": "string", - "description": "initial backoff delay (e.g. '1s')" - }, - "backoffMaxDelay": { - "type": "string", - "description": "maximum backoff delay (e.g. '5m')" - }, - "backoffResetAfter": { - "type": "string", - "description": "reset backoff after this duration of no failures (e.g. '15m')" - }, - "windowSize": { - "type": "string", - "description": "sliding window size (e.g. '1m')" - }, - "windowMaxHits": { - "type": "integer", - "description": "maximum hits allowed per window" - } - } - } - } - }, - "strictFields": { - "type": "boolean", - "description": "reject unknown fields not defined in the fields array (project-wide default)" - } - }, - "required": [ - "namespace" - ] -} \ No newline at end of file diff --git a/schemas/api-config/dashboard.json b/schemas/api-config/dashboard.json deleted file mode 100644 index bbc1249..0000000 --- a/schemas/api-config/dashboard.json +++ /dev/null @@ -1,357 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "properties": { - "dashboard": { - "type": "object", - "properties": { - "majorItems": { - "type": "array", - "items": { - "oneOf": [ - { "$ref": "#/definitions/sectionTitle" }, - { "$ref": "#/definitions/swiper" }, - { "$ref": "#/definitions/graph" }, - { "$ref": "#/definitions/table" }, - { "$ref": "#/definitions/reference" } - ] - } - }, - "minorItems": { - "type": "array", - "items": { "$ref": "#/definitions/minorItem" } - } - } - } - }, - "required": ["dashboard"], - "definitions": { - "sectionTitle": { - "type": "object", - "properties": { - "type": { "type": "string", "enum": ["sectionTitle"] }, - "title": { "type": "string" } - }, - "required": ["type", "title"], - "additionalProperties": false - }, - "swiper": { - "type": "object", - "properties": { - "type": { "type": "string", "enum": ["swiper"] }, - "class": { "type": "string" }, - "css": { - "type": "object", - "properties": { - "graph": { - "type": "object", - "properties": { - "wrapper": { - "$ref": "#/definitions/cssWithEval" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - "containerProps": { "$ref": "#/definitions/containerProps" }, - "elements": { - "type": "array", - "items": { "$ref": "#/definitions/graph" } - } - }, - "required": ["type", "elements"], - "additionalProperties": false - }, - "comparison": { - "$ref": "#/definitions/graph" - }, - - "graph": { - "type": "object", - "properties": { - "additionalApiParams": { - "$ref": "#/definitions/additionalApiParams" - }, - "class": { "type": "string" }, - "type": { "type": "string", "enum": ["graph", "comparison"] }, - "containerProps": { "$ref": "#/definitions/containerProps" }, - "css": { - "type": "object", - "properties": { - "graph": { - "type": "object", - "properties": { - "wrapper": { - "$ref": "#/definitions/cssWithEval" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - - "title": { - "oneOf": [ - { - "type": "object", - "properties": { - "value": { "type": "string" }, - "contentAfter": { "type": "string" }, - "contentBefore": { "type": "string" }, - "eval": { "type": "string" } - }, - "required": ["value"] - }, - { "type": "string" }, - { - "type": "object", - "properties": { - "de": { "type": "string" }, - "en": { "type": "string" } - }, - "required": ["de", "en"] - } - ] - }, - "subTitle": { - "oneOf": [ - { "type": "string" }, - { - "type": "object", - "properties": { - "de": { "type": "string" }, - "en": { "type": "string" } - }, - "required": ["de", "en"] - } - ] - }, - - "xAxis": { "type": "string", "enum": ["timeline"] }, - "timeInterval": { - "type": "string", - "enum": ["day", "month", "year"] - }, - "filter": { "type": "boolean" }, - "dateTimeField": { "type": "string" }, - "until": { - "type": "string", - "enum": ["lastWeek", "lastMonth", "lastYear", "allTime"] - }, - "graphType": { - "type": "string", - "enum": ["bar", "line", "pie", "donut", "area"] - }, - "graphs": { - "type": "array", - "items": { "$ref": "#/definitions/graphDetail" } - }, - "defaultUntil": { - "type": "string", - "enum": ["lastWeek", "lastMonth", "lastYear", "allTime"] - }, - "newPageRef": { "type": "boolean" }, - "limit": { "type": "number" }, - "value": { "type": "string", "enum": ["total", "amount"] }, - "valueType": { - "type": "string", - "enum": ["absolute", "relative"] - }, - "field": { "type": "string" }, - "path": { "type": "string" }, - "style": { - "type": "object", - "properties": { - "upper": { "type": "string" }, - "lower": { "type": "string" } - } - }, - "collection": { "type": "string" }, - "subNavigation": { "type": "number" }, - "graphBaseColor": { "type": "string" }, - "multipleYAxes": { "type": "boolean" }, - "timespan": { - "type": "string", - "enum": ["YTD", "QTD", "MTD"] - }, - "backgroundLines": { "type": "boolean" }, - "opacity": { "type": "number" }, - "elements": { - "type": "array", - "items": { "$ref": "#/definitions/graph" } - } - }, - "additionalProperties": false - }, - "graphDetail": { - "type": "object", - "properties": { - "graphName": { - "type": "object", - "properties": { - "de": { "type": "string" }, - "en": { "type": "string" } - }, - "required": ["de", "en"] - }, - "graphType": { - "type": "string", - "enum": ["bar", "line", "pie", "donut", "area"] - }, - "yAxisTitle": { "type": "string" }, - "yAxis": { - "type": "string", - "enum": ["sum", "amount"] - }, - "field": { "type": "string" } - }, - "required": ["graphName"] - }, - "table": { - "type": "object", - "properties": { - "containerProps": { "$ref": "#/definitions/containerProps" }, - "type": { "type": "string", "enum": ["table"] }, - "filter": { "type": "boolean" }, - "collection": { "type": "string" }, - "title": { - "oneOf": [ - { - "type": "object", - "properties": { - "de": { "type": "string" }, - "en": { "type": "string" } - }, - "required": ["de", "en"] - }, - { "type": "string" } - ] - }, - "subTitle": { - "oneOf": [ - { "type": "string" }, - { - "type": "object", - "properties": { - "de": { "type": "string" }, - "en": { "type": "string" } - }, - "required": ["de", "en"] - } - ] - }, - "columns": { - "type": "array", - "items": { "type": "string" } - }, - "filterGroups": { - "type": "array", - "items": { "type": "string" } - }, - "additionalApiParams": { - "$ref": "#/definitions/additionalApiParams" - } - }, - "required": ["type"], - "additionalProperties": false - }, - - "reference": { - "type": "object", - "properties": { - "type": { "type": "string", "enum": ["reference"] }, - "newPageRef": { "type": "boolean" }, - "collection": { "type": "string" }, - "style": { - "type": "object", - "properties": { - "upper": { "type": "string" }, - "lower": { "type": "string" } - } - }, - "subNavigation": { "type": "number" } - }, - "required": ["type"], - "additionalProperties": false - }, - "minorItem": { - "type": "object", - "properties": { - "collection": { "type": "string" }, - "subNavigation": { "type": "number" }, - "newPageRef": { "type": "boolean" } - }, - "required": ["collection"], - "additionalProperties": false - }, - - "containerProps": { - "type": "object", - "properties": { - "class": { "type": "string" }, - "style": { "type": "string" }, - "layout": { - "type": "object", - "properties": { - "breakBefore": { "type": "boolean" }, - "breakAfter": { "type": "boolean" }, - "size": { - "type": "object", - "properties": { - "default": { "type": "string" }, - "small": { "type": "string" }, - "large": { "type": "string" } - } - } - } - } - }, - "additionalProperties": false - }, - "additionalApiParams": { - "type": "object", - "properties": { - "offset": { "type": "number" }, - "limit": { "type": "number" }, - "sort": { "type": "string" }, - "filter": { "type": "object", "additionalProperties": true }, - "projection": { "type": "string" }, - "count": { "type": "number", "enum": [1] } - }, - "additionalProperties": false - }, - - "cssWithEval": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "eval": { - "type": "string" - } - }, - "required": ["eval"], - "additionalProperties": false - }, - { - "type": "object", - "patternProperties": { - "^[a-zA-Z_][a-zA-Z0-9_-]*$": { - "oneOf": [ - { "type": "string" }, - { "type": "number" } - ] - } - }, - "additionalProperties": false - } - ] - } - } -} diff --git a/schemas/api-config/definitions.json b/schemas/api-config/definitions.json deleted file mode 100644 index b29d13a..0000000 --- a/schemas/api-config/definitions.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "definitions": { - "evalObject": { - "type": "object", - "properties": { - "eval": { - "type": "string", - "description": "js code which must return same schema as value if directly defined" - } - } - }, - - "evalObjectWithRaw": { - "type": "object", - "properties": { - "eval": { - "type": "string", - "description": "js code which must return same schema as value if directly defined" - }, - "raw": { - "type": "boolean", - "description": "if true, the result of the eval passed as pure html" - } - } - }, - - "i18nString": { - "oneOf": [ - { - "type": "object", - "patternProperties": { - "^[a-z]$": { - "type": "string" - } - } - }, - { - "type": "string" - } - ] - }, - "evalExpressions": { - "oneOf": [ - { - "type": "object", - "additionalProperties": true, - "properties": { - "twig": { - "type": "string", - "description": "twig template" - }, - "projection": { - "type": "string", - "description": "query projection to use" - } - }, - "required": ["twig"] - }, - { - "type": "object", - "additionalProperties": true, - "properties": { - "eval": { - "type": "string", - "description": "javascript code" - }, - "projection": { - "type": "string", - "description": "query projection to use" - } - }, - "required": ["eval"] - } - ] - }, - "fieldSource": { - "oneOf": [ - { - "$ref": "#/definitions/fieldPath" - }, - { - "type": "object", - "properties": { - "source": { - "$ref": "#/definitions/fieldPath" - } - } - } - ] - }, - "fieldPath": { - "type": "string", - "description": "field, sub fields separated with dot (.)", - "pattern": "^[a-zA-Z0-9_]+(\\.[a-zA-Z0-9_]+)*$" - } - } -} diff --git a/schemas/api-config/field.json b/schemas/api-config/field.json deleted file mode 100644 index 0f62cd5..0000000 --- a/schemas/api-config/field.json +++ /dev/null @@ -1,162 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "JSON Schema tibi-server field configuration", - "description": "tibi-server field linter", - "type": "object", - "additionalProperties": false, - "patternProperties": { - "^x\\-": { - "description": "template property" - } - }, - "properties": { - "name": { - "type": "string", - "description": "name of field", - "pattern": "^[0-9a-zA-Z_]+$" - }, - "type": { - "enum": [ - "string", - "string[]", - "number", - "number[]", - "boolean", - "object", - "object[]", - "file", - "file[]", - "date", - "any" - ] - }, - "index": { - "type": "array", - "items": { - "enum": [ - "single", - "unique", - "text", - "sparse", - "background" - ] - } - }, - "subFields": { - "oneOf": [ - { - "$comment": "for include tag", - "type": "string" - }, - { - "type": "array", - "description": "sub fields of object", - "items": { - "oneOf": [ - { - "$comment": "for include tag", - "type": "string" - }, - { - "$ref": "#" - } - ] - } - } - ] - }, - "validator": { - "type": "object", - "description": "field validator", - "additionalProperties": false, - "properties": { - "required": { - "type": "boolean", - "description": "force field as required" - }, - "allowZero": { - "type": "boolean", - "description": "allow for required fields that the value can be an empty string or 0 for number type" - }, - "eval": { - "type": "string", - "description": "javascript validator which failes if evaluates to false or string as error message, with following variables: $this, $parent, $stack, $auth, context" - }, - "minLength": { - "type": "number", - "description": "minimum string length" - }, - "maxLength": { - "type": "number", - "description": "maximum string length" - }, - "pattern": { - "type": "string", - "description": "regex pattern the value must match" - }, - "min": { - "type": "number", - "description": "minimum value for number fields" - }, - "max": { - "type": "number", - "description": "maximum value for number fields" - }, - "in": { - "type": "array", - "description": "array of allowed values" - }, - "maxFileSize": { - "type": "string", - "description": "maximum decoded file size for file and file[] fields (e.g. '500KB')" - }, - "accept": { - "type": "array", - "description": "allowed MIME types for file and file[] fields; supports wildcards like 'image/*'", - "items": { - "type": "string" - } - } - } - }, - "readonly": { - "description": "field-level readonly — boolean or JS eval expression", - "oneOf": [ - { "type": "boolean" }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "eval": { "type": "string", "description": "JS expression that returns boolean" } - }, - "required": ["eval"] - } - ] - }, - "hidden": { - "description": "field-level hidden — boolean or JS eval expression", - "oneOf": [ - { "type": "boolean" }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "eval": { "type": "string", "description": "JS expression that returns boolean" } - }, - "required": ["eval"] - } - ] - }, - "strictFields": { - "type": "boolean", - "description": "reject unknown sub-fields not defined in subFields" - }, - "meta": { - "$ref": "fieldMeta.json" - } - }, - "required": [ - "name", - "type" - ] -} \ No newline at end of file diff --git a/schemas/api-config/fieldArray.json b/schemas/api-config/fieldArray.json deleted file mode 100644 index 91fe7f1..0000000 --- a/schemas/api-config/fieldArray.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "JSON Schema tibi-server field array configuration", - "description": "tibi-server collection linter", - "type": "array", - "items": { - "oneOf": [ - { - "$comment": "for include tag", - "type": "string" - }, - { - "$ref": "field.json" - } - ] - } -} diff --git a/schemas/api-config/fieldMeta.json b/schemas/api-config/fieldMeta.json deleted file mode 100644 index 40dae2e..0000000 --- a/schemas/api-config/fieldMeta.json +++ /dev/null @@ -1,1074 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "JSON Schema tibi-server field meta configuration", - "description": "tibi-server field meta linter", - "type": "object", - "anyOf": [ - { - "$ref": "#/definitions/minimum" - }, - { - "$ref": "#/definitions/minimum", - "patternProperties": { - "^x-.*|widget|inputProps$": { - "$comment": "stub for allOf" - } - }, - "allOf": [ - { - "properties": { - "widget": { - "enum": [ - "text", - "checkbox" - ] - } - } - }, - { - "properties": { - "inputProps": { - "type": "object", - "properties": { - "multiline": { - "type": "boolean" - } - } - } - } - } - ] - }, - { - "$ref": "#/definitions/minimum", - "patternProperties": { - "^x-.*|widget$": { - "$comment": "stub for allOf" - } - }, - "allOf": [ - { - "properties": { - "widget": { - "enum": [ - "previewBasedObjectArray" - ] - } - } - } - ] - }, - { - "$ref": "#/definitions/minimum", - "patternProperties": { - "^x-.*|widget$": { - "$comment": "stub for allOf" - } - }, - "allOf": [ - { - "properties": { - "widget": { - "enum": [ - "richtext", - "richText" - ] - } - } - } - ] - }, - { - "$ref": "#/definitions/minimum", - "patternProperties": { - "^x-.*|widget|choices$": { - "$comment": "stub for allOf" - } - }, - "allOf": [ - { - "required": [ - "choices" - ], - "properties": { - "widget": { - "enum": [ - "checkboxArray", - "select", - "selectArray", - "chipArray" - ] - }, - "choices": { - "oneOf": [ - { - "type": "object", - "description": "endpoint configuration", - "additionalProperties": false, - "properties": { - "endpoint": { - "type": "string", - "description": "endpoint URL or path" - }, - "params": { - "type": "object", - "description": "query parameters" - }, - "mapping": { - "type": "object", - "description": "data mapping", - "additionalProperties": false, - "properties": { - "id": { - "type": "string", - "description": "name of id field" - }, - "name": { - "type": "string", - "description": "name of name/description field" - } - } - } - } - }, - { - "type": "array", - "description": "choices to select from", - "items": { - "type": "object", - "properties": { - "id": { - "oneOf": [ - { - "type": "null" - }, - { - "type": "string" - }, - { - "type": "number" - } - ] - }, - "name": { - "description": "name is human readable value", - "$ref": "definitions.json#/definitions/i18nString" - }, - "chipStyle": { - "$ref": "https://raw.githubusercontent.com/rcorp/css-schema/master/schema.json" - } - }, - "required": [ - "id", - "name" - ] - } - } - ] - } - } - } - ] - }, - { - "$ref": "#/definitions/minimum", - "patternProperties": { - "^x-.*|widget$": { - "$comment": "stub for allOf" - } - }, - "allOf": [ - { - "properties": { - "widget": { - "enum": [ - "date", - "datetime" - ] - } - } - } - ] - }, - { - "$ref": "#/definitions/minimum", - "patternProperties": { - "^x-.*|widget$": { - "$comment": "stub for allOf" - } - }, - "allOf": [ - { - "properties": { - "widget": { - "enum": [ - "file", - "image" - ] - } - } - } - ] - }, - { - "$ref": "#/definitions/minimum", - "patternProperties": { - "^x-.*|widget$": { - "$comment": "stub for allOf" - } - }, - "allOf": [ - { - "properties": { - "widget": { - "enum": [ - "json" - ] - } - } - } - ] - }, - { - "$ref": "#/definitions/minimum", - "patternProperties": { - "^x-.*|widget$": { - "$comment": "stub for allOf" - } - }, - "allOf": [ - { - "properties": { - "widget": { - "enum": [ - "tabs" - ] - } - } - } - ] - }, - { - "$ref": "#/definitions/minimum", - "patternProperties": { - "^x-.*|widget|foreign$": { - "$comment": "stub for allOf" - } - }, - "allOf": [ - { - "properties": { - "widget": { - "enum": [ - "foreignKey" - ] - }, - "foreign": { - "additionalProperties": false, - "description": "foreign key configuration", - "type": "object", - "properties": { - "collection": { - "type": "string", - "description": "collection name" - }, - "id": { - "type": "string", - "description": "id field name" - }, - "projection": { - "type": "string", - "description": "projection name used for fetching initial foreign entries from database" - }, - "sort": { - "type": "string", - "description": "sort field name (-field for descending)" - }, - "subNavigation": { - "type": "number", - "description": "sub navigation index in foreign collection" - }, - "render": { - "description": "field name or eval object which returns string", - "oneOf": [ - { - "type": "string" - }, - { - "$ref": "definitions.json#/definitions/evalObjectWithRaw" - }, - { - "type": "object", - "properties": { - "defaultCollectionViews": { - "type": "boolean" - } - }, - "required": [ - "defaultCollectionViews" - ] - } - ] - } - } - } - } - } - ] - }, - { - "$ref": "#/definitions/minimum", - "patternProperties": { - "^x-.*|widget|baseHref|cssHref|imageSelect|fileSelect|videoSelect|customTags|style$": { - "$comment": "stub for allOf" - } - }, - "allOf": [ - { - "properties": { - "widget": { - "enum": [ - "contentbuilder" - ] - }, - "baseHref": { - "description": "base href for relative links inside of ContentBuilder", - "oneOf": [ - { - "type": "string" - }, - { - "$ref": "definitions.json#/definitions/evalObject" - } - ] - }, - "cssHref": { - "description": "href to css file for use with ContentBuilder", - "oneOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - }, - { - "$ref": "definitions.json#/definitions/evalObject" - } - ] - }, - "imageSelect": { - "description": "target for image selection", - "$ref": "#definitions/assetsTarget" - }, - "fileSelect": { - "description": "target for file selection", - "$ref": "#definitions/assetsTarget" - }, - "videoSelect": { - "description": "target for video selection", - "$ref": "#definitions/assetsTarget" - }, - "customTags": { - "description": "custom tags to add to ContentBuilder", - "oneOf": [ - { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "required": [ - "placeholder", - "label" - ], - "properties": { - "placeholder": { - "type": "string", - "description": "placeholder inside html code" - }, - "label": { - "$ref": "definitions.json#/definitions/i18nString" - } - } - } - }, - { - "$ref": "definitions.json#/definitions/evalObject" - } - ] - }, - "style": { - "description": "css style to inject into ContentBuilder", - "oneOf": [ - { - "type": "string" - }, - { - "$ref": "definitions.json#/definitions/evalObject" - } - ] - } - } - } - ] - }, - { - "$ref": "#/definitions/minimum", - "patternProperties": { - "^x-.*|widget|pagebuilder$": { - "$comment": "stub for allOf" - } - }, - "allOf": [ - { - "properties": { - "widget": { - "enum": [ - "pagebuilder", - "pageBuilder" - ] - }, - "pagebuilder": { - "type": "object", - "description": "Pagebuilder widget configuration. Field-level settings override collection-level fallbacks.", - "additionalProperties": false, - "properties": { - "mode": { - "type": "string", - "enum": [ - "overlay", - "inline", - "both" - ], - "description": "[Planned] Editor mode. Currently only 'overlay' is implemented. 'inline' and 'both' are planned." - }, - "defaultViewport": { - "type": "number", - "description": "Default viewport width in pixels for the preview. Common presets: 1280 (Desktop), 768 (Tablet), 375 (Phone). Default: 1280. Overrides collection-level fallback." - }, - "blockTypeField": { - "type": "string", - "description": "Name of the sub-field that holds the block type identifier. Default: 'blockType'. Overrides collection-level fallback." - }, - "blockRegistry": { - "type": "object", - "description": "Block registry configuration for loading available block definitions.", - "additionalProperties": false, - "properties": { - "file": { - "type": "string", - "description": "URL to an ES module that default-exports a BlockRegistry (Record). Overrides collection-level fallback." - } - } - } - } - } - } - } - ] - }, - { - "$ref": "#/definitions/minimum", - "patternProperties": { - "^x-.*|widget|pagebuilder$": { - "$comment": "stub for allOf" - } - }, - "allOf": [ - { - "properties": { - "widget": { - "enum": [ - "pagebuilder", - "pageBuilder" - ] - }, - "pagebuilder": { - "type": "object", - "description": "Pagebuilder widget configuration. Field-level settings override collection-level fallbacks.", - "additionalProperties": false, - "properties": { - "mode": { - "type": "string", - "enum": [ - "overlay", - "inline", - "both" - ], - "description": "[Planned] Editor mode. Currently only 'overlay' is implemented. 'inline' and 'both' are planned." - }, - "defaultViewport": { - "type": "number", - "description": "Default viewport width in pixels for the preview. Common presets: 1280 (Desktop), 768 (Tablet), 375 (Phone). Default: 1280. Overrides collection-level fallback." - }, - "blockTypeField": { - "type": "string", - "description": "Name of the sub-field that holds the block type identifier. Default: 'blockType'. Overrides collection-level fallback." - }, - "blockRegistry": { - "type": "object", - "description": "Block registry configuration for loading available block definitions.", - "additionalProperties": false, - "properties": { - "file": { - "type": "string", - "description": "URL to an ES module that default-exports a BlockRegistry (Record). Overrides collection-level fallback." - } - } - } - } - } - } - } - ] - }, - { - "$ref": "#/definitions/minimum", - "patternProperties": { - "^x-.*|widget|pagebuilder$": { - "$comment": "stub for allOf" - } - }, - "allOf": [ - { - "properties": { - "widget": { - "enum": [ - "pagebuilder", - "pageBuilder" - ] - }, - "pagebuilder": { - "type": "object", - "description": "Pagebuilder widget configuration. Field-level settings override collection-level fallbacks.", - "additionalProperties": false, - "properties": { - "mode": { - "type": "string", - "enum": [ - "overlay", - "inline", - "both" - ], - "description": "[Planned] Editor mode. Currently only 'overlay' is implemented. 'inline' and 'both' are planned." - }, - "defaultViewport": { - "type": "number", - "description": "Default viewport width in pixels for the preview. Common presets: 1280 (Desktop), 768 (Tablet), 375 (Phone). Default: 1280. Overrides collection-level fallback." - }, - "blockTypeField": { - "type": "string", - "description": "Name of the sub-field that holds the block type identifier. Default: 'blockType'. Overrides collection-level fallback." - }, - "blockRegistry": { - "type": "object", - "description": "Block registry configuration for loading available block definitions.", - "additionalProperties": false, - "properties": { - "file": { - "type": "string", - "description": "URL to an ES module that default-exports a BlockRegistry (Record). Overrides collection-level fallback." - } - } - } - } - } - } - } - ] - } - ], - "definitions": { - "minimum": { - "additionalProperties": false, - "patternProperties": { - "^x\\-": { - "description": "template property" - } - }, - "properties": { - "folding": { - "type": "object", - "description": "folding configuration for array fields", - "additionalProperties": false, - "properties": { - "previewUnfolded": { - "description": "preview for unfolded array element", - "oneOf": [ - { - "type": "string" - }, - { - "$ref": "definitions.json#/definitions/evalObjectWithRaw" - } - ] - }, - "previewFolded": { - "description": "preview for folded array element", - "oneOf": [ - { - "type": "string" - }, - { - "$ref": "definitions.json#/definitions/evalObjectWithRaw" - } - ] - } - } - }, - "hide": { - "type": "boolean", - "description": "hide field widget" - }, - "direction": { - "type": "string", - "enum": [ - "horizontal", - "vertical" - ] - }, - "pathStep": { - "type": "object", - "properties": { - "title": { - "type": "string" - }, - "icon": { - "type": "string" - } - }, - "required": [ - "title" - ] - }, - "preview": { - "description": "preview configuration for array elements", - "oneOf": [ - { - "type": "string" - }, - { - "$ref": "definitions.json#/definitions/evalObjectWithRaw" - } - ] - }, - "metaElements": { - "oneOf": [ - { - "type": "array", - "items": { - "type": "string" - } - }, - { - "type": "array", - "items": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - { - "type": "object", - "properties": { - "source": { - "type": "string" - } - }, - "required": [ - "source" - ] - }, - { - "type": "array", - "items": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "source": { - "type": "string", - "const": "layout" - } - }, - "required": [ - "source" - ] - } - ] - }, - { - "type": "object", - "properties": { - "tablist": { - "type": "object", - "properties": { - "tabs": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "label": { - "type": "string" - }, - "subFields": { - "type": "array", - "items": { - "type": "object", - "properties": { - "source": { - "type": "string" - } - }, - "required": [ - "source" - ] - } - } - }, - "required": [ - "name", - "label", - "subFields" - ] - } - } - }, - "required": [ - "tabs" - ] - } - } - } - ] - }, - "openapi": { - "type": "object" - }, - "label": { - "$ref": "definitions.json#/definitions/i18nString", - "description": "label for field" - }, - "addElementLabel": { - "$ref": "definitions.json#/definitions/i18nString", - "description": "label for adding element to list" - }, - "helperText": { - "$ref": "definitions.json#/definitions/i18nString", - "description": "helper text for field" - }, - "filter": { - "type": "object", - "description": "filter configuration for list view of this field", - "additionalProperties": false, - "properties": { - "type": { - "type": "string", - "description": "filter type", - "enum": [ - "default", - "select", - "bool", - "boolean", - "input", - "number", - "foreignKey" - ] - } - } - }, - "css": { - "description": "css styles for field widgets", - "oneOf": [ - { - "$comment": "for include tag", - "type": "string" - }, - { - "type": "object", - "properties": { - "input": { - "description": "css styles for input widget", - "oneOf": [ - { - "$comment": "for include tag", - "type": "string" - }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "wrapper": { - "description": "css styles for input widget wrapper", - "$ref": "#/definitions/css" - }, - "element": { - "description": "css styles for input widget sub elements", - "$ref": "#/definitions/css" - }, - "foreignEntry": { - "description": "css styles for input widget linked foreign entries", - "$ref": "#/definitions/css" - } - } - } - ] - }, - "view": { - "description": "css styles for input widget", - "oneOf": [ - { - "$comment": "for include tag", - "type": "string" - }, - { - "type": "object", - "additionalProperties": false, - "properties": { - "wrapper": { - "description": "css styles for view widget wrapper", - "$ref": "#/definitions/css" - }, - "element": { - "description": "css styles for view widget sub elements", - "$ref": "#/definitions/css" - }, - "foreignEntry": { - "description": "css styles for input widget linked foreign entries", - "$ref": "#/definitions/css" - } - } - } - ] - } - } - } - ] - }, - "dependsOn": { - "description": "depends on rule", - "oneOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "eval": { - "type": "string", - "description": "javascript code which hides field widget if evaluates to false with $, $this, $parent and $stack variables" - } - } - } - ] - }, - "containerProps": { - "type": "object", - "properties": { - "class": { - "type": "string" - }, - "layout": { - "type": "object", - "properties": { - "breakBefore": { - "type": "boolean" - }, - "breakAfter": { - "type": "boolean" - }, - "size": { - "oneOf": [ - { - "type": "string", - "description": "shorthand: single size for all breakpoints (e.g. col-3)" - }, - { - "type": "object", - "description": "responsive sizes per breakpoint", - "properties": { - "default": { - "type": "string" - }, - "small": { - "type": "string" - }, - "large": { - "type": "string" - } - } - } - ] - } - } - } - } - }, - "inputProps": { - "type": "object", - "additionalProperties": true - }, - "defaultValue": { - "description": "default value or code to get the value", - "oneOf": [ - { - "type": "array" - }, - { - "type": "string" - }, - { - "type": "null" - }, - { - "type": "boolean" - }, - { - "type": "number" - }, - { - "type": "object", - "properties": { - "eval": { - "type": "string", - "description": "js code with $navigation variable" - } - } - } - ] - }, - "valueMap": { - "type": "object", - "description": "map values to icons", - "patternProperties": { - ".*": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": { - "muiIcon": { - "type": "string" - }, - "style": { - "$comment": "https://raw.githubusercontent.com/rcorp/css-schema/master/schema.json" - } - } - } - ] - } - } - }, - "widget": { - "type": "string", - "description": "widget type for rendering the field in admin UI" - }, - "position": { - "type": "string", - "description": "field position in the editor layout: 'main' (default), 'sidebar' (default settings card), or 'sidebar:' (named sidebar card, e.g. 'sidebar:SEO')" - }, - "section": { - "description": "section grouping for the field in the editor", - "$ref": "definitions.json#/definitions/i18nString" - }, - "choices": { - "description": "choices for select/chipArray/checkboxArray widgets", - "oneOf": [ - { - "type": "array", - "items": { - "type": "object" - } - }, - { - "type": "object" - } - ] - }, - "foreign": { - "type": "object", - "description": "foreign key configuration for foreignKey/foreignFile widgets" - }, - "downscale": { - "type": "object", - "description": "image downscale configuration for file/image widgets", - "properties": { - "maxWidth": { - "type": "number", - "description": "maximum width in pixels" - }, - "maxHeight": { - "type": "number", - "description": "maximum height in pixels" - }, - "quality": { - "type": "number", - "description": "JPEG quality (0-100)" - } - } - }, - "drillDown": { - "type": "boolean", - "description": "whether object subFields should be rendered inline (false) or as drill-down navigation (true, default)" - }, - "readonly": { - "type": "boolean", - "description": "if true, the field is displayed as a read-only view (table/card style) instead of an editable widget. Use inputProps for a disabled input instead." - } - } - }, - "assetsTarget": { - "oneOf": [ - { - "type": "string", - "description": "url for assets view" - }, - { - "type": "object", - "properties": { - "collection": { - "type": "string", - "description": "collection name" - }, - "subNavigation": { - "type": "number", - "description": "sub navigation index in collections meta.subNavigation" - } - } - }, - { - "$ref": "definitions.json#/definitions/evalObject" - } - ] - }, - "css": { - "anyOf": [ - { - "type": "string" - }, - { - "$ref": "https://raw.githubusercontent.com/rcorp/css-schema/master/schema.json" - }, - { - "type": "object", - "properties": { - "eval": { - "type": "string", - "description": "javascript code evaled to string or css object" - } - } - } - ] - } - } -} diff --git a/schemas/api-config/hooks.json b/schemas/api-config/hooks.json deleted file mode 100644 index d6c869c..0000000 --- a/schemas/api-config/hooks.json +++ /dev/null @@ -1,148 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "JSON Schema tibi-server hooks configuration", - "description": "tibi-server hooks configuration", - "type": "object", - "additionalProperties": false, - "patternProperties": { - "^x\\-": { - "description": "template property" - } - }, - "properties": { - "get": { - "type": "object", - "description": "hooks for http GET", - "additionalProperties": false, - "properties": { - "read": { - "description": "hook before reading from database", - "$ref": "#/definitions/hookDef" - }, - "return": { - "description": "hook before returning entries read from database", - "$ref": "#/definitions/hookDef" - }, - "file": { - "description": "hook for file download requests (GET /collection/:id/:field/*path)", - "$ref": "#/definitions/hookDef" - } - } - }, - "post": { - "type": "object", - "description": "hooks for http POST", - "additionalProperties": false, - "properties": { - "bind": { - "description": "hook before binding data from post request", - "$ref": "#/definitions/hookDef" - }, - "validate": { - "description": "hook before validation data", - "$ref": "#/definitions/hookDef" - }, - "create": { - "description": "hook before creating entry in database", - "$ref": "#/definitions/hookDef" - }, - "return": { - "description": "hook before returning result (after database write)", - "$ref": "#/definitions/hookDef" - }, - "bulkCreate": { - "description": "hook for bulk create (POST with JSON array) — optimised single-DB-call path", - "$ref": "#/definitions/hookDef" - }, - "bulkReturn": { - "description": "hook before returning bulk create result", - "$ref": "#/definitions/hookDef" - } - } - }, - "put": { - "type": "object", - "description": "hooks for http PUT", - "additionalProperties": false, - "properties": { - "bind": { - "description": "hook before binding data from put request", - "$ref": "#/definitions/hookDef" - }, - "validate": { - "description": "hook before validation data", - "$ref": "#/definitions/hookDef" - }, - "update": { - "description": "hook before updating entry in database", - "$ref": "#/definitions/hookDef" - }, - "return": { - "description": "hook before returning result (after database write)", - "$ref": "#/definitions/hookDef" - }, - "bulkUpdate": { - "description": "hook for bulk update (PUT without ID) — optimised single-DB-call path", - "$ref": "#/definitions/hookDef" - }, - "bulkReturn": { - "description": "hook before returning bulk update result", - "$ref": "#/definitions/hookDef" - } - } - }, - "delete": { - "type": "object", - "description": "hooks for http DELETE", - "additionalProperties": false, - "properties": { - "delete": { - "description": "hook before deleting entry from database", - "$ref": "#/definitions/hookDef" - }, - "return": { - "description": "hook before returning result (after database write)", - "$ref": "#/definitions/hookDef" - }, - "bulkDelete": { - "description": "hook for bulk delete (DELETE without ID) — optimised single-DB-call path", - "$ref": "#/definitions/hookDef" - }, - "bulkReturn": { - "description": "hook before returning bulk delete result", - "$ref": "#/definitions/hookDef" - } - } - }, - "audit": { - "type": "object", - "description": "hooks for audit log entries", - "additionalProperties": false, - "properties": { - "return": { - "description": "hook before returning audit log entries for this collection, can be used to remove sensitive fields from snapshots", - "$ref": "#/definitions/hookDef" - } - } - } - }, - "definitions": { - "hookDef": { - "type": "object", - "properties": { - "type": { - "const": "javascript" - }, - "file": { - "type": "string", - "description": "location of javascript hook file relative to config.yml base" - }, - "timeout": { - "type": "integer", - "description": "execution timeout in seconds" - } - }, - "required": ["type", "file"] - } - } -} diff --git a/schemas/api-config/imageFilter.json b/schemas/api-config/imageFilter.json deleted file mode 100644 index 0741c4c..0000000 --- a/schemas/api-config/imageFilter.json +++ /dev/null @@ -1,102 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "JSON Schema tibi-server imageFilter configuration", - "description": "tibi-server imageFilter linter", - "type": "object", - "patternProperties": { - "^x\\-": { - "description": "template property" - }, - "^[a-zA-Z0-9_-]+$": { - "type": "array", - "items": { - "type": "object", - "properties": { - "fit": { - "type": "boolean" - }, - "fill": { - "type": "boolean" - }, - "width": { - "type": "number" - }, - "height": { - "type": "number" - }, - "blur": { - "type": "number" - }, - "brightness": { - "type": "number" - }, - "contrast": { - "type": "number" - }, - "gamma": { - "type": "number" - }, - "saturation": { - "type": "number" - }, - "sharpen": { - "type": "number" - }, - "invert": { - "type": "boolean" - }, - "grayscale": { - "type": "boolean" - }, - "resampling": { - "enum": [ - "lanczos", - "nearestNeighbor", - "hermite", - "linear", - "catmullRom", - "box", - "mitchellNetravili", - "bSpline", - "gaussian", - "bartlett", - "hann", - "hamming", - "blackman", - "welch", - "cosine" - ] - }, - "anchor": { - "enum": [ - "center", - "topLeft", - "top", - "topRight", - "left", - "right", - "bottomLeft", - "bottom", - "bottomRight" - ] - }, - "quality": { - "type": "number" - }, - "lossless": { - "type": "boolean" - }, - "skipLargerDimension": { - "type": "boolean" - }, - "skipLargerFilesize": { - "type": "boolean" - }, - "outputType": { - "enum": ["jpg", "jpeg", "png", "webp"] - } - } - } - } - } -} diff --git a/schemas/api-config/index.json b/schemas/api-config/index.json deleted file mode 100644 index a8a6a1c..0000000 --- a/schemas/api-config/index.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "JSON Schema tibi-server index configuration", - "description": "tibi-server index linter", - "type": "object", - "additionalProperties": false, - "patternProperties": { - "^x\\-": { - "description": "template property" - } - }, - "properties": { - "name": { - "type": "string", - "description": "name of index" - }, - "key": { - "type": "array", - "description": "array of keys, minus in front for reverse order", - "items": { - "type": "string" - } - }, - "unique": { - "type": "boolean", - "description": "unique index" - }, - "dropDups": { - "type": "boolean", - "description": "drop duplicates" - }, - "background": { - "type": "boolean", - "description": "build index in background" - }, - "sparse": { - "type": "boolean", - "description": "sparse index" - }, - "defaultLanguage": { - "type": "string", - "description": "default language for text index" - }, - "languageOverride": { - "type": "string", - "description": "language override field name" - } - }, - "required": ["key"] -} diff --git a/schemas/api-config/job.json b/schemas/api-config/job.json deleted file mode 100644 index 5309cbb..0000000 --- a/schemas/api-config/job.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "JSON Schema tibi-server cronjob configuration", - "description": "tibi-server cronjob linter", - "type": "object", - "additionalProperties": false, - "patternProperties": { - "^x\\-": { - "description": "template property" - } - }, - "properties": { - "meta": { - "type": "object", - "description": "meta object used to pass parameters to identify cronjob", - "additionalProperties": true - }, - "cron": { - "type": "string", - "description": "cronjob time/interval specification" - }, - "type": { - "const": "javascript", - "description": "type of job" - }, - "file": { - "type": "string", - "description": "javascript file relative to config.yml" - }, - "timeout": { - "type": "integer", - "description": "execution timeout in seconds" - } - }, - "required": ["type", "file"] -} diff --git a/schemas/api-config/permissions.json b/schemas/api-config/permissions.json deleted file mode 100644 index e7a75dc..0000000 --- a/schemas/api-config/permissions.json +++ /dev/null @@ -1,128 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "JSON Schema tibi-server permissions configuration", - "description": "tibi-server permissions linter", - "type": "object", - "additionalProperties": false, - "properties": { - "public": { - "description": "permissions for unauthorized public access", - "$ref": "#/definitions/permissionSet" - }, - "user": { - "description": "permissions for authorized users (nativ tibi-server auth)", - "$ref": "#/definitions/permissionSet" - } - }, - "patternProperties": { - "^x\\-": { - "description": "template property" - }, - "^token:": { - "description": "permissions for header or query token", - "$ref": "#/definitions/permissionSet" - }, - "^[a-zA-Z0-9_]+$": { - "description": "custom permissions", - "$ref": "#/definitions/permissionSet" - } - }, - "required": ["public", "user"], - "definitions": { - "permissionSet": { - "type": "object", - "additionalProperties": false, - "properties": { - "methods": { - "type": "object", - "description": "permissions for http methods", - "additionalProperties": false, - "properties": { - "get": { - "oneOf": [ - { "type": "boolean" }, - { - "type": "object", - "properties": { - "allow": { "type": "boolean", "description": "Allow GET." } - }, - "required": ["allow"], - "additionalProperties": false - } - ] - }, - "post": { - "oneOf": [ - { "type": "boolean" }, - { - "type": "object", - "properties": { - "allow": { "type": "boolean", "description": "Allow single-document POST." }, - "bulk": { "type": "boolean", "description": "Allow bulk POST (JSON array body)." } - }, - "required": ["allow"], - "additionalProperties": false - } - ] - }, - "put": { - "oneOf": [ - { "type": "boolean" }, - { - "type": "object", - "properties": { - "allow": { "type": "boolean", "description": "Allow single-document PUT." }, - "bulk": { "type": "boolean", "description": "Allow bulk PUT (without ID)." } - }, - "required": ["allow"], - "additionalProperties": false - } - ] - }, - "delete": { - "oneOf": [ - { "type": "boolean" }, - { - "type": "object", - "properties": { - "allow": { "type": "boolean", "description": "Allow single-document DELETE." }, - "bulk": { "type": "boolean", "description": "Allow bulk DELETE (without ID)." } - }, - "required": ["allow"], - "additionalProperties": false - } - ] - } - } - }, - "validProjections": { - "type": "array", - "description": "list of projection names this permission set is allowed to use", - "items": { - "type": "string" - } - }, - "filter": { - "type": "object", - "description": "MongoDB filter applied to all queries for this permission set", - "additionalProperties": true - }, - "readonlyFields": { - "type": "array", - "description": "fields that are read-only for this permission set", - "items": { - "type": "string" - } - }, - "hiddenFields": { - "type": "array", - "description": "fields that are hidden for this permission set", - "items": { - "type": "string" - } - } - }, - "required": ["methods"] - } - } -} diff --git a/schemas/api-config/projections.json b/schemas/api-config/projections.json deleted file mode 100644 index 2f78b3d..0000000 --- a/schemas/api-config/projections.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "JSON Schema tibi-server imageFilter configuration", - "description": "tibi-server projections linter", - "type": "object", - "patternProperties": { - "^x\\-": { - "description": "template property" - }, - "^[a-zA-Z0-9_-]+$": { - "type": "object", - "description": "dataset query projection config", - "properties": { - "select": { - "oneOf": [ - { "type": "null" }, - { - "type": "object", - "description": "mongo db selector", - "patternProperties": { - "^[a-zA-Z0-9_]+(\\.[a-zA-Z0-9]+)*$": { - "enum": [0, 1] - } - } - } - ] - } - } - } - } -} diff --git a/schemas/config/action.schema.json b/schemas/config/action.schema.json new file mode 100644 index 0000000..7d4a89d --- /dev/null +++ b/schemas/config/action.schema.json @@ -0,0 +1,434 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "action.schema.json", + "title": "Tibi action config", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-": {} + }, + "properties": { + "name": { + "type": "string", + "pattern": "^[0-9A-Za-z_-]+$" + }, + "meta": { + "$ref": "action.schema.json#/definitions/actionMeta" + }, + "permissions": { + "$ref": "action.schema.json#/definitions/actionPermissions" + }, + "hooks": { + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "hooks.schema.json" + } + ] + } + }, + "required": [ + "name" + ], + "definitions": { + "actionField": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-": {} + }, + "properties": { + "name": { + "type": "string", + "pattern": "^[0-9A-Za-z_]+$" + }, + "type": { + "type": "string", + "enum": [ + "string", + "string[]", + "number", + "number[]", + "boolean", + "object", + "object[]", + "file", + "file[]", + "date", + "any" + ] + }, + "index": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "single", + "unique", + "text", + "sparse", + "background" + ] + } + }, + "subFields": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "action.schema.json#/definitions/actionField" + } + ] + } + } + ] + }, + "validator": { + "$ref": "field.schema.json#/definitions/field/properties/validator" + }, + "readonly": { + "$ref": "defs.schema.json#/definitions/boolOrEval" + }, + "hidden": { + "$ref": "defs.schema.json#/definitions/boolOrEval" + }, + "strictFields": { + "type": "boolean" + }, + "meta": { + "$ref": "field-meta.schema.json" + } + }, + "required": [ + "name", + "type" + ] + }, + "actionMappedField": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-": {} + }, + "properties": { + "source": { + "$ref": "defs.schema.json#/definitions/fieldPath" + }, + "name": { + "type": "string", + "pattern": "^[0-9A-Za-z_]+$" + }, + "type": { + "type": "string", + "enum": [ + "string", + "string[]", + "number", + "number[]", + "boolean", + "object", + "object[]", + "file", + "file[]", + "date", + "any" + ] + }, + "subFields": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "action.schema.json#/definitions/actionMappedField" + } + ] + } + } + ] + }, + "meta": { + "$ref": "field-meta.schema.json" + } + }, + "required": [ + "type" + ], + "anyOf": [ + { + "required": [ + "source" + ] + }, + { + "required": [ + "name" + ] + } + ] + }, + "actionFieldSection": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-": {} + }, + "properties": { + "title": { + "$ref": "defs.schema.json#/definitions/i18nText" + }, + "description": { + "$ref": "defs.schema.json#/definitions/i18nText" + }, + "fields": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "action.schema.json#/definitions/actionField" + } + ] + } + } + } + }, + "actionExecutionMeta": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-": {} + }, + "properties": { + "kind": { + "type": "string" + }, + "cancellable": { + "type": "boolean" + } + } + }, + "actionOutputProgressMapping": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-": {} + }, + "properties": { + "indexField": { + "$ref": "defs.schema.json#/definitions/fieldPath" + }, + "countField": { + "$ref": "defs.schema.json#/definitions/fieldPath" + }, + "statusField": { + "oneOf": [ + { + "$ref": "defs.schema.json#/definitions/fieldPath" + }, + { + "type": "array", + "items": { + "$ref": "defs.schema.json#/definitions/fieldPath" + } + } + ] + }, + "messageField": { + "$ref": "defs.schema.json#/definitions/fieldPath" + }, + "fields": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "action.schema.json#/definitions/actionMappedField" + } + ] + } + } + } + }, + "actionOutputMappingSection": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-": {} + }, + "properties": { + "fields": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "action.schema.json#/definitions/actionMappedField" + } + ] + } + } + } + }, + "actionOutputMapping": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-": {} + }, + "properties": { + "summary": { + "$ref": "action.schema.json#/definitions/actionOutputMappingSection" + }, + "details": { + "$ref": "action.schema.json#/definitions/actionOutputMappingSection" + }, + "progress": { + "$ref": "action.schema.json#/definitions/actionOutputProgressMapping" + } + } + }, + "actionOutputMeta": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-": {} + }, + "properties": { + "title": { + "$ref": "defs.schema.json#/definitions/i18nText" + }, + "mapping": { + "$ref": "action.schema.json#/definitions/actionOutputMapping" + } + } + }, + "actionMethodMeta": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-": {} + }, + "properties": { + "query": { + "$ref": "action.schema.json#/definitions/actionFieldSection" + }, + "data": { + "$ref": "action.schema.json#/definitions/actionFieldSection" + }, + "execution": { + "$ref": "action.schema.json#/definitions/actionExecutionMeta" + }, + "output": { + "$ref": "action.schema.json#/definitions/actionOutputMeta" + } + } + }, + "actionMeta": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-": {} + }, + "properties": { + "label": { + "$ref": "defs.schema.json#/definitions/i18nText" + }, + "muiIcon": { + "type": "string" + }, + "description": { + "$ref": "defs.schema.json#/definitions/i18nText" + }, + "group": { + "type": "string" + }, + "hide": { + "type": "boolean" + }, + "methods": { + "type": "object", + "patternProperties": { + "^x-": {}, + "^[a-zA-Z0-9_-]+$": { + "$ref": "action.schema.json#/definitions/actionMethodMeta" + } + }, + "additionalProperties": false + } + } + }, + "actionPermissionSet": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-": {} + }, + "properties": { + "methods": { + "type": "object", + "additionalProperties": false, + "properties": { + "get": { + "$ref": "defs.schema.json#/definitions/methodPermissionGet" + }, + "post": { + "$ref": "defs.schema.json#/definitions/methodPermissionWrite" + } + } + } + }, + "required": [ + "methods" + ] + }, + "actionPermissions": { + "type": "object", + "additionalProperties": false, + "properties": { + "public": { + "$ref": "action.schema.json#/definitions/actionPermissionSet" + }, + "user": { + "$ref": "action.schema.json#/definitions/actionPermissionSet" + } + }, + "patternProperties": { + "^x-": {}, + "^token:[^\\s]+$": { + "$ref": "action.schema.json#/definitions/actionPermissionSet" + }, + "^[a-zA-Z0-9_]+$": { + "$ref": "action.schema.json#/definitions/actionPermissionSet" + } + }, + "required": [ + "public", + "user" + ] + } + } +} \ No newline at end of file diff --git a/schemas/config/asset.schema.json b/schemas/config/asset.schema.json new file mode 100644 index 0000000..ce3f131 --- /dev/null +++ b/schemas/config/asset.schema.json @@ -0,0 +1,22 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "asset.schema.json", + "title": "Tibi asset config", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-": {} + }, + "properties": { + "name": { + "type": "string" + }, + "path": { + "type": "string" + } + }, + "required": [ + "name", + "path" + ] +} \ No newline at end of file diff --git a/schemas/config/collection-meta.schema.json b/schemas/config/collection-meta.schema.json new file mode 100644 index 0000000..e290b67 --- /dev/null +++ b/schemas/config/collection-meta.schema.json @@ -0,0 +1,380 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "collection-meta.schema.json", + "title": "Tibi collection meta config", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-": {} + }, + "properties": { + "label": { + "$ref": "defs.schema.json#/definitions/i18nText" + }, + "muiIcon": { + "type": "string" + }, + "group": { + "type": "string" + }, + "imageUrl": { + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "defs.schema.json#/definitions/evalString" + } + ] + }, + "hide": { + "type": "boolean" + }, + "singleton": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean" + }, + "key": { + "type": "string" + } + }, + "required": [ + "enabled" + ] + }, + "subNavigation": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "label": { + "$ref": "defs.schema.json#/definitions/i18nText" + }, + "muiIcon": { + "type": "string" + }, + "filter": { + "type": "object", + "additionalProperties": true + }, + "defaultSort": { + "type": "object", + "additionalProperties": false, + "properties": { + "field": { + "$ref": "defs.schema.json#/definitions/sortField" + }, + "order": { + "type": "string", + "enum": [ + "ASC", + "DESC" + ] + } + }, + "required": [ + "field" + ] + }, + "setDefault": { + "type": "object", + "additionalProperties": false, + "description": "Pre-fill a field with a default value when creating a new entry while this subNav tab is active.", + "properties": { + "field": { + "type": "string" + }, + "value": {} + }, + "required": [ + "field", + "value" + ] + } + }, + "required": [ + "name" + ] + } + }, + "preview": { + "$ref": "defs.schema.json#/definitions/previewConfig" + }, + "i18n": { + "oneOf": [ + { + "type": "boolean", + "enum": [ + false + ] + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "defaultLanguage": { + "type": "string" + }, + "languages": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "code": { + "type": "string" + }, + "label": { + "$ref": "defs.schema.json#/definitions/i18nText" + } + }, + "required": [ + "code" + ] + } + }, + "entry": { + "type": "object", + "additionalProperties": false, + "properties": { + "languageField": { + "type": "string" + }, + "groupField": { + "type": "string" + }, + "clearFields": { + "type": "array", + "items": { + "type": "string" + } + }, + "copyFields": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "languageField", + "groupField" + ] + } + } + } + ] + }, + "defaultSort": { + "type": "object", + "additionalProperties": false, + "properties": { + "field": { + "$ref": "defs.schema.json#/definitions/sortField" + }, + "order": { + "type": "string", + "enum": [ + "ASC", + "DESC", + "MANUALLY" + ] + } + }, + "required": [ + "field" + ] + }, + "viewHint": { + "oneOf": [ + { + "type": "string", + "enum": [ + "table", + "cards" + ] + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "media": { + "type": "object", + "additionalProperties": false, + "properties": { + "upload": { + "type": "object", + "additionalProperties": false, + "properties": { + "autoFill": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } + }, + "ai": { + "type": "object", + "additionalProperties": false, + "properties": { + "targetField": { + "type": "string" + }, + "prompt": { + "type": "string" + }, + "image": { + "type": "object", + "additionalProperties": false, + "properties": { + "maxWidth": { + "type": "number", + "exclusiveMinimum": 0 + }, + "maxHeight": { + "type": "number", + "exclusiveMinimum": 0 + }, + "quality": { + "type": "number", + "exclusiveMinimum": 0, + "maximum": 1 + } + } + } + } + } + } + }, + "navigation": { + "type": "object", + "additionalProperties": false, + "properties": { + "nodesField": { + "type": "string" + }, + "preview": { + "$ref": "defs.schema.json#/definitions/previewConfig" + }, + "declaredTrees": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "label": { + "$ref": "defs.schema.json#/definitions/i18nText" + }, + "singleton": { + "type": "object", + "additionalProperties": true + }, + "maxLevel": { + "type": "number" + } + }, + "required": [ + "singleton" + ] + } + } + } + } + }, + "oneOf": [ + { + "required": [ + "media" + ] + }, + { + "required": [ + "navigation" + ] + } + ] + } + ] + }, + "pagebuilder": { + "type": "object", + "additionalProperties": false, + "properties": { + "defaultViewport": { + "$ref": "defs.schema.json#/definitions/pagebuilderViewport" + }, + "blockTypeField": { + "type": "string" + }, + "blockRegistry": { + "type": "object", + "additionalProperties": false, + "properties": { + "file": { + "type": "string" + } + }, + "required": [ + "file" + ] + }, + "screenshot": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "field": { + "$ref": "defs.schema.json#/definitions/fieldPath" + }, + "fileField": { + "$ref": "defs.schema.json#/definitions/fieldPath" + }, + "screenWidth": { + "type": "integer", + "minimum": 1 + }, + "imageWidth": { + "type": "integer", + "minimum": 1 + } + }, + "required": [ + "field", + "fileField" + ] + } + } + } + }, + "sidebar": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "group": { + "type": "string" + }, + "label": { + "$ref": "defs.schema.json#/definitions/i18nText" + } + }, + "required": [ + "group" + ] + } + }, + "openapi": { + "$ref": "openapi.schema.json" + } + } +} \ No newline at end of file diff --git a/schemas/config/collection.schema.json b/schemas/config/collection.schema.json new file mode 100644 index 0000000..30b7d37 --- /dev/null +++ b/schemas/config/collection.schema.json @@ -0,0 +1,431 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "collection.schema.json", + "title": "Tibi collection config", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-": {} + }, + "properties": { + "name": { + "type": "string" + }, + "defaultLanguage": { + "type": "string" + }, + "uploadPath": { + "type": "string" + }, + "upload": { + "$ref": "collection.schema.json#/definitions/uploadConfig" + }, + "permissions": { + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "permissions.schema.json" + } + ] + }, + "projections": { + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "projections.schema.json" + } + ] + }, + "hooks": { + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "hooks.schema.json" + } + ] + }, + "meta": { + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "collection-meta.schema.json" + } + ] + }, + "imageFilter": { + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "image-filter.schema.json" + } + ] + }, + "fields": { + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "field-list.schema.json" + } + ] + }, + "indexes": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "collection.schema.json#/definitions/indexConfig" + } + ] + } + } + ] + }, + "audit": { + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "type": "boolean" + }, + "actions": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "create", + "update", + "delete", + "bulkCreate", + "bulkUpdate", + "bulkDelete" + ] + } + } + } + }, + "bulk": { + "type": "object", + "additionalProperties": false, + "properties": { + "optimize": { + "type": "object", + "additionalProperties": false, + "properties": { + "create": { + "type": "boolean" + }, + "update": { + "type": "boolean" + }, + "delete": { + "type": "boolean" + } + } + } + } + }, + "queryLimits": { + "type": "object", + "additionalProperties": false, + "properties": { + "defaultLimit": { + "type": "integer", + "minimum": 0 + }, + "maxLimit": { + "type": "integer", + "minimum": 0 + } + } + }, + "readonlyFields": { + "type": "array", + "items": { + "$ref": "defs.schema.json#/definitions/fieldPath" + } + }, + "hiddenFields": { + "type": "array", + "items": { + "$ref": "defs.schema.json#/definitions/fieldPath" + } + }, + "strictFields": { + "type": "boolean" + }, + "search": { + "type": "array", + "items": { + "$ref": "collection.schema.json#/definitions/searchConfig" + } + } + }, + "required": [ + "name", + "permissions" + ], + "definitions": { + "uploadConfig": { + "type": "object", + "additionalProperties": false, + "properties": { + "maxBodySize": { + "type": "string" + }, + "allowedMimeTypes": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "indexConfig": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "key": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "unique": { + "type": "boolean" + }, + "dropDups": { + "type": "boolean" + }, + "background": { + "type": "boolean" + }, + "sparse": { + "type": "boolean" + }, + "defaultLanguage": { + "type": "string" + }, + "languageOverride": { + "type": "string" + } + }, + "required": [ + "key" + ] + }, + "searchConfig": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "mode": { + "type": "string", + "enum": [ + "text", + "regex", + "eval", + "filter", + "ngram", + "vector", + "combined" + ] + }, + "fields": { + "type": "array", + "items": { + "$ref": "defs.schema.json#/definitions/fieldPath" + } + }, + "meta": { + "type": "object", + "additionalProperties": true + }, + "eval": { + "type": "string" + }, + "filter": { + "type": "object", + "additionalProperties": true + }, + "ngram": { + "type": "object", + "additionalProperties": false, + "properties": { + "sizes": { + "type": "array", + "items": { + "type": "integer", + "minimum": 1 + } + }, + "normalize": { + "type": "object", + "additionalProperties": false, + "properties": { + "use": { + "type": "array", + "items": { + "type": "string" + } + }, + "eval": { + "type": "string" + } + } + } + } + }, + "regex": { + "type": "object", + "additionalProperties": false, + "properties": { + "weights": { + "type": "object", + "additionalProperties": { + "type": "number" + } + }, + "scoring": { + "type": "string", + "enum": [ + "count", + "binary", + "log" + ] + } + } + }, + "score": { + "type": "object", + "additionalProperties": false, + "properties": { + "expr": { + "type": "object", + "additionalProperties": true + } + } + }, + "vector": { + "type": "object", + "additionalProperties": false, + "properties": { + "provider": { + "type": "string" + }, + "documentPrefix": { + "type": "string" + }, + "queryPrefix": { + "type": "string" + }, + "overflow": { + "type": "string", + "enum": [ + "truncate", + "chunk" + ] + }, + "chunkSize": { + "type": "integer", + "minimum": 1 + }, + "chunkOverlap": { + "type": "integer", + "minimum": 0 + } + }, + "required": [ + "provider" + ] + }, + "rrf": { + "type": "object", + "additionalProperties": false, + "properties": { + "k": { + "type": "integer", + "minimum": 1 + }, + "topK": { + "type": "integer", + "minimum": 1 + }, + "weights": { + "type": "object", + "additionalProperties": { + "type": "number" + } + }, + "strategy": { + "type": "string", + "enum": ["rrf", "score_rrf", "score_sum", "rank_score", "hybrid"] + }, + "normalize": { + "type": "string", + "enum": ["none", "minmax", "zscore"] + }, + "scoreWeight": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "sourceConfig": { + "type": "object", + "additionalProperties": { + "type": "object", + "additionalProperties": false, + "properties": { + "scoreWeight": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "boost": { + "type": "number", + "minimum": 0 + } + } + } + } + } + }, + "async": { + "type": "boolean" + }, + "autoRegenerate": { + "type": "boolean" + } + }, + "required": [ + "name", + "mode" + ] + } + } +} \ No newline at end of file diff --git a/schemas/config/defs.schema.json b/schemas/config/defs.schema.json new file mode 100644 index 0000000..5c9d0cc --- /dev/null +++ b/schemas/config/defs.schema.json @@ -0,0 +1,508 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "defs.schema.json", + "title": "Shared definitions for Tibi config schemas", + "definitions": { + "i18nText": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "minProperties": 1, + "patternProperties": { + "^[a-z]{2}(-[A-Za-z0-9_]+)?$": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + }, + "fieldPath": { + "type": "string", + "pattern": "^[a-zA-Z0-9_]+(\\.[a-zA-Z0-9_]+)*$" + }, + "evalString": { + "type": "object", + "additionalProperties": false, + "properties": { + "eval": { + "type": "string" + }, + "select": { + "type": "array", + "items": { + "$ref": "defs.schema.json#/definitions/fieldPath" + } + } + }, + "required": [ + "eval" + ] + }, + "evalHtml": { + "type": "object", + "additionalProperties": false, + "properties": { + "eval": { + "type": "string" + }, + "raw": { + "type": "boolean" + } + }, + "required": [ + "eval" + ] + }, + "boolOrEval": { + "oneOf": [ + { + "type": "boolean" + }, + { + "$ref": "defs.schema.json#/definitions/evalString" + } + ] + }, + "boolOrUiCondition": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "string" + }, + { + "$ref": "defs.schema.json#/definitions/evalString" + } + ] + }, + "uiCondition": { + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "defs.schema.json#/definitions/evalString" + } + ] + }, + "sortField": { + "oneOf": [ + { + "$ref": "defs.schema.json#/definitions/fieldPath" + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "source": { + "$ref": "defs.schema.json#/definitions/fieldPath" + } + }, + "required": [ + "source" + ] + } + ] + }, + "pagebuilderViewport": { + "oneOf": [ + { + "type": "integer", + "minimum": 1 + }, + { + "type": "string", + "enum": [ + "desktop", + "tablet", + "phone" + ] + } + ] + }, + "badgeVariant": { + "type": "string", + "enum": [ + "success", + "error", + "warning", + "info", + "neutral" + ] + }, + "choiceItem": { + "type": "object", + "additionalProperties": false, + "properties": { + "id": { + "type": [ + "string", + "number", + "boolean", + "null" + ] + }, + "value": { + "type": [ + "string", + "number", + "boolean", + "null" + ] + }, + "name": { + "$ref": "defs.schema.json#/definitions/i18nText" + }, + "label": { + "$ref": "defs.schema.json#/definitions/i18nText" + }, + "description": { + "$ref": "defs.schema.json#/definitions/i18nText" + }, + "badgeVariant": { + "$ref": "defs.schema.json#/definitions/badgeVariant" + }, + "chipVariant": { + "$ref": "defs.schema.json#/definitions/badgeVariant" + }, + "badgeStyle": { + "type": "object", + "description": "Flat inline CSS properties for badge/chip rendering. Supports camelCase, kebab-case, and CSS custom properties. `bg`/`text` are aliases for `background`/`color`.", + "additionalProperties": { + "type": [ + "string", + "number", + "boolean" + ] + } + }, + "chipStyle": { + "type": "object", + "description": "Alias for badgeStyle with the same value shape.", + "additionalProperties": { + "type": [ + "string", + "number", + "boolean" + ] + } + } + }, + "anyOf": [ + { + "required": [ + "id" + ] + }, + { + "required": [ + "value" + ] + } + ], + "anyOfComment": "Choice labels are optional for now because some block-type helper lists are resolved elsewhere." + }, + "choices": { + "oneOf": [ + { + "type": "array", + "items": { + "$ref": "defs.schema.json#/definitions/choiceItem" + } + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "endpoint": { + "type": "string" + }, + "mapping": { + "type": "object", + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "params": { + "type": "object", + "additionalProperties": true + } + }, + "required": [ + "endpoint" + ] + } + ] + }, + "previewTableColumn": { + "oneOf": [ + { + "$ref": "defs.schema.json#/definitions/fieldPath" + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "source": { + "$ref": "defs.schema.json#/definitions/fieldPath" + }, + "label": { + "$ref": "defs.schema.json#/definitions/i18nText" + }, + "align": { + "type": "string", + "enum": [ + "left", + "center", + "right" + ] + }, + "eval": { + "type": "string" + }, + "raw": { + "type": "boolean" + }, + "select": { + "type": "array", + "items": { + "$ref": "defs.schema.json#/definitions/fieldPath" + } + } + }, + "anyOf": [ + { + "required": [ + "source" + ] + }, + { + "required": [ + "eval" + ] + } + ] + } + ] + }, + "previewSlots": { + "type": "object", + "additionalProperties": false, + "properties": { + "label": { + "oneOf": [ + { + "$ref": "defs.schema.json#/definitions/fieldPath" + }, + { + "$ref": "defs.schema.json#/definitions/evalString" + } + ] + }, + "labelStyle": { + "oneOf": [ + { + "$ref": "defs.schema.json#/definitions/fieldPath" + }, + { + "$ref": "defs.schema.json#/definitions/evalString" + } + ] + }, + "secondary": { + "oneOf": [ + { + "$ref": "defs.schema.json#/definitions/fieldPath" + }, + { + "$ref": "defs.schema.json#/definitions/evalString" + } + ] + }, + "tertiary": { + "oneOf": [ + { + "$ref": "defs.schema.json#/definitions/fieldPath" + }, + { + "$ref": "defs.schema.json#/definitions/evalString" + } + ] + }, + "badge": { + "oneOf": [ + { + "$ref": "defs.schema.json#/definitions/fieldPath" + }, + { + "$ref": "defs.schema.json#/definitions/evalString" + } + ] + }, + "image": { + "oneOf": [ + { + "$ref": "defs.schema.json#/definitions/fieldPath" + }, + { + "$ref": "defs.schema.json#/definitions/evalString" + } + ] + }, + "imageFallback": { + "oneOf": [ + { + "$ref": "defs.schema.json#/definitions/fieldPath" + }, + { + "$ref": "defs.schema.json#/definitions/evalString" + } + ] + }, + "mediaFile": { + "$ref": "defs.schema.json#/definitions/fieldPath" + }, + "eval": { + "type": "string" + }, + "raw": { + "type": "boolean" + }, + "select": { + "type": "array", + "items": { + "$ref": "defs.schema.json#/definitions/fieldPath" + } + }, + "table": { + "type": "array", + "items": { + "$ref": "defs.schema.json#/definitions/previewTableColumn" + } + } + }, + "anyOf": [ + { + "required": [ + "label" + ] + }, + { + "required": [ + "secondary" + ] + }, + { + "required": [ + "tertiary" + ] + }, + { + "required": [ + "badge" + ] + }, + { + "required": [ + "image" + ] + }, + { + "required": [ + "table" + ] + } + ] + }, + "previewConfig": { + "oneOf": [ + { + "$ref": "defs.schema.json#/definitions/fieldPath" + }, + { + "$ref": "defs.schema.json#/definitions/evalHtml" + }, + { + "$ref": "defs.schema.json#/definitions/previewSlots" + } + ] + }, + "foreignRender": { + "oneOf": [ + { + "$ref": "defs.schema.json#/definitions/fieldPath" + }, + { + "$ref": "defs.schema.json#/definitions/evalHtml" + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "defaultCollectionViews": { + "type": "boolean" + } + }, + "required": [ + "defaultCollectionViews" + ] + }, + { + "$ref": "defs.schema.json#/definitions/previewSlots" + } + ] + }, + "methodPermissionGet": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "allow": { + "type": "boolean" + } + }, + "required": [ + "allow" + ] + } + ] + }, + "methodPermissionWrite": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "allow": { + "type": "boolean" + }, + "bulk": { + "type": "boolean" + } + }, + "required": [ + "allow" + ] + } + ] + } + } +} \ No newline at end of file diff --git a/schemas/config/field-list.schema.json b/schemas/config/field-list.schema.json new file mode 100644 index 0000000..1ab5df3 --- /dev/null +++ b/schemas/config/field-list.schema.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "field-list.schema.json", + "title": "Tibi field list config", + "type": "array", + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "field.schema.json" + } + ] + } +} \ No newline at end of file diff --git a/schemas/config/field-meta.schema.json b/schemas/config/field-meta.schema.json new file mode 100644 index 0000000..66ce49a --- /dev/null +++ b/schemas/config/field-meta.schema.json @@ -0,0 +1,305 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "field-meta.schema.json", + "title": "Tibi field meta config", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-": {} + }, + "properties": { + "label": { + "$ref": "defs.schema.json#/definitions/i18nText" + }, + "widget": { + "type": "string", + "enum": [ + "text", + "textarea", + "checkbox", + "switch", + "select", + "selectArray", + "chipArray", + "date", + "datetime", + "file", + "image", + "json", + "richtext", + "html", + "foreignKey", + "foreignKeyChipArray", + "foreignFile", + "foreignMedia", + "pagebuilder", + "pageBuilder", + "containerLessObject", + "containerLessObjectArray", + "color" + ] + }, + "i18n": { + "const": false + }, + "helperText": { + "$ref": "defs.schema.json#/definitions/i18nText" + }, + "hideLabel": { + "type": "boolean" + }, + "hide": { + "type": "boolean" + }, + "protected": { + "$ref": "defs.schema.json#/definitions/boolOrUiCondition" + }, + "position": { + "type": "string", + "pattern": "^(main|sidebar|sidebar:.+)$" + }, + "section": { + "type": "string" + }, + "containerProps": { + "type": "object", + "additionalProperties": false, + "properties": { + "class": { + "type": "string" + }, + "layout": { + "type": "object", + "additionalProperties": false, + "properties": { + "breakBefore": { + "type": "boolean" + }, + "breakAfter": { + "type": "boolean" + }, + "size": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "default": { + "type": "string" + }, + "small": { + "type": "string" + }, + "large": { + "type": "string" + } + } + } + ] + } + } + } + } + }, + "defaultValue": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + }, + { + "type": "null" + }, + { + "type": "array" + }, + { + "type": "object" + } + ] + }, + "dependsOn": { + "$ref": "defs.schema.json#/definitions/uiCondition" + }, + "inputProps": { + "type": "object", + "additionalProperties": true + }, + "choices": { + "$ref": "defs.schema.json#/definitions/choices" + }, + "foreign": { + "type": "object", + "additionalProperties": false, + "properties": { + "collection": { + "type": "string" + }, + "id": { + "type": "string" + }, + "sort": { + "type": "string" + }, + "projection": { + "type": "string" + }, + "filter": { + "type": "object", + "additionalProperties": true + }, + "createDefaults": { + "type": "object", + "additionalProperties": true + }, + "subNavigation": { + "type": "integer", + "minimum": 0 + }, + "render": { + "$ref": "defs.schema.json#/definitions/foreignRender" + }, + "autoFill": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "required": [ + "collection" + ] + }, + "downscale": { + "type": "object", + "additionalProperties": false, + "properties": { + "maxWidth": { + "type": "number" + }, + "maxHeight": { + "type": "number" + }, + "quality": { + "type": "number" + } + } + }, + "imageEditor": { + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "crop": { + "type": "boolean" + }, + "rotate": { + "type": "boolean" + }, + "flip": { + "type": "boolean" + }, + "focalPoint": { + "type": "boolean" + }, + "adjust": { + "type": "boolean" + }, + "palette": { + "type": "boolean" + } + } + } + ] + }, + "drillDown": { + "type": "boolean" + }, + "pageSize": { + "type": "integer", + "minimum": 0 + }, + "preview": { + "$ref": "defs.schema.json#/definitions/previewConfig" + }, + "addElementLabel": { + "$ref": "defs.schema.json#/definitions/i18nText" + }, + "filter": { + "oneOf": [ + { + "type": "boolean", + "description": "Set to true to opt-in this field as a filterable field in the admin UI, or false to explicitly exclude it." + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "type": "string", + "enum": [ + "default", + "select", + "bool", + "boolean", + "input", + "number", + "foreignKey" + ] + }, + "facets": { + "type": "boolean" + } + } + } + ] + }, + "pagebuilder": { + "type": "object", + "additionalProperties": false, + "properties": { + "defaultViewport": { + "$ref": "defs.schema.json#/definitions/pagebuilderViewport" + }, + "blockTypeField": { + "type": "string" + }, + "mode": { + "type": "string", + "enum": [ + "overlay", + "inline", + "both" + ] + }, + "blockRegistry": { + "type": "object", + "additionalProperties": false, + "properties": { + "file": { + "type": "string" + } + }, + "required": [ + "file" + ] + } + } + }, + "openapi": { + "$ref": "openapi.schema.json#/definitions/fieldOpenapi" + } + } +} \ No newline at end of file diff --git a/schemas/config/field.schema.json b/schemas/config/field.schema.json new file mode 100644 index 0000000..3f27b25 --- /dev/null +++ b/schemas/config/field.schema.json @@ -0,0 +1,171 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "field.schema.json", + "title": "Tibi field config", + "$ref": "#/definitions/field", + "definitions": { + "field": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-": {} + }, + "properties": { + "name": { + "type": "string", + "pattern": "^[0-9A-Za-z_]+$" + }, + "type": { + "type": "string", + "enum": [ + "string", + "string[]", + "number", + "number[]", + "boolean", + "object", + "object[]", + "file", + "file[]", + "date", + "any" + ] + }, + "index": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "single", + "unique", + "text", + "sparse", + "background" + ] + } + }, + "subFields": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "#/definitions/field" + } + ] + } + } + ] + }, + "validator": { + "type": "object", + "additionalProperties": false, + "properties": { + "required": { + "type": "boolean" + }, + "allowZero": { + "type": "boolean" + }, + "format": { + "type": "string", + "enum": ["email", "url", "uuid", "slug"] + }, + "eval": { + "type": "string" + }, + "minItems": { + "type": "number" + }, + "maxItems": { + "type": "number" + }, + "minLength": { + "type": "number" + }, + "maxLength": { + "type": "number" + }, + "pattern": { + "type": "string" + }, + "min": { + "type": "number" + }, + "max": { + "type": "number" + }, + "minDate": { + "type": "string" + }, + "maxDate": { + "type": "string" + }, + "minFileSize": { + "type": "string" + }, + "in": { + "type": "array" + }, + "maxFileSize": { + "type": "string" + }, + "accept": { + "type": "array", + "items": { + "type": "string" + } + }, + "image": { + "type": "object", + "additionalProperties": false, + "properties": { + "minWidth": { + "type": "number" + }, + "maxWidth": { + "type": "number" + }, + "minHeight": { + "type": "number" + }, + "maxHeight": { + "type": "number" + }, + "allowNonImages": { + "type": "boolean" + }, + "allowUnknownDimensions": { + "type": "boolean" + } + } + } + } + }, + "readonly": { + "$ref": "defs.schema.json#/definitions/boolOrEval" + }, + "hidden": { + "$ref": "defs.schema.json#/definitions/boolOrEval" + }, + "strictFields": { + "type": "boolean" + }, + "meta": { + "$ref": "field-meta.schema.json" + } + }, + "required": [ + "name", + "type" + ] + } + } +} \ No newline at end of file diff --git a/schemas/config/hooks.schema.json b/schemas/config/hooks.schema.json new file mode 100644 index 0000000..75b5fd8 --- /dev/null +++ b/schemas/config/hooks.schema.json @@ -0,0 +1,145 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "hooks.schema.json", + "title": "Tibi hooks config", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-": {} + }, + "properties": { + "get": { + "$ref": "hooks.schema.json#/definitions/getHooks" + }, + "post": { + "$ref": "hooks.schema.json#/definitions/postHooks" + }, + "put": { + "$ref": "hooks.schema.json#/definitions/putHooks" + }, + "delete": { + "$ref": "hooks.schema.json#/definitions/deleteHooks" + }, + "audit": { + "$ref": "hooks.schema.json#/definitions/auditHooks" + } + }, + "definitions": { + "hook": { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "const": "javascript" + }, + "file": { + "type": "string" + }, + "timeout": { + "type": "integer", + "minimum": 0 + } + }, + "required": [ + "type", + "file" + ] + }, + "getHooks": { + "type": "object", + "additionalProperties": false, + "properties": { + "handle": { + "$ref": "hooks.schema.json#/definitions/hook" + }, + "read": { + "$ref": "hooks.schema.json#/definitions/hook" + }, + "return": { + "$ref": "hooks.schema.json#/definitions/hook" + }, + "file": { + "$ref": "hooks.schema.json#/definitions/hook" + } + } + }, + "postHooks": { + "type": "object", + "additionalProperties": false, + "properties": { + "bind": { + "$ref": "hooks.schema.json#/definitions/hook" + }, + "validate": { + "$ref": "hooks.schema.json#/definitions/hook" + }, + "handle": { + "$ref": "hooks.schema.json#/definitions/hook" + }, + "create": { + "$ref": "hooks.schema.json#/definitions/hook" + }, + "return": { + "$ref": "hooks.schema.json#/definitions/hook" + }, + "bulkCreate": { + "$ref": "hooks.schema.json#/definitions/hook" + }, + "bulkReturn": { + "$ref": "hooks.schema.json#/definitions/hook" + } + } + }, + "putHooks": { + "type": "object", + "additionalProperties": false, + "properties": { + "bind": { + "$ref": "hooks.schema.json#/definitions/hook" + }, + "validate": { + "$ref": "hooks.schema.json#/definitions/hook" + }, + "update": { + "$ref": "hooks.schema.json#/definitions/hook" + }, + "return": { + "$ref": "hooks.schema.json#/definitions/hook" + }, + "bulkUpdate": { + "$ref": "hooks.schema.json#/definitions/hook" + }, + "bulkReturn": { + "$ref": "hooks.schema.json#/definitions/hook" + } + } + }, + "deleteHooks": { + "type": "object", + "additionalProperties": false, + "properties": { + "delete": { + "$ref": "hooks.schema.json#/definitions/hook" + }, + "return": { + "$ref": "hooks.schema.json#/definitions/hook" + }, + "bulkDelete": { + "$ref": "hooks.schema.json#/definitions/hook" + }, + "bulkReturn": { + "$ref": "hooks.schema.json#/definitions/hook" + } + } + }, + "auditHooks": { + "type": "object", + "additionalProperties": false, + "properties": { + "return": { + "$ref": "hooks.schema.json#/definitions/hook" + } + } + } + } +} \ No newline at end of file diff --git a/schemas/config/image-filter.schema.json b/schemas/config/image-filter.schema.json new file mode 100644 index 0000000..7d002f1 --- /dev/null +++ b/schemas/config/image-filter.schema.json @@ -0,0 +1,110 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "image-filter.schema.json", + "title": "Tibi image filter config", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-": {}, + "^[a-zA-Z0-9_-]+$": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "width": { + "type": "number" + }, + "height": { + "type": "number" + }, + "fit": { + "type": "boolean" + }, + "fill": { + "type": "boolean" + }, + "resampling": { + "type": "string", + "enum": [ + "nearestNeighbor", + "hermite", + "linear", + "catmullRom", + "lanczos", + "box", + "mitchellNetravili", + "bSpline", + "gaussian", + "bartlett", + "hann", + "hamming", + "blackman", + "welch", + "cosine" + ] + }, + "anchor": { + "type": "string", + "enum": [ + "center", + "topLeft", + "top", + "topRight", + "left", + "right", + "bottomLeft", + "bottom", + "bottomRight" + ] + }, + "brightness": { + "type": "number" + }, + "saturation": { + "type": "number" + }, + "contrast": { + "type": "number" + }, + "gamma": { + "type": "number" + }, + "blur": { + "type": "number" + }, + "sharpen": { + "type": "number" + }, + "invert": { + "type": "boolean" + }, + "grayscale": { + "type": "boolean" + }, + "quality": { + "type": "number" + }, + "lossless": { + "type": "boolean" + }, + "skipLargerDimension": { + "type": "boolean" + }, + "skipLargerFilesize": { + "type": "boolean" + }, + "outputType": { + "type": "string", + "enum": [ + "jpg", + "jpeg", + "png", + "webp" + ] + } + } + } + } + } +} \ No newline at end of file diff --git a/schemas/config/job.schema.json b/schemas/config/job.schema.json new file mode 100644 index 0000000..8ecd012 --- /dev/null +++ b/schemas/config/job.schema.json @@ -0,0 +1,33 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "job.schema.json", + "title": "Tibi job config", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-": {} + }, + "properties": { + "cron": { + "type": "string" + }, + "type": { + "const": "javascript" + }, + "file": { + "type": "string" + }, + "timeout": { + "type": "integer", + "minimum": 0 + }, + "meta": { + "type": "object", + "additionalProperties": true + } + }, + "required": [ + "type", + "file" + ] +} \ No newline at end of file diff --git a/schemas/config/openapi.schema.json b/schemas/config/openapi.schema.json new file mode 100644 index 0000000..5111599 --- /dev/null +++ b/schemas/config/openapi.schema.json @@ -0,0 +1,187 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "openapi.schema.json", + "title": "Tibi OpenAPI collection config", + "description": "OpenAPI metadata for a collection, used to generate the project's OpenAPI 3.0 spec via /_openapi/:project", + "type": "object", + "additionalProperties": true, + "definitions": { + "i18nOrString": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "minProperties": 1, + "patternProperties": { + "^[a-z]{2}(-[A-Za-z0-9_]+)?$": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + }, + "codeSample": { + "type": "object", + "additionalProperties": true, + "properties": { + "lang": { + "type": "string", + "description": "Language identifier (e.g. Shell, JavaScript, Python)" + }, + "label": { + "type": "string", + "description": "Display label for the code sample" + }, + "source": { + "type": "string", + "description": "The code sample source" + } + }, + "required": ["lang", "source"] + }, + "responseObject": { + "type": "object", + "additionalProperties": true, + "properties": { + "description": { + "$ref": "#/definitions/i18nOrString" + }, + "x-summary": { + "type": "string" + } + } + }, + "operationObject": { + "type": "object", + "additionalProperties": true, + "properties": { + "disabled": { + "type": "boolean", + "description": "Exclude this operation from the generated OpenAPI spec" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + }, + "description": "OpenAPI tags for grouping" + }, + "summary": { + "$ref": "#/definitions/i18nOrString" + }, + "description": { + "$ref": "#/definitions/i18nOrString" + }, + "x-codeSamples": { + "type": "array", + "items": { + "$ref": "#/definitions/codeSample" + } + }, + "parameters": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true, + "properties": { + "name": { + "type": "string" + }, + "in": { + "type": "string", + "enum": ["query", "header", "path", "cookie"] + }, + "description": { + "$ref": "#/definitions/i18nOrString" + }, + "required": { + "type": "boolean" + }, + "schema": { + "type": "object", + "additionalProperties": true + } + }, + "required": ["name", "in"] + } + }, + "responses": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/responseObject" + }, + "propertyNames": { + "pattern": "^[1-5][0-9]{2}$|^default$" + } + }, + "security": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, + "fieldOpenapi": { + "type": "object", + "description": "OpenAPI metadata for a collection field. Extra properties are merged into the field's schema object.", + "additionalProperties": true, + "properties": { + "disabled": { + "type": "boolean", + "description": "Exclude this field from the generated OpenAPI schema" + }, + "description": { + "$ref": "#/definitions/i18nOrString" + }, + "example": {}, + "format": { + "type": "string", + "description": "OpenAPI format hint (e.g. date-time, email, uri)" + }, + "enum": { + "type": "array", + "description": "Restrict values to a fixed set" + }, + "deprecated": { + "type": "boolean" + } + } + } + }, + "properties": { + "disabled": { + "type": "boolean", + "description": "Exclude this entire collection from the generated OpenAPI spec" + }, + "get": { + "$ref": "#/definitions/operationObject", + "description": "OpenAPI operation overrides for the list endpoint (GET /{collection})" + }, + "getOne": { + "$ref": "#/definitions/operationObject", + "description": "OpenAPI operation overrides for the single-entry endpoint (GET /{collection}/{id})" + }, + "post": { + "$ref": "#/definitions/operationObject", + "description": "OpenAPI operation overrides for the create endpoint (POST /{collection})" + }, + "put": { + "$ref": "#/definitions/operationObject", + "description": "OpenAPI operation overrides for the update endpoint (PUT /{collection}/{id})" + }, + "delete": { + "$ref": "#/definitions/operationObject", + "description": "OpenAPI operation overrides for the delete endpoint (DELETE /{collection}/{id})" + } + } +} diff --git a/schemas/config/permissions.schema.json b/schemas/config/permissions.schema.json new file mode 100644 index 0000000..abbe55e --- /dev/null +++ b/schemas/config/permissions.schema.json @@ -0,0 +1,79 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "permissions.schema.json", + "title": "Tibi permissions config", + "type": "object", + "additionalProperties": false, + "properties": { + "public": { + "$ref": "permissions.schema.json#/definitions/permissionSet" + }, + "user": { + "$ref": "permissions.schema.json#/definitions/permissionSet" + } + }, + "patternProperties": { + "^x-": {}, + "^token:[^\\s]+$": { + "$ref": "permissions.schema.json#/definitions/permissionSet" + }, + "^[a-zA-Z0-9_]+$": { + "$ref": "permissions.schema.json#/definitions/permissionSet" + } + }, + "required": [ + "public", + "user" + ], + "definitions": { + "permissionSet": { + "type": "object", + "additionalProperties": false, + "properties": { + "methods": { + "type": "object", + "additionalProperties": false, + "properties": { + "get": { + "$ref": "defs.schema.json#/definitions/methodPermissionGet" + }, + "post": { + "$ref": "defs.schema.json#/definitions/methodPermissionWrite" + }, + "put": { + "$ref": "defs.schema.json#/definitions/methodPermissionWrite" + }, + "delete": { + "$ref": "defs.schema.json#/definitions/methodPermissionWrite" + } + } + }, + "filter": { + "type": "object", + "additionalProperties": true + }, + "validProjections": { + "type": "array", + "items": { + "type": "string" + } + }, + "readonlyFields": { + "type": "array", + "items": { + "$ref": "defs.schema.json#/definitions/fieldPath" + } + }, + "hiddenFields": { + "type": "array", + "items": { + "$ref": "defs.schema.json#/definitions/fieldPath" + } + } + }, + "required": [ + "methods" + ] + } + } +} \ No newline at end of file diff --git a/schemas/config/project.schema.json b/schemas/config/project.schema.json new file mode 100644 index 0000000..6bca848 --- /dev/null +++ b/schemas/config/project.schema.json @@ -0,0 +1,323 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "project.schema.json", + "title": "Tibi project config", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-": {} + }, + "properties": { + "namespace": { + "type": "string", + "pattern": "^[A-Za-z0-9_-]+$" + }, + "meta": { + "type": "object", + "additionalProperties": false, + "properties": { + "imageUrl": { + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "defs.schema.json#/definitions/evalString" + } + ] + }, + "permissions": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "label": { + "$ref": "defs.schema.json#/definitions/i18nText" + } + }, + "required": [ + "name" + ] + } + }, + "collectionGroups": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "label": { + "$ref": "defs.schema.json#/definitions/i18nText" + }, + "icon": { + "type": "string" + } + }, + "required": [ + "name" + ] + } + }, + "i18n": { + "type": "object", + "additionalProperties": false, + "properties": { + "defaultLanguage": { + "type": "string" + }, + "languages": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "code": { + "type": "string" + }, + "label": { + "$ref": "defs.schema.json#/definitions/i18nText" + } + }, + "required": [ + "code" + ] + } + } + }, + "required": [ + "defaultLanguage", + "languages" + ] + } + } + }, + "upload": { + "$ref": "project.schema.json#/definitions/uploadConfig" + }, + "collections": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "collection.schema.json" + } + ] + } + }, + "jobs": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "job.schema.json" + } + ] + } + }, + "actions": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "action.schema.json" + } + ] + } + }, + "assets": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "asset.schema.json" + } + ] + } + }, + "cors": { + "type": "object", + "additionalProperties": false, + "properties": { + "merge": { + "type": "boolean" + }, + "allowOrigins": { + "type": "array", + "items": { + "type": "string" + } + }, + "allowCredentials": { + "type": "boolean" + }, + "allowHeaders": { + "type": "array", + "items": { + "type": "string" + } + }, + "allowMethods": { + "type": "array", + "items": { + "type": "string" + } + }, + "exposeHeaders": { + "type": "array", + "items": { + "type": "string" + } + }, + "maxAge": { + "type": "integer", + "minimum": 0 + } + } + }, + "admin": { + "type": "object", + "additionalProperties": false, + "properties": { + "tokens": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "token": { + "type": "string" + }, + "allowReload": { + "type": "boolean" + }, + "allowRegenerateSearch": { + "type": "boolean" + }, + "allowBackup": { + "type": "boolean", + "description": "Allow this token to download and restore project backups via the backup/restore API." + }, + "allowSnapshots": { + "type": "boolean", + "description": "Allow this token to trigger, list, delete, restore, and download snapshots." + } + }, + "required": [ + "token" + ] + } + } + } + }, + "hook": { + "type": "object", + "additionalProperties": false, + "properties": { + "cache": { + "type": "object", + "additionalProperties": false, + "properties": { + "maxEntries": { + "type": "integer", + "minimum": 0 + }, + "defaultTTL": { + "type": "string" + } + } + }, + "ratelimit": { + "type": "object", + "additionalProperties": false, + "properties": { + "backoffInitialDelay": { + "type": "string" + }, + "backoffMaxDelay": { + "type": "string" + }, + "backoffResetAfter": { + "type": "string" + }, + "windowSize": { + "type": "string" + }, + "windowMaxHits": { + "type": "integer", + "minimum": 0 + } + } + } + } + }, + "strictFields": { + "type": "boolean" + }, + "snapshotPath": { + "type": "string", + "description": "Base directory for snapshot data, relative to configDir. Default: '{configDir}/snapshots'." + }, + "snapshots": { + "type": "array", + "description": "List of snapshot profiles for automated point-in-time backups.", + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "snapshot.schema.json" + } + ] + } + }, + "legacy": { + "type": "object", + "additionalProperties": false, + "properties": { + "httpCodes": { + "type": "boolean" + } + } + } + }, + "required": [ + "namespace" + ], + "definitions": { + "uploadConfig": { + "type": "object", + "additionalProperties": false, + "properties": { + "maxBodySize": { + "type": "string" + }, + "allowedMimeTypes": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } +} \ No newline at end of file diff --git a/schemas/config/projections.schema.json b/schemas/config/projections.schema.json new file mode 100644 index 0000000..24efac6 --- /dev/null +++ b/schemas/config/projections.schema.json @@ -0,0 +1,32 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "projections.schema.json", + "title": "Tibi projections config", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-": {}, + "^[a-zA-Z0-9_]+$": { + "type": "object", + "additionalProperties": false, + "properties": { + "select": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "type": "integer", + "enum": [ + 0, + 1 + ] + } + } + }, + "required": [ + "select" + ] + } + } +} \ No newline at end of file diff --git a/schemas/config/snapshot.schema.json b/schemas/config/snapshot.schema.json new file mode 100644 index 0000000..5547cf6 --- /dev/null +++ b/schemas/config/snapshot.schema.json @@ -0,0 +1,101 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "snapshot.schema.json", + "title": "Tibi snapshot config", + "description": "Configuration for automated point-in-time snapshots of project collections and media.", + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^x-": {} + }, + "properties": { + "name": { + "type": "string", + "description": "Unique identifier for this snapshot profile (e.g. 'content-navigation')" + }, + "description": { + "type": "string", + "description": "Optional human-readable explanation of this snapshot profile" + }, + "collections": { + "type": "array", + "description": "Collection filter: empty = all, include-mode = only listed, exclude-mode = all except listed. Mixed include+exclude is invalid.", + "items": { + "oneOf": [ + { + "type": "string", + "description": "Shorthand for {name: '...', mode: 'include'}" + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "mode": { + "type": "string", + "enum": ["include", "exclude"], + "description": "'include' = only this collection, 'exclude' = all except this collection. Default 'include' when omitted." + } + }, + "required": ["name"] + } + ] + } + }, + "includeMedia": { + "type": "boolean", + "description": "Whether to include media files (file/file[] fields) via hardlinks. Default: true.", + "default": true + }, + "collectionDedup": { + "type": "boolean", + "description": "Enable SHA256-based deduplication of collection JSON exports. Identical exports share one canonical copy via hardlinks in .store/collections/. Default: false.", + "default": false + }, + "mode": { + "type": "string", + "enum": ["json", "db", "both"], + "description": "Storage strategy for collection data. 'json' = BSON Extended JSON files on disk (current, implemented), 'db' = MongoDB $out copy (future), 'both' = both (future). Default: 'json'." + }, + "retention": { + "type": "object", + "description": "Auto-pruning rules for old snapshot entries. Omit for unlimited retention.", + "additionalProperties": false, + "properties": { + "maxCount": { + "type": "integer", + "minimum": 1, + "description": "Maximum number of snapshot entries to keep. Oldest entries are pruned first." + }, + "maxAge": { + "type": "string", + "description": "Maximum age of snapshot entries (e.g. '720h' = 30 days, '7d' = 7 days, '168h'). Entries older than this are pruned." + }, + "maxSizeBytes": { + "type": "integer", + "minimum": 1, + "description": "Maximum total size of snapshot entries in bytes. Oldest entries are pruned first until under this limit." + } + } + }, + "schedule": { + "type": "string", + "description": "Cron expression for automatic execution (e.g. '0 3 * * *' = daily 3am UTC, '@every 24h'). Empty = manual trigger only." + }, + "permissions": { + "type": "array", + "description": "Bare permission names for team-based access (Phase 2). Empty = admin-only in Phase 1.", + "items": { + "type": "string" + } + }, + "enabled": { + "type": "boolean", + "description": "Whether this snapshot profile is active. Disabled profiles are skipped by the scheduler but can still be triggered manually.", + "default": false + } + }, + "required": ["name"] +} diff --git a/scripts/validate-schemas.mjs b/scripts/validate-schemas.mjs new file mode 100644 index 0000000..c43d485 --- /dev/null +++ b/scripts/validate-schemas.mjs @@ -0,0 +1,110 @@ +import fs from "node:fs" +import path from "node:path" +import { fileURLToPath } from "node:url" + +import Ajv from "ajv" +import addFormats from "ajv-formats" +import { globSync } from "glob" +import yaml from "js-yaml" + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) +const repoRoot = path.resolve(__dirname, "..") +const schemaDir = path.join(repoRoot, "schemas", "config") + +const includeType = new yaml.Type("!include", { + kind: "scalar", + construct(data) { + return data ?? "" + }, +}) + +const yamlSchema = yaml.DEFAULT_SCHEMA.extend([includeType]) + +const ajv = new Ajv({ + allErrors: true, + strict: false, +}) + +addFormats(ajv) + +const schemaFiles = globSync("*.schema.json", { + cwd: schemaDir, + absolute: true, +}).sort() + +if (schemaFiles.length === 0) { + console.error("No schema files found in", schemaDir) + process.exit(1) +} + +for (const filePath of schemaFiles) { + const schema = JSON.parse(fs.readFileSync(filePath, "utf8")) + ajv.addSchema(schema, schema.$id || path.basename(filePath)) +} + +for (const filePath of schemaFiles) { + const schema = JSON.parse(fs.readFileSync(filePath, "utf8")) + ajv.getSchema(schema.$id || path.basename(filePath)) +} + +const validations = [ + { schema: "project.schema.json", pattern: "demo-api/config.y*ml" }, + { + schema: "collection.schema.json", + pattern: "demo-api/collections/*.y*ml", + }, + { + schema: "field.schema.json", + pattern: "demo-api/collections/fields/*.y*ml", + }, + { + schema: "field-list.schema.json", + pattern: "demo-api/collections/fieldLists/*.y*ml", + }, + { schema: "job.schema.json", pattern: "demo-api/jobs/*.y*ml" }, + { schema: "action.schema.json", pattern: "demo-api/actions/*.y*ml" }, + { schema: "asset.schema.json", pattern: "demo-api/assets/*.y*ml" }, +] + +let hasErrors = false +let validatedCount = 0 + +for (const { schema, pattern } of validations) { + const validate = ajv.getSchema(schema) + if (!validate) { + console.error(`Schema not registered: ${schema}`) + hasErrors = true + continue + } + + const files = globSync(pattern, { + cwd: repoRoot, + absolute: true, + }).sort() + + for (const filePath of files) { + const raw = fs.readFileSync(filePath, "utf8") + const parsed = yaml.load(raw, { schema: yamlSchema }) + const valid = validate(parsed) + validatedCount += 1 + + if (!valid) { + hasErrors = true + console.error( + `\nValidation failed: ${path.relative(repoRoot, filePath)}` + ) + for (const error of validate.errors || []) { + const location = error.instancePath || "/" + console.error(` ${location} ${error.message}`) + } + } + } +} + +if (hasErrors) { + process.exit(1) +} + +console.log( + `Validated ${validatedCount} YAML file(s) against ${schemaFiles.length} schema file(s).` +)