next-openapi-gen 1.1.1 → 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.
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":
@@ -12657,8 +12718,12 @@ var ZodSchemaConverter = class {
12657
12718
  }
12658
12719
  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
12720
  const methodName = node.callee.property.name;
12660
- if (methodName === "object" && node.arguments.length > 0) {
12661
- return this.processZodObject(node);
12721
+ if ((methodName === "object" || methodName === "strictObject") && node.arguments.length > 0) {
12722
+ const schema = this.processZodObject(node);
12723
+ if (methodName === "strictObject") {
12724
+ schema.additionalProperties = false;
12725
+ }
12726
+ return schema;
12662
12727
  } else if (methodName === "union" && node.arguments.length > 0) {
12663
12728
  return this.processZodUnion(node);
12664
12729
  } else if (methodName === "intersection" && node.arguments.length > 0) {
@@ -14816,6 +14881,8 @@ var SchemaProcessor = class {
14816
14881
  // Track imports per file for resolving ReturnType<typeof func>
14817
14882
  importMap = {};
14818
14883
  // { filePath: { importName: importPath } }
14884
+ // Inverted index: typeName → first filePath that imports it (O(1) lookup for findFileImportingType)
14885
+ typeToFileIndex = /* @__PURE__ */ new Map();
14819
14886
  currentFilePath = "";
14820
14887
  // Track the file being processed
14821
14888
  constructor(schemaDir, schemaType = "typescript", schemaFiles, apiDir, fileAccess = defaultFileAccess2, runtime) {
@@ -14969,6 +15036,13 @@ var SchemaProcessor = class {
14969
15036
  }
14970
15037
  collectImports(ast, filePath) {
14971
15038
  collectImports(ast, filePath, this.importMap);
15039
+ const normalizedPath = path14.normalize(filePath);
15040
+ const entries = this.importMap[normalizedPath] ?? {};
15041
+ for (const typeName of Object.keys(entries)) {
15042
+ if (!this.typeToFileIndex.has(typeName)) {
15043
+ this.typeToFileIndex.set(typeName, normalizedPath);
15044
+ }
15045
+ }
14972
15046
  }
14973
15047
  /**
14974
15048
  * Resolve an import path relative to the current file
@@ -15007,6 +15081,15 @@ var SchemaProcessor = class {
15007
15081
  }
15008
15082
  const typeDefEntry = this.typeDefinitions[typeName.toString()];
15009
15083
  if (!typeDefEntry) {
15084
+ const contextFile = this.findFileImportingType(typeName);
15085
+ if (contextFile) {
15086
+ logger.debug(`resolveType: "${typeName}" not in schema dirs; attempting TypeScript checker fallback via ${contextFile}`);
15087
+ const checkerSchema = this.resolveTypeWithTypeScriptChecker(typeName, contextFile);
15088
+ if (checkerSchema && Object.keys(checkerSchema).length > 0) {
15089
+ this.openapiDefinitions[typeName] = checkerSchema;
15090
+ return checkerSchema;
15091
+ }
15092
+ }
15010
15093
  logger.debug(`resolveType: no TypeScript definition found for "${typeName}" in ${this.currentFilePath}; returning empty schema`);
15011
15094
  return {};
15012
15095
  }
@@ -15218,6 +15301,14 @@ var SchemaProcessor = class {
15218
15301
  }
15219
15302
  return null;
15220
15303
  }
15304
+ /**
15305
+ * Return the path of the first scanned file that imports `typeName`, or `null` when none is
15306
+ * found. Used as a fallback context for {@link resolveTypeWithTypeScriptChecker} when the type
15307
+ * is not defined in any schema-dir file (e.g. comes from node_modules or a shared package).
15308
+ */
15309
+ findFileImportingType(typeName) {
15310
+ return this.typeToFileIndex.get(typeName) ?? null;
15311
+ }
15221
15312
  shouldUseTypeScriptChecker(node) {
15222
15313
  return t15.isTSConditionalType(node) || t15.isTSMappedType(node) || t15.isTSTemplateLiteralType(node) || t15.isTSImportType(node) || t15.isTSTypeOperator(node) && node.operator === "keyof";
15223
15314
  }
@@ -15281,7 +15372,9 @@ var SchemaProcessor = class {
15281
15372
  if (seen.has(seenKey)) {
15282
15373
  return { type: "object" };
15283
15374
  }
15284
- seen.add(seenKey);
15375
+ if (!(type.flags & (primitiveLikeFlags | ts2.TypeFlags.Any | ts2.TypeFlags.Never | ts2.TypeFlags.Unknown | ts2.TypeFlags.Void))) {
15376
+ seen.add(seenKey);
15377
+ }
15285
15378
  if (type.isStringLiteral()) {
15286
15379
  return { type: "string", enum: [type.value] };
15287
15380
  }
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":
@@ -12206,8 +12267,12 @@ var ZodSchemaConverter = class {
12206
12267
  }
12207
12268
  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
12269
  const methodName = node.callee.property.name;
12209
- if (methodName === "object" && node.arguments.length > 0) {
12210
- return this.processZodObject(node);
12270
+ if ((methodName === "object" || methodName === "strictObject") && node.arguments.length > 0) {
12271
+ const schema = this.processZodObject(node);
12272
+ if (methodName === "strictObject") {
12273
+ schema.additionalProperties = false;
12274
+ }
12275
+ return schema;
12211
12276
  } else if (methodName === "union" && node.arguments.length > 0) {
12212
12277
  return this.processZodUnion(node);
12213
12278
  } else if (methodName === "intersection" && node.arguments.length > 0) {
@@ -14365,6 +14430,8 @@ var SchemaProcessor = class {
14365
14430
  // Track imports per file for resolving ReturnType<typeof func>
14366
14431
  importMap = {};
14367
14432
  // { filePath: { importName: importPath } }
14433
+ // Inverted index: typeName → first filePath that imports it (O(1) lookup for findFileImportingType)
14434
+ typeToFileIndex = /* @__PURE__ */ new Map();
14368
14435
  currentFilePath = "";
14369
14436
  // Track the file being processed
14370
14437
  constructor(schemaDir, schemaType = "typescript", schemaFiles, apiDir, fileAccess = defaultFileAccess2, runtime) {
@@ -14518,6 +14585,13 @@ var SchemaProcessor = class {
14518
14585
  }
14519
14586
  collectImports(ast, filePath) {
14520
14587
  collectImports(ast, filePath, this.importMap);
14588
+ const normalizedPath = path9.normalize(filePath);
14589
+ const entries = this.importMap[normalizedPath] ?? {};
14590
+ for (const typeName of Object.keys(entries)) {
14591
+ if (!this.typeToFileIndex.has(typeName)) {
14592
+ this.typeToFileIndex.set(typeName, normalizedPath);
14593
+ }
14594
+ }
14521
14595
  }
14522
14596
  /**
14523
14597
  * Resolve an import path relative to the current file
@@ -14556,6 +14630,15 @@ var SchemaProcessor = class {
14556
14630
  }
14557
14631
  const typeDefEntry = this.typeDefinitions[typeName.toString()];
14558
14632
  if (!typeDefEntry) {
14633
+ const contextFile = this.findFileImportingType(typeName);
14634
+ if (contextFile) {
14635
+ logger.debug(`resolveType: "${typeName}" not in schema dirs; attempting TypeScript checker fallback via ${contextFile}`);
14636
+ const checkerSchema = this.resolveTypeWithTypeScriptChecker(typeName, contextFile);
14637
+ if (checkerSchema && Object.keys(checkerSchema).length > 0) {
14638
+ this.openapiDefinitions[typeName] = checkerSchema;
14639
+ return checkerSchema;
14640
+ }
14641
+ }
14559
14642
  logger.debug(`resolveType: no TypeScript definition found for "${typeName}" in ${this.currentFilePath}; returning empty schema`);
14560
14643
  return {};
14561
14644
  }
@@ -14767,6 +14850,14 @@ var SchemaProcessor = class {
14767
14850
  }
14768
14851
  return null;
14769
14852
  }
14853
+ /**
14854
+ * Return the path of the first scanned file that imports `typeName`, or `null` when none is
14855
+ * found. Used as a fallback context for {@link resolveTypeWithTypeScriptChecker} when the type
14856
+ * is not defined in any schema-dir file (e.g. comes from node_modules or a shared package).
14857
+ */
14858
+ findFileImportingType(typeName) {
14859
+ return this.typeToFileIndex.get(typeName) ?? null;
14860
+ }
14770
14861
  shouldUseTypeScriptChecker(node) {
14771
14862
  return t15.isTSConditionalType(node) || t15.isTSMappedType(node) || t15.isTSTemplateLiteralType(node) || t15.isTSImportType(node) || t15.isTSTypeOperator(node) && node.operator === "keyof";
14772
14863
  }
@@ -14830,7 +14921,9 @@ var SchemaProcessor = class {
14830
14921
  if (seen.has(seenKey)) {
14831
14922
  return { type: "object" };
14832
14923
  }
14833
- seen.add(seenKey);
14924
+ if (!(type.flags & (primitiveLikeFlags | ts2.TypeFlags.Any | ts2.TypeFlags.Never | ts2.TypeFlags.Unknown | ts2.TypeFlags.Void))) {
14925
+ seen.add(seenKey);
14926
+ }
14834
14927
  if (type.isStringLiteral()) {
14835
14928
  return { type: "string", enum: [type.value] };
14836
14929
  }
@@ -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":
@@ -13240,8 +13301,12 @@ var ZodSchemaConverter = class {
13240
13301
  }
13241
13302
  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
13303
  const methodName = node.callee.property.name;
13243
- if (methodName === "object" && node.arguments.length > 0) {
13244
- return this.processZodObject(node);
13304
+ if ((methodName === "object" || methodName === "strictObject") && node.arguments.length > 0) {
13305
+ const schema = this.processZodObject(node);
13306
+ if (methodName === "strictObject") {
13307
+ schema.additionalProperties = false;
13308
+ }
13309
+ return schema;
13245
13310
  } else if (methodName === "union" && node.arguments.length > 0) {
13246
13311
  return this.processZodUnion(node);
13247
13312
  } else if (methodName === "intersection" && node.arguments.length > 0) {
@@ -15399,6 +15464,8 @@ var SchemaProcessor = class {
15399
15464
  // Track imports per file for resolving ReturnType<typeof func>
15400
15465
  importMap = {};
15401
15466
  // { filePath: { importName: importPath } }
15467
+ // Inverted index: typeName → first filePath that imports it (O(1) lookup for findFileImportingType)
15468
+ typeToFileIndex = /* @__PURE__ */ new Map();
15402
15469
  currentFilePath = "";
15403
15470
  // Track the file being processed
15404
15471
  constructor(schemaDir, schemaType = "typescript", schemaFiles, apiDir, fileAccess = defaultFileAccess2, runtime) {
@@ -15552,6 +15619,13 @@ var SchemaProcessor = class {
15552
15619
  }
15553
15620
  collectImports(ast, filePath) {
15554
15621
  collectImports(ast, filePath, this.importMap);
15622
+ const normalizedPath = path12.normalize(filePath);
15623
+ const entries = this.importMap[normalizedPath] ?? {};
15624
+ for (const typeName of Object.keys(entries)) {
15625
+ if (!this.typeToFileIndex.has(typeName)) {
15626
+ this.typeToFileIndex.set(typeName, normalizedPath);
15627
+ }
15628
+ }
15555
15629
  }
15556
15630
  /**
15557
15631
  * Resolve an import path relative to the current file
@@ -15590,6 +15664,15 @@ var SchemaProcessor = class {
15590
15664
  }
15591
15665
  const typeDefEntry = this.typeDefinitions[typeName.toString()];
15592
15666
  if (!typeDefEntry) {
15667
+ const contextFile = this.findFileImportingType(typeName);
15668
+ if (contextFile) {
15669
+ logger.debug(`resolveType: "${typeName}" not in schema dirs; attempting TypeScript checker fallback via ${contextFile}`);
15670
+ const checkerSchema = this.resolveTypeWithTypeScriptChecker(typeName, contextFile);
15671
+ if (checkerSchema && Object.keys(checkerSchema).length > 0) {
15672
+ this.openapiDefinitions[typeName] = checkerSchema;
15673
+ return checkerSchema;
15674
+ }
15675
+ }
15593
15676
  logger.debug(`resolveType: no TypeScript definition found for "${typeName}" in ${this.currentFilePath}; returning empty schema`);
15594
15677
  return {};
15595
15678
  }
@@ -15801,6 +15884,14 @@ var SchemaProcessor = class {
15801
15884
  }
15802
15885
  return null;
15803
15886
  }
15887
+ /**
15888
+ * Return the path of the first scanned file that imports `typeName`, or `null` when none is
15889
+ * found. Used as a fallback context for {@link resolveTypeWithTypeScriptChecker} when the type
15890
+ * is not defined in any schema-dir file (e.g. comes from node_modules or a shared package).
15891
+ */
15892
+ findFileImportingType(typeName) {
15893
+ return this.typeToFileIndex.get(typeName) ?? null;
15894
+ }
15804
15895
  shouldUseTypeScriptChecker(node) {
15805
15896
  return t16.isTSConditionalType(node) || t16.isTSMappedType(node) || t16.isTSTemplateLiteralType(node) || t16.isTSImportType(node) || t16.isTSTypeOperator(node) && node.operator === "keyof";
15806
15897
  }
@@ -15864,7 +15955,9 @@ var SchemaProcessor = class {
15864
15955
  if (seen.has(seenKey)) {
15865
15956
  return { type: "object" };
15866
15957
  }
15867
- seen.add(seenKey);
15958
+ if (!(type.flags & (primitiveLikeFlags | ts3.TypeFlags.Any | ts3.TypeFlags.Never | ts3.TypeFlags.Unknown | ts3.TypeFlags.Void))) {
15959
+ seen.add(seenKey);
15960
+ }
15868
15961
  if (type.isStringLiteral()) {
15869
15962
  return { type: "string", enum: [type.value] };
15870
15963
  }
@@ -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":
@@ -12244,8 +12305,12 @@ var ZodSchemaConverter = class {
12244
12305
  }
12245
12306
  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
12307
  const methodName = node.callee.property.name;
12247
- if (methodName === "object" && node.arguments.length > 0) {
12248
- return this.processZodObject(node);
12308
+ if ((methodName === "object" || methodName === "strictObject") && node.arguments.length > 0) {
12309
+ const schema = this.processZodObject(node);
12310
+ if (methodName === "strictObject") {
12311
+ schema.additionalProperties = false;
12312
+ }
12313
+ return schema;
12249
12314
  } else if (methodName === "union" && node.arguments.length > 0) {
12250
12315
  return this.processZodUnion(node);
12251
12316
  } else if (methodName === "intersection" && node.arguments.length > 0) {
@@ -14403,6 +14468,8 @@ var SchemaProcessor = class {
14403
14468
  // Track imports per file for resolving ReturnType<typeof func>
14404
14469
  importMap = {};
14405
14470
  // { filePath: { importName: importPath } }
14471
+ // Inverted index: typeName → first filePath that imports it (O(1) lookup for findFileImportingType)
14472
+ typeToFileIndex = /* @__PURE__ */ new Map();
14406
14473
  currentFilePath = "";
14407
14474
  // Track the file being processed
14408
14475
  constructor(schemaDir, schemaType = "typescript", schemaFiles, apiDir, fileAccess = defaultFileAccess2, runtime) {
@@ -14556,6 +14623,13 @@ var SchemaProcessor = class {
14556
14623
  }
14557
14624
  collectImports(ast, filePath) {
14558
14625
  collectImports(ast, filePath, this.importMap);
14626
+ const normalizedPath = path9.normalize(filePath);
14627
+ const entries = this.importMap[normalizedPath] ?? {};
14628
+ for (const typeName of Object.keys(entries)) {
14629
+ if (!this.typeToFileIndex.has(typeName)) {
14630
+ this.typeToFileIndex.set(typeName, normalizedPath);
14631
+ }
14632
+ }
14559
14633
  }
14560
14634
  /**
14561
14635
  * Resolve an import path relative to the current file
@@ -14594,6 +14668,15 @@ var SchemaProcessor = class {
14594
14668
  }
14595
14669
  const typeDefEntry = this.typeDefinitions[typeName.toString()];
14596
14670
  if (!typeDefEntry) {
14671
+ const contextFile = this.findFileImportingType(typeName);
14672
+ if (contextFile) {
14673
+ logger.debug(`resolveType: "${typeName}" not in schema dirs; attempting TypeScript checker fallback via ${contextFile}`);
14674
+ const checkerSchema = this.resolveTypeWithTypeScriptChecker(typeName, contextFile);
14675
+ if (checkerSchema && Object.keys(checkerSchema).length > 0) {
14676
+ this.openapiDefinitions[typeName] = checkerSchema;
14677
+ return checkerSchema;
14678
+ }
14679
+ }
14597
14680
  logger.debug(`resolveType: no TypeScript definition found for "${typeName}" in ${this.currentFilePath}; returning empty schema`);
14598
14681
  return {};
14599
14682
  }
@@ -14805,6 +14888,14 @@ var SchemaProcessor = class {
14805
14888
  }
14806
14889
  return null;
14807
14890
  }
14891
+ /**
14892
+ * Return the path of the first scanned file that imports `typeName`, or `null` when none is
14893
+ * found. Used as a fallback context for {@link resolveTypeWithTypeScriptChecker} when the type
14894
+ * is not defined in any schema-dir file (e.g. comes from node_modules or a shared package).
14895
+ */
14896
+ findFileImportingType(typeName) {
14897
+ return this.typeToFileIndex.get(typeName) ?? null;
14898
+ }
14808
14899
  shouldUseTypeScriptChecker(node) {
14809
14900
  return t15.isTSConditionalType(node) || t15.isTSMappedType(node) || t15.isTSTemplateLiteralType(node) || t15.isTSImportType(node) || t15.isTSTypeOperator(node) && node.operator === "keyof";
14810
14901
  }
@@ -14868,7 +14959,9 @@ var SchemaProcessor = class {
14868
14959
  if (seen.has(seenKey)) {
14869
14960
  return { type: "object" };
14870
14961
  }
14871
- seen.add(seenKey);
14962
+ if (!(type.flags & (primitiveLikeFlags | ts2.TypeFlags.Any | ts2.TypeFlags.Never | ts2.TypeFlags.Unknown | ts2.TypeFlags.Void))) {
14963
+ seen.add(seenKey);
14964
+ }
14872
14965
  if (type.isStringLiteral()) {
14873
14966
  return { type: "string", enum: [type.value] };
14874
14967
  }
@@ -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":
@@ -12244,8 +12305,12 @@ var ZodSchemaConverter = class {
12244
12305
  }
12245
12306
  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
12307
  const methodName = node.callee.property.name;
12247
- if (methodName === "object" && node.arguments.length > 0) {
12248
- return this.processZodObject(node);
12308
+ if ((methodName === "object" || methodName === "strictObject") && node.arguments.length > 0) {
12309
+ const schema = this.processZodObject(node);
12310
+ if (methodName === "strictObject") {
12311
+ schema.additionalProperties = false;
12312
+ }
12313
+ return schema;
12249
12314
  } else if (methodName === "union" && node.arguments.length > 0) {
12250
12315
  return this.processZodUnion(node);
12251
12316
  } else if (methodName === "intersection" && node.arguments.length > 0) {
@@ -14403,6 +14468,8 @@ var SchemaProcessor = class {
14403
14468
  // Track imports per file for resolving ReturnType<typeof func>
14404
14469
  importMap = {};
14405
14470
  // { filePath: { importName: importPath } }
14471
+ // Inverted index: typeName → first filePath that imports it (O(1) lookup for findFileImportingType)
14472
+ typeToFileIndex = /* @__PURE__ */ new Map();
14406
14473
  currentFilePath = "";
14407
14474
  // Track the file being processed
14408
14475
  constructor(schemaDir, schemaType = "typescript", schemaFiles, apiDir, fileAccess = defaultFileAccess2, runtime) {
@@ -14556,6 +14623,13 @@ var SchemaProcessor = class {
14556
14623
  }
14557
14624
  collectImports(ast, filePath) {
14558
14625
  collectImports(ast, filePath, this.importMap);
14626
+ const normalizedPath = path9.normalize(filePath);
14627
+ const entries = this.importMap[normalizedPath] ?? {};
14628
+ for (const typeName of Object.keys(entries)) {
14629
+ if (!this.typeToFileIndex.has(typeName)) {
14630
+ this.typeToFileIndex.set(typeName, normalizedPath);
14631
+ }
14632
+ }
14559
14633
  }
14560
14634
  /**
14561
14635
  * Resolve an import path relative to the current file
@@ -14594,6 +14668,15 @@ var SchemaProcessor = class {
14594
14668
  }
14595
14669
  const typeDefEntry = this.typeDefinitions[typeName.toString()];
14596
14670
  if (!typeDefEntry) {
14671
+ const contextFile = this.findFileImportingType(typeName);
14672
+ if (contextFile) {
14673
+ logger.debug(`resolveType: "${typeName}" not in schema dirs; attempting TypeScript checker fallback via ${contextFile}`);
14674
+ const checkerSchema = this.resolveTypeWithTypeScriptChecker(typeName, contextFile);
14675
+ if (checkerSchema && Object.keys(checkerSchema).length > 0) {
14676
+ this.openapiDefinitions[typeName] = checkerSchema;
14677
+ return checkerSchema;
14678
+ }
14679
+ }
14597
14680
  logger.debug(`resolveType: no TypeScript definition found for "${typeName}" in ${this.currentFilePath}; returning empty schema`);
14598
14681
  return {};
14599
14682
  }
@@ -14805,6 +14888,14 @@ var SchemaProcessor = class {
14805
14888
  }
14806
14889
  return null;
14807
14890
  }
14891
+ /**
14892
+ * Return the path of the first scanned file that imports `typeName`, or `null` when none is
14893
+ * found. Used as a fallback context for {@link resolveTypeWithTypeScriptChecker} when the type
14894
+ * is not defined in any schema-dir file (e.g. comes from node_modules or a shared package).
14895
+ */
14896
+ findFileImportingType(typeName) {
14897
+ return this.typeToFileIndex.get(typeName) ?? null;
14898
+ }
14808
14899
  shouldUseTypeScriptChecker(node) {
14809
14900
  return t15.isTSConditionalType(node) || t15.isTSMappedType(node) || t15.isTSTemplateLiteralType(node) || t15.isTSImportType(node) || t15.isTSTypeOperator(node) && node.operator === "keyof";
14810
14901
  }
@@ -14868,7 +14959,9 @@ var SchemaProcessor = class {
14868
14959
  if (seen.has(seenKey)) {
14869
14960
  return { type: "object" };
14870
14961
  }
14871
- seen.add(seenKey);
14962
+ if (!(type.flags & (primitiveLikeFlags | ts2.TypeFlags.Any | ts2.TypeFlags.Never | ts2.TypeFlags.Unknown | ts2.TypeFlags.Void))) {
14963
+ seen.add(seenKey);
14964
+ }
14872
14965
  if (type.isStringLiteral()) {
14873
14966
  return { type: "string", enum: [type.value] };
14874
14967
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-openapi-gen",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
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",