next-openapi-gen 1.1.1 → 1.2.1
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/README.md +22 -0
- package/dist/cli.js +138 -12
- package/dist/index.js +138 -12
- package/dist/next/index.js +138 -12
- package/dist/react-router/index.js +138 -12
- package/dist/vite/index.js +138 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -162,6 +162,28 @@ For more adoption patterns, see
|
|
|
162
162
|
|
|
163
163
|
When you target modern OpenAPI output, the Zod path can also split request and response component refs when a supported Zod 4 schema emits different input and output JSON Schema shapes, while the TypeScript path can use selective checker fallback for mapped, conditional, template-literal, and import-based named types.
|
|
164
164
|
|
|
165
|
+
### Add OpenAPI metadata directly in Zod schemas
|
|
166
|
+
|
|
167
|
+
Use `.describe()` for a quick description, or Zod v4's `.meta()` to attach `description`, `examples`, `example`, `deprecated`, `title`, and custom `x-*` extensions without any JSDoc:
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
// .describe() → description field
|
|
171
|
+
z.string().describe("ISO 639-1 language code");
|
|
172
|
+
// → { type: "string", description: "ISO 639-1 language code" }
|
|
173
|
+
|
|
174
|
+
// .meta() → description + examples (and any other OpenAPI key)
|
|
175
|
+
z.number()
|
|
176
|
+
.int()
|
|
177
|
+
.positive()
|
|
178
|
+
.meta({
|
|
179
|
+
description: "PIM ID of the slider",
|
|
180
|
+
examples: [42, 1337],
|
|
181
|
+
});
|
|
182
|
+
// → { type: "integer", exclusiveMinimum: 0, description: "PIM ID of the slider", examples: [42, 1337] }
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Both work inside `z.object({...})` properties, in drizzle-zod override callbacks, and at the top level of named schemas. See [docs/zod4-support-matrix.md](./docs/zod4-support-matrix.md) for the full supported metadata surface.
|
|
186
|
+
|
|
165
187
|
### Generate docs from Drizzle schemas
|
|
166
188
|
|
|
167
189
|
`next-openapi-gen` works well with `drizzle-zod`, so your database schema, validation, and API docs can share the same source of truth.
|
package/dist/cli.js
CHANGED
|
@@ -3154,7 +3154,7 @@ var SymbolResolver = class {
|
|
|
3154
3154
|
|
|
3155
3155
|
// ../openapi-core/dist/schema/zod/drizzle-zod-processor.js
|
|
3156
3156
|
import * as t4 from "@babel/types";
|
|
3157
|
-
var DrizzleZodProcessor = class {
|
|
3157
|
+
var DrizzleZodProcessor = class _DrizzleZodProcessor {
|
|
3158
3158
|
/**
|
|
3159
3159
|
* Known drizzle-zod helper function names
|
|
3160
3160
|
*/
|
|
@@ -3580,6 +3580,16 @@ var DrizzleZodProcessor = class {
|
|
|
3580
3580
|
result.description = args[0].value;
|
|
3581
3581
|
}
|
|
3582
3582
|
break;
|
|
3583
|
+
case "meta": {
|
|
3584
|
+
const firstArg = args[0];
|
|
3585
|
+
if (firstArg && !t4.isSpreadElement(firstArg) && !t4.isArgumentPlaceholder(firstArg)) {
|
|
3586
|
+
const metadata = _DrizzleZodProcessor.extractStaticObject(firstArg);
|
|
3587
|
+
if (metadata) {
|
|
3588
|
+
Object.assign(result, metadata);
|
|
3589
|
+
}
|
|
3590
|
+
}
|
|
3591
|
+
break;
|
|
3592
|
+
}
|
|
3583
3593
|
case "default":
|
|
3584
3594
|
if (args.length > 0) {
|
|
3585
3595
|
if (t4.isStringLiteral(args[0])) {
|
|
@@ -3600,6 +3610,49 @@ var DrizzleZodProcessor = class {
|
|
|
3600
3610
|
static isDrizzleZodHelper(name) {
|
|
3601
3611
|
return this.DRIZZLE_ZOD_HELPERS.includes(name);
|
|
3602
3612
|
}
|
|
3613
|
+
static extractStaticObject(node) {
|
|
3614
|
+
if (!t4.isObjectExpression(node))
|
|
3615
|
+
return null;
|
|
3616
|
+
const out = {};
|
|
3617
|
+
for (const prop of node.properties) {
|
|
3618
|
+
if (!t4.isObjectProperty(prop))
|
|
3619
|
+
return null;
|
|
3620
|
+
const key = t4.isIdentifier(prop.key) ? prop.key.name : t4.isStringLiteral(prop.key) ? prop.key.value : null;
|
|
3621
|
+
if (!key)
|
|
3622
|
+
return null;
|
|
3623
|
+
const val = _DrizzleZodProcessor.extractStaticValue(prop.value);
|
|
3624
|
+
if (typeof val === "undefined")
|
|
3625
|
+
return null;
|
|
3626
|
+
out[key] = val;
|
|
3627
|
+
}
|
|
3628
|
+
return out;
|
|
3629
|
+
}
|
|
3630
|
+
static extractStaticValue(node) {
|
|
3631
|
+
if (t4.isStringLiteral(node))
|
|
3632
|
+
return node.value;
|
|
3633
|
+
if (t4.isNumericLiteral(node))
|
|
3634
|
+
return node.value;
|
|
3635
|
+
if (t4.isBooleanLiteral(node))
|
|
3636
|
+
return node.value;
|
|
3637
|
+
if (t4.isNullLiteral(node))
|
|
3638
|
+
return null;
|
|
3639
|
+
if (t4.isArrayExpression(node)) {
|
|
3640
|
+
const values = [];
|
|
3641
|
+
for (const el of node.elements) {
|
|
3642
|
+
if (!el || t4.isSpreadElement(el) || t4.isArgumentPlaceholder(el))
|
|
3643
|
+
return void 0;
|
|
3644
|
+
const v = _DrizzleZodProcessor.extractStaticValue(el);
|
|
3645
|
+
if (typeof v === "undefined")
|
|
3646
|
+
return void 0;
|
|
3647
|
+
values.push(v);
|
|
3648
|
+
}
|
|
3649
|
+
return values;
|
|
3650
|
+
}
|
|
3651
|
+
if (t4.isObjectExpression(node)) {
|
|
3652
|
+
return _DrizzleZodProcessor.extractStaticObject(node);
|
|
3653
|
+
}
|
|
3654
|
+
return void 0;
|
|
3655
|
+
}
|
|
3603
3656
|
};
|
|
3604
3657
|
|
|
3605
3658
|
// ../openapi-core/dist/schema/zod/converter-runtime.js
|
|
@@ -4570,8 +4623,12 @@ function processZodPrimitiveNode(node, context) {
|
|
|
4570
4623
|
};
|
|
4571
4624
|
break;
|
|
4572
4625
|
}
|
|
4626
|
+
case "strictObject":
|
|
4573
4627
|
case "object":
|
|
4574
4628
|
schema = node.arguments.length > 0 ? context.processObject(node) : { type: "object" };
|
|
4629
|
+
if (zodType === "strictObject") {
|
|
4630
|
+
schema.additionalProperties = false;
|
|
4631
|
+
}
|
|
4575
4632
|
break;
|
|
4576
4633
|
case "templateLiteral":
|
|
4577
4634
|
schema = { type: "string" };
|
|
@@ -11638,6 +11695,10 @@ var ZodRuntimeExporter = class {
|
|
|
11638
11695
|
return this.buildEnum(node);
|
|
11639
11696
|
case "array":
|
|
11640
11697
|
return node.arguments[0] && isProcessableNode(node.arguments[0]) ? array(this.buildSchema(node.arguments[0]) ?? unknown()) : array(unknown());
|
|
11698
|
+
case "strictObject": {
|
|
11699
|
+
const base = this.buildObject(node);
|
|
11700
|
+
return base && typeof base.strict === "function" ? base.strict() : base;
|
|
11701
|
+
}
|
|
11641
11702
|
case "object":
|
|
11642
11703
|
return this.buildObject(node);
|
|
11643
11704
|
case "record":
|
|
@@ -12045,7 +12106,9 @@ var ZodSchemaConverter = class {
|
|
|
12045
12106
|
apiDir;
|
|
12046
12107
|
zodSchemas = {};
|
|
12047
12108
|
processingSchemas = /* @__PURE__ */ new Set();
|
|
12048
|
-
|
|
12109
|
+
/** Memoization guard for processFileForZodSchema. Keys: `${filePath}|${schemaName}`.
|
|
12110
|
+
* Prevents infinite recursion when re-export files reference schemas via z.infer<typeof X>. */
|
|
12111
|
+
processedFileSchemaPairs = /* @__PURE__ */ new Set();
|
|
12049
12112
|
typeToSchemaMapping = {};
|
|
12050
12113
|
drizzleZodImports = /* @__PURE__ */ new Set();
|
|
12051
12114
|
factoryCache = /* @__PURE__ */ new Map();
|
|
@@ -12259,6 +12322,11 @@ var ZodSchemaConverter = class {
|
|
|
12259
12322
|
* Process a file to find Zod schema definitions
|
|
12260
12323
|
*/
|
|
12261
12324
|
processFileForZodSchema(filePath, schemaName) {
|
|
12325
|
+
const visitKey = `${filePath}|${schemaName}|${this.currentContentType}`;
|
|
12326
|
+
if (this.processedFileSchemaPairs.has(visitKey)) {
|
|
12327
|
+
return;
|
|
12328
|
+
}
|
|
12329
|
+
this.processedFileSchemaPairs.add(visitKey);
|
|
12262
12330
|
try {
|
|
12263
12331
|
const content = this.fileAccess.readFileSync(filePath, "utf-8");
|
|
12264
12332
|
if (!content.includes(schemaName)) {
|
|
@@ -12572,7 +12640,9 @@ var ZodSchemaConverter = class {
|
|
|
12572
12640
|
const param = path25.node.typeAnnotation.typeParameters.params[0];
|
|
12573
12641
|
if (t10.isTSTypeQuery(param) && t10.isIdentifier(param.exprName)) {
|
|
12574
12642
|
const referencedSchemaName = param.exprName.name;
|
|
12575
|
-
this.
|
|
12643
|
+
if (!this.getStoredSchema(referencedSchemaName)) {
|
|
12644
|
+
this.processFileForZodSchema(filePath, referencedSchemaName);
|
|
12645
|
+
}
|
|
12576
12646
|
}
|
|
12577
12647
|
}
|
|
12578
12648
|
}
|
|
@@ -12657,8 +12727,12 @@ var ZodSchemaConverter = class {
|
|
|
12657
12727
|
}
|
|
12658
12728
|
if (t10.isCallExpression(node) && t10.isMemberExpression(node.callee) && t10.isIdentifier(node.callee.object) && this.isZodLocalName(node.callee.object.name) && t10.isIdentifier(node.callee.property)) {
|
|
12659
12729
|
const methodName = node.callee.property.name;
|
|
12660
|
-
if (methodName === "object" && node.arguments.length > 0) {
|
|
12661
|
-
|
|
12730
|
+
if ((methodName === "object" || methodName === "strictObject") && node.arguments.length > 0) {
|
|
12731
|
+
const schema = this.processZodObject(node);
|
|
12732
|
+
if (methodName === "strictObject") {
|
|
12733
|
+
schema.additionalProperties = false;
|
|
12734
|
+
}
|
|
12735
|
+
return schema;
|
|
12662
12736
|
} else if (methodName === "union" && node.arguments.length > 0) {
|
|
12663
12737
|
return this.processZodUnion(node);
|
|
12664
12738
|
} else if (methodName === "intersection" && node.arguments.length > 0) {
|
|
@@ -13943,15 +14017,39 @@ function extractKeysFromLiteralType(node) {
|
|
|
13943
14017
|
}
|
|
13944
14018
|
return [];
|
|
13945
14019
|
}
|
|
14020
|
+
function parsePropertyComment(commentValue) {
|
|
14021
|
+
const text = commentValue.split("\n").map((line) => line.replace(/^\s*\*\s?/, "").trim()).filter((line) => line.length > 0).join(" ").trim();
|
|
14022
|
+
const result = {};
|
|
14023
|
+
let remaining = text;
|
|
14024
|
+
const formatMatch = remaining.match(/@format\s+(\S+)/);
|
|
14025
|
+
if (formatMatch?.[1]) {
|
|
14026
|
+
result.format = formatMatch[1];
|
|
14027
|
+
remaining = remaining.replace(formatMatch[0], "").trim();
|
|
14028
|
+
}
|
|
14029
|
+
const exampleMatch = remaining.match(/@example\s+(.+?)(?=\s*@\w|$)/);
|
|
14030
|
+
if (exampleMatch?.[1]) {
|
|
14031
|
+
const raw = exampleMatch[1].trim();
|
|
14032
|
+
try {
|
|
14033
|
+
result.example = JSON.parse(raw);
|
|
14034
|
+
} catch {
|
|
14035
|
+
result.example = raw;
|
|
14036
|
+
}
|
|
14037
|
+
remaining = remaining.replace(exampleMatch[0], "").trim();
|
|
14038
|
+
}
|
|
14039
|
+
remaining = remaining.replace(/@\w+(?:\s+\S+)*/g, "").trim();
|
|
14040
|
+
if (remaining) {
|
|
14041
|
+
result.description = remaining;
|
|
14042
|
+
}
|
|
14043
|
+
return result;
|
|
14044
|
+
}
|
|
13946
14045
|
function getPropertyOptions(node, contentType) {
|
|
13947
14046
|
const isOptional = !!node.optional;
|
|
13948
|
-
let description = null;
|
|
13949
|
-
if (node.trailingComments && node.trailingComments.length) {
|
|
13950
|
-
description = node.trailingComments[0].value.trim();
|
|
13951
|
-
}
|
|
13952
14047
|
const options = {};
|
|
13953
|
-
|
|
13954
|
-
|
|
14048
|
+
const leadingComment = node.leadingComments?.[node.leadingComments.length - 1];
|
|
14049
|
+
const trailingComment = node.trailingComments?.[0];
|
|
14050
|
+
const sourceComment = leadingComment ?? trailingComment;
|
|
14051
|
+
if (sourceComment) {
|
|
14052
|
+
Object.assign(options, parsePropertyComment(sourceComment.value));
|
|
13955
14053
|
}
|
|
13956
14054
|
if (contentType === "body") {
|
|
13957
14055
|
options.nullable = isOptional;
|
|
@@ -14816,6 +14914,8 @@ var SchemaProcessor = class {
|
|
|
14816
14914
|
// Track imports per file for resolving ReturnType<typeof func>
|
|
14817
14915
|
importMap = {};
|
|
14818
14916
|
// { filePath: { importName: importPath } }
|
|
14917
|
+
// Inverted index: typeName → first filePath that imports it (O(1) lookup for findFileImportingType)
|
|
14918
|
+
typeToFileIndex = /* @__PURE__ */ new Map();
|
|
14819
14919
|
currentFilePath = "";
|
|
14820
14920
|
// Track the file being processed
|
|
14821
14921
|
constructor(schemaDir, schemaType = "typescript", schemaFiles, apiDir, fileAccess = defaultFileAccess2, runtime) {
|
|
@@ -14969,6 +15069,13 @@ var SchemaProcessor = class {
|
|
|
14969
15069
|
}
|
|
14970
15070
|
collectImports(ast, filePath) {
|
|
14971
15071
|
collectImports(ast, filePath, this.importMap);
|
|
15072
|
+
const normalizedPath = path14.normalize(filePath);
|
|
15073
|
+
const entries = this.importMap[normalizedPath] ?? {};
|
|
15074
|
+
for (const typeName of Object.keys(entries)) {
|
|
15075
|
+
if (!this.typeToFileIndex.has(typeName)) {
|
|
15076
|
+
this.typeToFileIndex.set(typeName, normalizedPath);
|
|
15077
|
+
}
|
|
15078
|
+
}
|
|
14972
15079
|
}
|
|
14973
15080
|
/**
|
|
14974
15081
|
* Resolve an import path relative to the current file
|
|
@@ -15007,6 +15114,15 @@ var SchemaProcessor = class {
|
|
|
15007
15114
|
}
|
|
15008
15115
|
const typeDefEntry = this.typeDefinitions[typeName.toString()];
|
|
15009
15116
|
if (!typeDefEntry) {
|
|
15117
|
+
const contextFile = this.findFileImportingType(typeName);
|
|
15118
|
+
if (contextFile) {
|
|
15119
|
+
logger.debug(`resolveType: "${typeName}" not in schema dirs; attempting TypeScript checker fallback via ${contextFile}`);
|
|
15120
|
+
const checkerSchema = this.resolveTypeWithTypeScriptChecker(typeName, contextFile);
|
|
15121
|
+
if (checkerSchema && Object.keys(checkerSchema).length > 0) {
|
|
15122
|
+
this.openapiDefinitions[typeName] = checkerSchema;
|
|
15123
|
+
return checkerSchema;
|
|
15124
|
+
}
|
|
15125
|
+
}
|
|
15010
15126
|
logger.debug(`resolveType: no TypeScript definition found for "${typeName}" in ${this.currentFilePath}; returning empty schema`);
|
|
15011
15127
|
return {};
|
|
15012
15128
|
}
|
|
@@ -15218,6 +15334,14 @@ var SchemaProcessor = class {
|
|
|
15218
15334
|
}
|
|
15219
15335
|
return null;
|
|
15220
15336
|
}
|
|
15337
|
+
/**
|
|
15338
|
+
* Return the path of the first scanned file that imports `typeName`, or `null` when none is
|
|
15339
|
+
* found. Used as a fallback context for {@link resolveTypeWithTypeScriptChecker} when the type
|
|
15340
|
+
* is not defined in any schema-dir file (e.g. comes from node_modules or a shared package).
|
|
15341
|
+
*/
|
|
15342
|
+
findFileImportingType(typeName) {
|
|
15343
|
+
return this.typeToFileIndex.get(typeName) ?? null;
|
|
15344
|
+
}
|
|
15221
15345
|
shouldUseTypeScriptChecker(node) {
|
|
15222
15346
|
return t15.isTSConditionalType(node) || t15.isTSMappedType(node) || t15.isTSTemplateLiteralType(node) || t15.isTSImportType(node) || t15.isTSTypeOperator(node) && node.operator === "keyof";
|
|
15223
15347
|
}
|
|
@@ -15281,7 +15405,9 @@ var SchemaProcessor = class {
|
|
|
15281
15405
|
if (seen.has(seenKey)) {
|
|
15282
15406
|
return { type: "object" };
|
|
15283
15407
|
}
|
|
15284
|
-
|
|
15408
|
+
if (!(type.flags & (primitiveLikeFlags | ts2.TypeFlags.Any | ts2.TypeFlags.Never | ts2.TypeFlags.Unknown | ts2.TypeFlags.Void))) {
|
|
15409
|
+
seen.add(seenKey);
|
|
15410
|
+
}
|
|
15285
15411
|
if (type.isStringLiteral()) {
|
|
15286
15412
|
return { type: "string", enum: [type.value] };
|
|
15287
15413
|
}
|
package/dist/index.js
CHANGED
|
@@ -2703,7 +2703,7 @@ var SymbolResolver = class {
|
|
|
2703
2703
|
|
|
2704
2704
|
// ../openapi-core/dist/schema/zod/drizzle-zod-processor.js
|
|
2705
2705
|
import * as t4 from "@babel/types";
|
|
2706
|
-
var DrizzleZodProcessor = class {
|
|
2706
|
+
var DrizzleZodProcessor = class _DrizzleZodProcessor {
|
|
2707
2707
|
/**
|
|
2708
2708
|
* Known drizzle-zod helper function names
|
|
2709
2709
|
*/
|
|
@@ -3129,6 +3129,16 @@ var DrizzleZodProcessor = class {
|
|
|
3129
3129
|
result.description = args[0].value;
|
|
3130
3130
|
}
|
|
3131
3131
|
break;
|
|
3132
|
+
case "meta": {
|
|
3133
|
+
const firstArg = args[0];
|
|
3134
|
+
if (firstArg && !t4.isSpreadElement(firstArg) && !t4.isArgumentPlaceholder(firstArg)) {
|
|
3135
|
+
const metadata = _DrizzleZodProcessor.extractStaticObject(firstArg);
|
|
3136
|
+
if (metadata) {
|
|
3137
|
+
Object.assign(result, metadata);
|
|
3138
|
+
}
|
|
3139
|
+
}
|
|
3140
|
+
break;
|
|
3141
|
+
}
|
|
3132
3142
|
case "default":
|
|
3133
3143
|
if (args.length > 0) {
|
|
3134
3144
|
if (t4.isStringLiteral(args[0])) {
|
|
@@ -3149,6 +3159,49 @@ var DrizzleZodProcessor = class {
|
|
|
3149
3159
|
static isDrizzleZodHelper(name) {
|
|
3150
3160
|
return this.DRIZZLE_ZOD_HELPERS.includes(name);
|
|
3151
3161
|
}
|
|
3162
|
+
static extractStaticObject(node) {
|
|
3163
|
+
if (!t4.isObjectExpression(node))
|
|
3164
|
+
return null;
|
|
3165
|
+
const out = {};
|
|
3166
|
+
for (const prop of node.properties) {
|
|
3167
|
+
if (!t4.isObjectProperty(prop))
|
|
3168
|
+
return null;
|
|
3169
|
+
const key = t4.isIdentifier(prop.key) ? prop.key.name : t4.isStringLiteral(prop.key) ? prop.key.value : null;
|
|
3170
|
+
if (!key)
|
|
3171
|
+
return null;
|
|
3172
|
+
const val = _DrizzleZodProcessor.extractStaticValue(prop.value);
|
|
3173
|
+
if (typeof val === "undefined")
|
|
3174
|
+
return null;
|
|
3175
|
+
out[key] = val;
|
|
3176
|
+
}
|
|
3177
|
+
return out;
|
|
3178
|
+
}
|
|
3179
|
+
static extractStaticValue(node) {
|
|
3180
|
+
if (t4.isStringLiteral(node))
|
|
3181
|
+
return node.value;
|
|
3182
|
+
if (t4.isNumericLiteral(node))
|
|
3183
|
+
return node.value;
|
|
3184
|
+
if (t4.isBooleanLiteral(node))
|
|
3185
|
+
return node.value;
|
|
3186
|
+
if (t4.isNullLiteral(node))
|
|
3187
|
+
return null;
|
|
3188
|
+
if (t4.isArrayExpression(node)) {
|
|
3189
|
+
const values = [];
|
|
3190
|
+
for (const el of node.elements) {
|
|
3191
|
+
if (!el || t4.isSpreadElement(el) || t4.isArgumentPlaceholder(el))
|
|
3192
|
+
return void 0;
|
|
3193
|
+
const v = _DrizzleZodProcessor.extractStaticValue(el);
|
|
3194
|
+
if (typeof v === "undefined")
|
|
3195
|
+
return void 0;
|
|
3196
|
+
values.push(v);
|
|
3197
|
+
}
|
|
3198
|
+
return values;
|
|
3199
|
+
}
|
|
3200
|
+
if (t4.isObjectExpression(node)) {
|
|
3201
|
+
return _DrizzleZodProcessor.extractStaticObject(node);
|
|
3202
|
+
}
|
|
3203
|
+
return void 0;
|
|
3204
|
+
}
|
|
3152
3205
|
};
|
|
3153
3206
|
|
|
3154
3207
|
// ../openapi-core/dist/schema/zod/converter-runtime.js
|
|
@@ -4119,8 +4172,12 @@ function processZodPrimitiveNode(node, context) {
|
|
|
4119
4172
|
};
|
|
4120
4173
|
break;
|
|
4121
4174
|
}
|
|
4175
|
+
case "strictObject":
|
|
4122
4176
|
case "object":
|
|
4123
4177
|
schema = node.arguments.length > 0 ? context.processObject(node) : { type: "object" };
|
|
4178
|
+
if (zodType === "strictObject") {
|
|
4179
|
+
schema.additionalProperties = false;
|
|
4180
|
+
}
|
|
4124
4181
|
break;
|
|
4125
4182
|
case "templateLiteral":
|
|
4126
4183
|
schema = { type: "string" };
|
|
@@ -11187,6 +11244,10 @@ var ZodRuntimeExporter = class {
|
|
|
11187
11244
|
return this.buildEnum(node);
|
|
11188
11245
|
case "array":
|
|
11189
11246
|
return node.arguments[0] && isProcessableNode(node.arguments[0]) ? array(this.buildSchema(node.arguments[0]) ?? unknown()) : array(unknown());
|
|
11247
|
+
case "strictObject": {
|
|
11248
|
+
const base = this.buildObject(node);
|
|
11249
|
+
return base && typeof base.strict === "function" ? base.strict() : base;
|
|
11250
|
+
}
|
|
11190
11251
|
case "object":
|
|
11191
11252
|
return this.buildObject(node);
|
|
11192
11253
|
case "record":
|
|
@@ -11594,7 +11655,9 @@ var ZodSchemaConverter = class {
|
|
|
11594
11655
|
apiDir;
|
|
11595
11656
|
zodSchemas = {};
|
|
11596
11657
|
processingSchemas = /* @__PURE__ */ new Set();
|
|
11597
|
-
|
|
11658
|
+
/** Memoization guard for processFileForZodSchema. Keys: `${filePath}|${schemaName}`.
|
|
11659
|
+
* Prevents infinite recursion when re-export files reference schemas via z.infer<typeof X>. */
|
|
11660
|
+
processedFileSchemaPairs = /* @__PURE__ */ new Set();
|
|
11598
11661
|
typeToSchemaMapping = {};
|
|
11599
11662
|
drizzleZodImports = /* @__PURE__ */ new Set();
|
|
11600
11663
|
factoryCache = /* @__PURE__ */ new Map();
|
|
@@ -11808,6 +11871,11 @@ var ZodSchemaConverter = class {
|
|
|
11808
11871
|
* Process a file to find Zod schema definitions
|
|
11809
11872
|
*/
|
|
11810
11873
|
processFileForZodSchema(filePath, schemaName) {
|
|
11874
|
+
const visitKey = `${filePath}|${schemaName}|${this.currentContentType}`;
|
|
11875
|
+
if (this.processedFileSchemaPairs.has(visitKey)) {
|
|
11876
|
+
return;
|
|
11877
|
+
}
|
|
11878
|
+
this.processedFileSchemaPairs.add(visitKey);
|
|
11811
11879
|
try {
|
|
11812
11880
|
const content = this.fileAccess.readFileSync(filePath, "utf-8");
|
|
11813
11881
|
if (!content.includes(schemaName)) {
|
|
@@ -12121,7 +12189,9 @@ var ZodSchemaConverter = class {
|
|
|
12121
12189
|
const param = path25.node.typeAnnotation.typeParameters.params[0];
|
|
12122
12190
|
if (t10.isTSTypeQuery(param) && t10.isIdentifier(param.exprName)) {
|
|
12123
12191
|
const referencedSchemaName = param.exprName.name;
|
|
12124
|
-
this.
|
|
12192
|
+
if (!this.getStoredSchema(referencedSchemaName)) {
|
|
12193
|
+
this.processFileForZodSchema(filePath, referencedSchemaName);
|
|
12194
|
+
}
|
|
12125
12195
|
}
|
|
12126
12196
|
}
|
|
12127
12197
|
}
|
|
@@ -12206,8 +12276,12 @@ var ZodSchemaConverter = class {
|
|
|
12206
12276
|
}
|
|
12207
12277
|
if (t10.isCallExpression(node) && t10.isMemberExpression(node.callee) && t10.isIdentifier(node.callee.object) && this.isZodLocalName(node.callee.object.name) && t10.isIdentifier(node.callee.property)) {
|
|
12208
12278
|
const methodName = node.callee.property.name;
|
|
12209
|
-
if (methodName === "object" && node.arguments.length > 0) {
|
|
12210
|
-
|
|
12279
|
+
if ((methodName === "object" || methodName === "strictObject") && node.arguments.length > 0) {
|
|
12280
|
+
const schema = this.processZodObject(node);
|
|
12281
|
+
if (methodName === "strictObject") {
|
|
12282
|
+
schema.additionalProperties = false;
|
|
12283
|
+
}
|
|
12284
|
+
return schema;
|
|
12211
12285
|
} else if (methodName === "union" && node.arguments.length > 0) {
|
|
12212
12286
|
return this.processZodUnion(node);
|
|
12213
12287
|
} else if (methodName === "intersection" && node.arguments.length > 0) {
|
|
@@ -13492,15 +13566,39 @@ function extractKeysFromLiteralType(node) {
|
|
|
13492
13566
|
}
|
|
13493
13567
|
return [];
|
|
13494
13568
|
}
|
|
13569
|
+
function parsePropertyComment(commentValue) {
|
|
13570
|
+
const text = commentValue.split("\n").map((line) => line.replace(/^\s*\*\s?/, "").trim()).filter((line) => line.length > 0).join(" ").trim();
|
|
13571
|
+
const result = {};
|
|
13572
|
+
let remaining = text;
|
|
13573
|
+
const formatMatch = remaining.match(/@format\s+(\S+)/);
|
|
13574
|
+
if (formatMatch?.[1]) {
|
|
13575
|
+
result.format = formatMatch[1];
|
|
13576
|
+
remaining = remaining.replace(formatMatch[0], "").trim();
|
|
13577
|
+
}
|
|
13578
|
+
const exampleMatch = remaining.match(/@example\s+(.+?)(?=\s*@\w|$)/);
|
|
13579
|
+
if (exampleMatch?.[1]) {
|
|
13580
|
+
const raw = exampleMatch[1].trim();
|
|
13581
|
+
try {
|
|
13582
|
+
result.example = JSON.parse(raw);
|
|
13583
|
+
} catch {
|
|
13584
|
+
result.example = raw;
|
|
13585
|
+
}
|
|
13586
|
+
remaining = remaining.replace(exampleMatch[0], "").trim();
|
|
13587
|
+
}
|
|
13588
|
+
remaining = remaining.replace(/@\w+(?:\s+\S+)*/g, "").trim();
|
|
13589
|
+
if (remaining) {
|
|
13590
|
+
result.description = remaining;
|
|
13591
|
+
}
|
|
13592
|
+
return result;
|
|
13593
|
+
}
|
|
13495
13594
|
function getPropertyOptions(node, contentType) {
|
|
13496
13595
|
const isOptional = !!node.optional;
|
|
13497
|
-
let description = null;
|
|
13498
|
-
if (node.trailingComments && node.trailingComments.length) {
|
|
13499
|
-
description = node.trailingComments[0].value.trim();
|
|
13500
|
-
}
|
|
13501
13596
|
const options = {};
|
|
13502
|
-
|
|
13503
|
-
|
|
13597
|
+
const leadingComment = node.leadingComments?.[node.leadingComments.length - 1];
|
|
13598
|
+
const trailingComment = node.trailingComments?.[0];
|
|
13599
|
+
const sourceComment = leadingComment ?? trailingComment;
|
|
13600
|
+
if (sourceComment) {
|
|
13601
|
+
Object.assign(options, parsePropertyComment(sourceComment.value));
|
|
13504
13602
|
}
|
|
13505
13603
|
if (contentType === "body") {
|
|
13506
13604
|
options.nullable = isOptional;
|
|
@@ -14365,6 +14463,8 @@ var SchemaProcessor = class {
|
|
|
14365
14463
|
// Track imports per file for resolving ReturnType<typeof func>
|
|
14366
14464
|
importMap = {};
|
|
14367
14465
|
// { filePath: { importName: importPath } }
|
|
14466
|
+
// Inverted index: typeName → first filePath that imports it (O(1) lookup for findFileImportingType)
|
|
14467
|
+
typeToFileIndex = /* @__PURE__ */ new Map();
|
|
14368
14468
|
currentFilePath = "";
|
|
14369
14469
|
// Track the file being processed
|
|
14370
14470
|
constructor(schemaDir, schemaType = "typescript", schemaFiles, apiDir, fileAccess = defaultFileAccess2, runtime) {
|
|
@@ -14518,6 +14618,13 @@ var SchemaProcessor = class {
|
|
|
14518
14618
|
}
|
|
14519
14619
|
collectImports(ast, filePath) {
|
|
14520
14620
|
collectImports(ast, filePath, this.importMap);
|
|
14621
|
+
const normalizedPath = path9.normalize(filePath);
|
|
14622
|
+
const entries = this.importMap[normalizedPath] ?? {};
|
|
14623
|
+
for (const typeName of Object.keys(entries)) {
|
|
14624
|
+
if (!this.typeToFileIndex.has(typeName)) {
|
|
14625
|
+
this.typeToFileIndex.set(typeName, normalizedPath);
|
|
14626
|
+
}
|
|
14627
|
+
}
|
|
14521
14628
|
}
|
|
14522
14629
|
/**
|
|
14523
14630
|
* Resolve an import path relative to the current file
|
|
@@ -14556,6 +14663,15 @@ var SchemaProcessor = class {
|
|
|
14556
14663
|
}
|
|
14557
14664
|
const typeDefEntry = this.typeDefinitions[typeName.toString()];
|
|
14558
14665
|
if (!typeDefEntry) {
|
|
14666
|
+
const contextFile = this.findFileImportingType(typeName);
|
|
14667
|
+
if (contextFile) {
|
|
14668
|
+
logger.debug(`resolveType: "${typeName}" not in schema dirs; attempting TypeScript checker fallback via ${contextFile}`);
|
|
14669
|
+
const checkerSchema = this.resolveTypeWithTypeScriptChecker(typeName, contextFile);
|
|
14670
|
+
if (checkerSchema && Object.keys(checkerSchema).length > 0) {
|
|
14671
|
+
this.openapiDefinitions[typeName] = checkerSchema;
|
|
14672
|
+
return checkerSchema;
|
|
14673
|
+
}
|
|
14674
|
+
}
|
|
14559
14675
|
logger.debug(`resolveType: no TypeScript definition found for "${typeName}" in ${this.currentFilePath}; returning empty schema`);
|
|
14560
14676
|
return {};
|
|
14561
14677
|
}
|
|
@@ -14767,6 +14883,14 @@ var SchemaProcessor = class {
|
|
|
14767
14883
|
}
|
|
14768
14884
|
return null;
|
|
14769
14885
|
}
|
|
14886
|
+
/**
|
|
14887
|
+
* Return the path of the first scanned file that imports `typeName`, or `null` when none is
|
|
14888
|
+
* found. Used as a fallback context for {@link resolveTypeWithTypeScriptChecker} when the type
|
|
14889
|
+
* is not defined in any schema-dir file (e.g. comes from node_modules or a shared package).
|
|
14890
|
+
*/
|
|
14891
|
+
findFileImportingType(typeName) {
|
|
14892
|
+
return this.typeToFileIndex.get(typeName) ?? null;
|
|
14893
|
+
}
|
|
14770
14894
|
shouldUseTypeScriptChecker(node) {
|
|
14771
14895
|
return t15.isTSConditionalType(node) || t15.isTSMappedType(node) || t15.isTSTemplateLiteralType(node) || t15.isTSImportType(node) || t15.isTSTypeOperator(node) && node.operator === "keyof";
|
|
14772
14896
|
}
|
|
@@ -14830,7 +14954,9 @@ var SchemaProcessor = class {
|
|
|
14830
14954
|
if (seen.has(seenKey)) {
|
|
14831
14955
|
return { type: "object" };
|
|
14832
14956
|
}
|
|
14833
|
-
|
|
14957
|
+
if (!(type.flags & (primitiveLikeFlags | ts2.TypeFlags.Any | ts2.TypeFlags.Never | ts2.TypeFlags.Unknown | ts2.TypeFlags.Void))) {
|
|
14958
|
+
seen.add(seenKey);
|
|
14959
|
+
}
|
|
14834
14960
|
if (type.isStringLiteral()) {
|
|
14835
14961
|
return { type: "string", enum: [type.value] };
|
|
14836
14962
|
}
|
package/dist/next/index.js
CHANGED
|
@@ -3737,7 +3737,7 @@ var SymbolResolver = class {
|
|
|
3737
3737
|
|
|
3738
3738
|
// ../openapi-core/dist/schema/zod/drizzle-zod-processor.js
|
|
3739
3739
|
import * as t5 from "@babel/types";
|
|
3740
|
-
var DrizzleZodProcessor = class {
|
|
3740
|
+
var DrizzleZodProcessor = class _DrizzleZodProcessor {
|
|
3741
3741
|
/**
|
|
3742
3742
|
* Known drizzle-zod helper function names
|
|
3743
3743
|
*/
|
|
@@ -4163,6 +4163,16 @@ var DrizzleZodProcessor = class {
|
|
|
4163
4163
|
result.description = args[0].value;
|
|
4164
4164
|
}
|
|
4165
4165
|
break;
|
|
4166
|
+
case "meta": {
|
|
4167
|
+
const firstArg = args[0];
|
|
4168
|
+
if (firstArg && !t5.isSpreadElement(firstArg) && !t5.isArgumentPlaceholder(firstArg)) {
|
|
4169
|
+
const metadata = _DrizzleZodProcessor.extractStaticObject(firstArg);
|
|
4170
|
+
if (metadata) {
|
|
4171
|
+
Object.assign(result, metadata);
|
|
4172
|
+
}
|
|
4173
|
+
}
|
|
4174
|
+
break;
|
|
4175
|
+
}
|
|
4166
4176
|
case "default":
|
|
4167
4177
|
if (args.length > 0) {
|
|
4168
4178
|
if (t5.isStringLiteral(args[0])) {
|
|
@@ -4183,6 +4193,49 @@ var DrizzleZodProcessor = class {
|
|
|
4183
4193
|
static isDrizzleZodHelper(name) {
|
|
4184
4194
|
return this.DRIZZLE_ZOD_HELPERS.includes(name);
|
|
4185
4195
|
}
|
|
4196
|
+
static extractStaticObject(node) {
|
|
4197
|
+
if (!t5.isObjectExpression(node))
|
|
4198
|
+
return null;
|
|
4199
|
+
const out = {};
|
|
4200
|
+
for (const prop of node.properties) {
|
|
4201
|
+
if (!t5.isObjectProperty(prop))
|
|
4202
|
+
return null;
|
|
4203
|
+
const key = t5.isIdentifier(prop.key) ? prop.key.name : t5.isStringLiteral(prop.key) ? prop.key.value : null;
|
|
4204
|
+
if (!key)
|
|
4205
|
+
return null;
|
|
4206
|
+
const val = _DrizzleZodProcessor.extractStaticValue(prop.value);
|
|
4207
|
+
if (typeof val === "undefined")
|
|
4208
|
+
return null;
|
|
4209
|
+
out[key] = val;
|
|
4210
|
+
}
|
|
4211
|
+
return out;
|
|
4212
|
+
}
|
|
4213
|
+
static extractStaticValue(node) {
|
|
4214
|
+
if (t5.isStringLiteral(node))
|
|
4215
|
+
return node.value;
|
|
4216
|
+
if (t5.isNumericLiteral(node))
|
|
4217
|
+
return node.value;
|
|
4218
|
+
if (t5.isBooleanLiteral(node))
|
|
4219
|
+
return node.value;
|
|
4220
|
+
if (t5.isNullLiteral(node))
|
|
4221
|
+
return null;
|
|
4222
|
+
if (t5.isArrayExpression(node)) {
|
|
4223
|
+
const values = [];
|
|
4224
|
+
for (const el of node.elements) {
|
|
4225
|
+
if (!el || t5.isSpreadElement(el) || t5.isArgumentPlaceholder(el))
|
|
4226
|
+
return void 0;
|
|
4227
|
+
const v = _DrizzleZodProcessor.extractStaticValue(el);
|
|
4228
|
+
if (typeof v === "undefined")
|
|
4229
|
+
return void 0;
|
|
4230
|
+
values.push(v);
|
|
4231
|
+
}
|
|
4232
|
+
return values;
|
|
4233
|
+
}
|
|
4234
|
+
if (t5.isObjectExpression(node)) {
|
|
4235
|
+
return _DrizzleZodProcessor.extractStaticObject(node);
|
|
4236
|
+
}
|
|
4237
|
+
return void 0;
|
|
4238
|
+
}
|
|
4186
4239
|
};
|
|
4187
4240
|
|
|
4188
4241
|
// ../openapi-core/dist/schema/zod/converter-runtime.js
|
|
@@ -5153,8 +5206,12 @@ function processZodPrimitiveNode(node, context) {
|
|
|
5153
5206
|
};
|
|
5154
5207
|
break;
|
|
5155
5208
|
}
|
|
5209
|
+
case "strictObject":
|
|
5156
5210
|
case "object":
|
|
5157
5211
|
schema = node.arguments.length > 0 ? context.processObject(node) : { type: "object" };
|
|
5212
|
+
if (zodType === "strictObject") {
|
|
5213
|
+
schema.additionalProperties = false;
|
|
5214
|
+
}
|
|
5158
5215
|
break;
|
|
5159
5216
|
case "templateLiteral":
|
|
5160
5217
|
schema = { type: "string" };
|
|
@@ -12221,6 +12278,10 @@ var ZodRuntimeExporter = class {
|
|
|
12221
12278
|
return this.buildEnum(node);
|
|
12222
12279
|
case "array":
|
|
12223
12280
|
return node.arguments[0] && isProcessableNode(node.arguments[0]) ? array(this.buildSchema(node.arguments[0]) ?? unknown()) : array(unknown());
|
|
12281
|
+
case "strictObject": {
|
|
12282
|
+
const base = this.buildObject(node);
|
|
12283
|
+
return base && typeof base.strict === "function" ? base.strict() : base;
|
|
12284
|
+
}
|
|
12224
12285
|
case "object":
|
|
12225
12286
|
return this.buildObject(node);
|
|
12226
12287
|
case "record":
|
|
@@ -12628,7 +12689,9 @@ var ZodSchemaConverter = class {
|
|
|
12628
12689
|
apiDir;
|
|
12629
12690
|
zodSchemas = {};
|
|
12630
12691
|
processingSchemas = /* @__PURE__ */ new Set();
|
|
12631
|
-
|
|
12692
|
+
/** Memoization guard for processFileForZodSchema. Keys: `${filePath}|${schemaName}`.
|
|
12693
|
+
* Prevents infinite recursion when re-export files reference schemas via z.infer<typeof X>. */
|
|
12694
|
+
processedFileSchemaPairs = /* @__PURE__ */ new Set();
|
|
12632
12695
|
typeToSchemaMapping = {};
|
|
12633
12696
|
drizzleZodImports = /* @__PURE__ */ new Set();
|
|
12634
12697
|
factoryCache = /* @__PURE__ */ new Map();
|
|
@@ -12842,6 +12905,11 @@ var ZodSchemaConverter = class {
|
|
|
12842
12905
|
* Process a file to find Zod schema definitions
|
|
12843
12906
|
*/
|
|
12844
12907
|
processFileForZodSchema(filePath, schemaName) {
|
|
12908
|
+
const visitKey = `${filePath}|${schemaName}|${this.currentContentType}`;
|
|
12909
|
+
if (this.processedFileSchemaPairs.has(visitKey)) {
|
|
12910
|
+
return;
|
|
12911
|
+
}
|
|
12912
|
+
this.processedFileSchemaPairs.add(visitKey);
|
|
12845
12913
|
try {
|
|
12846
12914
|
const content = this.fileAccess.readFileSync(filePath, "utf-8");
|
|
12847
12915
|
if (!content.includes(schemaName)) {
|
|
@@ -13155,7 +13223,9 @@ var ZodSchemaConverter = class {
|
|
|
13155
13223
|
const param = path19.node.typeAnnotation.typeParameters.params[0];
|
|
13156
13224
|
if (t11.isTSTypeQuery(param) && t11.isIdentifier(param.exprName)) {
|
|
13157
13225
|
const referencedSchemaName = param.exprName.name;
|
|
13158
|
-
this.
|
|
13226
|
+
if (!this.getStoredSchema(referencedSchemaName)) {
|
|
13227
|
+
this.processFileForZodSchema(filePath, referencedSchemaName);
|
|
13228
|
+
}
|
|
13159
13229
|
}
|
|
13160
13230
|
}
|
|
13161
13231
|
}
|
|
@@ -13240,8 +13310,12 @@ var ZodSchemaConverter = class {
|
|
|
13240
13310
|
}
|
|
13241
13311
|
if (t11.isCallExpression(node) && t11.isMemberExpression(node.callee) && t11.isIdentifier(node.callee.object) && this.isZodLocalName(node.callee.object.name) && t11.isIdentifier(node.callee.property)) {
|
|
13242
13312
|
const methodName = node.callee.property.name;
|
|
13243
|
-
if (methodName === "object" && node.arguments.length > 0) {
|
|
13244
|
-
|
|
13313
|
+
if ((methodName === "object" || methodName === "strictObject") && node.arguments.length > 0) {
|
|
13314
|
+
const schema = this.processZodObject(node);
|
|
13315
|
+
if (methodName === "strictObject") {
|
|
13316
|
+
schema.additionalProperties = false;
|
|
13317
|
+
}
|
|
13318
|
+
return schema;
|
|
13245
13319
|
} else if (methodName === "union" && node.arguments.length > 0) {
|
|
13246
13320
|
return this.processZodUnion(node);
|
|
13247
13321
|
} else if (methodName === "intersection" && node.arguments.length > 0) {
|
|
@@ -14526,15 +14600,39 @@ function extractKeysFromLiteralType(node) {
|
|
|
14526
14600
|
}
|
|
14527
14601
|
return [];
|
|
14528
14602
|
}
|
|
14603
|
+
function parsePropertyComment(commentValue) {
|
|
14604
|
+
const text = commentValue.split("\n").map((line) => line.replace(/^\s*\*\s?/, "").trim()).filter((line) => line.length > 0).join(" ").trim();
|
|
14605
|
+
const result = {};
|
|
14606
|
+
let remaining = text;
|
|
14607
|
+
const formatMatch = remaining.match(/@format\s+(\S+)/);
|
|
14608
|
+
if (formatMatch?.[1]) {
|
|
14609
|
+
result.format = formatMatch[1];
|
|
14610
|
+
remaining = remaining.replace(formatMatch[0], "").trim();
|
|
14611
|
+
}
|
|
14612
|
+
const exampleMatch = remaining.match(/@example\s+(.+?)(?=\s*@\w|$)/);
|
|
14613
|
+
if (exampleMatch?.[1]) {
|
|
14614
|
+
const raw = exampleMatch[1].trim();
|
|
14615
|
+
try {
|
|
14616
|
+
result.example = JSON.parse(raw);
|
|
14617
|
+
} catch {
|
|
14618
|
+
result.example = raw;
|
|
14619
|
+
}
|
|
14620
|
+
remaining = remaining.replace(exampleMatch[0], "").trim();
|
|
14621
|
+
}
|
|
14622
|
+
remaining = remaining.replace(/@\w+(?:\s+\S+)*/g, "").trim();
|
|
14623
|
+
if (remaining) {
|
|
14624
|
+
result.description = remaining;
|
|
14625
|
+
}
|
|
14626
|
+
return result;
|
|
14627
|
+
}
|
|
14529
14628
|
function getPropertyOptions(node, contentType) {
|
|
14530
14629
|
const isOptional = !!node.optional;
|
|
14531
|
-
let description = null;
|
|
14532
|
-
if (node.trailingComments && node.trailingComments.length) {
|
|
14533
|
-
description = node.trailingComments[0].value.trim();
|
|
14534
|
-
}
|
|
14535
14630
|
const options = {};
|
|
14536
|
-
|
|
14537
|
-
|
|
14631
|
+
const leadingComment = node.leadingComments?.[node.leadingComments.length - 1];
|
|
14632
|
+
const trailingComment = node.trailingComments?.[0];
|
|
14633
|
+
const sourceComment = leadingComment ?? trailingComment;
|
|
14634
|
+
if (sourceComment) {
|
|
14635
|
+
Object.assign(options, parsePropertyComment(sourceComment.value));
|
|
14538
14636
|
}
|
|
14539
14637
|
if (contentType === "body") {
|
|
14540
14638
|
options.nullable = isOptional;
|
|
@@ -15399,6 +15497,8 @@ var SchemaProcessor = class {
|
|
|
15399
15497
|
// Track imports per file for resolving ReturnType<typeof func>
|
|
15400
15498
|
importMap = {};
|
|
15401
15499
|
// { filePath: { importName: importPath } }
|
|
15500
|
+
// Inverted index: typeName → first filePath that imports it (O(1) lookup for findFileImportingType)
|
|
15501
|
+
typeToFileIndex = /* @__PURE__ */ new Map();
|
|
15402
15502
|
currentFilePath = "";
|
|
15403
15503
|
// Track the file being processed
|
|
15404
15504
|
constructor(schemaDir, schemaType = "typescript", schemaFiles, apiDir, fileAccess = defaultFileAccess2, runtime) {
|
|
@@ -15552,6 +15652,13 @@ var SchemaProcessor = class {
|
|
|
15552
15652
|
}
|
|
15553
15653
|
collectImports(ast, filePath) {
|
|
15554
15654
|
collectImports(ast, filePath, this.importMap);
|
|
15655
|
+
const normalizedPath = path12.normalize(filePath);
|
|
15656
|
+
const entries = this.importMap[normalizedPath] ?? {};
|
|
15657
|
+
for (const typeName of Object.keys(entries)) {
|
|
15658
|
+
if (!this.typeToFileIndex.has(typeName)) {
|
|
15659
|
+
this.typeToFileIndex.set(typeName, normalizedPath);
|
|
15660
|
+
}
|
|
15661
|
+
}
|
|
15555
15662
|
}
|
|
15556
15663
|
/**
|
|
15557
15664
|
* Resolve an import path relative to the current file
|
|
@@ -15590,6 +15697,15 @@ var SchemaProcessor = class {
|
|
|
15590
15697
|
}
|
|
15591
15698
|
const typeDefEntry = this.typeDefinitions[typeName.toString()];
|
|
15592
15699
|
if (!typeDefEntry) {
|
|
15700
|
+
const contextFile = this.findFileImportingType(typeName);
|
|
15701
|
+
if (contextFile) {
|
|
15702
|
+
logger.debug(`resolveType: "${typeName}" not in schema dirs; attempting TypeScript checker fallback via ${contextFile}`);
|
|
15703
|
+
const checkerSchema = this.resolveTypeWithTypeScriptChecker(typeName, contextFile);
|
|
15704
|
+
if (checkerSchema && Object.keys(checkerSchema).length > 0) {
|
|
15705
|
+
this.openapiDefinitions[typeName] = checkerSchema;
|
|
15706
|
+
return checkerSchema;
|
|
15707
|
+
}
|
|
15708
|
+
}
|
|
15593
15709
|
logger.debug(`resolveType: no TypeScript definition found for "${typeName}" in ${this.currentFilePath}; returning empty schema`);
|
|
15594
15710
|
return {};
|
|
15595
15711
|
}
|
|
@@ -15801,6 +15917,14 @@ var SchemaProcessor = class {
|
|
|
15801
15917
|
}
|
|
15802
15918
|
return null;
|
|
15803
15919
|
}
|
|
15920
|
+
/**
|
|
15921
|
+
* Return the path of the first scanned file that imports `typeName`, or `null` when none is
|
|
15922
|
+
* found. Used as a fallback context for {@link resolveTypeWithTypeScriptChecker} when the type
|
|
15923
|
+
* is not defined in any schema-dir file (e.g. comes from node_modules or a shared package).
|
|
15924
|
+
*/
|
|
15925
|
+
findFileImportingType(typeName) {
|
|
15926
|
+
return this.typeToFileIndex.get(typeName) ?? null;
|
|
15927
|
+
}
|
|
15804
15928
|
shouldUseTypeScriptChecker(node) {
|
|
15805
15929
|
return t16.isTSConditionalType(node) || t16.isTSMappedType(node) || t16.isTSTemplateLiteralType(node) || t16.isTSImportType(node) || t16.isTSTypeOperator(node) && node.operator === "keyof";
|
|
15806
15930
|
}
|
|
@@ -15864,7 +15988,9 @@ var SchemaProcessor = class {
|
|
|
15864
15988
|
if (seen.has(seenKey)) {
|
|
15865
15989
|
return { type: "object" };
|
|
15866
15990
|
}
|
|
15867
|
-
|
|
15991
|
+
if (!(type.flags & (primitiveLikeFlags | ts3.TypeFlags.Any | ts3.TypeFlags.Never | ts3.TypeFlags.Unknown | ts3.TypeFlags.Void))) {
|
|
15992
|
+
seen.add(seenKey);
|
|
15993
|
+
}
|
|
15868
15994
|
if (type.isStringLiteral()) {
|
|
15869
15995
|
return { type: "string", enum: [type.value] };
|
|
15870
15996
|
}
|
|
@@ -2741,7 +2741,7 @@ var SymbolResolver = class {
|
|
|
2741
2741
|
|
|
2742
2742
|
// ../openapi-core/dist/schema/zod/drizzle-zod-processor.js
|
|
2743
2743
|
import * as t4 from "@babel/types";
|
|
2744
|
-
var DrizzleZodProcessor = class {
|
|
2744
|
+
var DrizzleZodProcessor = class _DrizzleZodProcessor {
|
|
2745
2745
|
/**
|
|
2746
2746
|
* Known drizzle-zod helper function names
|
|
2747
2747
|
*/
|
|
@@ -3167,6 +3167,16 @@ var DrizzleZodProcessor = class {
|
|
|
3167
3167
|
result.description = args[0].value;
|
|
3168
3168
|
}
|
|
3169
3169
|
break;
|
|
3170
|
+
case "meta": {
|
|
3171
|
+
const firstArg = args[0];
|
|
3172
|
+
if (firstArg && !t4.isSpreadElement(firstArg) && !t4.isArgumentPlaceholder(firstArg)) {
|
|
3173
|
+
const metadata = _DrizzleZodProcessor.extractStaticObject(firstArg);
|
|
3174
|
+
if (metadata) {
|
|
3175
|
+
Object.assign(result, metadata);
|
|
3176
|
+
}
|
|
3177
|
+
}
|
|
3178
|
+
break;
|
|
3179
|
+
}
|
|
3170
3180
|
case "default":
|
|
3171
3181
|
if (args.length > 0) {
|
|
3172
3182
|
if (t4.isStringLiteral(args[0])) {
|
|
@@ -3187,6 +3197,49 @@ var DrizzleZodProcessor = class {
|
|
|
3187
3197
|
static isDrizzleZodHelper(name) {
|
|
3188
3198
|
return this.DRIZZLE_ZOD_HELPERS.includes(name);
|
|
3189
3199
|
}
|
|
3200
|
+
static extractStaticObject(node) {
|
|
3201
|
+
if (!t4.isObjectExpression(node))
|
|
3202
|
+
return null;
|
|
3203
|
+
const out = {};
|
|
3204
|
+
for (const prop of node.properties) {
|
|
3205
|
+
if (!t4.isObjectProperty(prop))
|
|
3206
|
+
return null;
|
|
3207
|
+
const key = t4.isIdentifier(prop.key) ? prop.key.name : t4.isStringLiteral(prop.key) ? prop.key.value : null;
|
|
3208
|
+
if (!key)
|
|
3209
|
+
return null;
|
|
3210
|
+
const val = _DrizzleZodProcessor.extractStaticValue(prop.value);
|
|
3211
|
+
if (typeof val === "undefined")
|
|
3212
|
+
return null;
|
|
3213
|
+
out[key] = val;
|
|
3214
|
+
}
|
|
3215
|
+
return out;
|
|
3216
|
+
}
|
|
3217
|
+
static extractStaticValue(node) {
|
|
3218
|
+
if (t4.isStringLiteral(node))
|
|
3219
|
+
return node.value;
|
|
3220
|
+
if (t4.isNumericLiteral(node))
|
|
3221
|
+
return node.value;
|
|
3222
|
+
if (t4.isBooleanLiteral(node))
|
|
3223
|
+
return node.value;
|
|
3224
|
+
if (t4.isNullLiteral(node))
|
|
3225
|
+
return null;
|
|
3226
|
+
if (t4.isArrayExpression(node)) {
|
|
3227
|
+
const values = [];
|
|
3228
|
+
for (const el of node.elements) {
|
|
3229
|
+
if (!el || t4.isSpreadElement(el) || t4.isArgumentPlaceholder(el))
|
|
3230
|
+
return void 0;
|
|
3231
|
+
const v = _DrizzleZodProcessor.extractStaticValue(el);
|
|
3232
|
+
if (typeof v === "undefined")
|
|
3233
|
+
return void 0;
|
|
3234
|
+
values.push(v);
|
|
3235
|
+
}
|
|
3236
|
+
return values;
|
|
3237
|
+
}
|
|
3238
|
+
if (t4.isObjectExpression(node)) {
|
|
3239
|
+
return _DrizzleZodProcessor.extractStaticObject(node);
|
|
3240
|
+
}
|
|
3241
|
+
return void 0;
|
|
3242
|
+
}
|
|
3190
3243
|
};
|
|
3191
3244
|
|
|
3192
3245
|
// ../openapi-core/dist/schema/zod/converter-runtime.js
|
|
@@ -4157,8 +4210,12 @@ function processZodPrimitiveNode(node, context) {
|
|
|
4157
4210
|
};
|
|
4158
4211
|
break;
|
|
4159
4212
|
}
|
|
4213
|
+
case "strictObject":
|
|
4160
4214
|
case "object":
|
|
4161
4215
|
schema = node.arguments.length > 0 ? context.processObject(node) : { type: "object" };
|
|
4216
|
+
if (zodType === "strictObject") {
|
|
4217
|
+
schema.additionalProperties = false;
|
|
4218
|
+
}
|
|
4162
4219
|
break;
|
|
4163
4220
|
case "templateLiteral":
|
|
4164
4221
|
schema = { type: "string" };
|
|
@@ -11225,6 +11282,10 @@ var ZodRuntimeExporter = class {
|
|
|
11225
11282
|
return this.buildEnum(node);
|
|
11226
11283
|
case "array":
|
|
11227
11284
|
return node.arguments[0] && isProcessableNode(node.arguments[0]) ? array(this.buildSchema(node.arguments[0]) ?? unknown()) : array(unknown());
|
|
11285
|
+
case "strictObject": {
|
|
11286
|
+
const base = this.buildObject(node);
|
|
11287
|
+
return base && typeof base.strict === "function" ? base.strict() : base;
|
|
11288
|
+
}
|
|
11228
11289
|
case "object":
|
|
11229
11290
|
return this.buildObject(node);
|
|
11230
11291
|
case "record":
|
|
@@ -11632,7 +11693,9 @@ var ZodSchemaConverter = class {
|
|
|
11632
11693
|
apiDir;
|
|
11633
11694
|
zodSchemas = {};
|
|
11634
11695
|
processingSchemas = /* @__PURE__ */ new Set();
|
|
11635
|
-
|
|
11696
|
+
/** Memoization guard for processFileForZodSchema. Keys: `${filePath}|${schemaName}`.
|
|
11697
|
+
* Prevents infinite recursion when re-export files reference schemas via z.infer<typeof X>. */
|
|
11698
|
+
processedFileSchemaPairs = /* @__PURE__ */ new Set();
|
|
11636
11699
|
typeToSchemaMapping = {};
|
|
11637
11700
|
drizzleZodImports = /* @__PURE__ */ new Set();
|
|
11638
11701
|
factoryCache = /* @__PURE__ */ new Map();
|
|
@@ -11846,6 +11909,11 @@ var ZodSchemaConverter = class {
|
|
|
11846
11909
|
* Process a file to find Zod schema definitions
|
|
11847
11910
|
*/
|
|
11848
11911
|
processFileForZodSchema(filePath, schemaName) {
|
|
11912
|
+
const visitKey = `${filePath}|${schemaName}|${this.currentContentType}`;
|
|
11913
|
+
if (this.processedFileSchemaPairs.has(visitKey)) {
|
|
11914
|
+
return;
|
|
11915
|
+
}
|
|
11916
|
+
this.processedFileSchemaPairs.add(visitKey);
|
|
11849
11917
|
try {
|
|
11850
11918
|
const content = this.fileAccess.readFileSync(filePath, "utf-8");
|
|
11851
11919
|
if (!content.includes(schemaName)) {
|
|
@@ -12159,7 +12227,9 @@ var ZodSchemaConverter = class {
|
|
|
12159
12227
|
const param = path17.node.typeAnnotation.typeParameters.params[0];
|
|
12160
12228
|
if (t10.isTSTypeQuery(param) && t10.isIdentifier(param.exprName)) {
|
|
12161
12229
|
const referencedSchemaName = param.exprName.name;
|
|
12162
|
-
this.
|
|
12230
|
+
if (!this.getStoredSchema(referencedSchemaName)) {
|
|
12231
|
+
this.processFileForZodSchema(filePath, referencedSchemaName);
|
|
12232
|
+
}
|
|
12163
12233
|
}
|
|
12164
12234
|
}
|
|
12165
12235
|
}
|
|
@@ -12244,8 +12314,12 @@ var ZodSchemaConverter = class {
|
|
|
12244
12314
|
}
|
|
12245
12315
|
if (t10.isCallExpression(node) && t10.isMemberExpression(node.callee) && t10.isIdentifier(node.callee.object) && this.isZodLocalName(node.callee.object.name) && t10.isIdentifier(node.callee.property)) {
|
|
12246
12316
|
const methodName = node.callee.property.name;
|
|
12247
|
-
if (methodName === "object" && node.arguments.length > 0) {
|
|
12248
|
-
|
|
12317
|
+
if ((methodName === "object" || methodName === "strictObject") && node.arguments.length > 0) {
|
|
12318
|
+
const schema = this.processZodObject(node);
|
|
12319
|
+
if (methodName === "strictObject") {
|
|
12320
|
+
schema.additionalProperties = false;
|
|
12321
|
+
}
|
|
12322
|
+
return schema;
|
|
12249
12323
|
} else if (methodName === "union" && node.arguments.length > 0) {
|
|
12250
12324
|
return this.processZodUnion(node);
|
|
12251
12325
|
} else if (methodName === "intersection" && node.arguments.length > 0) {
|
|
@@ -13530,15 +13604,39 @@ function extractKeysFromLiteralType(node) {
|
|
|
13530
13604
|
}
|
|
13531
13605
|
return [];
|
|
13532
13606
|
}
|
|
13607
|
+
function parsePropertyComment(commentValue) {
|
|
13608
|
+
const text = commentValue.split("\n").map((line) => line.replace(/^\s*\*\s?/, "").trim()).filter((line) => line.length > 0).join(" ").trim();
|
|
13609
|
+
const result = {};
|
|
13610
|
+
let remaining = text;
|
|
13611
|
+
const formatMatch = remaining.match(/@format\s+(\S+)/);
|
|
13612
|
+
if (formatMatch?.[1]) {
|
|
13613
|
+
result.format = formatMatch[1];
|
|
13614
|
+
remaining = remaining.replace(formatMatch[0], "").trim();
|
|
13615
|
+
}
|
|
13616
|
+
const exampleMatch = remaining.match(/@example\s+(.+?)(?=\s*@\w|$)/);
|
|
13617
|
+
if (exampleMatch?.[1]) {
|
|
13618
|
+
const raw = exampleMatch[1].trim();
|
|
13619
|
+
try {
|
|
13620
|
+
result.example = JSON.parse(raw);
|
|
13621
|
+
} catch {
|
|
13622
|
+
result.example = raw;
|
|
13623
|
+
}
|
|
13624
|
+
remaining = remaining.replace(exampleMatch[0], "").trim();
|
|
13625
|
+
}
|
|
13626
|
+
remaining = remaining.replace(/@\w+(?:\s+\S+)*/g, "").trim();
|
|
13627
|
+
if (remaining) {
|
|
13628
|
+
result.description = remaining;
|
|
13629
|
+
}
|
|
13630
|
+
return result;
|
|
13631
|
+
}
|
|
13533
13632
|
function getPropertyOptions(node, contentType) {
|
|
13534
13633
|
const isOptional = !!node.optional;
|
|
13535
|
-
let description = null;
|
|
13536
|
-
if (node.trailingComments && node.trailingComments.length) {
|
|
13537
|
-
description = node.trailingComments[0].value.trim();
|
|
13538
|
-
}
|
|
13539
13634
|
const options = {};
|
|
13540
|
-
|
|
13541
|
-
|
|
13635
|
+
const leadingComment = node.leadingComments?.[node.leadingComments.length - 1];
|
|
13636
|
+
const trailingComment = node.trailingComments?.[0];
|
|
13637
|
+
const sourceComment = leadingComment ?? trailingComment;
|
|
13638
|
+
if (sourceComment) {
|
|
13639
|
+
Object.assign(options, parsePropertyComment(sourceComment.value));
|
|
13542
13640
|
}
|
|
13543
13641
|
if (contentType === "body") {
|
|
13544
13642
|
options.nullable = isOptional;
|
|
@@ -14403,6 +14501,8 @@ var SchemaProcessor = class {
|
|
|
14403
14501
|
// Track imports per file for resolving ReturnType<typeof func>
|
|
14404
14502
|
importMap = {};
|
|
14405
14503
|
// { filePath: { importName: importPath } }
|
|
14504
|
+
// Inverted index: typeName → first filePath that imports it (O(1) lookup for findFileImportingType)
|
|
14505
|
+
typeToFileIndex = /* @__PURE__ */ new Map();
|
|
14406
14506
|
currentFilePath = "";
|
|
14407
14507
|
// Track the file being processed
|
|
14408
14508
|
constructor(schemaDir, schemaType = "typescript", schemaFiles, apiDir, fileAccess = defaultFileAccess2, runtime) {
|
|
@@ -14556,6 +14656,13 @@ var SchemaProcessor = class {
|
|
|
14556
14656
|
}
|
|
14557
14657
|
collectImports(ast, filePath) {
|
|
14558
14658
|
collectImports(ast, filePath, this.importMap);
|
|
14659
|
+
const normalizedPath = path9.normalize(filePath);
|
|
14660
|
+
const entries = this.importMap[normalizedPath] ?? {};
|
|
14661
|
+
for (const typeName of Object.keys(entries)) {
|
|
14662
|
+
if (!this.typeToFileIndex.has(typeName)) {
|
|
14663
|
+
this.typeToFileIndex.set(typeName, normalizedPath);
|
|
14664
|
+
}
|
|
14665
|
+
}
|
|
14559
14666
|
}
|
|
14560
14667
|
/**
|
|
14561
14668
|
* Resolve an import path relative to the current file
|
|
@@ -14594,6 +14701,15 @@ var SchemaProcessor = class {
|
|
|
14594
14701
|
}
|
|
14595
14702
|
const typeDefEntry = this.typeDefinitions[typeName.toString()];
|
|
14596
14703
|
if (!typeDefEntry) {
|
|
14704
|
+
const contextFile = this.findFileImportingType(typeName);
|
|
14705
|
+
if (contextFile) {
|
|
14706
|
+
logger.debug(`resolveType: "${typeName}" not in schema dirs; attempting TypeScript checker fallback via ${contextFile}`);
|
|
14707
|
+
const checkerSchema = this.resolveTypeWithTypeScriptChecker(typeName, contextFile);
|
|
14708
|
+
if (checkerSchema && Object.keys(checkerSchema).length > 0) {
|
|
14709
|
+
this.openapiDefinitions[typeName] = checkerSchema;
|
|
14710
|
+
return checkerSchema;
|
|
14711
|
+
}
|
|
14712
|
+
}
|
|
14597
14713
|
logger.debug(`resolveType: no TypeScript definition found for "${typeName}" in ${this.currentFilePath}; returning empty schema`);
|
|
14598
14714
|
return {};
|
|
14599
14715
|
}
|
|
@@ -14805,6 +14921,14 @@ var SchemaProcessor = class {
|
|
|
14805
14921
|
}
|
|
14806
14922
|
return null;
|
|
14807
14923
|
}
|
|
14924
|
+
/**
|
|
14925
|
+
* Return the path of the first scanned file that imports `typeName`, or `null` when none is
|
|
14926
|
+
* found. Used as a fallback context for {@link resolveTypeWithTypeScriptChecker} when the type
|
|
14927
|
+
* is not defined in any schema-dir file (e.g. comes from node_modules or a shared package).
|
|
14928
|
+
*/
|
|
14929
|
+
findFileImportingType(typeName) {
|
|
14930
|
+
return this.typeToFileIndex.get(typeName) ?? null;
|
|
14931
|
+
}
|
|
14808
14932
|
shouldUseTypeScriptChecker(node) {
|
|
14809
14933
|
return t15.isTSConditionalType(node) || t15.isTSMappedType(node) || t15.isTSTemplateLiteralType(node) || t15.isTSImportType(node) || t15.isTSTypeOperator(node) && node.operator === "keyof";
|
|
14810
14934
|
}
|
|
@@ -14868,7 +14992,9 @@ var SchemaProcessor = class {
|
|
|
14868
14992
|
if (seen.has(seenKey)) {
|
|
14869
14993
|
return { type: "object" };
|
|
14870
14994
|
}
|
|
14871
|
-
|
|
14995
|
+
if (!(type.flags & (primitiveLikeFlags | ts2.TypeFlags.Any | ts2.TypeFlags.Never | ts2.TypeFlags.Unknown | ts2.TypeFlags.Void))) {
|
|
14996
|
+
seen.add(seenKey);
|
|
14997
|
+
}
|
|
14872
14998
|
if (type.isStringLiteral()) {
|
|
14873
14999
|
return { type: "string", enum: [type.value] };
|
|
14874
15000
|
}
|
package/dist/vite/index.js
CHANGED
|
@@ -2741,7 +2741,7 @@ var SymbolResolver = class {
|
|
|
2741
2741
|
|
|
2742
2742
|
// ../openapi-core/dist/schema/zod/drizzle-zod-processor.js
|
|
2743
2743
|
import * as t4 from "@babel/types";
|
|
2744
|
-
var DrizzleZodProcessor = class {
|
|
2744
|
+
var DrizzleZodProcessor = class _DrizzleZodProcessor {
|
|
2745
2745
|
/**
|
|
2746
2746
|
* Known drizzle-zod helper function names
|
|
2747
2747
|
*/
|
|
@@ -3167,6 +3167,16 @@ var DrizzleZodProcessor = class {
|
|
|
3167
3167
|
result.description = args[0].value;
|
|
3168
3168
|
}
|
|
3169
3169
|
break;
|
|
3170
|
+
case "meta": {
|
|
3171
|
+
const firstArg = args[0];
|
|
3172
|
+
if (firstArg && !t4.isSpreadElement(firstArg) && !t4.isArgumentPlaceholder(firstArg)) {
|
|
3173
|
+
const metadata = _DrizzleZodProcessor.extractStaticObject(firstArg);
|
|
3174
|
+
if (metadata) {
|
|
3175
|
+
Object.assign(result, metadata);
|
|
3176
|
+
}
|
|
3177
|
+
}
|
|
3178
|
+
break;
|
|
3179
|
+
}
|
|
3170
3180
|
case "default":
|
|
3171
3181
|
if (args.length > 0) {
|
|
3172
3182
|
if (t4.isStringLiteral(args[0])) {
|
|
@@ -3187,6 +3197,49 @@ var DrizzleZodProcessor = class {
|
|
|
3187
3197
|
static isDrizzleZodHelper(name) {
|
|
3188
3198
|
return this.DRIZZLE_ZOD_HELPERS.includes(name);
|
|
3189
3199
|
}
|
|
3200
|
+
static extractStaticObject(node) {
|
|
3201
|
+
if (!t4.isObjectExpression(node))
|
|
3202
|
+
return null;
|
|
3203
|
+
const out = {};
|
|
3204
|
+
for (const prop of node.properties) {
|
|
3205
|
+
if (!t4.isObjectProperty(prop))
|
|
3206
|
+
return null;
|
|
3207
|
+
const key = t4.isIdentifier(prop.key) ? prop.key.name : t4.isStringLiteral(prop.key) ? prop.key.value : null;
|
|
3208
|
+
if (!key)
|
|
3209
|
+
return null;
|
|
3210
|
+
const val = _DrizzleZodProcessor.extractStaticValue(prop.value);
|
|
3211
|
+
if (typeof val === "undefined")
|
|
3212
|
+
return null;
|
|
3213
|
+
out[key] = val;
|
|
3214
|
+
}
|
|
3215
|
+
return out;
|
|
3216
|
+
}
|
|
3217
|
+
static extractStaticValue(node) {
|
|
3218
|
+
if (t4.isStringLiteral(node))
|
|
3219
|
+
return node.value;
|
|
3220
|
+
if (t4.isNumericLiteral(node))
|
|
3221
|
+
return node.value;
|
|
3222
|
+
if (t4.isBooleanLiteral(node))
|
|
3223
|
+
return node.value;
|
|
3224
|
+
if (t4.isNullLiteral(node))
|
|
3225
|
+
return null;
|
|
3226
|
+
if (t4.isArrayExpression(node)) {
|
|
3227
|
+
const values = [];
|
|
3228
|
+
for (const el of node.elements) {
|
|
3229
|
+
if (!el || t4.isSpreadElement(el) || t4.isArgumentPlaceholder(el))
|
|
3230
|
+
return void 0;
|
|
3231
|
+
const v = _DrizzleZodProcessor.extractStaticValue(el);
|
|
3232
|
+
if (typeof v === "undefined")
|
|
3233
|
+
return void 0;
|
|
3234
|
+
values.push(v);
|
|
3235
|
+
}
|
|
3236
|
+
return values;
|
|
3237
|
+
}
|
|
3238
|
+
if (t4.isObjectExpression(node)) {
|
|
3239
|
+
return _DrizzleZodProcessor.extractStaticObject(node);
|
|
3240
|
+
}
|
|
3241
|
+
return void 0;
|
|
3242
|
+
}
|
|
3190
3243
|
};
|
|
3191
3244
|
|
|
3192
3245
|
// ../openapi-core/dist/schema/zod/converter-runtime.js
|
|
@@ -4157,8 +4210,12 @@ function processZodPrimitiveNode(node, context) {
|
|
|
4157
4210
|
};
|
|
4158
4211
|
break;
|
|
4159
4212
|
}
|
|
4213
|
+
case "strictObject":
|
|
4160
4214
|
case "object":
|
|
4161
4215
|
schema = node.arguments.length > 0 ? context.processObject(node) : { type: "object" };
|
|
4216
|
+
if (zodType === "strictObject") {
|
|
4217
|
+
schema.additionalProperties = false;
|
|
4218
|
+
}
|
|
4162
4219
|
break;
|
|
4163
4220
|
case "templateLiteral":
|
|
4164
4221
|
schema = { type: "string" };
|
|
@@ -11225,6 +11282,10 @@ var ZodRuntimeExporter = class {
|
|
|
11225
11282
|
return this.buildEnum(node);
|
|
11226
11283
|
case "array":
|
|
11227
11284
|
return node.arguments[0] && isProcessableNode(node.arguments[0]) ? array(this.buildSchema(node.arguments[0]) ?? unknown()) : array(unknown());
|
|
11285
|
+
case "strictObject": {
|
|
11286
|
+
const base = this.buildObject(node);
|
|
11287
|
+
return base && typeof base.strict === "function" ? base.strict() : base;
|
|
11288
|
+
}
|
|
11228
11289
|
case "object":
|
|
11229
11290
|
return this.buildObject(node);
|
|
11230
11291
|
case "record":
|
|
@@ -11632,7 +11693,9 @@ var ZodSchemaConverter = class {
|
|
|
11632
11693
|
apiDir;
|
|
11633
11694
|
zodSchemas = {};
|
|
11634
11695
|
processingSchemas = /* @__PURE__ */ new Set();
|
|
11635
|
-
|
|
11696
|
+
/** Memoization guard for processFileForZodSchema. Keys: `${filePath}|${schemaName}`.
|
|
11697
|
+
* Prevents infinite recursion when re-export files reference schemas via z.infer<typeof X>. */
|
|
11698
|
+
processedFileSchemaPairs = /* @__PURE__ */ new Set();
|
|
11636
11699
|
typeToSchemaMapping = {};
|
|
11637
11700
|
drizzleZodImports = /* @__PURE__ */ new Set();
|
|
11638
11701
|
factoryCache = /* @__PURE__ */ new Map();
|
|
@@ -11846,6 +11909,11 @@ var ZodSchemaConverter = class {
|
|
|
11846
11909
|
* Process a file to find Zod schema definitions
|
|
11847
11910
|
*/
|
|
11848
11911
|
processFileForZodSchema(filePath, schemaName) {
|
|
11912
|
+
const visitKey = `${filePath}|${schemaName}|${this.currentContentType}`;
|
|
11913
|
+
if (this.processedFileSchemaPairs.has(visitKey)) {
|
|
11914
|
+
return;
|
|
11915
|
+
}
|
|
11916
|
+
this.processedFileSchemaPairs.add(visitKey);
|
|
11849
11917
|
try {
|
|
11850
11918
|
const content = this.fileAccess.readFileSync(filePath, "utf-8");
|
|
11851
11919
|
if (!content.includes(schemaName)) {
|
|
@@ -12159,7 +12227,9 @@ var ZodSchemaConverter = class {
|
|
|
12159
12227
|
const param = path17.node.typeAnnotation.typeParameters.params[0];
|
|
12160
12228
|
if (t10.isTSTypeQuery(param) && t10.isIdentifier(param.exprName)) {
|
|
12161
12229
|
const referencedSchemaName = param.exprName.name;
|
|
12162
|
-
this.
|
|
12230
|
+
if (!this.getStoredSchema(referencedSchemaName)) {
|
|
12231
|
+
this.processFileForZodSchema(filePath, referencedSchemaName);
|
|
12232
|
+
}
|
|
12163
12233
|
}
|
|
12164
12234
|
}
|
|
12165
12235
|
}
|
|
@@ -12244,8 +12314,12 @@ var ZodSchemaConverter = class {
|
|
|
12244
12314
|
}
|
|
12245
12315
|
if (t10.isCallExpression(node) && t10.isMemberExpression(node.callee) && t10.isIdentifier(node.callee.object) && this.isZodLocalName(node.callee.object.name) && t10.isIdentifier(node.callee.property)) {
|
|
12246
12316
|
const methodName = node.callee.property.name;
|
|
12247
|
-
if (methodName === "object" && node.arguments.length > 0) {
|
|
12248
|
-
|
|
12317
|
+
if ((methodName === "object" || methodName === "strictObject") && node.arguments.length > 0) {
|
|
12318
|
+
const schema = this.processZodObject(node);
|
|
12319
|
+
if (methodName === "strictObject") {
|
|
12320
|
+
schema.additionalProperties = false;
|
|
12321
|
+
}
|
|
12322
|
+
return schema;
|
|
12249
12323
|
} else if (methodName === "union" && node.arguments.length > 0) {
|
|
12250
12324
|
return this.processZodUnion(node);
|
|
12251
12325
|
} else if (methodName === "intersection" && node.arguments.length > 0) {
|
|
@@ -13530,15 +13604,39 @@ function extractKeysFromLiteralType(node) {
|
|
|
13530
13604
|
}
|
|
13531
13605
|
return [];
|
|
13532
13606
|
}
|
|
13607
|
+
function parsePropertyComment(commentValue) {
|
|
13608
|
+
const text = commentValue.split("\n").map((line) => line.replace(/^\s*\*\s?/, "").trim()).filter((line) => line.length > 0).join(" ").trim();
|
|
13609
|
+
const result = {};
|
|
13610
|
+
let remaining = text;
|
|
13611
|
+
const formatMatch = remaining.match(/@format\s+(\S+)/);
|
|
13612
|
+
if (formatMatch?.[1]) {
|
|
13613
|
+
result.format = formatMatch[1];
|
|
13614
|
+
remaining = remaining.replace(formatMatch[0], "").trim();
|
|
13615
|
+
}
|
|
13616
|
+
const exampleMatch = remaining.match(/@example\s+(.+?)(?=\s*@\w|$)/);
|
|
13617
|
+
if (exampleMatch?.[1]) {
|
|
13618
|
+
const raw = exampleMatch[1].trim();
|
|
13619
|
+
try {
|
|
13620
|
+
result.example = JSON.parse(raw);
|
|
13621
|
+
} catch {
|
|
13622
|
+
result.example = raw;
|
|
13623
|
+
}
|
|
13624
|
+
remaining = remaining.replace(exampleMatch[0], "").trim();
|
|
13625
|
+
}
|
|
13626
|
+
remaining = remaining.replace(/@\w+(?:\s+\S+)*/g, "").trim();
|
|
13627
|
+
if (remaining) {
|
|
13628
|
+
result.description = remaining;
|
|
13629
|
+
}
|
|
13630
|
+
return result;
|
|
13631
|
+
}
|
|
13533
13632
|
function getPropertyOptions(node, contentType) {
|
|
13534
13633
|
const isOptional = !!node.optional;
|
|
13535
|
-
let description = null;
|
|
13536
|
-
if (node.trailingComments && node.trailingComments.length) {
|
|
13537
|
-
description = node.trailingComments[0].value.trim();
|
|
13538
|
-
}
|
|
13539
13634
|
const options = {};
|
|
13540
|
-
|
|
13541
|
-
|
|
13635
|
+
const leadingComment = node.leadingComments?.[node.leadingComments.length - 1];
|
|
13636
|
+
const trailingComment = node.trailingComments?.[0];
|
|
13637
|
+
const sourceComment = leadingComment ?? trailingComment;
|
|
13638
|
+
if (sourceComment) {
|
|
13639
|
+
Object.assign(options, parsePropertyComment(sourceComment.value));
|
|
13542
13640
|
}
|
|
13543
13641
|
if (contentType === "body") {
|
|
13544
13642
|
options.nullable = isOptional;
|
|
@@ -14403,6 +14501,8 @@ var SchemaProcessor = class {
|
|
|
14403
14501
|
// Track imports per file for resolving ReturnType<typeof func>
|
|
14404
14502
|
importMap = {};
|
|
14405
14503
|
// { filePath: { importName: importPath } }
|
|
14504
|
+
// Inverted index: typeName → first filePath that imports it (O(1) lookup for findFileImportingType)
|
|
14505
|
+
typeToFileIndex = /* @__PURE__ */ new Map();
|
|
14406
14506
|
currentFilePath = "";
|
|
14407
14507
|
// Track the file being processed
|
|
14408
14508
|
constructor(schemaDir, schemaType = "typescript", schemaFiles, apiDir, fileAccess = defaultFileAccess2, runtime) {
|
|
@@ -14556,6 +14656,13 @@ var SchemaProcessor = class {
|
|
|
14556
14656
|
}
|
|
14557
14657
|
collectImports(ast, filePath) {
|
|
14558
14658
|
collectImports(ast, filePath, this.importMap);
|
|
14659
|
+
const normalizedPath = path9.normalize(filePath);
|
|
14660
|
+
const entries = this.importMap[normalizedPath] ?? {};
|
|
14661
|
+
for (const typeName of Object.keys(entries)) {
|
|
14662
|
+
if (!this.typeToFileIndex.has(typeName)) {
|
|
14663
|
+
this.typeToFileIndex.set(typeName, normalizedPath);
|
|
14664
|
+
}
|
|
14665
|
+
}
|
|
14559
14666
|
}
|
|
14560
14667
|
/**
|
|
14561
14668
|
* Resolve an import path relative to the current file
|
|
@@ -14594,6 +14701,15 @@ var SchemaProcessor = class {
|
|
|
14594
14701
|
}
|
|
14595
14702
|
const typeDefEntry = this.typeDefinitions[typeName.toString()];
|
|
14596
14703
|
if (!typeDefEntry) {
|
|
14704
|
+
const contextFile = this.findFileImportingType(typeName);
|
|
14705
|
+
if (contextFile) {
|
|
14706
|
+
logger.debug(`resolveType: "${typeName}" not in schema dirs; attempting TypeScript checker fallback via ${contextFile}`);
|
|
14707
|
+
const checkerSchema = this.resolveTypeWithTypeScriptChecker(typeName, contextFile);
|
|
14708
|
+
if (checkerSchema && Object.keys(checkerSchema).length > 0) {
|
|
14709
|
+
this.openapiDefinitions[typeName] = checkerSchema;
|
|
14710
|
+
return checkerSchema;
|
|
14711
|
+
}
|
|
14712
|
+
}
|
|
14597
14713
|
logger.debug(`resolveType: no TypeScript definition found for "${typeName}" in ${this.currentFilePath}; returning empty schema`);
|
|
14598
14714
|
return {};
|
|
14599
14715
|
}
|
|
@@ -14805,6 +14921,14 @@ var SchemaProcessor = class {
|
|
|
14805
14921
|
}
|
|
14806
14922
|
return null;
|
|
14807
14923
|
}
|
|
14924
|
+
/**
|
|
14925
|
+
* Return the path of the first scanned file that imports `typeName`, or `null` when none is
|
|
14926
|
+
* found. Used as a fallback context for {@link resolveTypeWithTypeScriptChecker} when the type
|
|
14927
|
+
* is not defined in any schema-dir file (e.g. comes from node_modules or a shared package).
|
|
14928
|
+
*/
|
|
14929
|
+
findFileImportingType(typeName) {
|
|
14930
|
+
return this.typeToFileIndex.get(typeName) ?? null;
|
|
14931
|
+
}
|
|
14808
14932
|
shouldUseTypeScriptChecker(node) {
|
|
14809
14933
|
return t15.isTSConditionalType(node) || t15.isTSMappedType(node) || t15.isTSTemplateLiteralType(node) || t15.isTSImportType(node) || t15.isTSTypeOperator(node) && node.operator === "keyof";
|
|
14810
14934
|
}
|
|
@@ -14868,7 +14992,9 @@ var SchemaProcessor = class {
|
|
|
14868
14992
|
if (seen.has(seenKey)) {
|
|
14869
14993
|
return { type: "object" };
|
|
14870
14994
|
}
|
|
14871
|
-
|
|
14995
|
+
if (!(type.flags & (primitiveLikeFlags | ts2.TypeFlags.Any | ts2.TypeFlags.Never | ts2.TypeFlags.Unknown | ts2.TypeFlags.Void))) {
|
|
14996
|
+
seen.add(seenKey);
|
|
14997
|
+
}
|
|
14872
14998
|
if (type.isStringLiteral()) {
|
|
14873
14999
|
return { type: "string", enum: [type.value] };
|
|
14874
15000
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "next-openapi-gen",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "Automatically generate OpenAPI 3.0, 3.1, and 3.2 documentation from Next.js projects, with support for Zod schemas, TypeScript types, and reusable OpenAPI fragments.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"api",
|