@alt-stack/zod-openapi 1.2.0 → 1.3.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.
- package/.turbo/turbo-build.log +9 -9
- package/dist/index.cjs +89 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -4
- package/dist/index.d.ts +11 -4
- package/dist/index.js +89 -22
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/interface-generator.spec.ts +74 -1
- package/src/interface-generator.ts +98 -19
- package/src/registry.ts +32 -2
- package/src/to-typescript.spec.ts +60 -0
- package/src/to-typescript.ts +17 -4
package/dist/index.js
CHANGED
|
@@ -231,6 +231,9 @@ function isStringRegistration(reg) {
|
|
|
231
231
|
function isStringsRegistration(reg) {
|
|
232
232
|
return reg.type === "string" && "formats" in reg;
|
|
233
233
|
}
|
|
234
|
+
function isSupportedStringFormat(format) {
|
|
235
|
+
return Object.prototype.hasOwnProperty.call(SUPPORTED_STRING_FORMATS_MAP, format);
|
|
236
|
+
}
|
|
234
237
|
function getTypeFormatPairs(reg) {
|
|
235
238
|
if (isStringRegistration(reg)) {
|
|
236
239
|
return [{ type: "string", format: reg.format }];
|
|
@@ -281,6 +284,7 @@ var ZodSchemaRegistry = class {
|
|
|
281
284
|
* Reverse-lookup helper: given a string format, return the registered schema's exported variable name
|
|
282
285
|
*/
|
|
283
286
|
getSchemaExportedVariableNameForStringFormat(format) {
|
|
287
|
+
if (!isSupportedStringFormat(format)) return void 0;
|
|
284
288
|
for (const registration of this.map.values()) {
|
|
285
289
|
if (registration.type !== "string") continue;
|
|
286
290
|
if (isStringRegistration(registration) && registration.format === format) {
|
|
@@ -292,6 +296,17 @@ var ZodSchemaRegistry = class {
|
|
|
292
296
|
}
|
|
293
297
|
return void 0;
|
|
294
298
|
}
|
|
299
|
+
/**
|
|
300
|
+
* Reverse-lookup helper: given a primitive type, return the registered schema's exported variable name
|
|
301
|
+
*/
|
|
302
|
+
getSchemaExportedVariableNameForPrimitiveType(type) {
|
|
303
|
+
for (const registration of this.map.values()) {
|
|
304
|
+
if (registration.type === type) {
|
|
305
|
+
return registration.schemaExportedVariableName;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return void 0;
|
|
309
|
+
}
|
|
295
310
|
};
|
|
296
311
|
var schemaRegistry = new ZodSchemaRegistry();
|
|
297
312
|
function registerZodSchemaToOpenApiSchema(schema, openApiSchema) {
|
|
@@ -300,6 +315,9 @@ function registerZodSchemaToOpenApiSchema(schema, openApiSchema) {
|
|
|
300
315
|
function getSchemaExportedVariableNameForStringFormat(format) {
|
|
301
316
|
return schemaRegistry.getSchemaExportedVariableNameForStringFormat(format);
|
|
302
317
|
}
|
|
318
|
+
function getSchemaExportedVariableNameForPrimitiveType(type) {
|
|
319
|
+
return schemaRegistry.getSchemaExportedVariableNameForPrimitiveType(type);
|
|
320
|
+
}
|
|
303
321
|
function clearZodSchemaToOpenApiSchemaRegistry() {
|
|
304
322
|
schemaRegistry.clear();
|
|
305
323
|
}
|
|
@@ -674,7 +692,37 @@ var validIdentifierRegex2 = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
|
674
692
|
function quotePropertyName2(name) {
|
|
675
693
|
return validIdentifierRegex2.test(name) ? name : `'${name}'`;
|
|
676
694
|
}
|
|
677
|
-
function
|
|
695
|
+
function toPascalCase2(name) {
|
|
696
|
+
return name.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[^a-zA-Z0-9]/g, " ").split(" ").filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
|
697
|
+
}
|
|
698
|
+
function schemaExportNameToOutputAlias(name) {
|
|
699
|
+
return `${toPascalCase2(name)}Output`;
|
|
700
|
+
}
|
|
701
|
+
function registerOutputSchemaName(schemaName, options) {
|
|
702
|
+
options?.outputSchemaNames?.add(schemaName);
|
|
703
|
+
return schemaExportNameToOutputAlias(schemaName);
|
|
704
|
+
}
|
|
705
|
+
function getRegisteredOutputAlias(schema, options) {
|
|
706
|
+
if (!schema || typeof schema !== "object") return void 0;
|
|
707
|
+
if (schema["type"] === "string" && typeof schema["format"] === "string") {
|
|
708
|
+
const customSchemaName = getSchemaExportedVariableNameForStringFormat(
|
|
709
|
+
schema["format"]
|
|
710
|
+
);
|
|
711
|
+
if (customSchemaName) {
|
|
712
|
+
return registerOutputSchemaName(customSchemaName, options);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
if (schema["type"] === "number" || schema["type"] === "integer" || schema["type"] === "boolean") {
|
|
716
|
+
const customSchemaName = getSchemaExportedVariableNameForPrimitiveType(
|
|
717
|
+
schema["type"]
|
|
718
|
+
);
|
|
719
|
+
if (customSchemaName) {
|
|
720
|
+
return registerOutputSchemaName(customSchemaName, options);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
return void 0;
|
|
724
|
+
}
|
|
725
|
+
function schemaToTypeString(schema, options) {
|
|
678
726
|
if (!schema || typeof schema !== "object") return "unknown";
|
|
679
727
|
if (schema["$ref"] && typeof schema["$ref"] === "string") {
|
|
680
728
|
const match = schema["$ref"].match(
|
|
@@ -692,56 +740,64 @@ function schemaToTypeString(schema) {
|
|
|
692
740
|
let result = "unknown";
|
|
693
741
|
if ("oneOf" in schema && Array.isArray(schema["oneOf"])) {
|
|
694
742
|
const unionMembers = schema["oneOf"].map(
|
|
695
|
-
(s) => schemaToTypeString(s)
|
|
743
|
+
(s) => schemaToTypeString(s, options)
|
|
696
744
|
);
|
|
697
745
|
result = unionMembers.length > 1 ? `(${unionMembers.join(" | ")})` : unionMembers[0] ?? "unknown";
|
|
698
746
|
} else if ("allOf" in schema && Array.isArray(schema["allOf"])) {
|
|
699
747
|
const intersectionMembers = schema["allOf"].map(
|
|
700
|
-
(s) => schemaToTypeString(s)
|
|
748
|
+
(s) => schemaToTypeString(s, options)
|
|
701
749
|
);
|
|
702
750
|
result = intersectionMembers.length > 1 ? `(${intersectionMembers.join(" & ")})` : intersectionMembers[0] ?? "unknown";
|
|
703
751
|
} else if ("anyOf" in schema && Array.isArray(schema["anyOf"])) {
|
|
704
752
|
const unionMembers = schema["anyOf"].map(
|
|
705
|
-
(s) => schemaToTypeString(s)
|
|
753
|
+
(s) => schemaToTypeString(s, options)
|
|
706
754
|
);
|
|
707
755
|
result = unionMembers.length > 1 ? `(${unionMembers.join(" | ")})` : unionMembers[0] ?? "unknown";
|
|
708
756
|
} else {
|
|
709
757
|
switch (schema["type"]) {
|
|
710
|
-
case "string":
|
|
711
|
-
|
|
758
|
+
case "string": {
|
|
759
|
+
const registeredAlias = getRegisteredOutputAlias(schema, options);
|
|
760
|
+
if (registeredAlias) {
|
|
761
|
+
result = registeredAlias;
|
|
762
|
+
} else if (schema["enum"] && Array.isArray(schema["enum"])) {
|
|
712
763
|
result = schema["enum"].map((v) => JSON.stringify(v)).join(" | ");
|
|
713
764
|
} else {
|
|
714
765
|
result = "string";
|
|
715
766
|
}
|
|
716
767
|
break;
|
|
768
|
+
}
|
|
717
769
|
case "number":
|
|
718
|
-
case "integer":
|
|
719
|
-
|
|
770
|
+
case "integer": {
|
|
771
|
+
const registeredAlias = getRegisteredOutputAlias(schema, options);
|
|
772
|
+
if (registeredAlias) {
|
|
773
|
+
result = registeredAlias;
|
|
774
|
+
} else if (schema["enum"] && Array.isArray(schema["enum"])) {
|
|
720
775
|
result = schema["enum"].map((v) => String(v)).join(" | ");
|
|
721
776
|
} else {
|
|
722
777
|
result = "number";
|
|
723
778
|
}
|
|
724
779
|
break;
|
|
780
|
+
}
|
|
725
781
|
case "boolean":
|
|
726
|
-
result = "boolean";
|
|
782
|
+
result = getRegisteredOutputAlias(schema, options) ?? "boolean";
|
|
727
783
|
break;
|
|
728
784
|
case "null":
|
|
729
785
|
result = "null";
|
|
730
786
|
break;
|
|
731
787
|
case "array":
|
|
732
788
|
if (schema["items"]) {
|
|
733
|
-
const itemType = schemaToTypeString(schema["items"]);
|
|
789
|
+
const itemType = schemaToTypeString(schema["items"], options);
|
|
734
790
|
result = `Array<${itemType}>`;
|
|
735
791
|
} else {
|
|
736
792
|
result = "unknown[]";
|
|
737
793
|
}
|
|
738
794
|
break;
|
|
739
795
|
case "object":
|
|
740
|
-
result = objectSchemaToTypeString(schema);
|
|
796
|
+
result = objectSchemaToTypeString(schema, options);
|
|
741
797
|
break;
|
|
742
798
|
default:
|
|
743
799
|
if (schema["properties"]) {
|
|
744
|
-
result = objectSchemaToTypeString(schema);
|
|
800
|
+
result = objectSchemaToTypeString(schema, options);
|
|
745
801
|
} else if (schema["enum"] && Array.isArray(schema["enum"])) {
|
|
746
802
|
result = schema["enum"].map((v) => JSON.stringify(v)).join(" | ");
|
|
747
803
|
} else {
|
|
@@ -755,7 +811,7 @@ function schemaToTypeString(schema) {
|
|
|
755
811
|
}
|
|
756
812
|
return result;
|
|
757
813
|
}
|
|
758
|
-
function objectSchemaToTypeString(schema) {
|
|
814
|
+
function objectSchemaToTypeString(schema, options) {
|
|
759
815
|
const properties = schema["properties"];
|
|
760
816
|
const required = new Set(schema["required"] ?? []);
|
|
761
817
|
const additionalProperties = schema["additionalProperties"];
|
|
@@ -766,7 +822,7 @@ function objectSchemaToTypeString(schema) {
|
|
|
766
822
|
if (properties) {
|
|
767
823
|
for (const [propName, propSchema] of Object.entries(properties)) {
|
|
768
824
|
const isRequired = required.has(propName);
|
|
769
|
-
const propType = schemaToTypeString(propSchema);
|
|
825
|
+
const propType = schemaToTypeString(propSchema, options);
|
|
770
826
|
const quotedName = quotePropertyName2(propName);
|
|
771
827
|
propertyStrings.push(
|
|
772
828
|
`${quotedName}${isRequired ? "" : "?"}: ${propType}`
|
|
@@ -776,23 +832,23 @@ function objectSchemaToTypeString(schema) {
|
|
|
776
832
|
if (additionalProperties === true) {
|
|
777
833
|
propertyStrings.push("[key: string]: unknown");
|
|
778
834
|
} else if (typeof additionalProperties === "object" && additionalProperties !== null) {
|
|
779
|
-
const additionalType = schemaToTypeString(additionalProperties);
|
|
835
|
+
const additionalType = schemaToTypeString(additionalProperties, options);
|
|
780
836
|
propertyStrings.push(`[key: string]: ${additionalType}`);
|
|
781
837
|
}
|
|
782
838
|
return `{ ${propertyStrings.join("; ")} }`;
|
|
783
839
|
}
|
|
784
|
-
function generateInterface(name, schema) {
|
|
840
|
+
function generateInterface(name, schema, options) {
|
|
785
841
|
const properties = schema["properties"];
|
|
786
842
|
const required = new Set(schema["required"] ?? []);
|
|
787
843
|
if (schema["type"] !== "object" && !properties) {
|
|
788
|
-
return `export type ${name} = ${schemaToTypeString(schema)};`;
|
|
844
|
+
return `export type ${name} = ${schemaToTypeString(schema, options)};`;
|
|
789
845
|
}
|
|
790
846
|
const lines = [];
|
|
791
847
|
lines.push(`export interface ${name} {`);
|
|
792
848
|
if (properties) {
|
|
793
849
|
for (const [propName, propSchema] of Object.entries(properties)) {
|
|
794
850
|
const isRequired = required.has(propName);
|
|
795
|
-
const propType = schemaToTypeString(propSchema);
|
|
851
|
+
const propType = schemaToTypeString(propSchema, options);
|
|
796
852
|
const quotedName = quotePropertyName2(propName);
|
|
797
853
|
lines.push(` ${quotedName}${isRequired ? "" : "?"}: ${propType};`);
|
|
798
854
|
}
|
|
@@ -801,7 +857,7 @@ function generateInterface(name, schema) {
|
|
|
801
857
|
if (additionalProperties === true) {
|
|
802
858
|
lines.push(" [key: string]: unknown;");
|
|
803
859
|
} else if (typeof additionalProperties === "object" && additionalProperties !== null) {
|
|
804
|
-
const additionalType = schemaToTypeString(additionalProperties);
|
|
860
|
+
const additionalType = schemaToTypeString(additionalProperties, options);
|
|
805
861
|
lines.push(` [key: string]: ${additionalType};`);
|
|
806
862
|
}
|
|
807
863
|
lines.push("}");
|
|
@@ -1127,20 +1183,31 @@ var openApiToZodTsCode = (openapi, customImportLines, options) => {
|
|
|
1127
1183
|
const registry = createSchemaRegistry();
|
|
1128
1184
|
const sortedSchemaNames = topologicalSortSchemas(schemas);
|
|
1129
1185
|
const typeAssertions = [];
|
|
1186
|
+
const outputSchemaNames = /* @__PURE__ */ new Set();
|
|
1187
|
+
const schemaBlocks = [];
|
|
1130
1188
|
for (const name of sortedSchemaNames) {
|
|
1131
1189
|
const schema = schemas[name];
|
|
1132
1190
|
if (schema) {
|
|
1133
1191
|
const zodExpr = convertSchemaToZodString(schema);
|
|
1134
1192
|
const schemaName = `${name}Schema`;
|
|
1135
1193
|
const typeName = name;
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1194
|
+
schemaBlocks.push(generateInterface(typeName, schema, { outputSchemaNames }));
|
|
1195
|
+
schemaBlocks.push(`export const ${schemaName}: z.ZodType<${typeName}> = ${zodExpr};`);
|
|
1196
|
+
schemaBlocks.push("");
|
|
1139
1197
|
typeAssertions.push(`type _Assert${typeName} = _AssertEqual<${typeName}, z.infer<typeof ${schemaName}>>;`);
|
|
1140
1198
|
const fingerprint = getSchemaFingerprint(schema);
|
|
1141
1199
|
preRegisterSchema(registry, schemaName, fingerprint);
|
|
1142
1200
|
}
|
|
1143
1201
|
}
|
|
1202
|
+
if (outputSchemaNames.size > 0) {
|
|
1203
|
+
lines.push("// Zod output aliases for registered schemas");
|
|
1204
|
+
for (const schemaName of outputSchemaNames) {
|
|
1205
|
+
const aliasName = schemaExportNameToOutputAlias(schemaName);
|
|
1206
|
+
lines.push(`type ${aliasName} = z.output<typeof ${schemaName}>;`);
|
|
1207
|
+
}
|
|
1208
|
+
lines.push("");
|
|
1209
|
+
}
|
|
1210
|
+
lines.push(...schemaBlocks);
|
|
1144
1211
|
if (typeAssertions.length > 0) {
|
|
1145
1212
|
lines.push("// Compile-time type assertions - ensure interfaces match schemas");
|
|
1146
1213
|
lines.push(typeAssertions.join("\n"));
|