@orpc/zod 0.0.0-next.f4ed9ab → 0.0.0-next.f50512c
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -10
- package/dist/index.d.mts +16 -7
- package/dist/index.d.ts +16 -7
- package/dist/index.mjs +76 -39
- package/dist/zod4/index.d.mts +28 -16
- package/dist/zod4/index.d.ts +28 -16
- package/dist/zod4/index.mjs +127 -107
- package/package.json +8 -8
package/README.md
CHANGED
@@ -30,7 +30,7 @@
|
|
30
30
|
- **🔗 End-to-End Type Safety**: Ensure type-safe inputs, outputs, and errors from client to server.
|
31
31
|
- **📘 First-Class OpenAPI**: Built-in support that fully adheres to the OpenAPI standard.
|
32
32
|
- **📝 Contract-First Development**: Optionally define your API contract before implementation.
|
33
|
-
- **⚙️ Framework Integrations**: Seamlessly integrate with TanStack Query (React, Vue, Solid, Svelte), Pinia Colada, and more.
|
33
|
+
- **⚙️ Framework Integrations**: Seamlessly integrate with TanStack Query (React, Vue, Solid, Svelte, Angular), Pinia Colada, and more.
|
34
34
|
- **🚀 Server Actions**: Fully compatible with React Server Actions on Next.js, TanStack Start, and other platforms.
|
35
35
|
- **🔠 Standard Schema Support**: Works out of the box with Zod, Valibot, ArkType, and other schema validators.
|
36
36
|
- **🗃️ Native Types**: Supports native types like Date, File, Blob, BigInt, URL, and more.
|
@@ -49,14 +49,12 @@ You can find the full documentation [here](https://orpc.unnoq.com).
|
|
49
49
|
- [@orpc/contract](https://www.npmjs.com/package/@orpc/contract): Build your API contract.
|
50
50
|
- [@orpc/server](https://www.npmjs.com/package/@orpc/server): Build your API or implement API contract.
|
51
51
|
- [@orpc/client](https://www.npmjs.com/package/@orpc/client): Consume your API on the client with type-safety.
|
52
|
-
- [@orpc/
|
52
|
+
- [@orpc/openapi](https://www.npmjs.com/package/@orpc/openapi): Generate OpenAPI specs and handle OpenAPI requests.
|
53
|
+
- [@orpc/nest](https://www.npmjs.com/package/@orpc/nest): Deeply integrate oRPC with [NestJS](https://nestjs.com/).
|
53
54
|
- [@orpc/react](https://www.npmjs.com/package/@orpc/react): Utilities for integrating oRPC with React and React Server Actions.
|
54
|
-
- [@orpc/
|
55
|
-
- [@orpc/vue-query](https://www.npmjs.com/package/@orpc/vue-query): Integration with [Vue Query](https://tanstack.com/query/latest/docs/framework/vue/overview).
|
56
|
-
- [@orpc/solid-query](https://www.npmjs.com/package/@orpc/solid-query): Integration with [Solid Query](https://tanstack.com/query/latest/docs/framework/solid/overview).
|
57
|
-
- [@orpc/svelte-query](https://www.npmjs.com/package/@orpc/svelte-query): Integration with [Svelte Query](https://tanstack.com/query/latest/docs/framework/svelte/overview).
|
55
|
+
- [@orpc/tanstack-query](https://www.npmjs.com/package/@orpc/tanstack-query): [TanStack Query](https://tanstack.com/query/latest) integration.
|
58
56
|
- [@orpc/vue-colada](https://www.npmjs.com/package/@orpc/vue-colada): Integration with [Pinia Colada](https://pinia-colada.esm.dev/).
|
59
|
-
- [@orpc/
|
57
|
+
- [@orpc/hey-api](https://www.npmjs.com/package/@orpc/hey-api): [Hey API](https://heyapi.dev/) integration.
|
60
58
|
- [@orpc/zod](https://www.npmjs.com/package/@orpc/zod): More schemas that [Zod](https://zod.dev/) doesn't support yet.
|
61
59
|
- [@orpc/valibot](https://www.npmjs.com/package/@orpc/valibot): OpenAPI spec generation from [Valibot](https://valibot.dev/).
|
62
60
|
- [@orpc/arktype](https://www.npmjs.com/package/@orpc/arktype): OpenAPI spec generation from [ArkType](https://arktype.io/).
|
@@ -74,7 +72,7 @@ More schemas that [Zod](https://zod.dev/) doesn't support yet, and provides `Zod
|
|
74
72
|
|
75
73
|
```ts
|
76
74
|
import { oz } from '@orpc/zod'
|
77
|
-
import { z } from 'zod'
|
75
|
+
import { z } from 'zod/v3'
|
78
76
|
|
79
77
|
const Example = z.object({
|
80
78
|
url: oz.url(),
|
@@ -88,7 +86,7 @@ const Example = z.object({
|
|
88
86
|
|
89
87
|
```ts
|
90
88
|
import { OpenAPIGenerator } from '@orpc/openapi'
|
91
|
-
import { ZodToJsonSchemaConverter } from '@orpc/zod'
|
89
|
+
import { ZodToJsonSchemaConverter } from '@orpc/zod/zod4'
|
92
90
|
|
93
91
|
const openAPIGenerator = new OpenAPIGenerator({
|
94
92
|
schemaConverters: [new ZodToJsonSchemaConverter()],
|
@@ -113,7 +111,7 @@ const specFromRouter = await openAPIGenerator.generate(router, {
|
|
113
111
|
|
114
112
|
```ts
|
115
113
|
import { oz } from '@orpc/zod'
|
116
|
-
import
|
114
|
+
import * as z from 'zod'
|
117
115
|
|
118
116
|
const InputSchema = oz.openapi(
|
119
117
|
z.object({
|
package/dist/index.d.mts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import { JSONSchema, ConditionalSchemaConverter, SchemaConvertOptions } from '@orpc/openapi';
|
2
|
-
import { ZodTypeAny, input, output, ZodTypeDef, CustomErrorParams, ZodType, ZodEffects } from 'zod';
|
2
|
+
import { ZodTypeAny, input, output, ZodTypeDef, CustomErrorParams, ZodType, ZodEffects } from 'zod/v3';
|
3
3
|
import { Context } from '@orpc/server';
|
4
4
|
import { StandardHandlerPlugin, StandardHandlerOptions } from '@orpc/server/standard';
|
5
5
|
import { AnySchema } from '@orpc/contract';
|
@@ -40,34 +40,43 @@ declare class ZodSmartCoercionPlugin<TContext extends Context> implements Standa
|
|
40
40
|
|
41
41
|
interface ZodToJsonSchemaOptions {
|
42
42
|
/**
|
43
|
-
* Max depth of lazy type
|
43
|
+
* Max depth of lazy type
|
44
44
|
*
|
45
|
-
* Used `{}` when
|
45
|
+
* Used `{}` when exceed max depth
|
46
46
|
*
|
47
47
|
* @default 3
|
48
48
|
*/
|
49
49
|
maxLazyDepth?: number;
|
50
50
|
/**
|
51
|
-
*
|
51
|
+
* Max depth of nested types
|
52
52
|
*
|
53
|
-
*
|
53
|
+
* Used anyJsonSchema (`{}`) when exceed max depth
|
54
|
+
*
|
55
|
+
* @default 10
|
54
56
|
*/
|
55
|
-
|
57
|
+
maxStructureDepth?: number;
|
56
58
|
/**
|
57
59
|
* The schema to be used to represent the any | unknown type.
|
58
60
|
*
|
59
61
|
* @default { }
|
60
62
|
*/
|
61
63
|
anyJsonSchema?: Exclude<JSONSchema, boolean>;
|
64
|
+
/**
|
65
|
+
* The schema to be used when the Zod schema is unsupported.
|
66
|
+
*
|
67
|
+
* @default { not: {} }
|
68
|
+
*/
|
69
|
+
unsupportedJsonSchema?: Exclude<JSONSchema, boolean>;
|
62
70
|
}
|
63
71
|
declare class ZodToJsonSchemaConverter implements ConditionalSchemaConverter {
|
64
72
|
#private;
|
65
73
|
private readonly maxLazyDepth;
|
74
|
+
private readonly maxStructureDepth;
|
66
75
|
private readonly unsupportedJsonSchema;
|
67
76
|
private readonly anyJsonSchema;
|
68
77
|
constructor(options?: ZodToJsonSchemaOptions);
|
69
78
|
condition(schema: AnySchema | undefined): boolean;
|
70
|
-
convert(schema: AnySchema | undefined, options: SchemaConvertOptions, lazyDepth?: number, isHandledCustomJSONSchema?: boolean, isHandledZodDescription?: boolean): [required: boolean, jsonSchema: Exclude<JSONSchema, boolean>];
|
79
|
+
convert(schema: AnySchema | undefined, options: SchemaConvertOptions, lazyDepth?: number, isHandledCustomJSONSchema?: boolean, isHandledZodDescription?: boolean, structureDepth?: number): [required: boolean, jsonSchema: Exclude<JSONSchema, boolean>];
|
71
80
|
}
|
72
81
|
|
73
82
|
declare const oz: {
|
package/dist/index.d.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import { JSONSchema, ConditionalSchemaConverter, SchemaConvertOptions } from '@orpc/openapi';
|
2
|
-
import { ZodTypeAny, input, output, ZodTypeDef, CustomErrorParams, ZodType, ZodEffects } from 'zod';
|
2
|
+
import { ZodTypeAny, input, output, ZodTypeDef, CustomErrorParams, ZodType, ZodEffects } from 'zod/v3';
|
3
3
|
import { Context } from '@orpc/server';
|
4
4
|
import { StandardHandlerPlugin, StandardHandlerOptions } from '@orpc/server/standard';
|
5
5
|
import { AnySchema } from '@orpc/contract';
|
@@ -40,34 +40,43 @@ declare class ZodSmartCoercionPlugin<TContext extends Context> implements Standa
|
|
40
40
|
|
41
41
|
interface ZodToJsonSchemaOptions {
|
42
42
|
/**
|
43
|
-
* Max depth of lazy type
|
43
|
+
* Max depth of lazy type
|
44
44
|
*
|
45
|
-
* Used `{}` when
|
45
|
+
* Used `{}` when exceed max depth
|
46
46
|
*
|
47
47
|
* @default 3
|
48
48
|
*/
|
49
49
|
maxLazyDepth?: number;
|
50
50
|
/**
|
51
|
-
*
|
51
|
+
* Max depth of nested types
|
52
52
|
*
|
53
|
-
*
|
53
|
+
* Used anyJsonSchema (`{}`) when exceed max depth
|
54
|
+
*
|
55
|
+
* @default 10
|
54
56
|
*/
|
55
|
-
|
57
|
+
maxStructureDepth?: number;
|
56
58
|
/**
|
57
59
|
* The schema to be used to represent the any | unknown type.
|
58
60
|
*
|
59
61
|
* @default { }
|
60
62
|
*/
|
61
63
|
anyJsonSchema?: Exclude<JSONSchema, boolean>;
|
64
|
+
/**
|
65
|
+
* The schema to be used when the Zod schema is unsupported.
|
66
|
+
*
|
67
|
+
* @default { not: {} }
|
68
|
+
*/
|
69
|
+
unsupportedJsonSchema?: Exclude<JSONSchema, boolean>;
|
62
70
|
}
|
63
71
|
declare class ZodToJsonSchemaConverter implements ConditionalSchemaConverter {
|
64
72
|
#private;
|
65
73
|
private readonly maxLazyDepth;
|
74
|
+
private readonly maxStructureDepth;
|
66
75
|
private readonly unsupportedJsonSchema;
|
67
76
|
private readonly anyJsonSchema;
|
68
77
|
constructor(options?: ZodToJsonSchemaOptions);
|
69
78
|
condition(schema: AnySchema | undefined): boolean;
|
70
|
-
convert(schema: AnySchema | undefined, options: SchemaConvertOptions, lazyDepth?: number, isHandledCustomJSONSchema?: boolean, isHandledZodDescription?: boolean): [required: boolean, jsonSchema: Exclude<JSONSchema, boolean>];
|
79
|
+
convert(schema: AnySchema | undefined, options: SchemaConvertOptions, lazyDepth?: number, isHandledCustomJSONSchema?: boolean, isHandledZodDescription?: boolean, structureDepth?: number): [required: boolean, jsonSchema: Exclude<JSONSchema, boolean>];
|
71
80
|
}
|
72
81
|
|
73
82
|
declare const oz: {
|
package/dist/index.mjs
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
-
import { custom, ZodFirstPartyTypeKind } from 'zod';
|
1
|
+
import { custom, ZodFirstPartyTypeKind } from 'zod/v3';
|
2
2
|
import wcmatch from 'wildcard-match';
|
3
|
-
import { isObject, guard } from '@orpc/shared';
|
3
|
+
import { isObject, guard, toArray } from '@orpc/shared';
|
4
|
+
import { JsonSchemaXNativeType } from '@orpc/json-schema';
|
4
5
|
import { JSONSchemaFormat } from '@orpc/openapi';
|
5
6
|
import escapeStringRegexp from 'escape-string-regexp';
|
6
7
|
|
@@ -390,25 +391,39 @@ function safeToDate(value) {
|
|
390
391
|
|
391
392
|
class ZodToJsonSchemaConverter {
|
392
393
|
maxLazyDepth;
|
394
|
+
maxStructureDepth;
|
393
395
|
unsupportedJsonSchema;
|
394
396
|
anyJsonSchema;
|
395
397
|
constructor(options = {}) {
|
396
398
|
this.maxLazyDepth = options.maxLazyDepth ?? 3;
|
399
|
+
this.maxStructureDepth = options.maxStructureDepth ?? 10;
|
397
400
|
this.unsupportedJsonSchema = options.unsupportedJsonSchema ?? { not: {} };
|
398
401
|
this.anyJsonSchema = options.anyJsonSchema ?? {};
|
399
402
|
}
|
400
403
|
condition(schema) {
|
401
404
|
return schema !== void 0 && schema["~standard"].vendor === "zod";
|
402
405
|
}
|
403
|
-
convert(schema, options, lazyDepth = 0, isHandledCustomJSONSchema = false, isHandledZodDescription = false) {
|
406
|
+
convert(schema, options, lazyDepth = 0, isHandledCustomJSONSchema = false, isHandledZodDescription = false, structureDepth = 0) {
|
404
407
|
const def = schema._def;
|
408
|
+
if (structureDepth > this.maxStructureDepth) {
|
409
|
+
return [false, this.anyJsonSchema];
|
410
|
+
}
|
411
|
+
if (!options.minStructureDepthForRef || options.minStructureDepthForRef <= structureDepth) {
|
412
|
+
const components = toArray(options.components);
|
413
|
+
for (const component of components) {
|
414
|
+
if (component.schema === schema && component.allowedStrategies.includes(options.strategy)) {
|
415
|
+
return [component.required, { $ref: component.ref }];
|
416
|
+
}
|
417
|
+
}
|
418
|
+
}
|
405
419
|
if (!isHandledZodDescription && "description" in def && typeof def.description === "string") {
|
406
420
|
const [required, json] = this.convert(
|
407
421
|
schema,
|
408
422
|
options,
|
409
423
|
lazyDepth,
|
410
424
|
isHandledCustomJSONSchema,
|
411
|
-
true
|
425
|
+
true,
|
426
|
+
structureDepth
|
412
427
|
);
|
413
428
|
return [required, { ...json, description: def.description }];
|
414
429
|
}
|
@@ -420,7 +435,8 @@ class ZodToJsonSchemaConverter {
|
|
420
435
|
options,
|
421
436
|
lazyDepth,
|
422
437
|
true,
|
423
|
-
isHandledZodDescription
|
438
|
+
isHandledZodDescription,
|
439
|
+
structureDepth
|
424
440
|
);
|
425
441
|
return [required, { ...json, ...customJSONSchema }];
|
426
442
|
}
|
@@ -548,7 +564,11 @@ class ZodToJsonSchemaConverter {
|
|
548
564
|
return [true, json];
|
549
565
|
}
|
550
566
|
case ZodFirstPartyTypeKind.ZodBigInt: {
|
551
|
-
const json = {
|
567
|
+
const json = {
|
568
|
+
"type": "string",
|
569
|
+
"pattern": "^-?[0-9]+$",
|
570
|
+
"x-native-type": JsonSchemaXNativeType.BigInt
|
571
|
+
};
|
552
572
|
return [true, json];
|
553
573
|
}
|
554
574
|
case ZodFirstPartyTypeKind.ZodNaN: {
|
@@ -558,7 +578,11 @@ class ZodToJsonSchemaConverter {
|
|
558
578
|
return [true, { type: "boolean" }];
|
559
579
|
}
|
560
580
|
case ZodFirstPartyTypeKind.ZodDate: {
|
561
|
-
const schema2 = {
|
581
|
+
const schema2 = {
|
582
|
+
"type": "string",
|
583
|
+
"format": JSONSchemaFormat.DateTime,
|
584
|
+
"x-native-type": JsonSchemaXNativeType.Date
|
585
|
+
};
|
562
586
|
return [true, schema2];
|
563
587
|
}
|
564
588
|
case ZodFirstPartyTypeKind.ZodNull: {
|
@@ -591,7 +615,7 @@ class ZodToJsonSchemaConverter {
|
|
591
615
|
const schema_ = schema;
|
592
616
|
const def2 = schema_._def;
|
593
617
|
const json = { type: "array" };
|
594
|
-
const [itemRequired, itemJson] = this.convert(def2.type, options, lazyDepth, false, false);
|
618
|
+
const [itemRequired, itemJson] = this.convert(def2.type, options, lazyDepth, false, false, structureDepth + 1);
|
595
619
|
json.items = this.#toArrayItemJsonSchema(itemRequired, itemJson, options.strategy);
|
596
620
|
if (def2.exactLength) {
|
597
621
|
json.maxItems = def2.exactLength.value;
|
@@ -610,7 +634,7 @@ class ZodToJsonSchemaConverter {
|
|
610
634
|
const prefixItems = [];
|
611
635
|
const json = { type: "array" };
|
612
636
|
for (const item of schema_._def.items) {
|
613
|
-
const [itemRequired, itemJson] = this.convert(item, options, lazyDepth, false, false);
|
637
|
+
const [itemRequired, itemJson] = this.convert(item, options, lazyDepth, false, false, structureDepth + 1);
|
614
638
|
prefixItems.push(
|
615
639
|
this.#toArrayItemJsonSchema(itemRequired, itemJson, options.strategy)
|
616
640
|
);
|
@@ -619,7 +643,7 @@ class ZodToJsonSchemaConverter {
|
|
619
643
|
json.prefixItems = prefixItems;
|
620
644
|
}
|
621
645
|
if (schema_._def.rest) {
|
622
|
-
const [itemRequired, itemJson] = this.convert(schema_._def.rest, options, lazyDepth, false, false);
|
646
|
+
const [itemRequired, itemJson] = this.convert(schema_._def.rest, options, lazyDepth, false, false, structureDepth + 1);
|
623
647
|
json.items = this.#toArrayItemJsonSchema(itemRequired, itemJson, options.strategy);
|
624
648
|
}
|
625
649
|
return [true, json];
|
@@ -630,7 +654,7 @@ class ZodToJsonSchemaConverter {
|
|
630
654
|
const properties = {};
|
631
655
|
const required = [];
|
632
656
|
for (const [key, value] of Object.entries(schema_.shape)) {
|
633
|
-
const [itemRequired, itemJson] = this.convert(value, options, lazyDepth, false, false);
|
657
|
+
const [itemRequired, itemJson] = this.convert(value, options, lazyDepth, false, false, structureDepth + 1);
|
634
658
|
properties[key] = itemJson;
|
635
659
|
if (itemRequired) {
|
636
660
|
required.push(key);
|
@@ -648,7 +672,7 @@ class ZodToJsonSchemaConverter {
|
|
648
672
|
json.additionalProperties = false;
|
649
673
|
}
|
650
674
|
} else {
|
651
|
-
const [_, addJson] = this.convert(schema_._def.catchall, options, lazyDepth, false, false);
|
675
|
+
const [_, addJson] = this.convert(schema_._def.catchall, options, lazyDepth, false, false, structureDepth + 1);
|
652
676
|
json.additionalProperties = addJson;
|
653
677
|
}
|
654
678
|
return [true, json];
|
@@ -656,28 +680,32 @@ class ZodToJsonSchemaConverter {
|
|
656
680
|
case ZodFirstPartyTypeKind.ZodRecord: {
|
657
681
|
const schema_ = schema;
|
658
682
|
const json = { type: "object" };
|
659
|
-
const [__, keyJson] = this.convert(schema_._def.keyType, options, lazyDepth, false, false);
|
683
|
+
const [__, keyJson] = this.convert(schema_._def.keyType, options, lazyDepth, false, false, structureDepth + 1);
|
660
684
|
if (Object.entries(keyJson).some(([k, v]) => k !== "type" || v !== "string")) {
|
661
685
|
json.propertyNames = keyJson;
|
662
686
|
}
|
663
|
-
const [_, itemJson] = this.convert(schema_._def.valueType, options, lazyDepth, false, false);
|
687
|
+
const [_, itemJson] = this.convert(schema_._def.valueType, options, lazyDepth, false, false, structureDepth + 1);
|
664
688
|
json.additionalProperties = itemJson;
|
665
689
|
return [true, json];
|
666
690
|
}
|
667
691
|
case ZodFirstPartyTypeKind.ZodSet: {
|
668
692
|
const schema_ = schema;
|
669
|
-
const json = {
|
670
|
-
|
693
|
+
const json = {
|
694
|
+
"type": "array",
|
695
|
+
"uniqueItems": true,
|
696
|
+
"x-native-type": JsonSchemaXNativeType.Set
|
697
|
+
};
|
698
|
+
const [itemRequired, itemJson] = this.convert(schema_._def.valueType, options, lazyDepth, false, false, structureDepth + 1);
|
671
699
|
json.items = this.#toArrayItemJsonSchema(itemRequired, itemJson, options.strategy);
|
672
700
|
return [true, json];
|
673
701
|
}
|
674
702
|
case ZodFirstPartyTypeKind.ZodMap: {
|
675
703
|
const schema_ = schema;
|
676
|
-
const [keyRequired, keyJson] = this.convert(schema_._def.keyType, options, lazyDepth, false, false);
|
677
|
-
const [valueRequired, valueJson] = this.convert(schema_._def.valueType, options, lazyDepth, false, false);
|
678
|
-
|
679
|
-
type: "array",
|
680
|
-
items: {
|
704
|
+
const [keyRequired, keyJson] = this.convert(schema_._def.keyType, options, lazyDepth, false, false, structureDepth + 1);
|
705
|
+
const [valueRequired, valueJson] = this.convert(schema_._def.valueType, options, lazyDepth, false, false, structureDepth + 1);
|
706
|
+
const json = {
|
707
|
+
"type": "array",
|
708
|
+
"items": {
|
681
709
|
type: "array",
|
682
710
|
prefixItems: [
|
683
711
|
this.#toArrayItemJsonSchema(keyRequired, keyJson, options.strategy),
|
@@ -685,8 +713,10 @@ class ZodToJsonSchemaConverter {
|
|
685
713
|
],
|
686
714
|
maxItems: 2,
|
687
715
|
minItems: 2
|
688
|
-
}
|
689
|
-
|
716
|
+
},
|
717
|
+
"x-native-type": JsonSchemaXNativeType.Map
|
718
|
+
};
|
719
|
+
return [true, json];
|
690
720
|
}
|
691
721
|
case ZodFirstPartyTypeKind.ZodUnion:
|
692
722
|
case ZodFirstPartyTypeKind.ZodDiscriminatedUnion: {
|
@@ -694,7 +724,7 @@ class ZodToJsonSchemaConverter {
|
|
694
724
|
const anyOf = [];
|
695
725
|
let required = true;
|
696
726
|
for (const item of schema_._def.options) {
|
697
|
-
const [itemRequired, itemJson] = this.convert(item, options, lazyDepth, false, false);
|
727
|
+
const [itemRequired, itemJson] = this.convert(item, options, lazyDepth, false, false, structureDepth);
|
698
728
|
if (!itemRequired) {
|
699
729
|
required = false;
|
700
730
|
if (itemJson !== this.unsupportedJsonSchema) {
|
@@ -714,7 +744,7 @@ class ZodToJsonSchemaConverter {
|
|
714
744
|
const allOf = [];
|
715
745
|
let required = false;
|
716
746
|
for (const item of [schema_._def.left, schema_._def.right]) {
|
717
|
-
const [itemRequired, itemJson] = this.convert(item, options, lazyDepth, false, false);
|
747
|
+
const [itemRequired, itemJson] = this.convert(item, options, lazyDepth, false, false, structureDepth);
|
718
748
|
allOf.push(itemJson);
|
719
749
|
if (itemRequired) {
|
720
750
|
required = true;
|
@@ -723,25 +753,26 @@ class ZodToJsonSchemaConverter {
|
|
723
753
|
return [required, { allOf }];
|
724
754
|
}
|
725
755
|
case ZodFirstPartyTypeKind.ZodLazy: {
|
726
|
-
|
756
|
+
const currentLazyDepth = lazyDepth + 1;
|
757
|
+
if (currentLazyDepth > this.maxLazyDepth) {
|
727
758
|
return [false, this.anyJsonSchema];
|
728
759
|
}
|
729
760
|
const schema_ = schema;
|
730
|
-
return this.convert(schema_._def.getter(), options,
|
761
|
+
return this.convert(schema_._def.getter(), options, currentLazyDepth, false, false, structureDepth);
|
731
762
|
}
|
732
763
|
case ZodFirstPartyTypeKind.ZodOptional: {
|
733
764
|
const schema_ = schema;
|
734
|
-
const [_, inner] = this.convert(schema_._def.innerType, options, lazyDepth, false, false);
|
765
|
+
const [_, inner] = this.convert(schema_._def.innerType, options, lazyDepth, false, false, structureDepth);
|
735
766
|
return [false, inner];
|
736
767
|
}
|
737
768
|
case ZodFirstPartyTypeKind.ZodReadonly: {
|
738
769
|
const schema_ = schema;
|
739
|
-
const [required, json] = this.convert(schema_._def.innerType, options, lazyDepth, false, false);
|
770
|
+
const [required, json] = this.convert(schema_._def.innerType, options, lazyDepth, false, false, structureDepth);
|
740
771
|
return [required, { ...json, readOnly: true }];
|
741
772
|
}
|
742
773
|
case ZodFirstPartyTypeKind.ZodDefault: {
|
743
774
|
const schema_ = schema;
|
744
|
-
const [_, json] = this.convert(schema_._def.innerType, options, lazyDepth, false, false);
|
775
|
+
const [_, json] = this.convert(schema_._def.innerType, options, lazyDepth, false, false, structureDepth);
|
745
776
|
return [false, { default: schema_._def.defaultValue(), ...json }];
|
746
777
|
}
|
747
778
|
case ZodFirstPartyTypeKind.ZodEffects: {
|
@@ -749,15 +780,15 @@ class ZodToJsonSchemaConverter {
|
|
749
780
|
if (schema_._def.effect.type === "transform" && options.strategy === "output") {
|
750
781
|
return [false, this.anyJsonSchema];
|
751
782
|
}
|
752
|
-
return this.convert(schema_._def.schema, options, lazyDepth, false, false);
|
783
|
+
return this.convert(schema_._def.schema, options, lazyDepth, false, false, structureDepth);
|
753
784
|
}
|
754
785
|
case ZodFirstPartyTypeKind.ZodCatch: {
|
755
786
|
const schema_ = schema;
|
756
|
-
return this.convert(schema_._def.innerType, options, lazyDepth, false, false);
|
787
|
+
return this.convert(schema_._def.innerType, options, lazyDepth, false, false, structureDepth);
|
757
788
|
}
|
758
789
|
case ZodFirstPartyTypeKind.ZodBranded: {
|
759
790
|
const schema_ = schema;
|
760
|
-
return this.convert(schema_._def.type, options, lazyDepth, false, false);
|
791
|
+
return this.convert(schema_._def.type, options, lazyDepth, false, false, structureDepth);
|
761
792
|
}
|
762
793
|
case ZodFirstPartyTypeKind.ZodPipeline: {
|
763
794
|
const schema_ = schema;
|
@@ -766,13 +797,14 @@ class ZodToJsonSchemaConverter {
|
|
766
797
|
options,
|
767
798
|
lazyDepth,
|
768
799
|
false,
|
769
|
-
false
|
800
|
+
false,
|
801
|
+
structureDepth
|
770
802
|
);
|
771
803
|
}
|
772
804
|
case ZodFirstPartyTypeKind.ZodNullable: {
|
773
805
|
const schema_ = schema;
|
774
|
-
const [required, json] = this.convert(schema_._def.innerType, options, lazyDepth, false, false);
|
775
|
-
return [required, { anyOf: [{ type: "null" }
|
806
|
+
const [required, json] = this.convert(schema_._def.innerType, options, lazyDepth, false, false, structureDepth);
|
807
|
+
return [required, { anyOf: [json, { type: "null" }] }];
|
776
808
|
}
|
777
809
|
}
|
778
810
|
return [true, this.unsupportedJsonSchema];
|
@@ -791,12 +823,17 @@ class ZodToJsonSchemaConverter {
|
|
791
823
|
}
|
792
824
|
case "regexp": {
|
793
825
|
return {
|
794
|
-
type: "string",
|
795
|
-
pattern: "^\\/(.*)\\/([a-z]*)$"
|
826
|
+
"type": "string",
|
827
|
+
"pattern": "^\\/(.*)\\/([a-z]*)$",
|
828
|
+
"x-native-type": JsonSchemaXNativeType.RegExp
|
796
829
|
};
|
797
830
|
}
|
798
831
|
case "url": {
|
799
|
-
return {
|
832
|
+
return {
|
833
|
+
"type": "string",
|
834
|
+
"format": JSONSchemaFormat.URI,
|
835
|
+
"x-native-type": JsonSchemaXNativeType.Url
|
836
|
+
};
|
800
837
|
}
|
801
838
|
}
|
802
839
|
}
|
package/dist/zod4/index.d.mts
CHANGED
@@ -2,24 +2,35 @@ import { Context } from '@orpc/server';
|
|
2
2
|
import { StandardHandlerPlugin, StandardHandlerOptions } from '@orpc/server/standard';
|
3
3
|
import { AnySchema } from '@orpc/contract';
|
4
4
|
import { JSONSchema, SchemaConvertOptions, ConditionalSchemaConverter } from '@orpc/openapi';
|
5
|
-
import { Interceptor
|
5
|
+
import { Interceptor } from '@orpc/shared';
|
6
6
|
import * as zod_v4_core from 'zod/v4/core';
|
7
7
|
import { $ZodType, $input, $output } from 'zod/v4/core';
|
8
8
|
|
9
|
+
/**
|
10
|
+
* @deprecated Use [Smart Coercion Plugin](https://orpc.unnoq.com/docs/openapi/plugins/smart-coercion) instead.
|
11
|
+
*/
|
9
12
|
declare class experimental_ZodSmartCoercionPlugin<TContext extends Context> implements StandardHandlerPlugin<TContext> {
|
10
13
|
#private;
|
11
14
|
init(options: StandardHandlerOptions<TContext>): void;
|
12
15
|
}
|
13
16
|
|
14
|
-
interface
|
17
|
+
interface ZodToJsonSchemaConverterOptions {
|
15
18
|
/**
|
16
|
-
* Max depth of lazy type
|
19
|
+
* Max depth of lazy type.
|
17
20
|
*
|
18
|
-
* Used anyJsonSchema (`{}`) when
|
21
|
+
* Used anyJsonSchema (`{}`) when exceed max depth
|
19
22
|
*
|
20
23
|
* @default 2
|
21
24
|
*/
|
22
25
|
maxLazyDepth?: number;
|
26
|
+
/**
|
27
|
+
* Max depth of nested types.
|
28
|
+
*
|
29
|
+
* Used anyJsonSchema (`{}`) when exceed max depth
|
30
|
+
*
|
31
|
+
* @default 10
|
32
|
+
*/
|
33
|
+
maxStructureDepth?: number;
|
23
34
|
/**
|
24
35
|
* The schema to be used to represent the any | unknown type.
|
25
36
|
*
|
@@ -46,18 +57,19 @@ interface experimental_ZodToJsonSchemaOptions {
|
|
46
57
|
}, [
|
47
58
|
required: boolean,
|
48
59
|
jsonSchema: Exclude<JSONSchema, boolean>
|
49
|
-
]
|
60
|
+
]>[];
|
50
61
|
}
|
51
|
-
declare class
|
62
|
+
declare class ZodToJsonSchemaConverter implements ConditionalSchemaConverter {
|
52
63
|
#private;
|
53
64
|
private readonly maxLazyDepth;
|
65
|
+
private readonly maxStructureDepth;
|
54
66
|
private readonly anyJsonSchema;
|
55
67
|
private readonly unsupportedJsonSchema;
|
56
68
|
private readonly undefinedJsonSchema;
|
57
69
|
private readonly interceptors;
|
58
|
-
constructor(options?:
|
70
|
+
constructor(options?: ZodToJsonSchemaConverterOptions);
|
59
71
|
condition(schema: AnySchema | undefined): boolean;
|
60
|
-
convert(schema: AnySchema | undefined, options: SchemaConvertOptions):
|
72
|
+
convert(schema: AnySchema | undefined, options: SchemaConvertOptions): [required: boolean, jsonSchema: Exclude<JSONSchema, boolean>];
|
61
73
|
}
|
62
74
|
|
63
75
|
/**
|
@@ -77,7 +89,7 @@ declare class experimental_ZodToJsonSchemaConverter implements ConditionalSchema
|
|
77
89
|
* })
|
78
90
|
* ```
|
79
91
|
*/
|
80
|
-
declare const
|
92
|
+
declare const JSON_SCHEMA_REGISTRY: zod_v4_core.$ZodRegistry<{
|
81
93
|
$anchor?: string;
|
82
94
|
$comment?: string;
|
83
95
|
$defs?: Record<string, JSONSchema>;
|
@@ -138,7 +150,7 @@ declare const experimental_JSON_SCHEMA_REGISTRY: zod_v4_core.$ZodRegistry<{
|
|
138
150
|
unevaluatedProperties?: JSONSchema;
|
139
151
|
uniqueItems?: boolean;
|
140
152
|
writeOnly?: boolean;
|
141
|
-
}, zod_v4_core.$ZodType<unknown, unknown
|
153
|
+
}, zod_v4_core.$ZodType<unknown, unknown, zod_v4_core.$ZodTypeInternals<unknown, unknown>>>;
|
142
154
|
/**
|
143
155
|
* Zod registry for customizing generated JSON schema, only useful for .input
|
144
156
|
*
|
@@ -156,7 +168,7 @@ declare const experimental_JSON_SCHEMA_REGISTRY: zod_v4_core.$ZodRegistry<{
|
|
156
168
|
* })
|
157
169
|
* ```
|
158
170
|
*/
|
159
|
-
declare const
|
171
|
+
declare const JSON_SCHEMA_INPUT_REGISTRY: zod_v4_core.$ZodRegistry<{
|
160
172
|
$anchor?: string;
|
161
173
|
$comment?: string;
|
162
174
|
$defs?: Record<string, JSONSchema>;
|
@@ -217,7 +229,7 @@ declare const experimental_JSON_SCHEMA_INPUT_REGISTRY: zod_v4_core.$ZodRegistry<
|
|
217
229
|
unevaluatedProperties?: JSONSchema;
|
218
230
|
uniqueItems?: boolean;
|
219
231
|
writeOnly?: boolean;
|
220
|
-
}, zod_v4_core.$ZodType<unknown, unknown
|
232
|
+
}, zod_v4_core.$ZodType<unknown, unknown, zod_v4_core.$ZodTypeInternals<unknown, unknown>>>;
|
221
233
|
/**
|
222
234
|
* Zod registry for customizing generated JSON schema, only useful for .input
|
223
235
|
*
|
@@ -235,7 +247,7 @@ declare const experimental_JSON_SCHEMA_INPUT_REGISTRY: zod_v4_core.$ZodRegistry<
|
|
235
247
|
* })
|
236
248
|
* ```
|
237
249
|
*/
|
238
|
-
declare const
|
250
|
+
declare const JSON_SCHEMA_OUTPUT_REGISTRY: zod_v4_core.$ZodRegistry<{
|
239
251
|
$anchor?: string;
|
240
252
|
$comment?: string;
|
241
253
|
$defs?: Record<string, JSONSchema>;
|
@@ -296,7 +308,7 @@ declare const experimental_JSON_SCHEMA_OUTPUT_REGISTRY: zod_v4_core.$ZodRegistry
|
|
296
308
|
unevaluatedProperties?: JSONSchema;
|
297
309
|
uniqueItems?: boolean;
|
298
310
|
writeOnly?: boolean;
|
299
|
-
}, zod_v4_core.$ZodType<unknown, unknown
|
311
|
+
}, zod_v4_core.$ZodType<unknown, unknown, zod_v4_core.$ZodTypeInternals<unknown, unknown>>>;
|
300
312
|
|
301
|
-
export {
|
302
|
-
export type {
|
313
|
+
export { JSON_SCHEMA_INPUT_REGISTRY, JSON_SCHEMA_OUTPUT_REGISTRY, JSON_SCHEMA_REGISTRY, ZodToJsonSchemaConverter, experimental_ZodSmartCoercionPlugin };
|
314
|
+
export type { ZodToJsonSchemaConverterOptions };
|
package/dist/zod4/index.d.ts
CHANGED
@@ -2,24 +2,35 @@ import { Context } from '@orpc/server';
|
|
2
2
|
import { StandardHandlerPlugin, StandardHandlerOptions } from '@orpc/server/standard';
|
3
3
|
import { AnySchema } from '@orpc/contract';
|
4
4
|
import { JSONSchema, SchemaConvertOptions, ConditionalSchemaConverter } from '@orpc/openapi';
|
5
|
-
import { Interceptor
|
5
|
+
import { Interceptor } from '@orpc/shared';
|
6
6
|
import * as zod_v4_core from 'zod/v4/core';
|
7
7
|
import { $ZodType, $input, $output } from 'zod/v4/core';
|
8
8
|
|
9
|
+
/**
|
10
|
+
* @deprecated Use [Smart Coercion Plugin](https://orpc.unnoq.com/docs/openapi/plugins/smart-coercion) instead.
|
11
|
+
*/
|
9
12
|
declare class experimental_ZodSmartCoercionPlugin<TContext extends Context> implements StandardHandlerPlugin<TContext> {
|
10
13
|
#private;
|
11
14
|
init(options: StandardHandlerOptions<TContext>): void;
|
12
15
|
}
|
13
16
|
|
14
|
-
interface
|
17
|
+
interface ZodToJsonSchemaConverterOptions {
|
15
18
|
/**
|
16
|
-
* Max depth of lazy type
|
19
|
+
* Max depth of lazy type.
|
17
20
|
*
|
18
|
-
* Used anyJsonSchema (`{}`) when
|
21
|
+
* Used anyJsonSchema (`{}`) when exceed max depth
|
19
22
|
*
|
20
23
|
* @default 2
|
21
24
|
*/
|
22
25
|
maxLazyDepth?: number;
|
26
|
+
/**
|
27
|
+
* Max depth of nested types.
|
28
|
+
*
|
29
|
+
* Used anyJsonSchema (`{}`) when exceed max depth
|
30
|
+
*
|
31
|
+
* @default 10
|
32
|
+
*/
|
33
|
+
maxStructureDepth?: number;
|
23
34
|
/**
|
24
35
|
* The schema to be used to represent the any | unknown type.
|
25
36
|
*
|
@@ -46,18 +57,19 @@ interface experimental_ZodToJsonSchemaOptions {
|
|
46
57
|
}, [
|
47
58
|
required: boolean,
|
48
59
|
jsonSchema: Exclude<JSONSchema, boolean>
|
49
|
-
]
|
60
|
+
]>[];
|
50
61
|
}
|
51
|
-
declare class
|
62
|
+
declare class ZodToJsonSchemaConverter implements ConditionalSchemaConverter {
|
52
63
|
#private;
|
53
64
|
private readonly maxLazyDepth;
|
65
|
+
private readonly maxStructureDepth;
|
54
66
|
private readonly anyJsonSchema;
|
55
67
|
private readonly unsupportedJsonSchema;
|
56
68
|
private readonly undefinedJsonSchema;
|
57
69
|
private readonly interceptors;
|
58
|
-
constructor(options?:
|
70
|
+
constructor(options?: ZodToJsonSchemaConverterOptions);
|
59
71
|
condition(schema: AnySchema | undefined): boolean;
|
60
|
-
convert(schema: AnySchema | undefined, options: SchemaConvertOptions):
|
72
|
+
convert(schema: AnySchema | undefined, options: SchemaConvertOptions): [required: boolean, jsonSchema: Exclude<JSONSchema, boolean>];
|
61
73
|
}
|
62
74
|
|
63
75
|
/**
|
@@ -77,7 +89,7 @@ declare class experimental_ZodToJsonSchemaConverter implements ConditionalSchema
|
|
77
89
|
* })
|
78
90
|
* ```
|
79
91
|
*/
|
80
|
-
declare const
|
92
|
+
declare const JSON_SCHEMA_REGISTRY: zod_v4_core.$ZodRegistry<{
|
81
93
|
$anchor?: string;
|
82
94
|
$comment?: string;
|
83
95
|
$defs?: Record<string, JSONSchema>;
|
@@ -138,7 +150,7 @@ declare const experimental_JSON_SCHEMA_REGISTRY: zod_v4_core.$ZodRegistry<{
|
|
138
150
|
unevaluatedProperties?: JSONSchema;
|
139
151
|
uniqueItems?: boolean;
|
140
152
|
writeOnly?: boolean;
|
141
|
-
}, zod_v4_core.$ZodType<unknown, unknown
|
153
|
+
}, zod_v4_core.$ZodType<unknown, unknown, zod_v4_core.$ZodTypeInternals<unknown, unknown>>>;
|
142
154
|
/**
|
143
155
|
* Zod registry for customizing generated JSON schema, only useful for .input
|
144
156
|
*
|
@@ -156,7 +168,7 @@ declare const experimental_JSON_SCHEMA_REGISTRY: zod_v4_core.$ZodRegistry<{
|
|
156
168
|
* })
|
157
169
|
* ```
|
158
170
|
*/
|
159
|
-
declare const
|
171
|
+
declare const JSON_SCHEMA_INPUT_REGISTRY: zod_v4_core.$ZodRegistry<{
|
160
172
|
$anchor?: string;
|
161
173
|
$comment?: string;
|
162
174
|
$defs?: Record<string, JSONSchema>;
|
@@ -217,7 +229,7 @@ declare const experimental_JSON_SCHEMA_INPUT_REGISTRY: zod_v4_core.$ZodRegistry<
|
|
217
229
|
unevaluatedProperties?: JSONSchema;
|
218
230
|
uniqueItems?: boolean;
|
219
231
|
writeOnly?: boolean;
|
220
|
-
}, zod_v4_core.$ZodType<unknown, unknown
|
232
|
+
}, zod_v4_core.$ZodType<unknown, unknown, zod_v4_core.$ZodTypeInternals<unknown, unknown>>>;
|
221
233
|
/**
|
222
234
|
* Zod registry for customizing generated JSON schema, only useful for .input
|
223
235
|
*
|
@@ -235,7 +247,7 @@ declare const experimental_JSON_SCHEMA_INPUT_REGISTRY: zod_v4_core.$ZodRegistry<
|
|
235
247
|
* })
|
236
248
|
* ```
|
237
249
|
*/
|
238
|
-
declare const
|
250
|
+
declare const JSON_SCHEMA_OUTPUT_REGISTRY: zod_v4_core.$ZodRegistry<{
|
239
251
|
$anchor?: string;
|
240
252
|
$comment?: string;
|
241
253
|
$defs?: Record<string, JSONSchema>;
|
@@ -296,7 +308,7 @@ declare const experimental_JSON_SCHEMA_OUTPUT_REGISTRY: zod_v4_core.$ZodRegistry
|
|
296
308
|
unevaluatedProperties?: JSONSchema;
|
297
309
|
uniqueItems?: boolean;
|
298
310
|
writeOnly?: boolean;
|
299
|
-
}, zod_v4_core.$ZodType<unknown, unknown
|
311
|
+
}, zod_v4_core.$ZodType<unknown, unknown, zod_v4_core.$ZodTypeInternals<unknown, unknown>>>;
|
300
312
|
|
301
|
-
export {
|
302
|
-
export type {
|
313
|
+
export { JSON_SCHEMA_INPUT_REGISTRY, JSON_SCHEMA_OUTPUT_REGISTRY, JSON_SCHEMA_REGISTRY, ZodToJsonSchemaConverter, experimental_ZodSmartCoercionPlugin };
|
314
|
+
export type { ZodToJsonSchemaConverterOptions };
|
package/dist/zod4/index.mjs
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
import { isObject, guard, intercept } from '@orpc/shared';
|
1
|
+
import { isObject, guard, intercept, toArray } from '@orpc/shared';
|
2
|
+
import { JsonSchemaXNativeType } from '@orpc/json-schema';
|
2
3
|
import { JSONSchemaFormat, JSONSchemaContentEncoding } from '@orpc/openapi';
|
3
4
|
import { registry, globalRegistry } from 'zod/v4/core';
|
4
5
|
|
@@ -100,8 +101,7 @@ class experimental_ZodSmartCoercionPlugin {
|
|
100
101
|
}
|
101
102
|
return value;
|
102
103
|
}
|
103
|
-
case "object":
|
104
|
-
case "interface": {
|
104
|
+
case "object": {
|
105
105
|
const object = schema;
|
106
106
|
if (value === void 0) {
|
107
107
|
return {};
|
@@ -165,9 +165,21 @@ class experimental_ZodSmartCoercionPlugin {
|
|
165
165
|
return this.#coerce(union._zod.def.options[0], value);
|
166
166
|
}
|
167
167
|
if (isObject(value)) {
|
168
|
+
const discriminator = "discriminator" in union._zod.def && typeof union._zod.def.discriminator === "string" ? union._zod.def.discriminator : void 0;
|
168
169
|
for (const option of union._zod.def.options) {
|
169
|
-
if (option._zod.
|
170
|
-
|
170
|
+
if (!option._zod.propValues) {
|
171
|
+
continue;
|
172
|
+
}
|
173
|
+
if (discriminator !== void 0) {
|
174
|
+
if (option._zod.propValues[discriminator]?.has(value[discriminator])) {
|
175
|
+
return this.#coerce(option, value);
|
176
|
+
}
|
177
|
+
} else {
|
178
|
+
for (const key in option._zod.propValues) {
|
179
|
+
if (option._zod.propValues[key]?.has(value[key])) {
|
180
|
+
return this.#coerce(option, value);
|
181
|
+
}
|
182
|
+
}
|
171
183
|
}
|
172
184
|
}
|
173
185
|
}
|
@@ -255,44 +267,22 @@ class experimental_ZodSmartCoercionPlugin {
|
|
255
267
|
}
|
256
268
|
return value;
|
257
269
|
}
|
258
|
-
/**
|
259
|
-
* This function is inspired from Zod, because it's not exported
|
260
|
-
* https://github.com/colinhacks/zod/blob/v4/packages/core/src/schemas.ts#L1903C1-L1921C2
|
261
|
-
*/
|
262
|
-
#matchDiscriminators(input, discs) {
|
263
|
-
for (const [key, value] of discs) {
|
264
|
-
const data = input[key];
|
265
|
-
if (value.values.size && !value.values.has(data)) {
|
266
|
-
return false;
|
267
|
-
}
|
268
|
-
if (value.maps.length === 0) {
|
269
|
-
continue;
|
270
|
-
}
|
271
|
-
if (!isObject(data)) {
|
272
|
-
return false;
|
273
|
-
}
|
274
|
-
for (const m of value.maps) {
|
275
|
-
if (!this.#matchDiscriminators(data, m)) {
|
276
|
-
return false;
|
277
|
-
}
|
278
|
-
}
|
279
|
-
}
|
280
|
-
return true;
|
281
|
-
}
|
282
270
|
}
|
283
271
|
|
284
|
-
const
|
285
|
-
const
|
286
|
-
const
|
272
|
+
const JSON_SCHEMA_REGISTRY = registry();
|
273
|
+
const JSON_SCHEMA_INPUT_REGISTRY = registry();
|
274
|
+
const JSON_SCHEMA_OUTPUT_REGISTRY = registry();
|
287
275
|
|
288
|
-
class
|
276
|
+
class ZodToJsonSchemaConverter {
|
289
277
|
maxLazyDepth;
|
278
|
+
maxStructureDepth;
|
290
279
|
anyJsonSchema;
|
291
280
|
unsupportedJsonSchema;
|
292
281
|
undefinedJsonSchema;
|
293
282
|
interceptors;
|
294
283
|
constructor(options = {}) {
|
295
284
|
this.maxLazyDepth = options.maxLazyDepth ?? 2;
|
285
|
+
this.maxStructureDepth = options.maxStructureDepth ?? 10;
|
296
286
|
this.anyJsonSchema = options.anyJsonSchema ?? {};
|
297
287
|
this.unsupportedJsonSchema = options.unsupportedJsonSchema ?? { not: {} };
|
298
288
|
this.undefinedJsonSchema = options.undefinedJsonSchema ?? { not: {} };
|
@@ -302,17 +292,28 @@ class experimental_ZodToJsonSchemaConverter {
|
|
302
292
|
return schema !== void 0 && schema["~standard"].vendor === "zod";
|
303
293
|
}
|
304
294
|
convert(schema, options) {
|
305
|
-
return this.#convert(schema, options, 0);
|
295
|
+
return this.#convert(schema, options, 0, 0);
|
306
296
|
}
|
307
|
-
#convert(schema, options, lazyDepth, isHandledCustomJSONSchema = false) {
|
297
|
+
#convert(schema, options, lazyDepth, structureDepth, isHandledCustomJSONSchema = false) {
|
308
298
|
return intercept(
|
309
299
|
this.interceptors,
|
310
300
|
{ schema, options, lazyDepth, isHandledCustomJSONSchema },
|
311
|
-
|
301
|
+
({ schema: schema2, options: options2, lazyDepth: lazyDepth2, isHandledCustomJSONSchema: isHandledCustomJSONSchema2 }) => {
|
302
|
+
if (structureDepth > this.maxStructureDepth) {
|
303
|
+
return [false, this.anyJsonSchema];
|
304
|
+
}
|
305
|
+
if (!options2.minStructureDepthForRef || options2.minStructureDepthForRef <= structureDepth) {
|
306
|
+
const components = toArray(options2.components);
|
307
|
+
for (const component of components) {
|
308
|
+
if (component.schema === schema2 && component.allowedStrategies.includes(options2.strategy)) {
|
309
|
+
return [component.required, { $ref: component.ref }];
|
310
|
+
}
|
311
|
+
}
|
312
|
+
}
|
312
313
|
if (!isHandledCustomJSONSchema2) {
|
313
314
|
const customJSONSchema = this.#getCustomJsonSchema(schema2, options2);
|
314
315
|
if (customJSONSchema) {
|
315
|
-
const [required, json] =
|
316
|
+
const [required, json] = this.#convert(schema2, options2, lazyDepth2, structureDepth, true);
|
316
317
|
return [required, { ...json, ...customJSONSchema }];
|
317
318
|
}
|
318
319
|
}
|
@@ -320,21 +321,28 @@ class experimental_ZodToJsonSchemaConverter {
|
|
320
321
|
case "string": {
|
321
322
|
const string = schema2;
|
322
323
|
const json = { type: "string" };
|
323
|
-
const { minimum, maximum, format,
|
324
|
-
if (minimum
|
324
|
+
const { minimum, maximum, format, patterns, contentEncoding } = string._zod.bag;
|
325
|
+
if (typeof minimum === "number") {
|
325
326
|
json.minLength = minimum;
|
326
327
|
}
|
327
|
-
if (maximum
|
328
|
+
if (typeof maximum === "number") {
|
328
329
|
json.maxLength = maximum;
|
329
330
|
}
|
330
|
-
if (contentEncoding
|
331
|
+
if (typeof contentEncoding === "string") {
|
331
332
|
json.contentEncoding = this.#handleContentEncoding(contentEncoding);
|
332
333
|
}
|
333
|
-
if (format
|
334
|
+
if (typeof format === "string" && format !== "regex" && json.contentEncoding === void 0) {
|
334
335
|
json.format = this.#handleStringFormat(format);
|
335
336
|
}
|
336
|
-
if (
|
337
|
-
|
337
|
+
if (patterns instanceof Set && json.contentEncoding === void 0 && json.format === void 0) {
|
338
|
+
for (const pattern of patterns) {
|
339
|
+
if (json.pattern === void 0) {
|
340
|
+
json.pattern = pattern.source;
|
341
|
+
} else {
|
342
|
+
json.allOf ??= [];
|
343
|
+
json.allOf.push({ pattern: pattern.source });
|
344
|
+
}
|
345
|
+
}
|
338
346
|
}
|
339
347
|
if (format === "jwt" && json.contentEncoding === void 0 && json.format === void 0 && json.pattern === void 0) {
|
340
348
|
json.pattern = /^[\w-]+\.[\w-]+\.[\w-]+$/.source;
|
@@ -344,25 +352,23 @@ class experimental_ZodToJsonSchemaConverter {
|
|
344
352
|
case "number": {
|
345
353
|
const number = schema2;
|
346
354
|
const json = { type: "number" };
|
347
|
-
const { minimum, maximum, format, multipleOf,
|
348
|
-
if (format?.includes("int")) {
|
355
|
+
const { minimum, maximum, format, multipleOf, exclusiveMaximum, exclusiveMinimum } = number._zod.bag;
|
356
|
+
if (typeof format === "string" && format?.includes("int")) {
|
349
357
|
json.type = "integer";
|
350
358
|
}
|
351
|
-
if (minimum
|
352
|
-
|
353
|
-
json.minimum = minimum;
|
354
|
-
} else {
|
355
|
-
json.exclusiveMinimum = minimum;
|
356
|
-
}
|
359
|
+
if (typeof minimum === "number") {
|
360
|
+
json.minimum = minimum;
|
357
361
|
}
|
358
|
-
if (maximum
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
}
|
362
|
+
if (typeof maximum === "number") {
|
363
|
+
json.maximum = maximum;
|
364
|
+
}
|
365
|
+
if (typeof exclusiveMinimum === "number") {
|
366
|
+
json.exclusiveMinimum = exclusiveMinimum;
|
364
367
|
}
|
365
|
-
if (
|
368
|
+
if (typeof exclusiveMaximum === "number") {
|
369
|
+
json.exclusiveMaximum = exclusiveMaximum;
|
370
|
+
}
|
371
|
+
if (typeof multipleOf === "number") {
|
366
372
|
json.multipleOf = multipleOf;
|
367
373
|
}
|
368
374
|
return [true, json];
|
@@ -371,10 +377,18 @@ class experimental_ZodToJsonSchemaConverter {
|
|
371
377
|
return [true, { type: "boolean" }];
|
372
378
|
}
|
373
379
|
case "bigint": {
|
374
|
-
return [true, {
|
380
|
+
return [true, {
|
381
|
+
"type": "string",
|
382
|
+
"pattern": "^-?[0-9]+$",
|
383
|
+
"x-native-type": JsonSchemaXNativeType.BigInt
|
384
|
+
}];
|
375
385
|
}
|
376
386
|
case "date": {
|
377
|
-
return [true, {
|
387
|
+
return [true, {
|
388
|
+
"type": "string",
|
389
|
+
"format": JSONSchemaFormat.DateTime,
|
390
|
+
"x-native-type": JsonSchemaXNativeType.Date
|
391
|
+
}];
|
378
392
|
}
|
379
393
|
case "null": {
|
380
394
|
return [true, { type: "null" }];
|
@@ -395,21 +409,21 @@ class experimental_ZodToJsonSchemaConverter {
|
|
395
409
|
case "array": {
|
396
410
|
const array = schema2;
|
397
411
|
const json = { type: "array" };
|
398
|
-
const { minimum, maximum } = array._zod.
|
399
|
-
if (minimum
|
412
|
+
const { minimum, maximum } = array._zod.bag;
|
413
|
+
if (typeof minimum === "number") {
|
400
414
|
json.minItems = minimum;
|
401
415
|
}
|
402
|
-
if (maximum
|
416
|
+
if (typeof maximum === "number") {
|
403
417
|
json.maxItems = maximum;
|
404
418
|
}
|
405
|
-
json.items = this.#handleArrayItemJsonSchema(
|
419
|
+
json.items = this.#handleArrayItemJsonSchema(this.#convert(array._zod.def.element, options2, lazyDepth2, structureDepth + 1), options2);
|
406
420
|
return [true, json];
|
407
421
|
}
|
408
422
|
case "object": {
|
409
423
|
const object = schema2;
|
410
424
|
const json = { type: "object" };
|
411
425
|
for (const [key, value] of Object.entries(object._zod.def.shape)) {
|
412
|
-
const [itemRequired, itemJson] =
|
426
|
+
const [itemRequired, itemJson] = this.#convert(value, options2, lazyDepth2, structureDepth + 1);
|
413
427
|
json.properties ??= {};
|
414
428
|
json.properties[key] = itemJson;
|
415
429
|
if (itemRequired) {
|
@@ -421,7 +435,7 @@ class experimental_ZodToJsonSchemaConverter {
|
|
421
435
|
if (object._zod.def.catchall._zod.def.type === "never") {
|
422
436
|
json.additionalProperties = false;
|
423
437
|
} else {
|
424
|
-
const [_, addJson] =
|
438
|
+
const [_, addJson] = this.#convert(object._zod.def.catchall, options2, lazyDepth2, structureDepth + 1);
|
425
439
|
json.additionalProperties = addJson;
|
426
440
|
}
|
427
441
|
}
|
@@ -432,7 +446,7 @@ class experimental_ZodToJsonSchemaConverter {
|
|
432
446
|
const anyOf = [];
|
433
447
|
let required = true;
|
434
448
|
for (const item of union._zod.def.options) {
|
435
|
-
const [itemRequired, itemJson] =
|
449
|
+
const [itemRequired, itemJson] = this.#convert(item, options2, lazyDepth2, structureDepth);
|
436
450
|
if (!itemRequired) {
|
437
451
|
required = false;
|
438
452
|
}
|
@@ -453,7 +467,7 @@ class experimental_ZodToJsonSchemaConverter {
|
|
453
467
|
const json = { allOf: [] };
|
454
468
|
let required = false;
|
455
469
|
for (const item of [intersection._zod.def.left, intersection._zod.def.right]) {
|
456
|
-
const [itemRequired, itemJson] =
|
470
|
+
const [itemRequired, itemJson] = this.#convert(item, options2, lazyDepth2, structureDepth);
|
457
471
|
json.allOf.push(itemJson);
|
458
472
|
if (itemRequired) {
|
459
473
|
required = true;
|
@@ -465,16 +479,16 @@ class experimental_ZodToJsonSchemaConverter {
|
|
465
479
|
const tuple = schema2;
|
466
480
|
const json = { type: "array", prefixItems: [] };
|
467
481
|
for (const item of tuple._zod.def.items) {
|
468
|
-
json.prefixItems.push(this.#handleArrayItemJsonSchema(
|
482
|
+
json.prefixItems.push(this.#handleArrayItemJsonSchema(this.#convert(item, options2, lazyDepth2, structureDepth + 1), options2));
|
469
483
|
}
|
470
484
|
if (tuple._zod.def.rest) {
|
471
|
-
json.items = this.#handleArrayItemJsonSchema(
|
485
|
+
json.items = this.#handleArrayItemJsonSchema(this.#convert(tuple._zod.def.rest, options2, lazyDepth2, structureDepth + 1), options2);
|
472
486
|
}
|
473
|
-
const { minimum, maximum } = tuple._zod.
|
474
|
-
if (minimum
|
487
|
+
const { minimum, maximum } = tuple._zod.bag;
|
488
|
+
if (typeof minimum === "number") {
|
475
489
|
json.minItems = minimum;
|
476
490
|
}
|
477
|
-
if (maximum
|
491
|
+
if (typeof maximum === "number") {
|
478
492
|
json.maxItems = maximum;
|
479
493
|
}
|
480
494
|
return [true, json];
|
@@ -482,31 +496,33 @@ class experimental_ZodToJsonSchemaConverter {
|
|
482
496
|
case "record": {
|
483
497
|
const record = schema2;
|
484
498
|
const json = { type: "object" };
|
485
|
-
json.propertyNames =
|
486
|
-
json.additionalProperties =
|
499
|
+
json.propertyNames = this.#convert(record._zod.def.keyType, options2, lazyDepth2, structureDepth + 1)[1];
|
500
|
+
json.additionalProperties = this.#convert(record._zod.def.valueType, options2, lazyDepth2, structureDepth + 1)[1];
|
487
501
|
return [true, json];
|
488
502
|
}
|
489
503
|
case "map": {
|
490
504
|
const map = schema2;
|
491
505
|
return [true, {
|
492
|
-
type: "array",
|
493
|
-
items: {
|
506
|
+
"type": "array",
|
507
|
+
"items": {
|
494
508
|
type: "array",
|
495
509
|
prefixItems: [
|
496
|
-
this.#handleArrayItemJsonSchema(
|
497
|
-
this.#handleArrayItemJsonSchema(
|
510
|
+
this.#handleArrayItemJsonSchema(this.#convert(map._zod.def.keyType, options2, lazyDepth2, structureDepth + 1), options2),
|
511
|
+
this.#handleArrayItemJsonSchema(this.#convert(map._zod.def.valueType, options2, lazyDepth2, structureDepth + 1), options2)
|
498
512
|
],
|
499
513
|
maxItems: 2,
|
500
514
|
minItems: 2
|
501
|
-
}
|
515
|
+
},
|
516
|
+
"x-native-type": JsonSchemaXNativeType.Map
|
502
517
|
}];
|
503
518
|
}
|
504
519
|
case "set": {
|
505
520
|
const set = schema2;
|
506
521
|
return [true, {
|
507
|
-
type: "array",
|
508
|
-
uniqueItems: true,
|
509
|
-
items: this.#handleArrayItemJsonSchema(
|
522
|
+
"type": "array",
|
523
|
+
"uniqueItems": true,
|
524
|
+
"items": this.#handleArrayItemJsonSchema(this.#convert(set._zod.def.valueType, options2, lazyDepth2, structureDepth + 1), options2),
|
525
|
+
"x-native-type": JsonSchemaXNativeType.Set
|
510
526
|
}];
|
511
527
|
}
|
512
528
|
case "enum": {
|
@@ -530,12 +546,14 @@ class experimental_ZodToJsonSchemaConverter {
|
|
530
546
|
case "file": {
|
531
547
|
const file = schema2;
|
532
548
|
const oneOf = [];
|
533
|
-
const { mime } = file._zod.
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
549
|
+
const { mime } = file._zod.bag;
|
550
|
+
if (mime === void 0 || Array.isArray(mime) && mime.every((m) => typeof m === "string")) {
|
551
|
+
for (const type of mime ?? ["*/*"]) {
|
552
|
+
oneOf.push({
|
553
|
+
type: "string",
|
554
|
+
contentMediaType: type
|
555
|
+
});
|
556
|
+
}
|
539
557
|
}
|
540
558
|
return [true, oneOf.length === 1 ? oneOf[0] : { anyOf: oneOf }];
|
541
559
|
}
|
@@ -544,12 +562,12 @@ class experimental_ZodToJsonSchemaConverter {
|
|
544
562
|
}
|
545
563
|
case "nullable": {
|
546
564
|
const nullable = schema2;
|
547
|
-
const [required, json] =
|
565
|
+
const [required, json] = this.#convert(nullable._zod.def.innerType, options2, lazyDepth2, structureDepth);
|
548
566
|
return [required, { anyOf: [json, { type: "null" }] }];
|
549
567
|
}
|
550
568
|
case "nonoptional": {
|
551
569
|
const nonoptional = schema2;
|
552
|
-
const [, json] =
|
570
|
+
const [, json] = this.#convert(nonoptional._zod.def.innerType, options2, lazyDepth2, structureDepth);
|
553
571
|
return [true, json];
|
554
572
|
}
|
555
573
|
case "success": {
|
@@ -558,7 +576,7 @@ class experimental_ZodToJsonSchemaConverter {
|
|
558
576
|
case "default":
|
559
577
|
case "prefault": {
|
560
578
|
const default_ = schema2;
|
561
|
-
const [, json] =
|
579
|
+
const [, json] = this.#convert(default_._zod.def.innerType, options2, lazyDepth2, structureDepth);
|
562
580
|
return [false, {
|
563
581
|
...json,
|
564
582
|
default: default_._zod.def.defaultValue
|
@@ -566,18 +584,18 @@ class experimental_ZodToJsonSchemaConverter {
|
|
566
584
|
}
|
567
585
|
case "catch": {
|
568
586
|
const catch_ = schema2;
|
569
|
-
return
|
587
|
+
return this.#convert(catch_._zod.def.innerType, options2, lazyDepth2, structureDepth);
|
570
588
|
}
|
571
589
|
case "nan": {
|
572
590
|
return [true, options2.strategy === "input" ? this.unsupportedJsonSchema : { type: "null" }];
|
573
591
|
}
|
574
592
|
case "pipe": {
|
575
593
|
const pipe = schema2;
|
576
|
-
return
|
594
|
+
return this.#convert(options2.strategy === "input" ? pipe._zod.def.in : pipe._zod.def.out, options2, lazyDepth2, structureDepth);
|
577
595
|
}
|
578
596
|
case "readonly": {
|
579
597
|
const readonly_ = schema2;
|
580
|
-
const [required, json] =
|
598
|
+
const [required, json] = this.#convert(readonly_._zod.def.innerType, options2, lazyDepth2, structureDepth);
|
581
599
|
return [required, { ...json, readOnly: true }];
|
582
600
|
}
|
583
601
|
case "template_literal": {
|
@@ -589,15 +607,16 @@ class experimental_ZodToJsonSchemaConverter {
|
|
589
607
|
}
|
590
608
|
case "optional": {
|
591
609
|
const optional = schema2;
|
592
|
-
const [, json] =
|
610
|
+
const [, json] = this.#convert(optional._zod.def.innerType, options2, lazyDepth2, structureDepth);
|
593
611
|
return [false, json];
|
594
612
|
}
|
595
613
|
case "lazy": {
|
596
614
|
const lazy = schema2;
|
597
|
-
|
615
|
+
const currentLazyDepth = lazyDepth2 + 1;
|
616
|
+
if (currentLazyDepth > this.maxLazyDepth) {
|
598
617
|
return [false, this.anyJsonSchema];
|
599
618
|
}
|
600
|
-
return
|
619
|
+
return this.#convert(lazy._zod.def.getter(), options2, currentLazyDepth, structureDepth);
|
601
620
|
}
|
602
621
|
default: {
|
603
622
|
schema2._zod.def.type;
|
@@ -608,20 +627,21 @@ class experimental_ZodToJsonSchemaConverter {
|
|
608
627
|
);
|
609
628
|
}
|
610
629
|
#getCustomJsonSchema(schema, options) {
|
611
|
-
if (options.strategy === "input" &&
|
612
|
-
return
|
630
|
+
if (options.strategy === "input" && JSON_SCHEMA_INPUT_REGISTRY.has(schema)) {
|
631
|
+
return JSON_SCHEMA_INPUT_REGISTRY.get(schema);
|
613
632
|
}
|
614
|
-
if (options.strategy === "output" &&
|
615
|
-
return
|
633
|
+
if (options.strategy === "output" && JSON_SCHEMA_OUTPUT_REGISTRY.has(schema)) {
|
634
|
+
return JSON_SCHEMA_OUTPUT_REGISTRY.get(schema);
|
616
635
|
}
|
617
|
-
if (
|
618
|
-
return
|
636
|
+
if (JSON_SCHEMA_REGISTRY.has(schema)) {
|
637
|
+
return JSON_SCHEMA_REGISTRY.get(schema);
|
619
638
|
}
|
620
639
|
const global = globalRegistry.get(schema);
|
621
640
|
if (global) {
|
622
641
|
return {
|
642
|
+
title: global.title,
|
623
643
|
description: global.description,
|
624
|
-
examples: global.examples
|
644
|
+
examples: Array.isArray(global.examples) ? global.examples : void 0
|
625
645
|
};
|
626
646
|
}
|
627
647
|
}
|
@@ -657,4 +677,4 @@ class experimental_ZodToJsonSchemaConverter {
|
|
657
677
|
}
|
658
678
|
}
|
659
679
|
|
660
|
-
export {
|
680
|
+
export { JSON_SCHEMA_INPUT_REGISTRY, JSON_SCHEMA_OUTPUT_REGISTRY, JSON_SCHEMA_REGISTRY, ZodToJsonSchemaConverter, experimental_ZodSmartCoercionPlugin };
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@orpc/zod",
|
3
3
|
"type": "module",
|
4
|
-
"version": "0.0.0-next.
|
4
|
+
"version": "0.0.0-next.f50512c",
|
5
5
|
"license": "MIT",
|
6
6
|
"homepage": "https://orpc.unnoq.com",
|
7
7
|
"repository": {
|
@@ -29,19 +29,19 @@
|
|
29
29
|
"dist"
|
30
30
|
],
|
31
31
|
"peerDependencies": {
|
32
|
-
"zod": ">=3.
|
33
|
-
"@orpc/contract": "0.0.0-next.
|
34
|
-
"@orpc/server": "0.0.0-next.
|
32
|
+
"zod": ">=3.25.0",
|
33
|
+
"@orpc/contract": "0.0.0-next.f50512c",
|
34
|
+
"@orpc/server": "0.0.0-next.f50512c"
|
35
35
|
},
|
36
36
|
"dependencies": {
|
37
37
|
"escape-string-regexp": "^5.0.0",
|
38
38
|
"wildcard-match": "^5.1.3",
|
39
|
-
"@orpc/
|
40
|
-
"@orpc/
|
39
|
+
"@orpc/json-schema": "0.0.0-next.f50512c",
|
40
|
+
"@orpc/openapi": "0.0.0-next.f50512c",
|
41
|
+
"@orpc/shared": "0.0.0-next.f50512c"
|
41
42
|
},
|
42
43
|
"devDependencies": {
|
43
|
-
"zod": "
|
44
|
-
"zod-to-json-schema": "^3.24.5"
|
44
|
+
"zod": "^4.0.5"
|
45
45
|
},
|
46
46
|
"scripts": {
|
47
47
|
"build": "unbuild",
|