@alt-stack/zod-openapi 1.1.3 → 1.2.0

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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @alt-stack/zod-openapi@1.1.3 build /home/runner/work/alt-stack/alt-stack/packages/zod-openapi
2
+ > @alt-stack/zod-openapi@1.2.0 build /home/runner/work/alt-stack/alt-stack/packages/zod-openapi
3
3
  > tsup
4
4
 
5
5
  CLI Building entry: src/index.ts
@@ -10,13 +10,13 @@
10
10
  CLI Cleaning output folder
11
11
  ESM Build start
12
12
  CJS Build start
13
- ESM dist/index.js 32.90 KB
14
- ESM dist/index.js.map 72.42 KB
15
- ESM ⚡️ Build success in 47ms
16
- CJS dist/index.cjs 34.44 KB
17
- CJS dist/index.cjs.map 73.28 KB
18
- CJS ⚡️ Build success in 47ms
13
+ ESM dist/index.js 38.35 KB
14
+ ESM dist/index.js.map 84.13 KB
15
+ ESM ⚡️ Build success in 32ms
16
+ CJS dist/index.cjs 39.98 KB
17
+ CJS dist/index.cjs.map 85.09 KB
18
+ CJS ⚡️ Build success in 33ms
19
19
  DTS Build start
20
- DTS ⚡️ Build success in 1504ms
21
- DTS dist/index.d.ts 4.83 KB
22
- DTS dist/index.d.cts 4.83 KB
20
+ DTS ⚡️ Build success in 1497ms
21
+ DTS dist/index.d.ts 5.68 KB
22
+ DTS dist/index.d.cts 5.68 KB
package/dist/index.cjs CHANGED
@@ -23,12 +23,14 @@ __export(index_exports, {
23
23
  SUPPORTED_STRING_FORMATS: () => SUPPORTED_STRING_FORMATS,
24
24
  clearZodSchemaToOpenApiSchemaRegistry: () => clearZodSchemaToOpenApiSchemaRegistry,
25
25
  convertSchemaToZodString: () => convertSchemaToZodString,
26
+ generateInterface: () => generateInterface,
26
27
  generateRouteSchemaNames: () => generateRouteSchemaNames,
27
28
  getSchemaExportedVariableNameForStringFormat: () => getSchemaExportedVariableNameForStringFormat,
28
29
  openApiToZodTsCode: () => openApiToZodTsCode,
29
30
  parseOpenApiPaths: () => parseOpenApiPaths,
30
31
  registerZodSchemaToOpenApiSchema: () => registerZodSchemaToOpenApiSchema,
31
- schemaRegistry: () => schemaRegistry
32
+ schemaRegistry: () => schemaRegistry,
33
+ schemaToTypeString: () => schemaToTypeString
32
34
  });
33
35
  module.exports = __toCommonJS(index_exports);
34
36
 
@@ -703,11 +705,150 @@ function generateRouteSchemaNames(route) {
703
705
  return result;
704
706
  }
705
707
 
706
- // src/to-typescript.ts
708
+ // src/interface-generator.ts
707
709
  var validIdentifierRegex2 = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
708
710
  function quotePropertyName2(name) {
709
711
  return validIdentifierRegex2.test(name) ? name : `'${name}'`;
710
712
  }
