@orpc/zod 0.0.0-next.9588d75 → 0.0.0-next.9914009

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.js CHANGED
@@ -340,6 +340,22 @@ function zodCoerceInternal(schema, value, options) {
340
340
  return value;
341
341
  }
342
342
 
343
+ // src/converter.ts
344
+ import { JSONSchemaFormat } from "@orpc/openapi";
345
+
346
+ // ../../node_modules/.pnpm/escape-string-regexp@5.0.0/node_modules/escape-string-regexp/index.js
347
+ function escapeStringRegexp(string) {
348
+ if (typeof string !== "string") {
349
+ throw new TypeError("Expected a string");
350
+ }
351
+ return string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d");
352
+ }
353
+
354
+ // src/converter.ts
355
+ import {
356
+ ZodFirstPartyTypeKind as ZodFirstPartyTypeKind2
357
+ } from "zod";
358
+
343
359
  // src/schemas.ts
344
360
  import wcmatch from "wildcard-match";
345
361
  import {
@@ -475,8 +491,473 @@ var oz = {
475
491
  regexp,
476
492
  url
477
493
  };
494
+
495
+ // src/converter.ts
496
+ var NON_LOGIC_KEYWORDS = [
497
+ // Core Documentation Keywords
498
+ "$anchor",
499
+ "$comment",
500
+ "$defs",
501
+ "$id",
502
+ "title",
503
+ "description",
504
+ // Value Keywords
505
+ "default",
506
+ "deprecated",
507
+ "examples",
508
+ // Metadata Keywords
509
+ "$schema",
510
+ "definitions",
511
+ // Legacy, but still used
512
+ "readOnly",
513
+ "writeOnly",
514
+ // Display and UI Hints
515
+ "contentMediaType",
516
+ "contentEncoding",
517
+ "format",
518
+ // Custom Extensions
519
+ "$vocabulary",
520
+ "$dynamicAnchor",
521
+ "$dynamicRef"
522
+ ];
523
+ var UNSUPPORTED_JSON_SCHEMA = { not: {} };
524
+ var UNDEFINED_JSON_SCHEMA = { const: "undefined" };
525
+ function zodToJsonSchema(schema, options) {
526
+ if (schema["~standard"].vendor !== "zod") {
527
+ console.warn(`Generate JSON schema not support ${schema["~standard"].vendor} yet`);
528
+ return {};
529
+ }
530
+ const schema__ = schema;
531
+ if (!options?.isHandledZodDescription && "description" in schema__._def) {
532
+ const json = zodToJsonSchema(schema__, {
533
+ ...options,
534
+ isHandledZodDescription: true
535
+ });
536
+ return {
537
+ description: schema__._def.description,
538
+ ...json
539
+ };
540
+ }
541
+ if (!options?.isHandledCustomJSONSchema) {
542
+ const customJSONSchema = getCustomJSONSchema(schema__._def, options);
543
+ if (customJSONSchema) {
544
+ const json = zodToJsonSchema(schema__, {
545
+ ...options,
546
+ isHandledCustomJSONSchema: true
547
+ });
548
+ return {
549
+ ...json,
550
+ ...customJSONSchema
551
+ };
552
+ }
553
+ }
554
+ const childOptions = { ...options, isHandledCustomJSONSchema: false, isHandledZodDescription: false };
555
+ const customType = getCustomZodType(schema__._def);
556
+ switch (customType) {
557
+ case "Blob": {
558
+ return { type: "string", contentMediaType: "*/*" };
559
+ }
560
+ case "File": {
561
+ const mimeType = getCustomZodFileMimeType(schema__._def) ?? "*/*";
562
+ return { type: "string", contentMediaType: mimeType };
563
+ }
564
+ case "Invalid Date": {
565
+ return { const: "Invalid Date" };
566
+ }
567
+ case "RegExp": {
568
+ return {
569
+ type: "string",
570
+ pattern: "^\\/(.*)\\/([a-z]*)$"
571
+ };
572
+ }
573
+ case "URL": {
574
+ return { type: "string", format: JSONSchemaFormat.URI };
575
+ }
576
+ }
577
+ const _expectedCustomType = customType;
578
+ const typeName = schema__._def.typeName;
579
+ switch (typeName) {
580
+ case ZodFirstPartyTypeKind2.ZodString: {
581
+ const schema_ = schema__;
582
+ const json = { type: "string" };
583
+ for (const check of schema_._def.checks) {
584
+ switch (check.kind) {
585
+ case "base64":
586
+ json.contentEncoding = "base64";
587
+ break;
588
+ case "cuid":
589
+ json.pattern = "^[0-9A-HJKMNP-TV-Z]{26}$";
590
+ break;
591
+ case "email":
592
+ json.format = JSONSchemaFormat.Email;
593
+ break;
594
+ case "url":
595
+ json.format = JSONSchemaFormat.URI;
596
+ break;
597
+ case "uuid":
598
+ json.format = JSONSchemaFormat.UUID;
599
+ break;
600
+ case "regex":
601
+ json.pattern = check.regex.source;
602
+ break;
603
+ case "min":
604
+ json.minLength = check.value;
605
+ break;
606
+ case "max":
607
+ json.maxLength = check.value;
608
+ break;
609
+ case "length":
610
+ json.minLength = check.value;
611
+ json.maxLength = check.value;
612
+ break;
613
+ case "includes":
614
+ json.pattern = escapeStringRegexp(check.value);
615
+ break;
616
+ case "startsWith":
617
+ json.pattern = `^${escapeStringRegexp(check.value)}`;
618
+ break;
619
+ case "endsWith":
620
+ json.pattern = `${escapeStringRegexp(check.value)}$`;
621
+ break;
622
+ case "emoji":
623
+ json.pattern = "^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$";
624
+ break;
625
+ case "nanoid":
626
+ json.pattern = "^[a-zA-Z0-9_-]{21}$";
627
+ break;
628
+ case "cuid2":
629
+ json.pattern = "^[0-9a-z]+$";
630
+ break;
631
+ case "ulid":
632
+ json.pattern = "^[0-9A-HJKMNP-TV-Z]{26}$";
633
+ break;
634
+ case "datetime":
635
+ json.format = JSONSchemaFormat.DateTime;
636
+ break;
637
+ case "date":
638
+ json.format = JSONSchemaFormat.Date;
639
+ break;
640
+ case "time":
641
+ json.format = JSONSchemaFormat.Time;
642
+ break;
643
+ case "duration":
644
+ json.format = JSONSchemaFormat.Duration;
645
+ break;
646
+ case "ip":
647
+ json.format = JSONSchemaFormat.IPv4;
648
+ break;
649
+ case "jwt":
650
+ json.pattern = "^[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]+\\.[A-Za-z0-9-_]*$";
651
+ break;
652
+ case "base64url":
653
+ json.pattern = "^([0-9a-zA-Z-_]{4})*(([0-9a-zA-Z-_]{2}(==)?)|([0-9a-zA-Z-_]{3}(=)?))?$";
654
+ break;
655
+ default: {
656
+ const _expect = check.kind;
657
+ }
658
+ }
659
+ }
660
+ return json;
661
+ }
662
+ case ZodFirstPartyTypeKind2.ZodNumber: {
663
+ const schema_ = schema__;
664
+ const json = { type: "number" };
665
+ for (const check of schema_._def.checks) {
666
+ switch (check.kind) {
667
+ case "int":
668
+ json.type = "integer";
669
+ break;
670
+ case "min":
671
+ json.minimum = check.value;
672
+ break;
673
+ case "max":
674
+ json.maximum = check.value;
675
+ break;
676
+ case "multipleOf":
677
+ json.multipleOf = check.value;
678
+ break;
679
+ default: {
680
+ const _expect = check.kind;
681
+ }
682
+ }
683
+ }
684
+ return json;
685
+ }
686
+ case ZodFirstPartyTypeKind2.ZodNaN: {
687
+ return { const: "NaN" };
688
+ }
689
+ case ZodFirstPartyTypeKind2.ZodBigInt: {
690
+ const json = { type: "string", pattern: "^-?[0-9]+$" };
691
+ return json;
692
+ }
693
+ case ZodFirstPartyTypeKind2.ZodBoolean: {
694
+ return { type: "boolean" };
695
+ }
696
+ case ZodFirstPartyTypeKind2.ZodDate: {
697
+ const schema2 = { type: "string", format: JSONSchemaFormat.Date };
698
+ return schema2;
699
+ }
700
+ case ZodFirstPartyTypeKind2.ZodNull: {
701
+ return { type: "null" };
702
+ }
703
+ case ZodFirstPartyTypeKind2.ZodVoid:
704
+ case ZodFirstPartyTypeKind2.ZodUndefined: {
705
+ return UNDEFINED_JSON_SCHEMA;
706
+ }
707
+ case ZodFirstPartyTypeKind2.ZodLiteral: {
708
+ const schema_ = schema__;
709
+ return { const: schema_._def.value };
710
+ }
711
+ case ZodFirstPartyTypeKind2.ZodEnum: {
712
+ const schema_ = schema__;
713
+ return {
714
+ enum: schema_._def.values
715
+ };
716
+ }
717
+ case ZodFirstPartyTypeKind2.ZodNativeEnum: {
718
+ const schema_ = schema__;
719
+ return {
720
+ enum: Object.values(schema_._def.values)
721
+ };
722
+ }
723
+ case ZodFirstPartyTypeKind2.ZodArray: {
724
+ const schema_ = schema__;
725
+ const def = schema_._def;
726
+ const json = { type: "array" };
727
+ if (def.exactLength) {
728
+ json.maxItems = def.exactLength.value;
729
+ json.minItems = def.exactLength.value;
730
+ }
731
+ if (def.minLength) {
732
+ json.minItems = def.minLength.value;
733
+ }
734
+ if (def.maxLength) {
735
+ json.maxItems = def.maxLength.value;
736
+ }
737
+ return json;
738
+ }
739
+ case ZodFirstPartyTypeKind2.ZodTuple: {
740
+ const schema_ = schema__;
741
+ const prefixItems = [];
742
+ const json = { type: "array" };
743
+ for (const item of schema_._def.items) {
744
+ prefixItems.push(zodToJsonSchema(item, childOptions));
745
+ }
746
+ if (prefixItems?.length) {
747
+ json.prefixItems = prefixItems;
748
+ }
749
+ if (schema_._def.rest) {
750
+ const items = zodToJsonSchema(schema_._def.rest, childOptions);
751
+ if (items) {
752
+ json.items = items;
753
+ }
754
+ }
755
+ return json;
756
+ }
757
+ case ZodFirstPartyTypeKind2.ZodObject: {
758
+ const schema_ = schema__;
759
+ const json = { type: "object" };
760
+ const properties = {};
761
+ const required = [];
762
+ for (const [key, value] of Object.entries(schema_.shape)) {
763
+ const { schema: schema2, matches } = extractJSONSchema(
764
+ zodToJsonSchema(value, childOptions),
765
+ (schema3) => schema3 === UNDEFINED_JSON_SCHEMA
766
+ );
767
+ if (schema2) {
768
+ properties[key] = schema2;
769
+ }
770
+ if (matches.length === 0) {
771
+ required.push(key);
772
+ }
773
+ }
774
+ if (Object.keys(properties).length) {
775
+ json.properties = properties;
776
+ }
777
+ if (required.length) {
778
+ json.required = required;
779
+ }
780
+ const additionalProperties = zodToJsonSchema(
781
+ schema_._def.catchall,
782
+ childOptions
783
+ );
784
+ if (schema_._def.unknownKeys === "strict") {
785
+ json.additionalProperties = additionalProperties === UNSUPPORTED_JSON_SCHEMA ? false : additionalProperties;
786
+ } else {
787
+ if (additionalProperties && additionalProperties !== UNSUPPORTED_JSON_SCHEMA) {
788
+ json.additionalProperties = additionalProperties;
789
+ }
790
+ }
791
+ return json;
792
+ }
793
+ case ZodFirstPartyTypeKind2.ZodRecord: {
794
+ const schema_ = schema__;
795
+ const json = { type: "object" };
796
+ json.additionalProperties = zodToJsonSchema(
797
+ schema_._def.valueType,
798
+ childOptions
799
+ );
800
+ return json;
801
+ }
802
+ case ZodFirstPartyTypeKind2.ZodSet: {
803
+ const schema_ = schema__;
804
+ return {
805
+ type: "array",
806
+ items: zodToJsonSchema(schema_._def.valueType, childOptions)
807
+ };
808
+ }
809
+ case ZodFirstPartyTypeKind2.ZodMap: {
810
+ const schema_ = schema__;
811
+ return {
812
+ type: "array",
813
+ items: {
814
+ type: "array",
815
+ prefixItems: [
816
+ zodToJsonSchema(schema_._def.keyType, childOptions),
817
+ zodToJsonSchema(schema_._def.valueType, childOptions)
818
+ ],
819
+ maxItems: 2,
820
+ minItems: 2
821
+ }
822
+ };
823
+ }
824
+ case ZodFirstPartyTypeKind2.ZodUnion:
825
+ case ZodFirstPartyTypeKind2.ZodDiscriminatedUnion: {
826
+ const schema_ = schema__;
827
+ const anyOf = [];
828
+ for (const s of schema_._def.options) {
829
+ anyOf.push(zodToJsonSchema(s, childOptions));
830
+ }
831
+ return { anyOf };
832
+ }
833
+ case ZodFirstPartyTypeKind2.ZodIntersection: {
834
+ const schema_ = schema__;
835
+ const allOf = [];
836
+ for (const s of [schema_._def.left, schema_._def.right]) {
837
+ allOf.push(zodToJsonSchema(s, childOptions));
838
+ }
839
+ return { allOf };
840
+ }
841
+ case ZodFirstPartyTypeKind2.ZodLazy: {
842
+ const schema_ = schema__;
843
+ const maxLazyDepth = childOptions?.maxLazyDepth ?? 5;
844
+ const lazyDepth = childOptions?.lazyDepth ?? 0;
845
+ if (lazyDepth > maxLazyDepth) {
846
+ return {};
847
+ }
848
+ return zodToJsonSchema(schema_._def.getter(), {
849
+ ...childOptions,
850
+ lazyDepth: lazyDepth + 1
851
+ });
852
+ }
853
+ case ZodFirstPartyTypeKind2.ZodUnknown:
854
+ case ZodFirstPartyTypeKind2.ZodAny:
855
+ case void 0: {
856
+ return {};
857
+ }
858
+ case ZodFirstPartyTypeKind2.ZodOptional: {
859
+ const schema_ = schema__;
860
+ const inner = zodToJsonSchema(schema_._def.innerType, childOptions);
861
+ return {
862
+ anyOf: [UNDEFINED_JSON_SCHEMA, inner]
863
+ };
864
+ }
865
+ case ZodFirstPartyTypeKind2.ZodReadonly: {
866
+ const schema_ = schema__;
867
+ return zodToJsonSchema(schema_._def.innerType, childOptions);
868
+ }
869
+ case ZodFirstPartyTypeKind2.ZodDefault: {
870
+ const schema_ = schema__;
871
+ return zodToJsonSchema(schema_._def.innerType, childOptions);
872
+ }
873
+ case ZodFirstPartyTypeKind2.ZodEffects: {
874
+ const schema_ = schema__;
875
+ if (schema_._def.effect.type === "transform" && childOptions?.mode === "output") {
876
+ return {};
877
+ }
878
+ return zodToJsonSchema(schema_._def.schema, childOptions);
879
+ }
880
+ case ZodFirstPartyTypeKind2.ZodCatch: {
881
+ const schema_ = schema__;
882
+ return zodToJsonSchema(schema_._def.innerType, childOptions);
883
+ }
884
+ case ZodFirstPartyTypeKind2.ZodBranded: {
885
+ const schema_ = schema__;
886
+ return zodToJsonSchema(schema_._def.type, childOptions);
887
+ }
888
+ case ZodFirstPartyTypeKind2.ZodPipeline: {
889
+ const schema_ = schema__;
890
+ return zodToJsonSchema(
891
+ childOptions?.mode === "output" ? schema_._def.out : schema_._def.in,
892
+ childOptions
893
+ );
894
+ }
895
+ case ZodFirstPartyTypeKind2.ZodNullable: {
896
+ const schema_ = schema__;
897
+ const inner = zodToJsonSchema(schema_._def.innerType, childOptions);
898
+ return {
899
+ anyOf: [{ type: "null" }, inner]
900
+ };
901
+ }
902
+ }
903
+ const _expected = typeName;
904
+ return UNSUPPORTED_JSON_SCHEMA;
905
+ }
906
+ function extractJSONSchema(schema, check, matches = []) {
907
+ if (check(schema)) {
908
+ matches.push(schema);
909
+ return { schema: void 0, matches };
910
+ }
911
+ if (typeof schema === "boolean") {
912
+ return { schema, matches };
913
+ }
914
+ if (schema.anyOf && Object.keys(schema).every(
915
+ (k) => k === "anyOf" || NON_LOGIC_KEYWORDS.includes(k)
916
+ )) {
917
+ const anyOf = schema.anyOf.map((s) => extractJSONSchema(s, check, matches).schema).filter((v) => !!v);
918
+ if (anyOf.length === 1 && typeof anyOf[0] === "object") {
919
+ return { schema: { ...schema, anyOf: void 0, ...anyOf[0] }, matches };
920
+ }
921
+ return {
922
+ schema: {
923
+ ...schema,
924
+ anyOf
925
+ },
926
+ matches
927
+ };
928
+ }
929
+ if (schema.oneOf && Object.keys(schema).every(
930
+ (k) => k === "oneOf" || NON_LOGIC_KEYWORDS.includes(k)
931
+ )) {
932
+ const oneOf = schema.oneOf.map((s) => extractJSONSchema(s, check, matches).schema).filter((v) => !!v);
933
+ if (oneOf.length === 1 && typeof oneOf[0] === "object") {
934
+ return { schema: { ...schema, oneOf: void 0, ...oneOf[0] }, matches };
935
+ }
936
+ return {
937
+ schema: {
938
+ ...schema,
939
+ oneOf
940
+ },
941
+ matches
942
+ };
943
+ }
944
+ return { schema, matches };
945
+ }
946
+ var ZodToJsonSchemaConverter = class {
947
+ condition(schema) {
948
+ return Boolean(schema && schema["~standard"].vendor === "zod");
949
+ }
950
+ convert(schema, options) {
951
+ const jsonSchema = schema;
952
+ return zodToJsonSchema(jsonSchema, { mode: options.strategy });
953
+ }
954
+ };
478
955
  export {
956
+ NON_LOGIC_KEYWORDS,
957
+ UNDEFINED_JSON_SCHEMA,
958
+ UNSUPPORTED_JSON_SCHEMA,
479
959
  ZodCoercer,
960
+ ZodToJsonSchemaConverter,
480
961
  blob,
481
962
  file,
482
963
  getCustomJSONSchema,
@@ -486,6 +967,7 @@ export {
486
967
  openapi,
487
968
  oz,
488
969
  regexp,
489
- url
970
+ url,
971
+ zodToJsonSchema
490
972
  };
491
973
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,50 @@
1
+ import type { Schema } from '@orpc/contract';
2
+ import type { JSONSchema, SchemaConverter, SchemaConvertOptions } from '@orpc/openapi';
3
+ import type { StandardSchemaV1 } from '@standard-schema/spec';
4
+ export declare const NON_LOGIC_KEYWORDS: ("$anchor" | "$comment" | "$defs" | "$dynamicAnchor" | "$dynamicRef" | "$id" | "$schema" | "$vocabulary" | "contentEncoding" | "contentMediaType" | "default" | "definitions" | "deprecated" | "description" | "examples" | "format" | "readOnly" | "title" | "writeOnly")[];
5
+ export declare const UNSUPPORTED_JSON_SCHEMA: {
6
+ not: {};
7
+ };
8
+ export declare const UNDEFINED_JSON_SCHEMA: {
9
+ const: string;
10
+ };
11
+ export interface ZodToJsonSchemaOptions {
12
+ /**
13
+ * Max depth of lazy type, if it exceeds.
14
+ *
15
+ * Used `{}` when reach max depth
16
+ *
17
+ * @default 5
18
+ */
19
+ maxLazyDepth?: number;
20
+ /**
21
+ * The length used to track the depth of lazy type
22
+ *
23
+ * @internal
24
+ */
25
+ lazyDepth?: number;
26
+ /**
27
+ * The expected json schema for input or output zod schema
28
+ *
29
+ * @default input
30
+ */
31
+ mode?: 'input' | 'output';
32
+ /**
33
+ * Track if current level schema is handled custom json schema to prevent recursive
34
+ *
35
+ * @internal
36
+ */
37
+ isHandledCustomJSONSchema?: boolean;
38
+ /**
39
+ * Track if current level schema is handled zod description to prevent recursive
40
+ *
41
+ * @internal
42
+ */
43
+ isHandledZodDescription?: boolean;
44
+ }
45
+ export declare function zodToJsonSchema(schema: StandardSchemaV1, options?: ZodToJsonSchemaOptions): Exclude<JSONSchema.JSONSchema, boolean>;
46
+ export declare class ZodToJsonSchemaConverter implements SchemaConverter {
47
+ condition(schema: Schema): boolean;
48
+ convert(schema: Schema, options: SchemaConvertOptions): JSONSchema.JSONSchema;
49
+ }
50
+ //# sourceMappingURL=converter.d.ts.map
@@ -1,3 +1,4 @@
1
1
  export * from './coercer';
2
+ export * from './converter';
2
3
  export * from './schemas';
3
4
  //# sourceMappingURL=index.d.ts.map
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.9588d75",
4
+ "version": "0.0.0-next.9914009",
5
5
  "license": "MIT",
6
6
  "homepage": "https://orpc.unnoq.com",
7
7
  "repository": {
@@ -29,7 +29,7 @@
29
29
  "dist"
30
30
  ],
31
31
  "peerDependencies": {
32
- "@orpc/openapi": "0.0.0-next.9588d75"
32
+ "@orpc/openapi": "0.0.0-next.9914009"
33
33
  },
34
34
  "dependencies": {
35
35
  "json-schema-typed": "^8.0.1",