next-openapi-gen 1.2.2 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -288,33 +288,34 @@ Version guidance:
288
288
  | `ignoreRoutes` | Exclude routes with wildcard support |
289
289
  | `defaultResponseSet` / `responseSets` | Reusable error-response groups |
290
290
  | `errorConfig` | Shared error schema templates |
291
+ | `authPresets` | Override or extend the `@auth` keyword → scheme-name mapping |
291
292
 
292
293
  For a fuller setup guide, Pages Router notes, response sets, and route exclusion
293
294
  patterns, see [docs/getting-started.md](./docs/getting-started.md).
294
295
 
295
296
  ## JSDoc tags you will use most
296
297
 
297
- | Tag | Purpose |
298
- | -------------------------- | ---------------------------------------------------------------- |
299
- | `@pathParams` | Path parameter schema or type |
300
- | `@params` / `@queryParams` | Query parameter schema or type |
301
- | `@header` / `@cookie` | Header / cookie parameter schema or type |
302
- | `@body` | Request body schema or type |
303
- | `@response` | Response schema, code, and optional description |
304
- | `@responseDescription` | Response description without redefining the schema |
305
- | `@responseHeader` | Add a response header to a given status code |
306
- | `@link` | Add an OpenAPI link to a response |
307
- | `@auth` / `@security` | Security requirement(s); `@security` accepts explicit scopes |
308
- | `@servers` | Operation-level servers |
309
- | `@externalDocs` | Operation-level external documentation |
310
- | `@callback` | OpenAPI operation callback |
311
- | `@webhook` | Mark the handler as a webhook (`3.1`+ `webhooks` section) |
312
- | `@contentType` | Request content type such as `multipart/form-data` |
313
- | `@examples` | Request, response, and querystring examples |
314
- | `@openapi` | Explicit inclusion marker when `includeOpenApiRoutes` is enabled |
315
- | `@openapi-override` | Deep-merge extra OpenAPI fields onto the operation |
316
- | `@ignore` | Exclude a route from generation |
317
- | `@method` | Required HTTP method tag for Pages Router handlers |
298
+ | Tag | Purpose |
299
+ | -------------------------- | ------------------------------------------------------------------------------------------------------- |
300
+ | `@pathParams` | Path parameter schema or type |
301
+ | `@params` / `@queryParams` | Query parameter schema or type |
302
+ | `@header` / `@cookie` | Header / cookie parameter schema or type |
303
+ | `@body` | Request body schema or type |
304
+ | `@response` | Response schema, code, and optional description |
305
+ | `@responseDescription` | Response description without redefining the schema |
306
+ | `@responseHeader` | Add a response header to a given status code |
307
+ | `@link` | Add an OpenAPI link to a response |
308
+ | `@auth` / `@security` | Security requirement(s); built-in presets: `bearer`, `basic`, `apikey` — configurable via `authPresets` |
309
+ | `@servers` | Operation-level servers |
310
+ | `@externalDocs` | Operation-level external documentation |
311
+ | `@callback` | OpenAPI operation callback |
312
+ | `@webhook` | Mark the handler as a webhook (`3.1`+ `webhooks` section) |
313
+ | `@contentType` | Request content type such as `multipart/form-data` |
314
+ | `@examples` | Request, response, and querystring examples |
315
+ | `@openapi` | Explicit inclusion marker when `includeOpenApiRoutes` is enabled |
316
+ | `@openapi-override` | Deep-merge extra OpenAPI fields onto the operation |
317
+ | `@ignore` | Exclude a route from generation |
318
+ | `@method` | Required HTTP method tag for Pages Router handlers |
318
319
 
319
320
  For the complete tag guide and usage recipes, see
320
321
  [docs/jsdoc-reference.md](./docs/jsdoc-reference.md).
