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

Sign up to get free protection for your applications and to get access to all the features.
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",