@kubb/ast 5.0.0-beta.60 → 5.0.0-beta.62

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.
Files changed (87) hide show
  1. package/README.md +2 -1
  2. package/dist/defineMacro-BryDiuQl.cjs +115 -0
  3. package/dist/defineMacro-BryDiuQl.cjs.map +1 -0
  4. package/dist/defineMacro-CNkUpxia.js +99 -0
  5. package/dist/defineMacro-CNkUpxia.js.map +1 -0
  6. package/dist/defineMacro-Vw-xQvq7.d.ts +467 -0
  7. package/dist/factory.cjs +64 -4
  8. package/dist/factory.cjs.map +1 -0
  9. package/dist/factory.d.ts +2 -37
  10. package/dist/factory.js +58 -3
  11. package/dist/factory.js.map +1 -0
  12. package/dist/{index-BzjwdK2M.d.ts → index-CTaN_ee_.d.ts} +235 -198
  13. package/dist/index.cjs +33 -32
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.ts +36 -36
  16. package/dist/index.js +19 -14
  17. package/dist/index.js.map +1 -1
  18. package/dist/macros.cjs +130 -0
  19. package/dist/macros.cjs.map +1 -0
  20. package/dist/macros.d.ts +61 -0
  21. package/dist/macros.js +128 -0
  22. package/dist/macros.js.map +1 -0
  23. package/dist/{operationParams-BZ07xDm0.d.ts → operationParams-B44p6EQy.d.ts} +2 -2
  24. package/dist/{response-DS5S3IG4.cjs → response-BeG6hq2D.cjs} +389 -173
  25. package/dist/response-BeG6hq2D.cjs.map +1 -0
  26. package/dist/{response-DKxTr522.js → response-Rd1uisM1.js} +377 -162
  27. package/dist/response-Rd1uisM1.js.map +1 -0
  28. package/dist/types-DQTgVSwE.d.ts +314 -0
  29. package/dist/types.d.ts +5 -5
  30. package/dist/{utils-D83JA6Xx.cjs → utils-BDI3kXlb.cjs} +20 -753
  31. package/dist/utils-BDI3kXlb.cjs.map +1 -0
  32. package/dist/{utils-Dj_KoXMv.js → utils-DW9rnfsD.js} +11 -630
  33. package/dist/utils-DW9rnfsD.js.map +1 -0
  34. package/dist/utils.cjs +11 -9
  35. package/dist/utils.d.ts +38 -4
  36. package/dist/utils.js +4 -3
  37. package/dist/visitor-B7Zlnp4c.cjs +604 -0
  38. package/dist/visitor-B7Zlnp4c.cjs.map +1 -0
  39. package/dist/visitor-DepQEKyp.js +510 -0
  40. package/dist/visitor-DepQEKyp.js.map +1 -0
  41. package/package.json +5 -1
  42. package/src/constants.ts +0 -30
  43. package/src/dedupe.ts +1 -9
  44. package/src/defineMacro.ts +139 -0
  45. package/src/{node.ts → defineNode.ts} +19 -42
  46. package/src/dialect.ts +42 -19
  47. package/src/factory.ts +3 -90
  48. package/src/index.ts +9 -15
  49. package/src/macros/index.ts +3 -0
  50. package/src/macros/macroDiscriminatorEnum.ts +50 -0
  51. package/src/macros/macroEnumName.ts +33 -0
  52. package/src/macros/macroSimplifyUnion.ts +60 -0
  53. package/src/nodes/code.ts +32 -75
  54. package/src/nodes/content.ts +2 -7
  55. package/src/nodes/file.ts +102 -14
  56. package/src/nodes/function.ts +46 -46
  57. package/src/nodes/index.ts +2 -3
  58. package/src/nodes/input.ts +13 -9
  59. package/src/nodes/operation.ts +5 -5
  60. package/src/nodes/output.ts +1 -1
  61. package/src/nodes/parameter.ts +6 -6
  62. package/src/nodes/property.ts +6 -6
  63. package/src/nodes/requestBody.ts +6 -13
  64. package/src/nodes/response.ts +93 -8
  65. package/src/nodes/schema.ts +1 -1
  66. package/src/registry.ts +1 -1
  67. package/src/types.ts +4 -4
  68. package/src/utils/extractStringsFromNodes.ts +1 -0
  69. package/src/utils/index.ts +2 -2
  70. package/src/utils/operationParams.ts +1 -1
  71. package/src/utils/refs.ts +27 -1
  72. package/src/utils/schemaMerge.ts +34 -0
  73. package/src/visitor.ts +2 -13
  74. package/dist/extractStringsFromNodes-WMYJ8nQL.d.ts +0 -82
  75. package/dist/factory-Cl8Z7mcc.cjs +0 -299
  76. package/dist/factory-Cl8Z7mcc.cjs.map +0 -1
  77. package/dist/factory-Du7nEP4B.js +0 -282
  78. package/dist/factory-Du7nEP4B.js.map +0 -1
  79. package/dist/response-DKxTr522.js.map +0 -1
  80. package/dist/response-DS5S3IG4.cjs.map +0 -1
  81. package/dist/types-olVl9v5p.d.ts +0 -764
  82. package/dist/utils-D83JA6Xx.cjs.map +0 -1
  83. package/dist/utils-Dj_KoXMv.js.map +0 -1
  84. package/src/mocks.ts +0 -56
  85. package/src/nodes/http.ts +0 -85
  86. package/src/transformers.ts +0 -191
  87. /package/src/{printer.ts → definePrinter.ts} +0 -0
