diff --git a/package.json b/package.json index b4754a780..5675f96d7 100644 --- a/package.json +++ b/package.json @@ -113,6 +113,7 @@ "@libsql/client": "*", "@valibot/to-json-schema": "^1.5.0", "better-sqlite3": "^12.5.0", + "effect": "^3.19.13", "sqlite3": "*", "valibot": "^1.2.0" }, @@ -154,6 +155,7 @@ "@types/pg": "^8.16.0", "@types/ws": "^8.18.1", "@valibot/to-json-schema": "^1.5.0", + "effect": "^3.19.13", "csvtojson": "^2.0.14", "eslint": "^9.39.2", "happy-dom": "^20.0.11", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cd54f6e95..c0d7424e6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -224,6 +224,9 @@ importers: csvtojson: specifier: ^2.0.14 version: 2.0.14 + effect: + specifier: ^3.19.13 + version: 3.19.13 eslint: specifier: ^9.39.2 version: 9.39.2(jiti@2.6.1) @@ -5056,6 +5059,9 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + effect@3.19.13: + resolution: {integrity: sha512-8MZ783YuHRwHZX2Mmm+bpGxq+7XPd88sWwYAz2Ysry80sEKpftDZXs2Hg9ZyjESi1IBTNHF0oDKe0zJRkUlyew==} + electron-to-chromium@1.5.267: resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} @@ -5430,6 +5436,10 @@ packages: resolution: {integrity: sha512-CGnyrvbhPlWYMngksqrSSUT1BAVP49dZocrHuK0SvtR0D5TMs5wP0o3j7jexDJW01KSadjBp1M/71o/KR3nD1w==} engines: {node: '>=18'} + fast-check@3.23.2: + resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==} + engines: {node: '>=8.0.0'} + fast-content-type-parse@3.0.0: resolution: {integrity: sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==} @@ -7568,6 +7578,9 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + pure-rand@6.1.0: + resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + qs@6.14.0: resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} engines: {node: '>=0.6'} @@ -14074,6 +14087,11 @@ snapshots: ee-first@1.1.1: {} + effect@3.19.13: + dependencies: + '@standard-schema/spec': 1.1.0 + fast-check: 3.23.2 + electron-to-chromium@1.5.267: {} embla-carousel-auto-height@8.6.0(embla-carousel@8.6.0): @@ -14579,6 +14597,10 @@ snapshots: fake-indexeddb@6.2.5: {} + fast-check@3.23.2: + dependencies: + pure-rand: 6.1.0 + fast-content-type-parse@3.0.0: {} fast-deep-equal@3.1.3: {} @@ -17488,6 +17510,8 @@ snapshots: punycode@2.3.1: {} + pure-rand@6.1.0: {} + qs@6.14.0: dependencies: side-channel: 1.1.0 diff --git a/src/utils/context.ts b/src/utils/context.ts index de94683dc..0a7b8a1fd 100644 --- a/src/utils/context.ts +++ b/src/utils/context.ts @@ -1,6 +1,6 @@ import { createContext } from 'unctx' -type ContextKey = 'zod3' | 'zod4' | 'valibot' | 'unknown' +type ContextKey = 'zod3' | 'zod4' | 'valibot' | 'effect' | 'unknown' const nuxtContentContext = { zod3: { @@ -27,6 +27,14 @@ const nuxtContentContext = { ) }, }, + effect: { + toJSONSchema: (_schema: unknown, _name: string) => { + throw new Error( + 'It seems you are using Effect Schema for collection schema, but Effect is not installed, ' + + 'Nuxt Content does not ship with effect, install `effect` it will work.', + ) + }, + }, unknown: { toJSONSchema: (_schema: unknown, _name: string) => { throw new Error('Unknown schema vendor') diff --git a/src/utils/dependencies.ts b/src/utils/dependencies.ts index fd5eb8040..8ebccc36c 100644 --- a/src/utils/dependencies.ts +++ b/src/utils/dependencies.ts @@ -72,6 +72,9 @@ export async function initiateValidatorsContext() { if (await isPackageInstalled('valibot') && await isPackageInstalled('@valibot/to-json-schema')) { nuxtContentContext().set('valibot', await import('./schema/valibot')) } + if (await isPackageInstalled('effect')) { + nuxtContentContext().set('effect', await import('./schema/effect')) + } if (await isPackageInstalled('zod')) { nuxtContentContext().set('zod3', await import('./schema/zod3')) nuxtContentContext().set('zod4', await import('./schema/zod4')) diff --git a/src/utils/schema/effect.ts b/src/utils/schema/effect.ts new file mode 100644 index 000000000..58dedcfc4 --- /dev/null +++ b/src/utils/schema/effect.ts @@ -0,0 +1,17 @@ +import { JSONSchema } from 'effect' +import type { Draft07, Draft07Definition } from '../../types' + +export function toJSONSchema(schema: unknown, name: string): Draft07 { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const definitions = JSONSchema.make(schema as any, { + target: 'jsonSchema7', + }) as Draft07Definition + + return { + $schema: 'http://json-schema.org/draft-07/schema#', + $ref: `#/definitions/${name}`, + definitions: { + [name]: definitions, + }, + } +} diff --git a/src/utils/schema/index.ts b/src/utils/schema/index.ts index 6b1bb1aeb..9b44c842f 100644 --- a/src/utils/schema/index.ts +++ b/src/utils/schema/index.ts @@ -22,6 +22,9 @@ export function property(input: T): Property< case 'valibot': schema.$content = $content return + case 'effect': + schema.$content = $content + return case 'zod4': (schema as unknown as { def: { $content: ContentConfig } }).def.$content = $content return @@ -98,6 +101,10 @@ export function detectSchemaVendor(schema: ContentStandardSchemaV1) { return 'valibot' } + if (schema['~standard']?.vendor === 'effect') { + return 'effect' + } + if (schema['~standard']?.vendor === 'zod') { return (schema as unknown as Record).def ? 'zod4' : 'zod3' }