package/dist/cli.js CHANGED
@@ -667,6 +667,7 @@ function normalizeOpenApiConfig(template) {
667
667
  outputDir: template.outputDir ?? DEFAULT_OUTPUT_DIR,
668
668
  includeOpenApiRoutes: template.includeOpenApiRoutes ?? DEFAULT_INCLUDE_OPENAPI_ROUTES,
669
669
  ignoreRoutes: template.ignoreRoutes ?? [],
670
+ excludeSchemas: template.excludeSchemas ?? [],
670
671
  schemaType: template.schemaType ?? DEFAULT_RUNTIME_SCHEMA_TYPE,
671
672
  schemaBackends,
672
673
  schemaFiles: template.schemaFiles ?? [],
@@ -1406,6 +1407,17 @@ function mergeJSDocData(target, source) {
1406
1407
  function cleanComment(commentValue) {
1407
1408
  return commentValue.replace(/\*\s*/g, "").trim();
1408
1409
  }
1410
+ function extractInternalFlagFromComments(comments) {
1411
+ if (!comments)
1412
+ return false;
1413
+ for (const comment of comments) {
1414
+ const cleaned = cleanComment(comment.value);
1415
+ if (/@internal\b/.test(cleaned) || /@schema\s+false\b/.test(cleaned)) {
1416
+ return true;
1417
+ }
1418
+ }
1419
+ return false;
1420
+ }
1409
1421
  function extractSchemaIdFromComments(comments) {
1410
1422
  if (!comments)
1411
1423
  return null;
@@ -1702,7 +1714,8 @@ var INTERNAL_OPENAPI_CONFIG_KEYS = [
1702
1714
  "framework",
1703
1715
  "next",
1704
1716
  "diagnostics",
1705
- "debug"
1717
+ "debug",
1718
+ "excludeSchemas"
1706
1719
  ];
1707
1720
  var AUTH_PRESET_REPLACEMENTS = {
1708
1721
  bearer: "BearerAuth",
@@ -12142,6 +12155,11 @@ var ZodSchemaConverter = class {
12142
12155
  schemaNameToFiles = /* @__PURE__ */ new Map();
12143
12156
  /** Per-file import alias for the `zod` module (`import { z as zod }` sets this to `"zod"`). */
12144
12157
  zodImportAlias = /* @__PURE__ */ new Map();
12158
+ /** Schema variable names whose component name was overridden via .meta({ id }). These must
12159
+ * NOT be copied back under the original variable name in the OpenAPI components object. */
12160
+ metaIdSchemaNames = /* @__PURE__ */ new Set();
12161
+ /** Schema variable names marked @internal — excluded from components/schemas output. */
12162
+ internalSchemaNames = /* @__PURE__ */ new Set();
12145
12163
  // Current processing context (set during file processing)
12146
12164
  currentFilePath;
12147
12165
  currentAST;
@@ -12168,7 +12186,14 @@ var ZodSchemaConverter = class {
12168
12186
  }
12169
12187
  logger.debug(`Looking for Zod schema: ${schemaName}`);
12170
12188
  const requestedSchemaName = schemaName;
12171
- const mappedSchemaName = this.typeToSchemaMapping[schemaName];
12189
+ let mappedSchemaName = this.typeToSchemaMapping[schemaName];
12190
+ if (!mappedSchemaName) {
12191
+ const candidate = this.deriveSchemaNameByConvention(schemaName);
12192
+ if (candidate && this.locateSchemaByConvention(candidate)) {
12193
+ this.typeToSchemaMapping[schemaName] = candidate;
12194
+ mappedSchemaName = candidate;
12195
+ }
12196
+ }
12172
12197
  if (mappedSchemaName) {
12173
12198
  logger.debug(`Type '${schemaName}' is mapped to schema '${mappedSchemaName}'`);
12174
12199
  schemaName = mappedSchemaName;
@@ -12218,7 +12243,7 @@ var ZodSchemaConverter = class {
12218
12243
  this.processingSchemas.delete(schemaName);
12219
12244
  if (mappedSchemaName && requestedSchemaName !== schemaName) {
12220
12245
  const resolvedReference = this.getSchemaReferenceName(schemaName, this.currentContentType);
12221
- if (this.zodSchemas[resolvedReference] && !this.zodSchemas[requestedSchemaName]) {
12246
+ if (!this.metaIdSchemaNames.has(requestedSchemaName) && this.zodSchemas[resolvedReference] && !this.zodSchemas[requestedSchemaName]) {
12222
12247
  this.zodSchemas[requestedSchemaName] = this.zodSchemas[resolvedReference];
12223
12248
  }
12224
12249
  this.schemaVariantRefs.set(this.getVariantKey(requestedSchemaName, this.currentContentType), this.zodSchemas[requestedSchemaName] ? requestedSchemaName : resolvedReference);
@@ -13756,7 +13781,13 @@ var ZodSchemaConverter = class {
13756
13781
  * Get all processed Zod schemas
13757
13782
  */
13758
13783
  getProcessedSchemas() {
13759
- return this.zodSchemas;
13784
+ const result = {};
13785
+ for (const [name, schema] of Object.entries(this.zodSchemas)) {
13786
+ if (!this.internalSchemaNames.has(name)) {
13787
+ result[name] = schema;
13788
+ }
13789
+ }
13790
+ return result;
13760
13791
  }
13761
13792
  /**
13762
13793
  * Pre-scan all files to build type mappings
@@ -13808,6 +13839,15 @@ var ZodSchemaConverter = class {
13808
13839
  if (t10.isIdentifier(declaration.id) && declaration.init) {
13809
13840
  const schemaName = declaration.id.name;
13810
13841
  if (this.isZodSchema(declaration.init)) {
13842
+ const decl = path25.node.declaration;
13843
+ const allComments = [
13844
+ ...path25.node.leadingComments ?? [],
13845
+ ...decl?.leadingComments ?? [],
13846
+ ...declaration.leadingComments ?? []
13847
+ ];
13848
+ if (extractInternalFlagFromComments(allComments)) {
13849
+ this.internalSchemaNames.add(schemaName);
13850
+ }
13811
13851
  if (!this.getStoredSchema(schemaName)) {
13812
13852
  logger.debug(`Pre-processing Zod schema: ${schemaName}`);
13813
13853
  this.processingSchemas.add(schemaName);
@@ -13833,6 +13873,13 @@ var ZodSchemaConverter = class {
13833
13873
  if (t10.isIdentifier(declaration.id) && declaration.init) {
13834
13874
  const schemaName = declaration.id.name;
13835
13875
  if (this.isZodSchema(declaration.init)) {
13876
+ const allComments = [
13877
+ ...path25.node.leadingComments ?? [],
13878
+ ...declaration.leadingComments ?? []
13879
+ ];
13880
+ if (extractInternalFlagFromComments(allComments)) {
13881
+ this.internalSchemaNames.add(schemaName);
13882
+ }
13836
13883
  if (!this.getStoredSchema(schemaName) && !this.processingSchemas.has(schemaName)) {
13837
13884
  logger.debug(`Pre-processing Zod schema: ${schemaName}`);
13838
13885
  this.processingSchemas.add(schemaName);
@@ -13875,15 +13922,57 @@ var ZodSchemaConverter = class {
13875
13922
  if (finalName !== schemaName) {
13876
13923
  this.indexSchemaName(finalName, filePath);
13877
13924
  }
13878
- if (!this.getStoredSchema(finalName)) {
13925
+ if (!this.zodSchemas[finalName]) {
13879
13926
  if (overrideId && overrideId !== schemaName) {
13880
13927
  this.typeToSchemaMapping[schemaName] = overrideId;
13928
+ this.metaIdSchemaNames.add(schemaName);
13929
+ if (this.typeToSchemaMapping[overrideId] === schemaName) {
13930
+ delete this.typeToSchemaMapping[overrideId];
13931
+ }
13881
13932
  }
13882
- this.storeResolvedSchema(finalName, schema);
13933
+ const variantKey = this.getVariantKey(finalName, this.currentContentType);
13934
+ this.zodSchemas[finalName] = schema;
13935
+ this.schemaVariantRefs.set(variantKey, finalName);
13883
13936
  } else {
13884
13937
  logger.warn(`Schema component name '${overrideId ?? finalName}' conflicts with an existing schema, ignoring .meta({ id }) on '${schemaName}'`);
13885
13938
  }
13886
13939
  }
13940
+ /**
13941
+ * Derives the conventional Zod schema name from a TypeScript type name.
13942
+ * e.g. "Slider" → "sliderSchema", "SliderItem" → "sliderItemSchema".
13943
+ * Returns null when the input is already a schema name or is not PascalCase.
13944
+ */
13945
+ deriveSchemaNameByConvention(typeName) {
13946
+ if (!typeName || !/^[A-Z]/.test(typeName) || typeName.endsWith("Schema")) {
13947
+ return null;
13948
+ }
13949
+ return typeName[0].toLowerCase() + typeName.slice(1) + "Schema";
13950
+ }
13951
+ /**
13952
+ * Checks whether a Zod schema with the given name is present in schemaDirs
13953
+ * WITHOUT populating the processed-schema cache. A file-content substring check
13954
+ * (the same heuristic used by processFileForZodSchema) is sufficient here: we
13955
+ * only want to know whether the candidate *might* live in schemaDirs so that the
13956
+ * convention mapping can be registered; the actual processing happens later in
13957
+ * the normal convertZodSchemaToOpenApi lookup flow.
13958
+ */
13959
+ locateSchemaByConvention(candidate) {
13960
+ if (this.schemaNameToFiles.has(candidate)) {
13961
+ return true;
13962
+ }
13963
+ for (const dir of this.schemaDirs) {
13964
+ for (const filePath of this.getSchemaFiles(dir)) {
13965
+ try {
13966
+ const content = this.fileAccess.readFileSync(filePath, "utf-8");
13967
+ if (content.includes(candidate)) {
13968
+ return true;
13969
+ }
13970
+ } catch {
13971
+ }
13972
+ }
13973
+ }
13974
+ return false;
13975
+ }
13887
13976
  /**
13888
13977
  * Check if node is Zod schema
13889
13978
  */
@@ -14525,7 +14614,7 @@ function collectFirstMemberLeadingComments(interfaceDecl) {
14525
14614
  const firstMember = body.body?.[0];
14526
14615
  return firstMember?.leadingComments ?? [];
14527
14616
  }
14528
- function collectAllExportedDefinitions(ast, typeDefinitions, currentFile, schemaIdAliases) {
14617
+ function collectAllExportedDefinitions(ast, typeDefinitions, currentFile, schemaIdAliases, internalSchemaNames) {
14529
14618
  function registerDefinition(name, entry, allComments) {
14530
14619
  if (!typeDefinitions[name]) {
14531
14620
  typeDefinitions[name] = entry;
@@ -14537,6 +14626,9 @@ function collectAllExportedDefinitions(ast, typeDefinitions, currentFile, schema
14537
14626
  typeDefinitions[overrideId] = entry;
14538
14627
  }
14539
14628
  }
14629
+ if (internalSchemaNames && extractInternalFlagFromComments(allComments)) {
14630
+ internalSchemaNames.add(name);
14631
+ }
14540
14632
  }
14541
14633
  resolvedTraverse(ast, {
14542
14634
  TSTypeAliasDeclaration: (path25) => {
@@ -15016,6 +15108,7 @@ var SchemaProcessor = class {
15016
15108
  schemaTypes;
15017
15109
  isResolvingPickOmitBase = false;
15018
15110
  schemaIdAliases = {};
15111
+ internalSchemaNames = /* @__PURE__ */ new Set();
15019
15112
  fileAccess;
15020
15113
  symbolResolver;
15021
15114
  // Track imports per file for resolving ReturnType<typeof func>
@@ -15056,7 +15149,7 @@ var SchemaProcessor = class {
15056
15149
  getDefinedSchemas() {
15057
15150
  const filteredSchemas = {};
15058
15151
  Object.entries(this.openapiDefinitions).forEach(([key, value]) => {
15059
- if (!this.schemaIdAliases[key] && !this.isGenericTypeParameter(key) && !this.isInvalidSchemaName(key) && !this.isBuiltInUtilityType(key) && !this.isFunctionSchema(key)) {
15152
+ if (!this.schemaIdAliases[key] && !this.isGenericTypeParameter(key) && !this.isInvalidSchemaName(key) && !this.isBuiltInUtilityType(key) && !this.isFunctionSchema(key) && !this.internalSchemaNames.has(key)) {
15060
15153
  filteredSchemas[key] = value;
15061
15154
  }
15062
15155
  });
@@ -15066,6 +15159,22 @@ var SchemaProcessor = class {
15066
15159
  this.customSchemaProcessor.getDefinedSchemas()
15067
15160
  ]);
15068
15161
  }
15162
+ getInternalSchemas() {
15163
+ const result = {};
15164
+ for (const name of this.internalSchemaNames) {
15165
+ const def = this.openapiDefinitions[name];
15166
+ if (def)
15167
+ result[name] = def;
15168
+ }
15169
+ if (this.zodSchemaConverter) {
15170
+ for (const name of this.zodSchemaConverter.internalSchemaNames) {
15171
+ const schema = this.zodSchemaConverter.zodSchemas[name];
15172
+ if (schema)
15173
+ result[name] = schema;
15174
+ }
15175
+ }
15176
+ return result;
15177
+ }
15069
15178
  findSchemaDefinition(schemaName, contentType) {
15070
15179
  this.contentType = contentType;
15071
15180
  if (schemaName.includes("<") && schemaName.includes(">")) {
@@ -15216,7 +15325,7 @@ var SchemaProcessor = class {
15216
15325
  * Used when processing imported files to ensure all referenced types are available
15217
15326
  */
15218
15327
  collectAllExportedDefinitions(ast, filePath) {
15219
- collectAllExportedDefinitions(ast, this.typeDefinitions, filePath || this.currentFilePath, this.schemaIdAliases);
15328
+ collectAllExportedDefinitions(ast, this.typeDefinitions, filePath || this.currentFilePath, this.schemaIdAliases, this.internalSchemaNames);
15220
15329
  }
15221
15330
  collectTypeDefinitions(ast, schemaName, filePath) {
15222
15331
  collectTypeDefinitions(ast, schemaName, this.typeDefinitions, filePath || this.currentFilePath);
@@ -17202,6 +17311,64 @@ function generateErrorResponsesFromConfig(document, errorConfig) {
17202
17311
  });
17203
17312
  }
17204
17313
 
17314
+ // ../openapi-core/dist/core/exclude-schemas.js
17315
+ function patternToRegExp(pattern) {
17316
+ const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
17317
+ return new RegExp(`^${escaped.replace(/\*/g, ".*")}$`);
17318
+ }
17319
+ function matchExcludePatterns(names, patterns) {
17320
+ if (patterns.length === 0)
17321
+ return [];
17322
+ const regexes = patterns.map(patternToRegExp);
17323
+ return names.filter((name) => regexes.some((re) => re.test(name)));
17324
+ }
17325
+ function applyExcludeSchemas(document, mergedSchemas, excludedSchemas) {
17326
+ const excludedNames = new Set(Object.keys(excludedSchemas));
17327
+ if (excludedNames.size === 0)
17328
+ return;
17329
+ walkAndInline(document, excludedSchemas, excludedNames, /* @__PURE__ */ new Set());
17330
+ for (const name of excludedNames) {
17331
+ delete mergedSchemas[name];
17332
+ }
17333
+ }
17334
+ function walkAndInline(obj, excluded, excludedNames, visiting) {
17335
+ if (!obj || typeof obj !== "object")
17336
+ return;
17337
+ if (Array.isArray(obj)) {
17338
+ for (const item of obj) {
17339
+ walkAndInline(item, excluded, excludedNames, visiting);
17340
+ }
17341
+ return;
17342
+ }
17343
+ const rec = obj;
17344
+ const ref = rec["$ref"];
17345
+ if (typeof ref === "string") {
17346
+ const match = ref.match(/^#\/components\/schemas\/(.+)$/);
17347
+ const name = match?.[1];
17348
+ if (name && excludedNames.has(name)) {
17349
+ if (visiting.has(name)) {
17350
+ logger.warn(`Circular reference to internal schema "${name}", keeping $ref`);
17351
+ return;
17352
+ }
17353
+ const schemaDef = excluded[name];
17354
+ if (schemaDef) {
17355
+ const cloned = JSON.parse(JSON.stringify(schemaDef));
17356
+ delete rec["$ref"];
17357
+ Object.assign(rec, cloned);
17358
+ const newVisiting = new Set(visiting);
17359
+ newVisiting.add(name);
17360
+ for (const key of Object.keys(rec)) {
17361
+ walkAndInline(rec[key], excluded, excludedNames, newVisiting);
17362
+ }
17363
+ return;
17364
+ }
17365
+ }
17366
+ }
17367
+ for (const key of Object.keys(rec)) {
17368
+ walkAndInline(rec[key], excluded, excludedNames, visiting);
17369
+ }
17370
+ }
17371
+
17205
17372
  // ../openapi-core/dist/core/orchestrator.js
17206
17373
  function runGenerationOrchestrator({ config: config2, template, hooks, runtime, createFrameworkSource }) {
17207
17374
  const diagnostics = new DiagnosticsCollector();
@@ -17272,11 +17439,21 @@ function runGenerationOrchestrator({ config: config2, template, hooks, runtime,
17272
17439
  }
17273
17440
  profile.defaultComponentsAndErrorsMs = performance.now() - phaseStartedAt;
17274
17441
  phaseStartedAt = performance.now();
17275
- const definedSchemas = routeProcessor.getSchemaProcessor().getDefinedSchemas();
17442
+ const schemaProcessor = routeProcessor.getSchemaProcessor();
17443
+ const definedSchemas = schemaProcessor.getDefinedSchemas();
17276
17444
  const mergedSchemas = {
17277
17445
  ...document.components.schemas,
17278
17446
  ...definedSchemas
17279
17447
  };
17448
+ const internalSchemas = schemaProcessor.getInternalSchemas();
17449
+ const patternExcludedNames = matchExcludePatterns(Object.keys(mergedSchemas), config2.excludeSchemas ?? []);
17450
+ const allExcludedSchemas = {
17451
+ ...internalSchemas,
17452
+ ...Object.fromEntries(patternExcludedNames.map((name) => [name, mergedSchemas[name]]))
17453
+ };
17454
+ if (Object.keys(allExcludedSchemas).length > 0) {
17455
+ applyExcludeSchemas(document, mergedSchemas, allExcludedSchemas);
17456
+ }
17280
17457
  if (Object.keys(mergedSchemas).length > 0) {
17281
17458
  document.components.schemas = Object.fromEntries(Object.entries(mergedSchemas).sort(([a], [b]) => a.localeCompare(b, "en", { sensitivity: "base" })));
17282
17459
  }