@@ -1,5 +1,20 @@
1
1
  import "./chunk-CNktS9qV.js";
2
- //#region src/node.ts
2
+ import { hash } from "node:crypto";
3
+ import path from "node:path";
4
+ //#region src/defineNode.ts
5
+ /**
6
+ * Visitor callback names, one per traversable node kind, in traversal order.
7
+ * Kept in sync with the keys of `Visitor` in `visitor.ts`.
8
+ */
9
+ const visitorKeys = [
10
+ "input",
11
+ "output",
12
+ "operation",
13
+ "schema",
14
+ "property",
15
+ "parameter",
16
+ "response"
17
+ ];
3
18
  /**
4
19
  * Builds a type guard that matches nodes of the given `kind`.
5
20
  */
@@ -7,52 +22,30 @@ function isKind(kind) {
7
22
  return (node) => node.kind === kind;
8
23
  }
9
24
  /**
10
- * Updates a schema's `optional` and `nullish` flags from a parent's `required`
11
- * value and the schema's own `nullable`. Mirrors how OpenAPI parameters and
12
- * object properties combine "required" and "nullable" into a single AST.
13
- *
14
- * - Non-required + non-nullable → `optional: true`.
15
- * - Non-required + nullable → `nullish: true`.
16
- * - Required → both flags cleared.
17
- */
18
- function syncOptionality(schema, required) {
19
- const nullable = schema.nullable ?? false;
20
- return {
21
- ...schema,
22
- optional: !required && !nullable ? true : void 0,
23
- nullish: !required && nullable ? true : void 0
24
- };
25
- }
26
- /**
27
25
  * Defines a node once and derives its `create` builder, `is` guard, and traversal
28
26
  * metadata. `create` merges `defaults`, the `build` hook (or the raw input), and the
29
27
  * `kind`, so node construction lives in one place without scattered `as` casts.
30
28
  *
31
- * Set `rebuild: true` when the `build` hook derives fields from children. After a
32
- * transform rewrites those children, the registry reruns `create` so the derived
33
- * fields stay correct.
34
- *
35
29
  * @example Simple node
36
30
  * ```ts
37
31
  * const importDef = defineNode<ImportNode>({ kind: 'Import' })
38
32
  * const createImport = importDef.create
39
33
  * ```
40
34
  *
41
- * @example Node with a build hook that is rerun on transform
35
+ * @example Node with a build hook
42
36
  * ```ts
43
37
  * const propertyDef = defineNode<PropertyNode, UserPropertyNode>({
44
38
  * kind: 'Property',
45
39
  * build: (props) => ({ ...props, required: props.required ?? false }),
46
40
  * children: ['schema'],
47
41
  * visitorKey: 'property',
48
- * rebuild: true,
49
42
  * })
50
43
  * ```
51
44
  */