713
+ function schemaToTypeString(schema) {
714
+ if (!schema || typeof schema !== "object") return "unknown";
715
+ if (schema["$ref"] && typeof schema["$ref"] === "string") {
716
+ const match = schema["$ref"].match(
717
+ /#\/components\/schemas\/(.+)/
718
+ );
719
+ let result2 = "unknown";
720
+ if (match && match[1]) {
721
+ result2 = decodeURIComponent(match[1]);
722
+ }
723
+ if (schema["nullable"] === true) {
724
+ result2 = `(${result2} | null)`;
725
+ }
726
+ return result2;
727
+ }
728
+ let result = "unknown";
729
+ if ("oneOf" in schema && Array.isArray(schema["oneOf"])) {
730
+ const unionMembers = schema["oneOf"].map(
731
+ (s) => schemaToTypeString(s)
732
+ );
733
+ result = unionMembers.length > 1 ? `(${unionMembers.join(" | ")})` : unionMembers[0] ?? "unknown";
734
+ } else if ("allOf" in schema && Array.isArray(schema["allOf"])) {
735
+ const intersectionMembers = schema["allOf"].map(
736
+ (s) => schemaToTypeString(s)
737
+ );
738
+ result = intersectionMembers.length > 1 ? `(${intersectionMembers.join(" & ")})` : intersectionMembers[0] ?? "unknown";
739
+ } else if ("anyOf" in schema && Array.isArray(schema["anyOf"])) {
740
+ const unionMembers = schema["anyOf"].map(
741
+ (s) => schemaToTypeString(s)
742
+ );
743
+ result = unionMembers.length > 1 ? `(${unionMembers.join(" | ")})` : unionMembers[0] ?? "unknown";
744
+ } else {
745
+ switch (schema["type"]) {
746
+ case "string":
747
+ if (schema["enum"] && Array.isArray(schema["enum"])) {
748
+ result = schema["enum"].map((v) => JSON.stringify(v)).join(" | ");
749
+ } else {
750
+ result = "string";
751
+ }
752
+ break;
753
+ case "number":
754
+ case "integer":
755
+ if (schema["enum"] && Array.isArray(schema["enum"])) {
756
+ result = schema["enum"].map((v) => String(v)).join(" | ");
757
+ } else {
758
+ result = "number";
759
+ }
760
+ break;
761
+ case "boolean":
762
+ result = "boolean";
763
+ break;
764
+ case "null":
765
+ result = "null";
766
+ break;
767
+ case "array":
768
+ if (schema["items"]) {
769
+ const itemType = schemaToTypeString(schema["items"]);
770
+ result = `Array<${itemType}>`;
771
+ } else {
772
+ result = "unknown[]";
773
+ }
774
+ break;
775
+ case "object":
776
+ result = objectSchemaToTypeString(schema);
777
+ break;
778
+ default:
779
+ if (schema["properties"]) {
780
+ result = objectSchemaToTypeString(schema);
781
+ } else if (schema["enum"] && Array.isArray(schema["enum"])) {
782
+ result = schema["enum"].map((v) => JSON.stringify(v)).join(" | ");
783
+ } else {
784
+ result = "unknown";
785
+ }
786
+ break;
787
+ }
788
+ }
789
+ if (schema["nullable"] === true) {
790
+ result = `(${result} | null)`;
791
+ }
792
+ return result;
793
+ }
794
+ function objectSchemaToTypeString(schema) {
795
+ const properties = schema["properties"];
796
+ const required = new Set(schema["required"] ?? []);
797
+ const additionalProperties = schema["additionalProperties"];
798
+ if (!properties && !additionalProperties) {
799
+ return "Record<string, unknown>";
800
+ }
801
+ const propertyStrings = [];
802
+ if (properties) {
803
+ for (const [propName, propSchema] of Object.entries(properties)) {
804
+ const isRequired = required.has(propName);
805
+ const propType = schemaToTypeString(propSchema);
806
+ const quotedName = quotePropertyName2(propName);
807
+ propertyStrings.push(
808
+ `${quotedName}${isRequired ? "" : "?"}: ${propType}`
809
+ );
810
+ }
811
+ }
812
+ if (additionalProperties === true) {
813
+ propertyStrings.push("[key: string]: unknown");
814
+ } else if (typeof additionalProperties === "object" && additionalProperties !== null) {
815
+ const additionalType = schemaToTypeString(additionalProperties);
816
+ propertyStrings.push(`[key: string]: ${additionalType}`);
817
+ }
818
+ return `{ ${propertyStrings.join("; ")} }`;
819
+ }
820
+ function generateInterface(name, schema) {
821
+ const properties = schema["properties"];
822
+ const required = new Set(schema["required"] ?? []);
823
+ if (schema["type"] !== "object" && !properties) {
824
+ return `export type ${name} = ${schemaToTypeString(schema)};`;
825
+ }
826
+ const lines = [];
827
+ lines.push(`export interface ${name} {`);
828
+ if (properties) {
829
+ for (const [propName, propSchema] of Object.entries(properties)) {
830
+ const isRequired = required.has(propName);
831
+ const propType = schemaToTypeString(propSchema);
832
+ const quotedName = quotePropertyName2(propName);
833
+ lines.push(` ${quotedName}${isRequired ? "" : "?"}: ${propType};`);
834
+ }
835
+ }
836
+ const additionalProperties = schema["additionalProperties"];
837
+ if (additionalProperties === true) {
838
+ lines.push(" [key: string]: unknown;");
839
+ } else if (typeof additionalProperties === "object" && additionalProperties !== null) {
840
+ const additionalType = schemaToTypeString(additionalProperties);
841
+ lines.push(` [key: string]: ${additionalType};`);
842
+ }
843
+ lines.push("}");
844
+ return lines.join("\n");
845
+ }
846
+
847
+ // src/to-typescript.ts
848
+ var validIdentifierRegex3 = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
849
+ function quotePropertyName3(name) {
850
+ return validIdentifierRegex3.test(name) ? name : `'${name}'`;
851
+ }
711
852
  function generateRouteSchemaName2(path, method, suffix) {
712
853
  const pathParts = path.split("/").filter((p) => p).map((p) => {
713
854
  if (p.startsWith("{") && p.endsWith("}")) {
@@ -749,7 +890,7 @@ function generateRouteSchemas(routes, convertSchema, registry) {
749
890
  const properties = [];
750
891
  for (const param of pathParams) {
751
892
  const zodExpr = convertSchema(param.schema);
752
- properties.push(`${quotePropertyName2(param.name)}: ${zodExpr}`);
893
+ properties.push(`${quotePropertyName3(param.name)}: ${zodExpr}`);
753
894
  }
754
895
  declarations.push(
755
896
  `export const ${names.paramsSchemaName} = z.object({ ${properties.join(", ")} });`
@@ -785,7 +926,7 @@ function generateRouteSchemas(routes, convertSchema, registry) {
785
926
  if (!param.required) {
786
927
  zodExpr += ".optional()";
787
928
  }
788
- properties.push(`${quotePropertyName2(param.name)}: ${zodExpr}`);
929
+ properties.push(`${quotePropertyName3(param.name)}: ${zodExpr}`);
789
930
  }
790
931
  declarations.push(
791
932
  `export const ${names.querySchemaName} = z.object({ ${properties.join(", ")} });`
@@ -821,7 +962,7 @@ function generateRouteSchemas(routes, convertSchema, registry) {
821
962
  if (!param.required) {
822
963
  zodExpr += ".optional()";
823
964
  }
824
- properties.push(`${quotePropertyName2(param.name)}: ${zodExpr}`);
965
+ properties.push(`${quotePropertyName3(param.name)}: ${zodExpr}`);
825
966
  }
826
967
  declarations.push(
827
968
  `export const ${names.headersSchemaName} = z.object({ ${properties.join(", ")} });`
@@ -1016,21 +1157,31 @@ var openApiToZodTsCode = (openapi, customImportLines, options) => {
1016
1157
  lines.push("import { z } from 'zod';");
1017
1158
  lines.push(...customImportLines ?? []);
1018
1159
  lines.push("");
1160
+ lines.push("// Type assertion helper - verifies interface matches schema at compile time");
1161
+ lines.push("type _AssertEqual<T, U> = [T] extends [U] ? ([U] extends [T] ? true : never) : never;");
1162
+ lines.push("");
1019
1163
  const registry = createSchemaRegistry();
1020
1164
  const sortedSchemaNames = topologicalSortSchemas(schemas);
1165
+ const typeAssertions = [];
1021
1166
  for (const name of sortedSchemaNames) {
1022
1167
  const schema = schemas[name];
1023
1168
  if (schema) {
1024
1169
  const zodExpr = convertSchemaToZodString(schema);
1025
1170
  const schemaName = `${name}Schema`;
1026
1171
  const typeName = name;
1027
- lines.push(`export const ${schemaName} = ${zodExpr};`);
1028
- lines.push(`export type ${typeName} = z.infer<typeof ${schemaName}>;`);
1172
+ lines.push(generateInterface(typeName, schema));
1173
+ lines.push(`export const ${schemaName}: z.ZodType<${typeName}> = ${zodExpr};`);
1029
1174
  lines.push("");
1175
+ typeAssertions.push(`type _Assert${typeName} = _AssertEqual<${typeName}, z.infer<typeof ${schemaName}>>;`);
1030
1176
  const fingerprint = getSchemaFingerprint(schema);
1031
1177
  preRegisterSchema(registry, schemaName, fingerprint);
1032
1178
  }
1033
1179
  }
1180
+ if (typeAssertions.length > 0) {
1181
+ lines.push("// Compile-time type assertions - ensure interfaces match schemas");
1182
+ lines.push(typeAssertions.join("\n"));
1183
+ lines.push("");
1184
+ }
1034
1185
  if (options?.includeRoutes) {
1035
1186
  const routes = parseOpenApiPaths(openapi);
1036
1187
  if (routes.length > 0) {
@@ -1069,11 +1220,13 @@ var openApiToZodTsCode = (openapi, customImportLines, options) => {
1069
1220
  SUPPORTED_STRING_FORMATS,
1070
1221
  clearZodSchemaToOpenApiSchemaRegistry,
1071
1222
  convertSchemaToZodString,
1223
+ generateInterface,
1072
1224
  generateRouteSchemaNames,
1073
1225
  getSchemaExportedVariableNameForStringFormat,
1074
1226
  openApiToZodTsCode,
1075
1227
  parseOpenApiPaths,
1076
1228
  registerZodSchemaToOpenApiSchema,
1077
- schemaRegistry
1229
+ schemaRegistry,
1230
+ schemaToTypeString
1078
1231
  });
1079
1232
  //# sourceMappingURL=index.cjs.map