@orpc/zod 0.0.0-next.1431467 → 0.0.0-next.14dd190

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/dist/index.mjs CHANGED
@@ -1,17 +1,18 @@
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
 
7
8
  const CUSTOM_JSON_SCHEMA_SYMBOL = Symbol("ORPC_CUSTOM_JSON_SCHEMA");
8
9
  const CUSTOM_JSON_SCHEMA_INPUT_SYMBOL = Symbol("ORPC_CUSTOM_JSON_SCHEMA_INPUT");
9
10
  const CUSTOM_JSON_SCHEMA_OUTPUT_SYMBOL = Symbol("ORPC_CUSTOM_JSON_SCHEMA_OUTPUT");
10
- function getCustomJsonSchema(def, strategy) {
11
- if (strategy === "input" && CUSTOM_JSON_SCHEMA_INPUT_SYMBOL in def) {
11
+ function getCustomJsonSchema(def, options) {
12
+ if (options.strategy === "input" && CUSTOM_JSON_SCHEMA_INPUT_SYMBOL in def) {
12
13
  return def[CUSTOM_JSON_SCHEMA_INPUT_SYMBOL];
13
14
  }
14
- if (strategy === "output" && CUSTOM_JSON_SCHEMA_OUTPUT_SYMBOL in def) {
15
+ if (options.strategy === "output" && CUSTOM_JSON_SCHEMA_OUTPUT_SYMBOL in def) {
15
16
  return def[CUSTOM_JSON_SCHEMA_OUTPUT_SYMBOL];
16
17
  }
17
18
  if (CUSTOM_JSON_SCHEMA_SYMBOL in def) {
@@ -19,8 +20,8 @@ function getCustomJsonSchema(def, strategy) {
19
20
  }
20
21
  return void 0;
21
22
  }
22
- function customJsonSchema(schema, custom, strategy) {
23
- const SYMBOL = strategy === "input" ? CUSTOM_JSON_SCHEMA_INPUT_SYMBOL : strategy === "output" ? CUSTOM_JSON_SCHEMA_OUTPUT_SYMBOL : CUSTOM_JSON_SCHEMA_SYMBOL;
23
+ function customJsonSchema(schema, custom, options = {}) {
24
+ const SYMBOL = options.strategy === "input" ? CUSTOM_JSON_SCHEMA_INPUT_SYMBOL : options.strategy === "output" ? CUSTOM_JSON_SCHEMA_OUTPUT_SYMBOL : CUSTOM_JSON_SCHEMA_SYMBOL;
24
25
  const This = schema.constructor;
25
26
  const newSchema = new This({
26
27
  ...schema._def,
@@ -128,7 +129,7 @@ class ZodSmartCoercionPlugin {
128
129
  options.clientInterceptors ??= [];
129
130
  options.clientInterceptors.unshift((options2) => {
130
131
  const inputSchema = options2.procedure["~orpc"].inputSchema;
131
- if (!inputSchema || inputSchema["~standard"].vendor !== "zod") {
132
+ if (!inputSchema || inputSchema["~standard"].vendor !== "zod" || "_zod" in inputSchema) {
132
133
  return options2.next();
133
134
  }
134
135
  const coercedInput = zodCoerceInternal(inputSchema, options2.input);
@@ -390,46 +391,61 @@ 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
- return Boolean(schema && schema["~standard"].vendor === "zod");
404
+ return schema !== void 0 && schema["~standard"].vendor === "zod" && !("_zod" in schema);
402
405
  }
403
- convert(schema, strategy, 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
- strategy,
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
  }
415
430
  if (!isHandledCustomJSONSchema) {
416
- const customJSONSchema = getCustomJsonSchema(def, strategy);
431
+ const customJSONSchema = getCustomJsonSchema(def, options);
417
432
  if (customJSONSchema) {
418
433
  const [required, json] = this.convert(
419
434
  schema,
420
- strategy,
435
+ options,
421
436
  lazyDepth,
422
437
  true,
423
- isHandledZodDescription
438
+ isHandledZodDescription,
439
+ structureDepth
424
440
  );
425
441
  return [required, { ...json, ...customJSONSchema }];
426
442
  }
427
443
  }
428
- const customSchema = this.handleCustomZodDef(def);
444
+ const customSchema = this.#handleCustomZodDef(def);
429
445
  if (customSchema) {
430
446
  return [true, customSchema];
431
447
  }
432
- const typeName = this.getZodTypeName(def);
448
+ const typeName = this.#getZodTypeName(def);
433
449
  switch (typeName) {
434
450
  case ZodFirstPartyTypeKind.ZodString: {
435
451
  const schema_ = schema;
@@ -548,17 +564,25 @@ class ZodToJsonSchemaConverter {
548
564
  return [true, json];
549
565
  }
550
566
  case ZodFirstPartyTypeKind.ZodBigInt: {
551
- const json = { type: "string", pattern: "^-?[0-9]+$" };
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: {
555
- return strategy === "input" ? [true, this.unsupportedJsonSchema] : [true, { type: "null" }];
575
+ return options.strategy === "input" ? [true, this.unsupportedJsonSchema] : [true, { type: "null" }];
556
576
  }
557
577
  case ZodFirstPartyTypeKind.ZodBoolean: {
558
578
  return [true, { type: "boolean" }];
559
579
  }
560
580
  case ZodFirstPartyTypeKind.ZodDate: {
561
- const schema2 = { type: "string", format: JSONSchemaFormat.DateTime };
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,8 +615,8 @@ 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, strategy, lazyDepth, false, false);
595
- json.items = this.toArrayItemJsonSchema(itemRequired, itemJson, strategy);
618
+ const [itemRequired, itemJson] = this.convert(def2.type, options, lazyDepth, false, false, structureDepth + 1);
619
+ json.items = this.#toArrayItemJsonSchema(itemRequired, itemJson, options.strategy);
596
620
  if (def2.exactLength) {
597
621
  json.maxItems = def2.exactLength.value;
598
622
  json.minItems = def2.exactLength.value;
@@ -610,17 +634,17 @@ 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, strategy, lazyDepth, false, false);
637
+ const [itemRequired, itemJson] = this.convert(item, options, lazyDepth, false, false, structureDepth + 1);
614
638
  prefixItems.push(
615
- this.toArrayItemJsonSchema(itemRequired, itemJson, strategy)
639
+ this.#toArrayItemJsonSchema(itemRequired, itemJson, options.strategy)
616
640
  );
617
641
  }
618
642
  if (prefixItems?.length) {
619
643
  json.prefixItems = prefixItems;
620
644
  }
621
645
  if (schema_._def.rest) {
622
- const [itemRequired, itemJson] = this.convert(schema_._def.rest, strategy, lazyDepth, false, false);
623
- json.items = this.toArrayItemJsonSchema(itemRequired, itemJson, strategy);
646
+ const [itemRequired, itemJson] = this.convert(schema_._def.rest, options, lazyDepth, false, false, structureDepth + 1);
647
+ json.items = this.#toArrayItemJsonSchema(itemRequired, itemJson, options.strategy);
624
648
  }
625
649
  return [true, json];
626
650
  }
@@ -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, strategy, 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);
@@ -642,13 +666,13 @@ class ZodToJsonSchemaConverter {
642
666
  if (required.length) {
643
667
  json.required = required;
644
668
  }
645
- const catchAllTypeName = this.getZodTypeName(schema_._def.catchall._def);
669
+ const catchAllTypeName = this.#getZodTypeName(schema_._def.catchall._def);
646
670
  if (catchAllTypeName === ZodFirstPartyTypeKind.ZodNever) {
647
671
  if (schema_._def.unknownKeys === "strict") {
648
672
  json.additionalProperties = false;
649
673
  }
650
674
  } else {
651
- const [_, addJson] = this.convert(schema_._def.catchall, strategy, 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,33 +680,43 @@ class ZodToJsonSchemaConverter {
656
680
  case ZodFirstPartyTypeKind.ZodRecord: {
657
681
  const schema_ = schema;
658
682
  const json = { type: "object" };
659
- const [_, itemJson] = this.convert(schema_._def.valueType, strategy, lazyDepth, false, false);
683
+ const [__, keyJson] = this.convert(schema_._def.keyType, options, lazyDepth, false, false, structureDepth + 1);
684
+ if (Object.entries(keyJson).some(([k, v]) => k !== "type" || v !== "string")) {
685
+ json.propertyNames = keyJson;
686
+ }
687
+ const [_, itemJson] = this.convert(schema_._def.valueType, options, lazyDepth, false, false, structureDepth + 1);
660
688
  json.additionalProperties = itemJson;
661
689
  return [true, json];
662
690
  }
663
691
  case ZodFirstPartyTypeKind.ZodSet: {
664
692
  const schema_ = schema;
665
- const json = { type: "array", uniqueItems: true };
666
- const [itemRequired, itemJson] = this.convert(schema_._def.valueType, strategy, lazyDepth, false, false);
667
- json.items = this.toArrayItemJsonSchema(itemRequired, itemJson, strategy);
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);
699
+ json.items = this.#toArrayItemJsonSchema(itemRequired, itemJson, options.strategy);
668
700
  return [true, json];
669
701
  }
670
702
  case ZodFirstPartyTypeKind.ZodMap: {
671
703
  const schema_ = schema;
672
- const [keyRequired, keyJson] = this.convert(schema_._def.keyType, strategy, lazyDepth, false, false);
673
- const [valueRequired, valueJson] = this.convert(schema_._def.valueType, strategy, lazyDepth, false, false);
674
- return [true, {
675
- type: "array",
676
- 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": {
677
709
  type: "array",
678
710
  prefixItems: [
679
- this.toArrayItemJsonSchema(keyRequired, keyJson, strategy),
680
- this.toArrayItemJsonSchema(valueRequired, valueJson, strategy)
711
+ this.#toArrayItemJsonSchema(keyRequired, keyJson, options.strategy),
712
+ this.#toArrayItemJsonSchema(valueRequired, valueJson, options.strategy)
681
713
  ],
682
714
  maxItems: 2,
683
715
  minItems: 2
684
- }
685
- }];
716
+ },
717
+ "x-native-type": JsonSchemaXNativeType.Map
718
+ };
719
+ return [true, json];
686
720
  }
687
721
  case ZodFirstPartyTypeKind.ZodUnion:
688
722
  case ZodFirstPartyTypeKind.ZodDiscriminatedUnion: {
@@ -690,7 +724,7 @@ class ZodToJsonSchemaConverter {
690
724
  const anyOf = [];
691
725
  let required = true;
692
726
  for (const item of schema_._def.options) {
693
- const [itemRequired, itemJson] = this.convert(item, strategy, lazyDepth, false, false);
727
+ const [itemRequired, itemJson] = this.convert(item, options, lazyDepth, false, false, structureDepth);
694
728
  if (!itemRequired) {
695
729
  required = false;
696
730
  if (itemJson !== this.unsupportedJsonSchema) {
@@ -710,7 +744,7 @@ class ZodToJsonSchemaConverter {
710
744
  const allOf = [];
711
745
  let required = false;
712
746
  for (const item of [schema_._def.left, schema_._def.right]) {
713
- const [itemRequired, itemJson] = this.convert(item, strategy, lazyDepth, false, false);
747
+ const [itemRequired, itemJson] = this.convert(item, options, lazyDepth, false, false, structureDepth);
714
748
  allOf.push(itemJson);
715
749
  if (itemRequired) {
716
750
  required = true;
@@ -719,60 +753,63 @@ class ZodToJsonSchemaConverter {
719
753
  return [required, { allOf }];
720
754
  }
721
755
  case ZodFirstPartyTypeKind.ZodLazy: {
722
- if (lazyDepth >= this.maxLazyDepth) {
756
+ const currentLazyDepth = lazyDepth + 1;
757
+ if (currentLazyDepth > this.maxLazyDepth) {
723
758
  return [false, this.anyJsonSchema];
724
759
  }
725
760
  const schema_ = schema;
726
- return this.convert(schema_._def.getter(), strategy, lazyDepth + 1, false, false);
761
+ return this.convert(schema_._def.getter(), options, currentLazyDepth, false, false, structureDepth);
727
762
  }
728
763
  case ZodFirstPartyTypeKind.ZodOptional: {
729
764
  const schema_ = schema;
730
- const [_, inner] = this.convert(schema_._def.innerType, strategy, lazyDepth, false, false);
765
+ const [_, inner] = this.convert(schema_._def.innerType, options, lazyDepth, false, false, structureDepth);
731
766
  return [false, inner];
732
767
  }
733
768
  case ZodFirstPartyTypeKind.ZodReadonly: {
734
769
  const schema_ = schema;
735
- return this.convert(schema_._def.innerType, strategy, lazyDepth, false, false);
770
+ const [required, json] = this.convert(schema_._def.innerType, options, lazyDepth, false, false, structureDepth);
771
+ return [required, { ...json, readOnly: true }];
736
772
  }
737
773
  case ZodFirstPartyTypeKind.ZodDefault: {
738
774
  const schema_ = schema;
739
- const [_, json] = this.convert(schema_._def.innerType, strategy, lazyDepth, false, false);
775
+ const [_, json] = this.convert(schema_._def.innerType, options, lazyDepth, false, false, structureDepth);
740
776
  return [false, { default: schema_._def.defaultValue(), ...json }];
741
777
  }
742
778
  case ZodFirstPartyTypeKind.ZodEffects: {
743
779
  const schema_ = schema;
744
- if (schema_._def.effect.type === "transform" && strategy === "output") {
780
+ if (schema_._def.effect.type === "transform" && options.strategy === "output") {
745
781
  return [false, this.anyJsonSchema];
746
782
  }
747
- return this.convert(schema_._def.schema, strategy, lazyDepth, false, false);
783
+ return this.convert(schema_._def.schema, options, lazyDepth, false, false, structureDepth);
748
784
  }
749
785
  case ZodFirstPartyTypeKind.ZodCatch: {
750
786
  const schema_ = schema;
751
- return this.convert(schema_._def.innerType, strategy, lazyDepth, false, false);
787
+ return this.convert(schema_._def.innerType, options, lazyDepth, false, false, structureDepth);
752
788
  }
753
789
  case ZodFirstPartyTypeKind.ZodBranded: {
754
790
  const schema_ = schema;
755
- return this.convert(schema_._def.type, strategy, lazyDepth, false, false);
791
+ return this.convert(schema_._def.type, options, lazyDepth, false, false, structureDepth);
756
792
  }
757
793
  case ZodFirstPartyTypeKind.ZodPipeline: {
758
794
  const schema_ = schema;
759
795
  return this.convert(
760
- strategy === "input" ? schema_._def.in : schema_._def.out,
761
- strategy,
796
+ options.strategy === "input" ? schema_._def.in : schema_._def.out,
797
+ options,
762
798
  lazyDepth,
763
799
  false,
764
- false
800
+ false,
801
+ structureDepth
765
802
  );
766
803
  }
767
804
  case ZodFirstPartyTypeKind.ZodNullable: {
768
805
  const schema_ = schema;
769
- const [required, json] = this.convert(schema_._def.innerType, strategy, lazyDepth, false, false);
770
- return [required, { anyOf: [{ type: "null" }, json] }];
806
+ const [required, json] = this.convert(schema_._def.innerType, options, lazyDepth, false, false, structureDepth);
807
+ return [required, { anyOf: [json, { type: "null" }] }];
771
808
  }
772
809
  }
773
810
  return [true, this.unsupportedJsonSchema];
774
811
  }
775
- handleCustomZodDef(def) {
812
+ #handleCustomZodDef(def) {
776
813
  const customZodDef = getCustomZodDef(def);
777
814
  if (!customZodDef) {
778
815
  return void 0;
@@ -786,19 +823,24 @@ class ZodToJsonSchemaConverter {
786
823
  }
787
824
  case "regexp": {
788
825
  return {
789
- type: "string",
790
- pattern: "^\\/(.*)\\/([a-z]*)$"
826
+ "type": "string",
827
+ "pattern": "^\\/(.*)\\/([a-z]*)$",
828
+ "x-native-type": JsonSchemaXNativeType.RegExp
791
829
  };
792
830
  }
793
831
  case "url": {
794
- return { type: "string", format: JSONSchemaFormat.URI };
832
+ return {
833
+ "type": "string",
834
+ "format": JSONSchemaFormat.URI,
835
+ "x-native-type": JsonSchemaXNativeType.Url
836
+ };
795
837
  }
796
838
  }
797
839
  }
798
- getZodTypeName(def) {
840
+ #getZodTypeName(def) {
799
841
  return def.typeName;
800
842
  }
801
- toArrayItemJsonSchema(required, schema, strategy) {
843
+ #toArrayItemJsonSchema(required, schema, strategy) {
802
844
  if (required) {
803
845
  return schema;
804
846
  }