next-openapi-gen 1.4.1 → 1.4.3

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.
@@ -369,7 +369,6 @@ function parseJSDocBlock(commentValue, filePath) {
369
369
  result.summary = explicitSummary;
370
370
  }
371
371
  result.description = extractLineValue(normalizedComment, "@description");
372
- result.tag = extractLineValue(normalizedComment, "@tag");
373
372
  result.tagSummary = extractLineValue(normalizedComment, "@tagSummary");
374
373
  result.tagKind = extractLineValue(normalizedComment, "@tagKind");
375
374
  result.tagParent = extractLineValue(normalizedComment, "@tagParent");
@@ -403,17 +402,28 @@ function parseJSDocBlock(commentValue, filePath) {
403
402
  if (Array.isArray(responsePrefixEncoding)) {
404
403
  result.responsePrefixEncoding = responsePrefixEncoding;
405
404
  }
406
- const parsedResponse = parseResponseTag(normalizedComment);
407
- if (parsedResponse) {
408
- result.successCode = parsedResponse.successCode;
409
- result.responseType = parsedResponse.responseType;
410
- if (!result.responseDescription && parsedResponse.responseDescription) {
411
- result.responseDescription = parsedResponse.responseDescription;
405
+ const responseMatches = [...normalizedComment.matchAll(/@response\s+([^\n\r@]+)/g)];
406
+ if (responseMatches.length > 0) {
407
+ const firstRaw = responseMatches[0]?.[1]?.trim();
408
+ if (firstRaw) {
409
+ const parsedResponse = parseResponseRawValue(firstRaw);
410
+ result.successCode = parsedResponse.successCode;
411
+ result.responseType = parsedResponse.responseType;
412
+ if (!result.responseDescription && parsedResponse.responseDescription) {
413
+ result.responseDescription = parsedResponse.responseDescription;
414
+ }
415
+ }
416
+ const extraResponses = responseMatches.slice(1).map((m) => m[1]?.trim()).filter((t16) => Boolean(t16));
417
+ if (extraResponses.length > 0) {
418
+ result.addResponses = extraResponses.join(",");
412
419
  }
413
420
  }
414
421
  const addMatches = [...normalizedComment.matchAll(/@add\s+([^\n\r@]*)/g)];
415
422
  if (addMatches.length > 0) {
416
- result.addResponses = addMatches.map((match) => match[1]?.trim() || "").filter(Boolean).join(",");
423
+ const addEntries = addMatches.map((match) => match[1]?.trim() || "").filter(Boolean).join(",");
424
+ if (addEntries) {
425
+ result.addResponses = result.addResponses ? `${result.addResponses},${addEntries}` : addEntries;
426
+ }
417
427
  }
418
428
  const examples = collectExampleDefinitions(normalizedComment, "@examples", filePath);
419
429
  result.requestExamples = buildExampleMap(examples.definitions, "request");
@@ -426,6 +436,17 @@ function parseJSDocBlock(commentValue, filePath) {
426
436
  if (additionalTags.length > 0) {
427
437
  result.tags = additionalTags;
428
438
  }
439
+ const tagMatches = [...normalizedComment.matchAll(/@tag\s+([^\n\r@]*)/g)];
440
+ if (tagMatches.length > 0) {
441
+ const primaryTag = tagMatches[0]?.[1]?.trim();
442
+ if (primaryTag) {
443
+ result.tag = primaryTag;
444
+ }
445
+ const extraTags = tagMatches.slice(1).map((m) => m[1]?.trim()).filter((t16) => Boolean(t16));
446
+ if (extraTags.length > 0) {
447
+ result.tags = [...result.tags ?? [], ...extraTags];
448
+ }
449
+ }
429
450
  const servers = parseServersTag(normalizedComment);
430
451
  if (servers.length > 0) {
431
452
  result.servers = servers;
@@ -628,11 +649,7 @@ function parseOpenApiOverrideTag(commentValue) {
628
649
  function extractTypeFromComment(commentValue, tag) {
629
650
  return commentValue.match(new RegExp(`^\\s*\\*?\\s*${tag}\\s+([\\w<>,\\s[\\]]+)`, "m"))?.[1]?.trim() || "";
630
651
  }
631
- function parseResponseTag(commentValue) {
632
- const rawValue = commentValue.match(/@response\s+([^\n\r@]+)/)?.[1]?.trim();
633
- if (!rawValue) {
634
- return null;
635
- }
652
+ function parseResponseRawValue(rawValue) {
636
653
  if (isStatusCodeToken(rawValue)) {
637
654
  return {
638
655
  responseDescription: "",
@@ -1081,6 +1098,18 @@ function getOperationId(routePath, method) {
1081
1098
  const operation = routePath.replaceAll(/\//g, "-").replace(/^-/, "");
1082
1099
  return `${method}-${operation}`;
1083
1100
  }
1101
+ function deepMerge(target, source) {
1102
+ for (const key of Object.keys(source)) {
1103
+ const sourceVal = source[key];
1104
+ const targetVal = target[key];
1105
+ if (sourceVal !== null && typeof sourceVal === "object" && !Array.isArray(sourceVal) && targetVal !== null && typeof targetVal === "object" && !Array.isArray(targetVal)) {
1106
+ deepMerge(targetVal, sourceVal);
1107
+ } else {
1108
+ target[key] = sourceVal;
1109
+ }
1110
+ }
1111
+ return target;
1112
+ }
1084
1113
  var DEFAULT_PARSER_OPTIONS = {
1085
1114
  sourceType: "module",
1086
1115
  plugins: ["typescript", "jsx", "decorators-legacy"]
@@ -2455,23 +2484,58 @@ var SymbolResolver = class {
2455
2484
  }
2456
2485
  /**
2457
2486
  * Returns a simple literal value (string/number/boolean/null) for a `const` declarator,
2458
- * or `null` if no such declarator exists.
2487
+ * following imports and re-exports when the name is not declared locally.
2459
2488
  */
2460
2489
  resolveLiteral(filePath, name) {
2490
+ const visited = /* @__PURE__ */ new Set();
2491
+ return this.resolveLiteralInternal(filePath, name, visited);
2492
+ }
2493
+ resolveLiteralInternal(filePath, name, visited) {
2494
+ if (visited.has(filePath))
2495
+ return void 0;
2496
+ visited.add(filePath);
2461
2497
  const index = this.getIndex(filePath);
2462
2498
  if (!index)
2463
2499
  return void 0;
2464
2500
  const literal2 = index.constLiterals.get(name);
2465
- if (!literal2)
2466
- return void 0;
2467
- if (t3.isStringLiteral(literal2))
2468
- return literal2.value;
2469
- if (t3.isNumericLiteral(literal2))
2470
- return literal2.value;
2471
- if (t3.isBooleanLiteral(literal2))
2472
- return literal2.value;
2473
- if (t3.isNullLiteral(literal2))
2474
- return null;
2501
+ if (literal2) {
2502
+ if (t3.isStringLiteral(literal2))
2503
+ return literal2.value;
2504
+ if (t3.isNumericLiteral(literal2))
2505
+ return literal2.value;
2506
+ if (t3.isBooleanLiteral(literal2))
2507
+ return literal2.value;
2508
+ if (t3.isNullLiteral(literal2))
2509
+ return null;
2510
+ }
2511
+ const imports = this.getImports(filePath);
2512
+ const importInfo = imports?.get(name);
2513
+ if (importInfo) {
2514
+ const resolved = this.resolveImportPath(filePath, importInfo.source);
2515
+ if (resolved) {
2516
+ const targetName = importInfo.importedName === "default" ? name : importInfo.importedName;
2517
+ const result = this.resolveLiteralInternal(resolved, targetName, visited);
2518
+ if (result !== void 0)
2519
+ return result;
2520
+ }
2521
+ }
2522
+ const reExport = index.namedReExports.get(name);
2523
+ if (reExport) {
2524
+ const resolved = this.resolveImportPath(filePath, reExport.source);
2525
+ if (resolved) {
2526
+ const result = this.resolveLiteralInternal(resolved, reExport.importedName, visited);
2527
+ if (result !== void 0)
2528
+ return result;
2529
+ }
2530
+ }
2531
+ for (const starSrc of index.exportsStar) {
2532
+ const resolved = this.resolveImportPath(filePath, starSrc);
2533
+ if (!resolved)
2534
+ continue;
2535
+ const result = this.resolveLiteralInternal(resolved, name, visited);
2536
+ if (result !== void 0)
2537
+ return result;
2538
+ }
2475
2539
  return void 0;
2476
2540
  }
2477
2541
  /**
@@ -12794,6 +12858,58 @@ var ZodSchemaConverter = class {
12794
12858
  this.symbolResolver.primeAST(this.currentFilePath, this.currentAST);
12795
12859
  return this.symbolResolver.resolveLiteral(this.currentFilePath, name);
12796
12860
  }
12861
+ /**
12862
+ * Unwrap a possible `TSAsExpression` / `TSSatisfiesExpression` to get the
12863
+ * underlying expression node. Returns the node itself when no wrapper is
12864
+ * present.
12865
+ */
12866
+ unwrapTypeAssertion(node) {
12867
+ if (!node)
12868
+ return void 0;
12869
+ if (t10.isTSAsExpression(node) || t10.isTSSatisfiesExpression(node)) {
12870
+ return node.expression;
12871
+ }
12872
+ return node;
12873
+ }
12874
+ /**
12875
+ * Resolve a numeric value from a call-expression argument.
12876
+ * Handles: numeric literals, identifier references to const numbers,
12877
+ * and `x as number` / `x satisfies number` wrappers around either.
12878
+ */
12879
+ resolveNumericArg(arg) {
12880
+ if (!arg)
12881
+ return void 0;
12882
+ const node = this.unwrapTypeAssertion(arg);
12883
+ if (t10.isNumericLiteral(node))
12884
+ return node.value;
12885
+ if (t10.isUnaryExpression(node) && node.operator === "-" && t10.isNumericLiteral(node.argument)) {
12886
+ return -node.argument.value;
12887
+ }
12888
+ if (t10.isIdentifier(node)) {
12889
+ const val = this.resolveLiteralValue(node.name);
12890
+ if (typeof val === "number")
12891
+ return val;
12892
+ }
12893
+ return void 0;
12894
+ }
12895
+ /**
12896
+ * Resolve a string value from a call-expression argument.
12897
+ * Handles: string literals, identifier references to const strings,
12898
+ * and `x as string` / `x satisfies string` wrappers around either.
12899
+ */
12900
+ resolveStringArg(arg) {
12901
+ if (!arg)
12902
+ return void 0;
12903
+ const node = this.unwrapTypeAssertion(arg);
12904
+ if (t10.isStringLiteral(node))
12905
+ return node.value;
12906
+ if (t10.isIdentifier(node)) {
12907
+ const val = this.resolveLiteralValue(node.name);
12908
+ if (typeof val === "string")
12909
+ return val;
12910
+ }
12911
+ return void 0;
12912
+ }
12797
12913
  /**
12798
12914
  * Resolve an identifier referring to a `z.object({...})` (or similar) call expression.
12799
12915
  * This lets callers inline the referenced object's shape.
@@ -12862,6 +12978,14 @@ var ZodSchemaConverter = class {
12862
12978
  if (t10.isNullLiteral(node)) {
12863
12979
  return null;
12864
12980
  }
12981
+ if (t10.isIdentifier(node)) {
12982
+ const val = this.resolveLiteralValue(node.name);
12983
+ if (val !== void 0)
12984
+ return val;
12985
+ }
12986
+ if (t10.isTSAsExpression(node) || t10.isTSSatisfiesExpression(node)) {
12987
+ return this.extractStaticJsonValue(node.expression);
12988
+ }
12865
12989
  if (t10.isArrayExpression(node)) {
12866
12990
  const values = [];
12867
12991
  for (const element of node.elements) {
@@ -12960,53 +13084,60 @@ var ZodSchemaConverter = class {
12960
13084
  schema.nullable = true;
12961
13085
  }
12962
13086
  break;
12963
- case "describe":
12964
- if (node.arguments.length > 0 && t10.isStringLiteral(node.arguments[0])) {
12965
- const description = node.arguments[0].value;
12966
- if (description.startsWith("@deprecated")) {
13087
+ case "describe": {
13088
+ const descVal = this.resolveStringArg(node.arguments[0]);
13089
+ if (descVal !== void 0) {
13090
+ if (descVal.startsWith("@deprecated")) {
12967
13091
  schema.deprecated = true;
12968
- schema.description = description.replace("@deprecated", "").trim();
13092
+ schema.description = descVal.replace("@deprecated", "").trim();
12969
13093
  } else {
12970
- schema.description = description;
13094
+ schema.description = descVal;
12971
13095
  }
12972
13096
  }
12973
13097
  break;
13098
+ }
12974
13099
  case "deprecated":
12975
13100
  schema.deprecated = true;
12976
13101
  break;
12977
- case "min":
12978
- if (node.arguments.length > 0 && t10.isNumericLiteral(node.arguments[0])) {
13102
+ case "min": {
13103
+ const minVal = this.resolveNumericArg(node.arguments[0]);
13104
+ if (minVal !== void 0) {
12979
13105
  if (schema.type === "string") {
12980
- schema.minLength = node.arguments[0].value;
13106
+ schema.minLength = minVal;
12981
13107
  } else if (schema.type === "number" || schema.type === "integer") {
12982
- schema.minimum = node.arguments[0].value;
13108
+ schema.minimum = minVal;
12983
13109
  } else if (schema.type === "array") {
12984
- schema.minItems = node.arguments[0].value;
13110
+ schema.minItems = minVal;
12985
13111
  }
12986
13112
  }
12987
13113
  break;
12988
- case "max":
12989
- if (node.arguments.length > 0 && t10.isNumericLiteral(node.arguments[0])) {
13114
+ }
13115
+ case "max": {
13116
+ const maxVal = this.resolveNumericArg(node.arguments[0]);
13117
+ if (maxVal !== void 0) {
12990
13118
  if (schema.type === "string") {
12991
- schema.maxLength = node.arguments[0].value;
13119
+ schema.maxLength = maxVal;
12992
13120
  } else if (schema.type === "number" || schema.type === "integer") {
12993
- schema.maximum = node.arguments[0].value;
13121
+ schema.maximum = maxVal;
12994
13122
  } else if (schema.type === "array") {
12995
- schema.maxItems = node.arguments[0].value;
13123
+ schema.maxItems = maxVal;
12996
13124
  }
12997
13125
  }
12998
13126
  break;
12999
- case "length":
13000
- if (node.arguments.length > 0 && t10.isNumericLiteral(node.arguments[0])) {
13127
+ }
13128
+ case "length": {
13129
+ const lenVal = this.resolveNumericArg(node.arguments[0]);
13130
+ if (lenVal !== void 0) {
13001
13131
  if (schema.type === "string") {
13002
- schema.minLength = node.arguments[0].value;
13003
- schema.maxLength = node.arguments[0].value;
13132
+ schema.minLength = lenVal;
13133
+ schema.maxLength = lenVal;
13004
13134
  } else if (schema.type === "array") {
13005
- schema.minItems = node.arguments[0].value;
13006
- schema.maxItems = node.arguments[0].value;
13135
+ schema.minItems = lenVal;
13136
+ schema.maxItems = lenVal;
13007
13137
  }
13008
13138
  }
13009
13139
  break;
13140
+ }
13010
13141
  case "nonempty":
13011
13142
  if (schema.type === "array") {
13012
13143
  schema.minItems = Math.max(schema.minItems ?? 0, 1);
@@ -13082,21 +13213,27 @@ var ZodSchemaConverter = class {
13082
13213
  schema.pattern = node.arguments[0].pattern;
13083
13214
  }
13084
13215
  break;
13085
- case "startsWith":
13086
- if (node.arguments.length > 0 && t10.isStringLiteral(node.arguments[0])) {
13087
- schema.pattern = `^${this.escapeRegExp(node.arguments[0].value)}`;
13216
+ case "startsWith": {
13217
+ const swVal = this.resolveStringArg(node.arguments[0]);
13218
+ if (swVal !== void 0) {
13219
+ schema.pattern = `^${this.escapeRegExp(swVal)}`;
13088
13220
  }
13089
13221
  break;
13090
- case "endsWith":
13091
- if (node.arguments.length > 0 && t10.isStringLiteral(node.arguments[0])) {
13092
- schema.pattern = `${this.escapeRegExp(node.arguments[0].value)}$`;
13222
+ }
13223
+ case "endsWith": {
13224
+ const ewVal = this.resolveStringArg(node.arguments[0]);
13225
+ if (ewVal !== void 0) {
13226
+ schema.pattern = `${this.escapeRegExp(ewVal)}$`;
13093
13227
  }
13094
13228
  break;
13095
- case "includes":
13096
- if (node.arguments.length > 0 && t10.isStringLiteral(node.arguments[0])) {
13097
- schema.pattern = this.escapeRegExp(node.arguments[0].value);
13229
+ }
13230
+ case "includes": {
13231
+ const incVal = this.resolveStringArg(node.arguments[0]);
13232
+ if (incVal !== void 0) {
13233
+ schema.pattern = this.escapeRegExp(incVal);
13098
13234
  }
13099
13235
  break;
13236
+ }
13100
13237
  case "int":
13101
13238
  schema.type = "integer";
13102
13239
  break;
@@ -13120,17 +13257,22 @@ var ZodSchemaConverter = class {
13120
13257
  break;
13121
13258
  case "default":
13122
13259
  if (node.arguments.length > 0) {
13123
- if (t10.isStringLiteral(node.arguments[0])) {
13124
- schema.default = node.arguments[0].value;
13125
- } else if (t10.isNumericLiteral(node.arguments[0])) {
13126
- schema.default = node.arguments[0].value;
13127
- } else if (t10.isBooleanLiteral(node.arguments[0])) {
13128
- schema.default = node.arguments[0].value;
13129
- } else if (t10.isNullLiteral(node.arguments[0])) {
13260
+ const defaultArg = this.unwrapTypeAssertion(node.arguments[0]);
13261
+ if (t10.isStringLiteral(defaultArg)) {
13262
+ schema.default = defaultArg.value;
13263
+ } else if (t10.isNumericLiteral(defaultArg)) {
13264
+ schema.default = defaultArg.value;
13265
+ } else if (t10.isBooleanLiteral(defaultArg)) {
13266
+ schema.default = defaultArg.value;
13267
+ } else if (t10.isNullLiteral(defaultArg)) {
13130
13268
  schema.default = null;
13131
- } else if (t10.isObjectExpression(node.arguments[0])) {
13269
+ } else if (t10.isIdentifier(defaultArg)) {
13270
+ const val = this.resolveLiteralValue(defaultArg.name);
13271
+ if (val !== void 0)
13272
+ schema.default = val;
13273
+ } else if (t10.isObjectExpression(defaultArg)) {
13132
13274
  const defaultObj = {};
13133
- node.arguments[0].properties.forEach((prop) => {
13275
+ defaultArg.properties.forEach((prop) => {
13134
13276
  if (t10.isObjectProperty(prop) && (t10.isIdentifier(prop.key) || t10.isStringLiteral(prop.key)) && (t10.isStringLiteral(prop.value) || t10.isNumericLiteral(prop.value) || t10.isBooleanLiteral(prop.value))) {
13135
13277
  const key = t10.isIdentifier(prop.key) ? prop.key.name : prop.key.value;
13136
13278
  const value = t10.isStringLiteral(prop.value) ? prop.value.value : t10.isNumericLiteral(prop.value) ? prop.value.value : t10.isBooleanLiteral(prop.value) ? prop.value.value : null;
@@ -16520,7 +16662,7 @@ var OperationProcessor = class {
16520
16662
  this.applyResponseHeaders(definition, responseHeaders);
16521
16663
  this.applyResponseLinks(definition, responseLinks);
16522
16664
  if (openapiOverride) {
16523
- Object.assign(definition, structuredClone(openapiOverride));
16665
+ deepMerge(definition, structuredClone(openapiOverride));
16524
16666
  }
16525
16667
  return {
16526
16668
  routePath,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "next-openapi-gen",
3
- "version": "1.4.1",
3
+ "version": "1.4.3",
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",