52
45
  function defineNode(config) {
53
- const { kind, defaults, build, children, visitorKey, rebuild } = config;
54
- function create(input) {
55
- const base = build ? build(input) : input;
46
+ const { kind, defaults, build, children, visitorKey } = config;
47
+ function create(input, dialect) {
48
+ const base = build ? build(input, dialect) : input;
56
49
  return {
57
50
  ...defaults,
58
51
  ...base,
@@ -64,8 +57,7 @@ function defineNode(config) {
64
57
  create,
65
58
  is: isKind(kind),
66
59
  children,
67
- visitorKey,
68
- rebuild
60
+ visitorKey
69
61
  };
70
62
  }
71
63
  //#endregion
@@ -163,6 +155,23 @@ function pascalCase(text, { prefix = "", suffix = "" } = {}) {
163
155
  return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
164
156
  }
165
157
  //#endregion
158
+ //#region ../../internals/utils/src/fs.ts
159
+ /**
160
+ * Strips the file extension from a path or file name.
161
+ * Only removes the last `.ext` segment when the dot is not part of a directory name.
162
+ *
163
+ * @example
164
+ * trimExtName('petStore.ts') // 'petStore'
165
+ * trimExtName('/src/models/pet.ts') // '/src/models/pet'
166
+ * trimExtName('/project.v2/gen/pet.ts') // '/project.v2/gen/pet'
167
+ * trimExtName('noExtension') // 'noExtension'
168
+ */
169
+ function trimExtName(text) {
170
+ const dotIndex = text.lastIndexOf(".");
171
+ if (dotIndex > 0 && !text.includes("/", dotIndex)) return text.slice(0, dotIndex);
172
+ return text;
173
+ }
174
+ //#endregion
166
175
  //#region src/utils/extractStringsFromNodes.ts
167
176
  /**
168
177
  * Extracts all string content from a `CodeNode` tree recursively.
@@ -188,45 +197,44 @@ function extractStringsFromNodes(nodes) {
188
197
  }).filter(Boolean).join("\n");
189
198
  }
190
199
  //#endregion
191
- //#region src/nodes/property.ts
200
+ //#region src/nodes/code.ts
192
201
  /**
193
- * Definition for the {@link PropertyNode}. `required` defaults to `false` and the
194
- * schema's `optional`/`nullish` flags are kept in sync with it.
202
+ * Definition for the {@link ConstNode}.
195
203
  */
196
- const propertyDef = defineNode({
197
- kind: "Property",
198
- build: (props) => {
199
- const required = props.required ?? false;
200
- return {
201
- ...props,
202
- required,
203
- schema: syncOptionality(props.schema, required)
204
- };
205
- },
206
- children: ["schema"],
207
- visitorKey: "property",
208
- rebuild: true
204
+ const constDef = defineNode({ kind: "Const" });
205
+ /**
206
+ * Definition for the {@link TypeNode}.
207
+ */
208
+ const typeDef = defineNode({ kind: "Type" });
209
+ /**
210
+ * Definition for the {@link FunctionNode}.
211
+ */
212
+ const functionDef = defineNode({ kind: "Function" });
213
+ /**
214
+ * Definition for the {@link ArrowFunctionNode}.
215
+ */
216
+ const arrowFunctionDef = defineNode({ kind: "ArrowFunction" });
217
+ /**
218
+ * Definition for the {@link TextNode}.
219
+ */
220
+ const textDef = defineNode({
221
+ kind: "Text",
222
+ build: (value) => ({ value })
209
223
  });
210
224
  /**
211
- * Creates a `PropertyNode`.
212
- *
213
- * @example
214
- * ```ts
215
- * const property = createProperty({
216
- * name: 'status',
217
- * required: true,
218
- * schema: createSchema({ type: 'string', nullable: true }),
219
- * })
220
- * // required=true, no optional/nullish
221
- * ```
225
+ * Definition for the {@link BreakNode}.
222
226
  */
223
- const createProperty = propertyDef.create;
224
- //#endregion
225
- //#region src/nodes/code.ts
227
+ const breakDef = defineNode({
228
+ kind: "Break",
229
+ build: () => ({})
230
+ });
226
231
  /**
227
- * Definition for the {@link ConstNode}.
232
+ * Definition for the {@link JsxNode}.
228
233
  */
229
- const constDef = defineNode({ kind: "Const" });
234
+ const jsxDef = defineNode({
235
+ kind: "Jsx",
236
+ build: (value) => ({ value })
237
+ });
230
238
  /**
231
239
  * Creates a `ConstNode` representing a TypeScript `const` declaration.
232
240
  *
@@ -238,10 +246,6 @@ const constDef = defineNode({ kind: "Const" });
238
246
  */
239
247
  const createConst = constDef.create;
240
248
  /**
241
- * Definition for the {@link TypeNode}.
242
- */
243
- const typeDef = defineNode({ kind: "Type" });
244
- /**
245
249
  * Creates a `TypeNode` representing a TypeScript `type` alias declaration.
246
250
  *
247
251
  * @example
@@ -252,10 +256,6 @@ const typeDef = defineNode({ kind: "Type" });
252
256
  */
253
257
  const createType = typeDef.create;
254
258
  /**
255
- * Definition for the {@link FunctionNode}.
256
- */
257
- const functionDef = defineNode({ kind: "Function" });
258
- /**
259
259
  * Creates a `FunctionNode` representing a TypeScript `function` declaration.
260
260
  *
261
261
  * @example
@@ -266,10 +266,6 @@ const functionDef = defineNode({ kind: "Function" });
266
266
  */
267
267
  const createFunction = functionDef.create;
268
268
  /**
269
- * Definition for the {@link ArrowFunctionNode}.
270
- */
271
- const arrowFunctionDef = defineNode({ kind: "ArrowFunction" });
272
- /**
273
269
  * Creates an `ArrowFunctionNode` representing a TypeScript arrow function.
274
270
  *
275
271
  * @example
@@ -280,13 +276,6 @@ const arrowFunctionDef = defineNode({ kind: "ArrowFunction" });
280
276
  */
281
277
  const createArrowFunction = arrowFunctionDef.create;
282
278
  /**
283
- * Definition for the {@link TextNode}.
284
- */
285
- const textDef = defineNode({
286
- kind: "Text",
287
- build: (value) => ({ value })
288
- });
289
- /**
290
279
  * Creates a {@link TextNode} representing a raw string fragment in the source output.
291
280
  *
292
281
  * @example
@@ -297,13 +286,6 @@ const textDef = defineNode({
297
286
  */
298
287
  const createText = textDef.create;
299
288
  /**
300
- * Definition for the {@link BreakNode}.
301
- */
302
- const breakDef = defineNode({
303
- kind: "Break",
304
- build: () => ({})
305
- });
306
- /**
307
289
  * Creates a {@link BreakNode} representing a line break in the source output.
308
290
  *
309
291
  * @example
@@ -316,13 +298,6 @@ function createBreak() {
316
298
  return breakDef.create();
317
299
  }
318
300
  /**
319
- * Definition for the {@link JsxNode}.
320
- */
321
- const jsxDef = defineNode({
322
- kind: "Jsx",
323
- build: (value) => ({ value })
324
- });
325
- /**
326
301
  * Creates a {@link JsxNode} representing a raw JSX fragment in the source output.
327
302
  *
328
303
  * @example
@@ -346,12 +321,171 @@ const contentDef = defineNode({
346
321
  */
347
322
  const createContent = contentDef.create;
348
323
  //#endregion
324
+ //#region src/utils/fileMerge.ts
325
+ function sourceKey(source) {
326
+ return `${source.name ?? extractStringsFromNodes(source.nodes)}:${source.isExportable ?? false}:${source.isTypeOnly ?? false}`;
327
+ }
328
+ function pathTypeKey(path, isTypeOnly) {
329
+ return `${path}:${isTypeOnly ?? false}`;
330
+ }
331
+ function exportKey(path, name, isTypeOnly, asAlias) {
332
+ return `${path}:${name ?? ""}:${isTypeOnly ?? false}:${asAlias ?? ""}`;
333
+ }
334
+ function importKey(path, name, isTypeOnly) {
335
+ return `${path}:${name ?? ""}:${isTypeOnly ?? false}`;
336
+ }
337
+ /**
338
+ * Computes a multi-level sort key for exports and imports:
339
+ * non-array names first (wildcards/namespace aliases). Type-only before value. Alphabetical path. Unnamed before named.
340
+ */
341
+ function sortKey(node) {
342
+ const isArray = Array.isArray(node.name) ? "1" : "0";
343
+ const typeOnly = node.isTypeOnly ? "0" : "1";
344
+ const hasName = node.name != null ? "1" : "0";
345
+ const name = Array.isArray(node.name) ? node.name.toSorted().join("\0") : node.name ?? "";
346
+ return `${isArray}:${typeOnly}:${node.path}:${hasName}:${name}`;
347
+ }
348
+ /**
349
+ * Deduplicates and merges `SourceNode` objects by `name + isExportable + isTypeOnly`.
350
+ *
351
+ * Unnamed sources are deduplicated by object reference. Returns a deduplicated array in original order.
352
+ */
353
+ function combineSources(sources) {
354
+ const seen = /* @__PURE__ */ new Map();
355
+ for (const source of sources) {
356
+ const key = sourceKey(source);
357
+ if (!seen.has(key)) seen.set(key, source);
358
+ }
359
+ return [...seen.values()];
360
+ }
361
+ /**
362
+ * Merges `incoming` names into `existing`, preserving order and dropping duplicates.
363
+ *
364
+ * Shared by `combineExports` and `combineImports` for the same-path name-merge case.
365
+ */
366
+ function mergeNameArrays(existing, incoming) {
367
+ const merged = new Set(existing);
368
+ for (const name of incoming) merged.add(name);
369
+ return [...merged];
370
+ }
371
+ /**
372
+ * Deduplicates and merges `ExportNode` objects by path and type.
373
+ *
374
+ * Named exports with the same path and `isTypeOnly` flag have their names merged into a single export.
375
+ * Non-array exports are deduplicated by exact identity. Returns a sorted, deduplicated array.
376
+ */
377
+ function combineExports(exports) {
378
+ const result = [];
379
+ const namedByPath = /* @__PURE__ */ new Map();
380
+ const seen = /* @__PURE__ */ new Set();
381
+ const keyed = exports.map((node) => ({
382
+ node,
383
+ key: sortKey(node)
384
+ }));
385
+ keyed.sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0);
386
+ for (const { node: curr } of keyed) {
387
+ const { name, path, isTypeOnly, asAlias } = curr;
388
+ if (Array.isArray(name)) {
389
+ if (!name.length) continue;
390
+ const key = pathTypeKey(path, isTypeOnly);
391
+ const existing = namedByPath.get(key);
392
+ if (existing && Array.isArray(existing.name)) existing.name = mergeNameArrays(existing.name, name);
393
+ else {
394
+ const newItem = {
395
+ ...curr,
396
+ name: [...new Set(name)]
397
+ };
398
+ result.push(newItem);
399
+ namedByPath.set(key, newItem);
400
+ }
401
+ } else {
402
+ const key = exportKey(path, name, isTypeOnly, asAlias);
403
+ if (!seen.has(key)) {
404
+ result.push(curr);
405
+ seen.add(key);
406
+ }
407
+ }
408
+ }
409
+ return result;
410
+ }
411
+ /**
412
+ * Deduplicates and merges `ImportNode` objects, filtering out unused imports.
413
+ *
414
+ * Retains imports that are referenced in `source` or re-exported. Imports with the same path and
415
+ * `isTypeOnly` flag have their names merged. Returns a sorted, deduplicated, filtered array.
416
+ */
417
+ function combineImports(imports, exports, source) {
418
+ const exportedNames = new Set(exports.flatMap((e) => Array.isArray(e.name) ? e.name : e.name ? [e.name] : []));
419
+ const isUsed = (importName) => !source || source.includes(importName) || exportedNames.has(importName);
420
+ const importNameMemo = /* @__PURE__ */ new Map();
421
+ const canonicalizeName = (n) => {
422
+ if (typeof n === "string") return n;
423
+ const key = `${n.propertyName}:${n.name ?? ""}`;
424
+ if (!importNameMemo.has(key)) importNameMemo.set(key, n);
425
+ return importNameMemo.get(key);
426
+ };
427
+ const pathsWithUsedNamedImport = /* @__PURE__ */ new Set();
428
+ for (const node of imports) {
429
+ if (!Array.isArray(node.name)) continue;
430
+ if (node.name.some((item) => typeof item === "string" ? isUsed(item) : isUsed(item.name ?? item.propertyName))) pathsWithUsedNamedImport.add(node.path);
431
+ }
432
+ const result = [];
433
+ const namedByPath = /* @__PURE__ */ new Map();
434
+ const seen = /* @__PURE__ */ new Set();
435
+ const keyed = imports.map((node) => ({
436
+ node,
437
+ key: sortKey(node)
438
+ }));
439
+ keyed.sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0);
440
+ for (const { node: curr } of keyed) {
441
+ if (curr.path === curr.root) continue;
442
+ const { path, isTypeOnly } = curr;
443
+ let { name } = curr;
444
+ if (Array.isArray(name)) {
445
+ name = [...new Set(name.map(canonicalizeName))].filter((item) => typeof item === "string" ? isUsed(item) : isUsed(item.name ?? item.propertyName));
446
+ if (!name.length) continue;
447
+ const key = pathTypeKey(path, isTypeOnly);
448
+ const existing = namedByPath.get(key);
449
+ if (existing && Array.isArray(existing.name)) existing.name = mergeNameArrays(existing.name, name);
450
+ else {
451
+ const newItem = {
452
+ ...curr,
453
+ name
454
+ };
455
+ result.push(newItem);
456
+ namedByPath.set(key, newItem);
457
+ }
458
+ } else {
459
+ if (name && !isUsed(name) && !pathsWithUsedNamedImport.has(path)) continue;
460
+ const key = importKey(path, name, isTypeOnly);
461
+ if (!seen.has(key)) {
462
+ result.push(curr);
463
+ seen.add(key);
464
+ }
465
+ }
466
+ }
467
+ return result;
468
+ }
469
+ //#endregion
349
470
  //#region src/nodes/file.ts
350
471
  /**
351
472
  * Definition for the {@link ImportNode}.
352
473
  */
353
474
  const importDef = defineNode({ kind: "Import" });
354
475
  /**
476
+ * Definition for the {@link ExportNode}.
477
+ */
478
+ const exportDef = defineNode({ kind: "Export" });
479
+ /**
480
+ * Definition for the {@link SourceNode}.
481
+ */
482
+ const sourceDef = defineNode({ kind: "Source" });
483
+ /**
484
+ * Definition for the {@link FileNode}. The fully resolved builder lives in
485
+ * `createFile`, so this definition only supplies the guard.
486
+ */
487
+ const fileDef = defineNode({ kind: "File" });
488
+ /**
355
489
  * Creates an `ImportNode` representing a language-agnostic import/dependency declaration.
356
490
  *
357
491
  * @example Named import
@@ -362,10 +496,6 @@ const importDef = defineNode({ kind: "Import" });
362
496
  */
363
497
  const createImport = importDef.create;
364
498
  /**
365
- * Definition for the {@link ExportNode}.
366
- */
367
- const exportDef = defineNode({ kind: "Export" });
368
- /**
369
499
  * Creates an `ExportNode` representing a language-agnostic export/public API declaration.
370
500
  *
371
501
  * @example Named export
@@ -376,10 +506,6 @@ const exportDef = defineNode({ kind: "Export" });
376
506
  */
377
507
  const createExport = exportDef.create;
378
508
  /**
379
- * Definition for the {@link SourceNode}.
380
- */
381
- const sourceDef = defineNode({ kind: "Source" });
382
- /**
383
509
  * Creates a `SourceNode` representing a fragment of source code within a file.
384
510
  *
385
511
  * @example
@@ -389,10 +515,64 @@ const sourceDef = defineNode({ kind: "Source" });
389
515
  */
390
516
  const createSource = sourceDef.create;
391
517
  /**
392
- * Definition for the {@link FileNode}. The fully resolved builder lives in
393
- * `createFile`, so this definition only supplies the guard.
518
+ * Creates a fully resolved `FileNode` from a file input descriptor.
519
+ *
520
+ * Computes:
521
+ * - `id` SHA256 hash of the file path
522
+ * - `name` `baseName` without extension
523
+ * - `extname` extension extracted from `baseName`
524
+ *
525
+ * Deduplicates:
526
+ * - `sources` via `combineSources`
527
+ * - `exports` via `combineExports`
528
+ * - `imports` via `combineImports` (also filters unused imports)
529
+ *
530
+ * @throws {Error} when `baseName` has no extension.
531
+ *
532
+ * @example
533
+ * ```ts
534
+ * const file = createFile({
535
+ * baseName: 'petStore.ts',
536
+ * path: 'src/models/petStore.ts',
537
+ * sources: [createSource({ name: 'Pet', nodes: [createText('export type Pet = { id: number }')] })],
538
+ * imports: [createImport({ name: ['z'], path: 'zod' })],
539
+ * exports: [createExport({ name: ['Pet'], path: './petStore' })],
540
+ * })
541
+ * // file.id = SHA256 hash of 'src/models/petStore.ts'
542
+ * // file.name = 'petStore'
543
+ * // file.extname = '.ts'
544
+ * ```
394
545
  */
395
- const fileDef = defineNode({ kind: "File" });
546
+ function createFile(input) {
547
+ const extname = path.extname(input.baseName) || (input.baseName.startsWith(".") ? input.baseName : "");
548
+ if (!extname) throw new Error(`No extname found for ${input.baseName}`);
549
+ const source = (input.sources ?? []).flatMap((item) => item.nodes ?? []).map((node) => extractStringsFromNodes([node])).filter(Boolean).join("\n\n");
550
+ const resolvedExports = input.exports?.length ? combineExports(input.exports) : [];
551
+ const combinedImports = input.imports?.length ? combineImports(input.imports, resolvedExports, source || void 0) : [];
552
+ const localNames = new Set((input.sources ?? []).map((item) => item.name).filter((name) => Boolean(name)));
553
+ const nameOf = (item) => typeof item === "string" ? item : item.name ?? item.propertyName;
554
+ const resolvedImports = combinedImports.filter((imp) => imp.path !== input.path).flatMap((imp) => {
555
+ if (!Array.isArray(imp.name)) return typeof imp.name === "string" && localNames.has(imp.name) ? [] : [imp];
556
+ const kept = imp.name.filter((item) => !localNames.has(nameOf(item)));
557
+ if (!kept.length) return [];
558
+ return [kept.length === imp.name.length ? imp : {
559
+ ...imp,
560
+ name: kept
561
+ }];
562
+ });
563
+ const resolvedSources = input.sources?.length ? combineSources(input.sources) : [];
564
+ return {
565
+ kind: "File",
566
+ ...input,
567
+ id: hash("sha256", input.path, "hex"),
568
+ name: trimExtName(input.baseName),
569
+ extname,
570
+ imports: resolvedImports,
571
+ exports: resolvedExports,
572
+ sources: resolvedSources,
573
+ meta: input.meta ?? {}
574
+ };
575
+ }
396
576
  //#endregion
397
577
  //#region src/nodes/function.ts
398
578
  /**
@@ -400,44 +580,14 @@ const fileDef = defineNode({ kind: "File" });
400
580
  */
401
581
  const typeLiteralDef = defineNode({ kind: "TypeLiteral" });
402
582
  /**
403
- * Creates a {@link TypeLiteralNode} representing an inline anonymous object type.
404
- *
405
- * @example
406
- * ```ts
407
- * createTypeLiteral({ members: [{ name: 'petId', type: 'string', optional: false }] })
408
- * // { petId: string }
409
- * ```
410
- */
411
- const createTypeLiteral = typeLiteralDef.create;
412
- /**
413
583
  * Definition for the {@link IndexedAccessTypeNode}.
414
584
  */
415
585
  const indexedAccessTypeDef = defineNode({ kind: "IndexedAccessType" });
416
586
  /**
417
- * Creates an {@link IndexedAccessTypeNode} representing a single field accessed from a named type.
418
- *
419
- * @example
420
- * ```ts
421
- * createIndexedAccessType({ objectType: 'DeletePetPathParams', indexType: 'petId' })
422
- * // DeletePetPathParams['petId']
423
- * ```
424
- */
425
- const createIndexedAccessType = indexedAccessTypeDef.create;
426
- /**
427
587
  * Definition for the {@link ObjectBindingPatternNode}.
428
588
  */
429
589
  const objectBindingPatternDef = defineNode({ kind: "ObjectBindingPattern" });
430
590
  /**
431
- * Creates an {@link ObjectBindingPatternNode} for a destructured parameter binding.
432
- *
433
- * @example
434
- * ```ts
435
- * createObjectBindingPattern({ elements: [{ name: 'id' }, { name: 'name' }] })
436
- * // { id, name }
437
- * ```
438
- */
439
- const createObjectBindingPattern = objectBindingPatternDef.create;
440
- /**
441
591
  * Definition for the {@link FunctionParameterNode}. `optional` defaults to `false`.
442
592
  * Passing `properties` builds a destructured group: an {@link ObjectBindingPatternNode} name
443
593
  * paired with a {@link TypeLiteralNode} type.
@@ -462,6 +612,43 @@ const functionParameterDef = defineNode({
462
612
  }
463
613
  });
464
614
  /**
615
+ * Definition for the {@link FunctionParametersNode}.
616
+ */
617
+ const functionParametersDef = defineNode({
618
+ kind: "FunctionParameters",
619
+ defaults: { params: [] }
620
+ });
621
+ /**
622
+ * Creates a {@link TypeLiteralNode} representing an inline anonymous object type.
623
+ *
624
+ * @example
625
+ * ```ts
626
+ * createTypeLiteral({ members: [{ name: 'petId', type: 'string', optional: false }] })
627
+ * // { petId: string }
628
+ * ```
629
+ */
630
+ const createTypeLiteral = typeLiteralDef.create;
631
+ /**
632
+ * Creates an {@link IndexedAccessTypeNode} representing a single field accessed from a named type.
633
+ *
634
+ * @example
635
+ * ```ts
636
+ * createIndexedAccessType({ target: 'DeletePetPathParams', key: 'petId' })
637
+ * // DeletePetPathParams['petId']
638
+ * ```
639
+ */
640
+ const createIndexedAccessType = indexedAccessTypeDef.create;
641
+ /**
642
+ * Creates an {@link ObjectBindingPatternNode} for a destructured parameter binding.
643
+ *
644
+ * @example
645
+ * ```ts
646
+ * createObjectBindingPattern({ elements: [{ name: 'id' }, { name: 'name' }] })
647
+ * // { id, name }
648
+ * ```
649
+ */
650
+ const createObjectBindingPattern = objectBindingPatternDef.create;
651
+ /**
465
652
  * Creates a `FunctionParameterNode`. `optional` defaults to `false`.
466
653
  *
467
654
  * @example Optional param
@@ -478,13 +665,6 @@ const functionParameterDef = defineNode({
478
665
  */
479
666
  const createFunctionParameter = functionParameterDef.create;
480
667
  /**
481
- * Definition for the {@link FunctionParametersNode}.
482
- */
483
- const functionParametersDef = defineNode({
484
- kind: "FunctionParameters",
485
- defaults: { params: [] }
486
- });
487
- /**
488
668
  * Creates a `FunctionParametersNode` from an ordered list of parameters.
489
669
  *
490
670
  * @example
@@ -516,8 +696,8 @@ const inputDef = defineNode({
516
696
  });
517
697
  /**
518
698
  * Creates an `InputNode`. Pass `stream: true` for the streaming variant whose `schemas` and
519
- * `operations` are `AsyncIterable` sources and whose `meta` is optional. Otherwise it builds the
520
- * eager variant with array `schemas`/`operations` and the defaulted `meta`.
699
+ * `operations` are `AsyncIterable` sources. Otherwise it builds the eager variant with array
700
+ * `schemas`/`operations`. Both variants get the defaulted `meta`.
521
701
  *
522
702
  * @example Eager
523
703
  * ```ts
@@ -534,6 +714,10 @@ function createInput(options = {}) {
534
714
  const { stream, ...overrides } = options;
535
715
  if (stream) return {
536
716
  kind: "Input",
717
+ meta: {
718
+ circularNames: [],
719
+ enumNames: []
720
+ },
537
721
  ...overrides
538
722
  };
539
723
  return inputDef.create(overrides);
@@ -541,18 +725,15 @@ function createInput(options = {}) {
541
725
  //#endregion
542
726
  //#region src/nodes/requestBody.ts
543
727
  /**
544
- * Definition for the {@link RequestBodyNode}, normalizing each content entry into a `ContentNode`.
728
+ * Definition for the {@link RequestBodyNode}. Content entries are built upfront with
729
+ * {@link createContent}, mirroring how `parameters` and `responses` take prebuilt nodes.
545
730
  */
546
731
  const requestBodyDef = defineNode({
547
732
  kind: "RequestBody",
548
- build: (props) => ({
549
- ...props,
550
- content: props.content?.map(createContent)
551
- }),
552
733
  children: ["content"]
553
734
  });
554
735
  /**
555
- * Creates a `RequestBodyNode`, normalizing each content entry into a `ContentNode`.
736
+ * Creates a `RequestBodyNode`.
556
737
  */
557
738
  const createRequestBody = requestBodyDef.create;
558
739
  //#endregion
@@ -611,22 +792,22 @@ function createOutput(overrides = {}) {
611
792
  //#endregion
612
793
  //#region src/nodes/parameter.ts
613
794
  /**
614
- * Definition for the {@link ParameterNode}. `required` defaults to `false` and the
615
- * schema's `optional`/`nullish` flags are kept in sync with it.
795
+ * Definition for the {@link ParameterNode}. `required` defaults to `false`. When a `dialect` is
796
+ * passed to `create`, the schema's `optional`/`nullish` flags are derived through its
797
+ * `optionality`; without one, the schema is left as-is.
616
798
  */
617
799
  const parameterDef = defineNode({
618
800
  kind: "Parameter",
619
- build: (props) => {
801
+ build: (props, dialect) => {
620
802
  const required = props.required ?? false;
621
803
  return {
622
804
  ...props,
623
805
  required,
624
- schema: syncOptionality(props.schema, required)
806
+ schema: dialect ? dialect.schema.optionality(props.schema, required) : props.schema
625
807
  };
626
808
  },
627
809
  children: ["schema"],
628
- visitorKey: "parameter",
629
- rebuild: true
810
+ visitorKey: "parameter"
630
811
  });
631
812
  /**
632
813
  * Creates a `ParameterNode`.
@@ -643,6 +824,40 @@ const parameterDef = defineNode({
643
824
  */
644
825
  const createParameter = parameterDef.create;
645
826
  //#endregion
827
+ //#region src/nodes/property.ts
828
+ /**
829
+ * Definition for the {@link PropertyNode}. `required` defaults to `false`. When a `dialect` is
830
+ * passed to `create`, the schema's `optional`/`nullish` flags are derived through its
831
+ * `optionality`; without one, the schema is left as-is.
832
+ */
833
+ const propertyDef = defineNode({
834
+ kind: "Property",
835
+ build: (props, dialect) => {
836
+ const required = props.required ?? false;
837
+ return {
838
+ ...props,
839
+ required,
840
+ schema: dialect ? dialect.schema.optionality(props.schema, required) : props.schema
841
+ };
842
+ },
843
+ children: ["schema"],
844
+ visitorKey: "property"
845
+ });
846
+ /**
847
+ * Creates a `PropertyNode`.
848
+ *
849
+ * @example
850
+ * ```ts
851
+ * const property = createProperty({
852
+ * name: 'status',
853
+ * required: true,
854
+ * schema: createSchema({ type: 'string', nullable: true }),
855
+ * })
856
+ * // required=true, no optional/nullish
857
+ * ```
858
+ */
859
+ const createProperty = propertyDef.create;
860
+ //#endregion
646
861
  //#region src/nodes/response.ts
647
862
  /**
648
863
  * Definition for the {@link ResponseNode}. A single legacy `schema` (with optional
@@ -652,14 +867,14 @@ const responseDef = defineNode({
652
867
  kind: "Response",
653
868
  build: (props) => {
654
869
  const { schema, mediaType, keysToOmit, content, ...rest } = props;
655
- const entries = content ?? (schema ? [{
870
+ const entries = content ?? (schema ? [createContent({
656
871
  contentType: mediaType ?? "application/json",
657
872
  schema,
658
873
  keysToOmit: keysToOmit ?? null
659
- }] : void 0);
874
+ })] : void 0);
660
875
  return {
661
876
  ...rest,
662
- content: entries?.map(createContent)
877
+ content: entries
663
878
  };
664
879
  },
665
880
  children: ["content"],
@@ -672,12 +887,12 @@ const responseDef = defineNode({
672
887
  * ```ts
673
888
  * const response = createResponse({
674
889
  * statusCode: '200',
675
- * content: [{ contentType: 'application/json', schema: createSchema({ type: 'object', properties: [] }) }],
890
+ * content: [createContent({ contentType: 'application/json', schema: createSchema({ type: 'object', properties: [] }) })],
676
891
  * })
677
892
  * ```
678
893
  */
679
894
  const createResponse = responseDef.create;
680
895
  //#endregion
681
- export { defineNode as $, contentDef as A, createText as B, createExport as C, fileDef as D, exportDef as E, createArrowFunction as F, typeDef as G, functionDef as H, createBreak as I, extractStringsFromNodes as J, createProperty as K, createConst as L, arrowFunctionDef as M, breakDef as N, importDef as O, constDef as P, schemaDef as Q, createFunction as R, typeLiteralDef as S, createSource as T, jsxDef as U, createType as V, textDef as W, pascalCase as X, camelCase as Y, createSchema as Z, createTypeLiteral as _, createOutput as a, indexedAccessTypeDef as b, operationDef as c, createInput as d, syncOptionality as et, inputDef as f, createObjectBindingPattern as g, createIndexedAccessType as h, parameterDef as i, createContent as j, sourceDef as k, createRequestBody as l, createFunctionParameters as m, responseDef as n, outputDef as o, createFunctionParameter as p, propertyDef as q, createParameter as r, createOperation as s, createResponse as t, requestBodyDef as u, functionParameterDef as v, createImport as w, objectBindingPatternDef as x, functionParametersDef as y, createJsx as z };
896
+ export { schemaDef as $, fileDef as A, createConst as B, objectBindingPatternDef as C, createImport as D, createFile as E, arrowFunctionDef as F, functionDef as G, createJsx as H, breakDef as I, typeDef as J, jsxDef as K, constDef as L, sourceDef as M, contentDef as N, createSource as O, createContent as P, createSchema as Q, createArrowFunction as R, indexedAccessTypeDef as S, createExport as T, createText as U, createFunction as V, createType as W, camelCase as X, extractStringsFromNodes as Y, pascalCase as Z, createIndexedAccessType as _, createParameter as a, functionParameterDef as b, outputDef as c, createRequestBody as d, defineNode as et, requestBodyDef as f, createFunctionParameters as g, createFunctionParameter as h, propertyDef as i, importDef as j, exportDef as k, createOperation as l, inputDef as m, responseDef as n, parameterDef as o, createInput as p, textDef as q, createProperty as r, createOutput as s, createResponse as t, visitorKeys as tt, operationDef as u, createObjectBindingPattern as v, typeLiteralDef as w, functionParametersDef as x, createTypeLiteral as y, createBreak as z };
682
897
 
683
- //# sourceMappingURL=response-DKxTr522.js.map
898
+ //# sourceMappingURL=response-Rd1uisM1.js.map