@kubb/ast 5.0.0-beta.11 → 5.0.0-beta.110

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/dist/index.cjs CHANGED
@@ -6,6 +6,15 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __getProtoOf = Object.getPrototypeOf;
8
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __exportAll = (all, no_symbols) => {
10
+ let target = {};
11
+ for (var name in all) __defProp(target, name, {
12
+ get: all[name],
13
+ enumerable: true
14
+ });
15
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
16
+ return target;
17
+ };
9
18
  var __copyProps = (to, from, except, desc) => {
10
19
  if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
20
  key = keys[i];
@@ -16,7 +25,7 @@ var __copyProps = (to, from, except, desc) => {
16
25
  }
17
26
  return to;
18
27
  };
19
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
28
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
20
29
  value: mod,
21
30
  enumerable: true
22
31
  }) : target, mod));
@@ -29,31 +38,10 @@ const visitorDepths = {
29
38
  shallow: "shallow",
30
39
  deep: "deep"
31
40
  };
32
- const nodeKinds = {
33
- input: "Input",
34
- output: "Output",
35
- operation: "Operation",
36
- schema: "Schema",
37
- property: "Property",
38
- parameter: "Parameter",
39
- response: "Response",
40
- functionParameter: "FunctionParameter",
41
- parameterGroup: "ParameterGroup",
42
- functionParameters: "FunctionParameters",
43
- type: "Type",
44
- file: "File",
45
- import: "Import",
46
- export: "Export",
47
- source: "Source",
48
- text: "Text",
49
- break: "Break"
50
- };
51
41
  /**
52
42
  * Schema type discriminators used by all AST schema nodes.
53
43
  *
54
- * These values serve as stable discriminators across the AST (e.g., `schema.type === schemaTypes.object`).
55
- * Grouped by category: primitives (`string`, `number`, `boolean`), structural types (`object`, `array`, `union`),
56
- * and format-specific types (`date`, `uuid`, `email`). Use `isScalarPrimitive()` to check for scalar types.
44
+ * Each value is a stable discriminator across the AST (for example `schema.type === schemaTypes.object`).
57
45
  */
58
46
  const schemaTypes = {
59
47
  /**
@@ -73,7 +61,7 @@ const schemaTypes = {
73
61
  */
74
62
  bigint: "bigint",
75
63
  /**
76
- * Boolean value
64
+ * Boolean value.
77
65
  */
78
66
  boolean: "boolean",
79
67
  /**
@@ -161,928 +149,324 @@ const schemaTypes = {
161
149
  */
162
150
  never: "never"
163
151
  };
152
+ //#endregion
153
+ //#region src/guards.ts
164
154
  /**
165
- * Scalar primitive schema types used for union simplification and type narrowing.
166
- *
167
- * Use `isScalarPrimitive()` to safely check whether a type is a scalar primitive.
168
- */
169
- const SCALAR_PRIMITIVE_TYPES = new Set([
170
- "string",
171
- "number",
172
- "integer",
173
- "bigint",
174
- "boolean"
175
- ]);
176
- /**
177
- * Type guard that returns `true` when `type` is a scalar primitive schema type.
155
+ * Narrows a `SchemaNode` to the variant that matches `type`.
178
156
  *
179
- * Use this to check if a schema type can be directly assigned without wrapping (e.g., `string | number | boolean`).
157
+ * @example
158
+ * ```ts
159
+ * const schema = createSchema({ type: 'string' })
160
+ * const stringNode = narrowSchema(schema, 'string') // StringSchemaNode | null
161
+ * ```
180
162
  */
181
- function isScalarPrimitive(type) {
182
- return SCALAR_PRIMITIVE_TYPES.has(type);
163
+ function narrowSchema(node, type) {
164
+ return node?.type === type ? node : null;
183
165
  }
184
166
  /**
185
- * HTTP method identifiers used by operation nodes.
167
+ * Narrows an `OperationNode` to an `HttpOperationNode` so `method` and `path` are present.
186
168
  *
187
- * Includes all standard HTTP methods (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS, TRACE).
169
+ * @example
170
+ * ```ts
171
+ * if (isHttpOperationNode(node)) {
172
+ * console.log(node.method, node.path)
173
+ * }
174
+ * ```
188
175
  */
189
- const httpMethods = {
190
- get: "GET",
191
- post: "POST",
192
- put: "PUT",
193
- patch: "PATCH",
194
- delete: "DELETE",
195
- head: "HEAD",
196
- options: "OPTIONS",
197
- trace: "TRACE"
198
- };
199
- /**
200
- * Common MIME types used in request/response content negotiation.
201
- *
202
- * Covers JSON, XML, form data, PDFs, images, audio, and video formats.
203
- * Use these as keys when serializing request/response bodies.
204
- */
205
- const mediaTypes = {
206
- applicationJson: "application/json",
207
- applicationXml: "application/xml",
208
- applicationFormUrlEncoded: "application/x-www-form-urlencoded",
209
- applicationOctetStream: "application/octet-stream",
210
- applicationPdf: "application/pdf",
211
- applicationZip: "application/zip",
212
- applicationGraphql: "application/graphql",
213
- multipartFormData: "multipart/form-data",
214
- textPlain: "text/plain",
215
- textHtml: "text/html",
216
- textCsv: "text/csv",
217
- textXml: "text/xml",
218
- imagePng: "image/png",
219
- imageJpeg: "image/jpeg",
220
- imageGif: "image/gif",
221
- imageWebp: "image/webp",
222
- imageSvgXml: "image/svg+xml",
223
- audioMpeg: "audio/mpeg",
224
- videoMp4: "video/mp4"
225
- };
176
+ function isHttpOperationNode(node) {
177
+ return node.protocol === "http" || node.method !== void 0 && node.path !== void 0;
178
+ }
226
179
  //#endregion
227
- //#region ../../internals/utils/src/casing.ts
180
+ //#region src/defineNode.ts
228
181
  /**
229
- * Shared implementation for camelCase and PascalCase conversion.
230
- * Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
231
- * and capitalizes each word according to `pascal`.
232
- *
233
- * When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
182
+ * Visitor callback names, one per traversable node kind, in traversal order.
183
+ * Kept in sync with the keys of `Visitor` in `visitor.ts`.
234
184
  */
235
- function toCamelOrPascal(text, pascal) {
236
- return text.trim().replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/(\d)([a-z])/g, "$1 $2").split(/[\s\-_./\\:]+/).filter(Boolean).map((word, i) => {
237
- if (word.length > 1 && word === word.toUpperCase()) return word;
238
- if (i === 0 && !pascal) return word.charAt(0).toLowerCase() + word.slice(1);
239
- return word.charAt(0).toUpperCase() + word.slice(1);
240
- }).join("").replace(/[^a-zA-Z0-9]/g, "");
241
- }
185
+ const visitorKeys = [
186
+ "input",
187
+ "output",
188
+ "operation",
189
+ "schema",
190
+ "property",
191
+ "parameter",
192
+ "response"
193
+ ];
242
194
  /**
243
- * Splits `text` on `.` and applies `transformPart` to each segment.
244
- * The last segment receives `isLast = true`, all earlier segments receive `false`.
245
- * Segments are joined with `/` to form a file path.
246
- *
247
- * Only splits on dots followed by a letter so that version numbers
248
- * embedded in operationIds (e.g. `v2025.0`) are kept intact.
249
- *
250
- * Empty segments are filtered before joining. They arise when the text starts with
251
- * a dot followed immediately by a letter (e.g. `..Schema` splits into `['..', 'Schema']`
252
- * and `'..'` transforms to an empty string). Without this filter the join would produce
253
- * a leading `/`, which `path.resolve` would interpret as an absolute path, allowing
254
- * generated files to escape the configured output directory.
195
+ * Builds a type guard that matches nodes of the given `kind`.
255
196
  */
256
- function applyToFileParts(text, transformPart) {
257
- const parts = text.split(/\.(?=[a-zA-Z])/);
258
- return parts.map((part, i) => transformPart(part, i === parts.length - 1)).filter(Boolean).join("/");
259
- }
260
- /**
261
- * Converts `text` to camelCase.
262
- * When `isFile` is `true`, dot-separated segments are each cased independently and joined with `/`.
263
- *
264
- * @example
265
- * camelCase('hello-world') // 'helloWorld'
266
- * camelCase('pet.petId', { isFile: true }) // 'pet/petId'
267
- */
268
- function camelCase(text, { isFile, prefix = "", suffix = "" } = {}) {
269
- if (isFile) return applyToFileParts(text, (part, isLast) => camelCase(part, isLast ? {
270
- prefix,
271
- suffix
272
- } : {}));
273
- return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
197
+ function isKind(kind) {
198
+ return (node) => node?.kind === kind;
274
199
  }
275
200
  /**
276
- * Converts `text` to PascalCase.
277
- * When `isFile` is `true`, the last dot-separated segment is PascalCased and earlier segments are camelCased.
201
+ * Defines a node once and derives its `create` builder, `is` guard, and traversal
202
+ * metadata. `create` merges `defaults`, the `build` hook (or the raw input), and the
203
+ * `kind`, so node construction lives in one place without scattered `as` casts.
278
204
  *
279
- * @example
280
- * pascalCase('hello-world') // 'HelloWorld'
281
- * pascalCase('pet.petId', { isFile: true }) // 'pet/PetId'
282
- */
283
- function pascalCase(text, { isFile, prefix = "", suffix = "" } = {}) {
284
- if (isFile) return applyToFileParts(text, (part, isLast) => isLast ? pascalCase(part, {
285
- prefix,
286
- suffix
287
- }) : camelCase(part));
288
- return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
289
- }
290
- //#endregion
291
- //#region ../../internals/utils/src/reserved.ts
292
- /**
293
- * JavaScript and Java reserved words.
294
- * @link https://github.com/jonschlinkert/reserved/blob/master/index.js
295
- */
296
- const reservedWords = new Set([
297
- "abstract",
298
- "arguments",
299
- "boolean",
300
- "break",
301
- "byte",
302
- "case",
303
- "catch",
304
- "char",
305
- "class",
306
- "const",
307
- "continue",
308
- "debugger",
309
- "default",
310
- "delete",
311
- "do",
312
- "double",
313
- "else",
314
- "enum",
315
- "eval",
316
- "export",
317
- "extends",
318
- "false",
319
- "final",
320
- "finally",
321
- "float",
322
- "for",
323
- "function",
324
- "goto",
325
- "if",
326
- "implements",
327
- "import",
328
- "in",
329
- "instanceof",
330
- "int",
331
- "interface",
332
- "let",
333
- "long",
334
- "native",
335
- "new",
336
- "null",
337
- "package",
338
- "private",
339
- "protected",
340
- "public",
341
- "return",
342
- "short",
343
- "static",
344
- "super",
345
- "switch",
346
- "synchronized",
347
- "this",
348
- "throw",
349
- "throws",
350
- "transient",
351
- "true",
352
- "try",
353
- "typeof",
354
- "var",
355
- "void",
356
- "volatile",
357
- "while",
358
- "with",
359
- "yield",
360
- "Array",
361
- "Date",
362
- "hasOwnProperty",
363
- "Infinity",
364
- "isFinite",
365
- "isNaN",
366
- "isPrototypeOf",
367
- "length",
368
- "Math",
369
- "name",
370
- "NaN",
371
- "Number",
372
- "Object",
373
- "prototype",
374
- "String",
375
- "toString",
376
- "undefined",
377
- "valueOf"
378
- ]);
379
- /**
380
- * Returns `true` when `name` is a syntactically valid JavaScript variable name.
205
+ * @example Simple node
206
+ * ```ts
207
+ * const importDef = defineNode<ImportNode>({ kind: 'Import' })
208
+ * const createImport = importDef.create
209
+ * ```
381
210
  *
382
- * @example
211
+ * @example Node with a build hook
383
212
  * ```ts
384
- * isValidVarName('status') // true
385
- * isValidVarName('class') // false (reserved word)
386
- * isValidVarName('42foo') // false (starts with digit)
213
+ * const propertyDef = defineNode<PropertyNode, UserPropertyNode>({
214
+ * kind: 'Property',
215
+ * build: (props) => ({ ...props, required: props.required ?? false }),
216
+ * children: ['schema'],
217
+ * visitorKey: 'property',
218
+ * })
387
219
  * ```
388
220
  */
389
- function isValidVarName(name) {
390
- if (!name || reservedWords.has(name)) return false;
391
- return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
221
+ function defineNode(config) {
222
+ const { kind, defaults, build, children, visitorKey } = config;
223
+ function create(input) {
224
+ const base = build ? build(input) : input;
225
+ const node = {
226
+ kind,
227
+ ...defaults,
228
+ ...base
229
+ };
230
+ node.kind = kind;
231
+ return node;
232
+ }
233
+ return {
234
+ kind,
235
+ create,
236
+ is: isKind(kind),
237
+ children,
238
+ visitorKey
239
+ };
392
240
  }
393
241
  //#endregion
394
- //#region ../../internals/utils/src/string.ts
242
+ //#region src/nodes/code.ts
395
243
  /**
396
- * Strips the file extension from a path or file name.
397
- * Only removes the last `.ext` segment when the dot is not part of a directory name.
398
- *
399
- * @example
400
- * trimExtName('petStore.ts') // 'petStore'
401
- * trimExtName('/src/models/pet.ts') // '/src/models/pet'
402
- * trimExtName('/project.v2/gen/pet.ts') // '/project.v2/gen/pet'
403
- * trimExtName('noExtension') // 'noExtension'
244
+ * Definition for the {@link ConstNode}.
404
245
  */
405
- function trimExtName(text) {
406
- const dotIndex = text.lastIndexOf(".");
407
- if (dotIndex > 0 && !text.includes("/", dotIndex)) return text.slice(0, dotIndex);
408
- return text;
409
- }
410
- //#endregion
411
- //#region src/guards.ts
246
+ const constDef = defineNode({ kind: "Const" });
412
247
  /**
413
- * Narrows a `SchemaNode` to the variant that matches `type`.
414
- *
415
- * @example
416
- * ```ts
417
- * const schema = createSchema({ type: 'string' })
418
- * const stringNode = narrowSchema(schema, 'string') // StringSchemaNode | undefined
419
- * ```
248
+ * Definition for the {@link TypeNode}.
420
249
  */
421
- function narrowSchema(node, type) {
422
- return node?.type === type ? node : void 0;
423
- }
424
- function isKind(kind) {
425
- return (node) => node.kind === kind;
426
- }
250
+ const typeDef = defineNode({ kind: "Type" });
427
251
  /**
428
- * Returns `true` when the input is an `InputNode`.
429
- *
430
- * @example
431
- * ```ts
432
- * if (isInputNode(node)) {
433
- * console.log(node.schemas.length)
434
- * }
435
- * ```
252
+ * Definition for the {@link FunctionNode}.
436
253
  */
437
- const isInputNode = isKind("Input");
254
+ const functionDef = defineNode({ kind: "Function" });
438
255
  /**
439
- * Returns `true` when the input is an `OutputNode`.
440
- *
441
- * @example
442
- * ```ts
443
- * if (isOutputNode(node)) {
444
- * console.log(node.files.length)
445
- * }
446
- * ```
256
+ * Definition for the {@link ArrowFunctionNode}.
447
257
  */
448
- const isOutputNode = isKind("Output");
258
+ const arrowFunctionDef = defineNode({ kind: "ArrowFunction" });
449
259
  /**
450
- * Returns `true` when the input is an `OperationNode`.
451
- *
452
- * @example
453
- * ```ts
454
- * if (isOperationNode(node)) {
455
- * console.log(node.operationId)
456
- * }
457
- * ```
260
+ * Definition for the {@link TextNode}.
261
+ */
262
+ const textDef = defineNode({
263
+ kind: "Text",
264
+ build: (value) => ({ value })
265
+ });
266
+ /**
267
+ * Definition for the {@link BreakNode}.
458
268
  */
459
- const isOperationNode = isKind("Operation");
269
+ const breakDef = defineNode({
270
+ kind: "Break",
271
+ build: () => ({})
272
+ });
460
273
  /**
461
- * Returns `true` when the input is a `SchemaNode`.
274
+ * Definition for the {@link JsxNode}.
275
+ */
276
+ const jsxDef = defineNode({
277
+ kind: "Jsx",
278
+ build: (value) => ({ value })
279
+ });
280
+ /**
281
+ * Creates a `ConstNode` representing a TypeScript `const` declaration.
462
282
  *
463
- * @example
283
+ * @example Exported constant with type and `as const`
464
284
  * ```ts
465
- * if (isSchemaNode(node)) {
466
- * console.log(node.type)
467
- * }
285
+ * createConst({ name: 'pets', export: true, type: 'Pet[]', asConst: true })
286
+ * // export const pets: Pet[] = ... as const
468
287
  * ```
469
288
  */
470
- const isSchemaNode = isKind("Schema");
471
- //#endregion
472
- //#region src/refs.ts
289
+ const createConst = constDef.create;
473
290
  /**
474
- * Returns the last path segment of a reference string.
475
- *
476
- * Example: `#/components/schemas/Pet` becomes `Pet`.
291
+ * Creates a `TypeNode` representing a TypeScript `type` alias declaration.
477
292
  *
478
293
  * @example
479
294
  * ```ts
480
- * extractRefName('#/components/schemas/Pet') // 'Pet'
295
+ * createType({ name: 'Pet', export: true })
296
+ * // export type Pet = ...
481
297
  * ```
482
298
  */
483
- function extractRefName(ref) {
484
- return ref.split("/").at(-1) ?? ref;
485
- }
486
- //#endregion
487
- //#region src/visitor.ts
299
+ const createType = typeDef.create;
488
300
  /**
489
- * Creates a small async concurrency limiter.
490
- *
491
- * At most `concurrency` tasks are in flight at once. Extra tasks are queued.
301
+ * Creates a `FunctionNode` representing a TypeScript `function` declaration.
492
302
  *
493
303
  * @example
494
304
  * ```ts
495
- * const limit = createLimit(2)
496
- * for (const task of [taskA, taskB, taskC]) {
497
- * await limit(() => task())
498
- * }
499
- * // only 2 tasks run at the same time
305
+ * createFunction({ name: 'fetchPet', export: true, async: true, returnType: 'Pet' })
306
+ * // export async function fetchPet(): Promise<Pet> { ... }
500
307
  * ```
501
308
  */
502
- function createLimit(concurrency) {
503
- let active = 0;
504
- const queue = [];
505
- function next() {
506
- if (active < concurrency && queue.length > 0) {
507
- active++;
508
- queue.shift()();
509
- }
510
- }
511
- return function limit(fn) {
512
- return new Promise((resolve, reject) => {
513
- queue.push(() => {
514
- Promise.resolve(fn()).then(resolve, reject).finally(() => {
515
- active--;
516
- next();
517
- });
518
- });
519
- next();
520
- });
521
- };
522
- }
309
+ const createFunction = functionDef.create;
523
310
  /**
524
- * Returns the immediate traversable children of `node`.
525
- *
526
- * For `Schema` nodes, children (`properties`, `items`, `members`, and non-boolean
527
- * `additionalProperties`) are only included
528
- * when `recurse` is `true`; shallow mode skips them.
311
+ * Creates an `ArrowFunctionNode` representing a TypeScript arrow function.
529
312
  *
530
313
  * @example
531
314
  * ```ts
532
- * const children = getChildren(operationNode, true)
533
- * // returns parameters, requestBody schema (if present), and responses
315
+ * createArrowFunction({ name: 'double', export: true, params: 'n: number', singleLine: true })
316
+ * // export const double = (n: number) => ...
534
317
  * ```
535
318
  */
536
- function getChildren(node, recurse) {
537
- switch (node.kind) {
538
- case "Input": return [...node.schemas, ...node.operations];
539
- case "Output": return [];
540
- case "Operation": return [
541
- ...node.parameters,
542
- ...node.requestBody?.content?.flatMap((c) => c.schema ? [c.schema] : []) ?? [],
543
- ...node.responses
544
- ];
545
- case "Schema": {
546
- const children = [];
547
- if (!recurse) return [];
548
- if ("properties" in node && node.properties.length > 0) children.push(...node.properties);
549
- if ("items" in node && node.items) children.push(...node.items);
550
- if ("members" in node && node.members) children.push(...node.members);
551
- if ("additionalProperties" in node && node.additionalProperties && node.additionalProperties !== true) children.push(node.additionalProperties);
552
- return children;
553
- }
554
- case "Property": return [node.schema];
555
- case "Parameter": return [node.schema];
556
- case "Response": return node.schema ? [node.schema] : [];
557
- case "FunctionParameter":
558
- case "ParameterGroup":
559
- case "FunctionParameters":
560
- case "Type": return [];
561
- default: return [];
562
- }
563
- }
319
+ const createArrowFunction = arrowFunctionDef.create;
564
320
  /**
565
- * Depth-first traversal for side effects. Visitor return values are ignored.
566
- * Sibling nodes at each level are visited concurrently up to `options.concurrency`
567
- * (default: `WALK_CONCURRENCY`).
321
+ * Creates a {@link TextNode} representing a raw string fragment in the source output.
568
322
  *
569
323
  * @example
570
324
  * ```ts
571
- * await walk(root, {
572
- * operation(node) {
573
- * console.log(node.operationId)
574
- * },
575
- * })
325
+ * createText('return fetch(id)')
326
+ * // { kind: 'Text', value: 'return fetch(id)' }
576
327
  * ```
328
+ */
329
+ const createText = textDef.create;
330
+ /**
331
+ * Creates a {@link BreakNode} representing a line break in the source output.
577
332
  *
578
333
  * @example
579
334
  * ```ts
580
- * // Visit only the current node
581
- * await walk(root, { depth: 'shallow', root: () => {} })
335
+ * createBreak()
336
+ * // { kind: 'Break' }
582
337
  * ```
583
338
  */
584
- async function walk(node, options) {
585
- return _walk(node, options, (options.depth ?? visitorDepths.deep) === visitorDepths.deep, createLimit(options.concurrency ?? 30), void 0);
586
- }
587
- async function _walk(node, visitor, recurse, limit, parent) {
588
- switch (node.kind) {
589
- case "Input":
590
- await limit(() => visitor.input?.(node, { parent }));
591
- break;
592
- case "Output":
593
- await limit(() => visitor.output?.(node, { parent }));
594
- break;
595
- case "Operation":
596
- await limit(() => visitor.operation?.(node, { parent }));
597
- break;
598
- case "Schema":
599
- await limit(() => visitor.schema?.(node, { parent }));
600
- break;
601
- case "Property":
602
- await limit(() => visitor.property?.(node, { parent }));
603
- break;
604
- case "Parameter":
605
- await limit(() => visitor.parameter?.(node, { parent }));
606
- break;
607
- case "Response":
608
- await limit(() => visitor.response?.(node, { parent }));
609
- break;
610
- case "FunctionParameter":
611
- case "ParameterGroup":
612
- case "FunctionParameters": break;
613
- }
614
- const children = getChildren(node, recurse);
615
- for (const child of children) await _walk(child, visitor, recurse, limit, node);
616
- }
617
- function transform(node, options) {
618
- const { depth, parent, ...visitor } = options;
619
- const recurse = (depth ?? visitorDepths.deep) === visitorDepths.deep;
620
- switch (node.kind) {
621
- case "Input": {
622
- let input = node;
623
- const replaced = visitor.input?.(input, { parent });
624
- if (replaced) input = replaced;
625
- return {
626
- ...input,
627
- schemas: input.schemas.map((s) => transform(s, {
628
- ...options,
629
- parent: input
630
- })),
631
- operations: input.operations.map((op) => transform(op, {
632
- ...options,
633
- parent: input
634
- }))
635
- };
636
- }
637
- case "Output": {
638
- let output = node;
639
- const replaced = visitor.output?.(output, { parent });
640
- if (replaced) output = replaced;
641
- return output;
642
- }
643
- case "Operation": {
644
- let op = node;
645
- const replaced = visitor.operation?.(op, { parent });
646
- if (replaced) op = replaced;
647
- return {
648
- ...op,
649
- parameters: op.parameters.map((p) => transform(p, {
650
- ...options,
651
- parent: op
652
- })),
653
- requestBody: op.requestBody ? {
654
- ...op.requestBody,
655
- content: op.requestBody.content?.map((c) => ({
656
- ...c,
657
- schema: c.schema ? transform(c.schema, {
658
- ...options,
659
- parent: op
660
- }) : void 0
661
- }))
662
- } : void 0,
663
- responses: op.responses.map((r) => transform(r, {
664
- ...options,
665
- parent: op
666
- }))
667
- };
668
- }
669
- case "Schema": {
670
- let schema = node;
671
- const replaced = visitor.schema?.(schema, { parent });
672
- if (replaced) schema = replaced;
673
- const childOptions = {
674
- ...options,
675
- parent: schema
676
- };
677
- return {
678
- ...schema,
679
- ..."properties" in schema && recurse ? { properties: schema.properties.map((p) => transform(p, childOptions)) } : {},
680
- ..."items" in schema && recurse ? { items: schema.items?.map((i) => transform(i, childOptions)) } : {},
681
- ..."members" in schema && recurse ? { members: schema.members?.map((m) => transform(m, childOptions)) } : {},
682
- ..."additionalProperties" in schema && recurse && schema.additionalProperties && schema.additionalProperties !== true ? { additionalProperties: transform(schema.additionalProperties, childOptions) } : {}
683
- };
684
- }
685
- case "Property": {
686
- let prop = node;
687
- const replaced = visitor.property?.(prop, { parent });
688
- if (replaced) prop = replaced;
689
- return createProperty({
690
- ...prop,
691
- schema: transform(prop.schema, {
692
- ...options,
693
- parent: prop
694
- })
695
- });
696
- }
697
- case "Parameter": {
698
- let param = node;
699
- const replaced = visitor.parameter?.(param, { parent });
700
- if (replaced) param = replaced;
701
- return createParameter({
702
- ...param,
703
- schema: transform(param.schema, {
704
- ...options,
705
- parent: param
706
- })
707
- });
708
- }
709
- case "Response": {
710
- let response = node;
711
- const replaced = visitor.response?.(response, { parent });
712
- if (replaced) response = replaced;
713
- return {
714
- ...response,
715
- schema: transform(response.schema, {
716
- ...options,
717
- parent: response
718
- })
719
- };
720
- }
721
- case "FunctionParameter":
722
- case "ParameterGroup":
723
- case "FunctionParameters":
724
- case "Type": return node;
725
- default: return node;
726
- }
339
+ function createBreak() {
340
+ return breakDef.create();
727
341
  }
728
342
  /**
729
- * Runs a depth-first synchronous collection pass.
730
- *
731
- * Non-`undefined` values returned by visitor callbacks are appended to the result.
732
- *
733
- * @example
734
- * ```ts
735
- * const ids = collect(root, {
736
- * operation(node) {
737
- * return node.operationId
738
- * },
739
- * })
740
- * ```
343
+ * Creates a {@link JsxNode} representing a raw JSX fragment in the source output.
741
344
  *
742
345
  * @example
743
346
  * ```ts
744
- * // Collect from only the current node
745
- * const values = collect(root, { depth: 'shallow', root: () => 'root' })
347
+ * createJsx('<>\n <a href={href}>Open</a>\n</>')
348
+ * // { kind: 'Jsx', value: '<>\n <a href={href}>Open</a>\n</>' }
746
349
  * ```
747
350
  */
748
- function collect(node, options) {
749
- const { depth, parent, ...visitor } = options;
750
- const recurse = (depth ?? visitorDepths.deep) === visitorDepths.deep;
751
- const results = [];
752
- let v;
753
- switch (node.kind) {
754
- case "Input":
755
- v = visitor.input?.(node, { parent });
756
- break;
757
- case "Output":
758
- v = visitor.output?.(node, { parent });
759
- break;
760
- case "Operation":
761
- v = visitor.operation?.(node, { parent });
762
- break;
763
- case "Schema":
764
- v = visitor.schema?.(node, { parent });
765
- break;
766
- case "Property":
767
- v = visitor.property?.(node, { parent });
768
- break;
769
- case "Parameter":
770
- v = visitor.parameter?.(node, { parent });
771
- break;
772
- case "Response":
773
- v = visitor.response?.(node, { parent });
774
- break;
775
- case "FunctionParameter":
776
- case "ParameterGroup":
777
- case "FunctionParameters": break;
778
- }
779
- if (v !== void 0) results.push(v);
780
- for (const child of getChildren(node, recurse)) for (const item of collect(child, {
781
- ...options,
782
- parent: node
783
- })) results.push(item);
784
- return results;
785
- }
351
+ const createJsx = jsxDef.create;
786
352
  //#endregion
787
- //#region src/utils.ts
788
- const plainStringTypes = new Set([
789
- "string",
790
- "uuid",
791
- "email",
792
- "url",
793
- "datetime"
794
- ]);
353
+ //#region src/nodes/content.ts
795
354
  /**
796
- * Merges a ref node with its resolved schema, giving usage-site fields precedence.
797
- *
798
- * Usage-site fields (`description`, `readOnly`, `nullable`, `deprecated`) on the ref node
799
- * override the same fields in the resolved `node.schema`. Non-ref nodes are returned unchanged.
800
- *
801
- * @example
802
- * ```ts
803
- * // Ref with description override
804
- * const ref = createSchema({ type: 'ref', ref: '#/components/schemas/Pet', description: 'A cute pet' })
805
- * const merged = syncSchemaRef(ref) // merges with resolved Pet schema
806
- * ```
355
+ * Definition for the {@link ContentNode}.
807
356
  */
808
- function syncSchemaRef(node) {
809
- const ref = narrowSchema(node, "ref");
810
- if (!ref) return node;
811
- if (!ref.schema) return node;
812
- const { kind: _kind, type: _type, name: _name, ref: _ref, schema: _schema, ...overrides } = ref;
813
- const definedOverrides = Object.fromEntries(Object.entries(overrides).filter(([, v]) => v !== void 0));
814
- return createSchema({
815
- ...ref.schema,
816
- ...definedOverrides
817
- });
818
- }
357
+ const contentDef = defineNode({
358
+ kind: "Content",
359
+ children: ["schema"]
360
+ });
819
361
  /**
820
- * Type guard that returns `true` when a schema emits as a plain `string` type.
821
- *
822
- * Covers `string`, `uuid`, `email`, `url`, and `datetime` types. For `date` and `time`
823
- * types, returns `true` only when `representation` is `'string'` rather than `'date'`.
362
+ * Creates a `ContentNode` for a single request-body or response content type.
824
363
  */
825
- function isStringType(node) {
826
- if (plainStringTypes.has(node.type)) return true;
827
- const temporal = narrowSchema(node, "date") ?? narrowSchema(node, "time");
828
- if (temporal) return temporal.representation !== "date";
829
- return false;
830
- }
364
+ const createContent = contentDef.create;
365
+ //#endregion
366
+ //#region ../../internals/utils/src/fs.ts
831
367
  /**
832
- * Applies casing rules to parameter names and returns a new parameter array.
368
+ * Strips the file extension from a path or file name.
369
+ * Only removes the last `.ext` segment when the dot is not part of a directory name.
833
370
  *
834
- * Use this before passing parameters to schema builders so output property keys match
835
- * the desired casing while preserving `OperationNode.parameters` for other consumers.
836
- * The input array is not mutated. When `casing` is not set, the original array is returned unchanged.
371
+ * @example
372
+ * trimExtName('petStore.ts') // 'petStore'
373
+ * trimExtName('/src/models/pet.ts') // '/src/models/pet'
374
+ * trimExtName('/project.v2/gen/pet.ts') // '/project.v2/gen/pet'
375
+ * trimExtName('noExtension') // 'noExtension'
837
376
  */
838
- function caseParams(params, casing) {
839
- if (!casing) return params;
840
- return params.map((param) => {
841
- const transformed = casing === "camelcase" || !isValidVarName(param.name) ? camelCase(param.name) : param.name;
842
- return {
843
- ...param,
844
- name: transformed
845
- };
846
- });
377
+ function trimExtName(text) {
378
+ const dotIndex = text.lastIndexOf(".");
379
+ if (dotIndex > 0 && !text.includes("/", dotIndex)) return text.slice(0, dotIndex);
380
+ return text;
847
381
  }
382
+ //#endregion
383
+ //#region ../../internals/utils/src/promise.ts
848
384
  /**
849
- * Creates a single-property object schema used as a discriminator literal.
385
+ * Wraps `factory` with a keyed cache backed by the provided store.
850
386
  *
851
- * @example
387
+ * Pass a `WeakMap` for object keys (results are GC-eligible when the key is
388
+ * collected) or a `Map` for primitive keys. For multi-argument functions,
389
+ * nest two `memoize` calls — the outer keyed by the first argument, the
390
+ * inner (created once per outer miss) keyed by the second.
391
+ *
392
+ * Because the cache is owned by the caller, it can be shared, inspected, or
393
+ * cleared independently of the memoized function.
394
+ *
395
+ * @example Single WeakMap key
852
396
  * ```ts
853
- * createDiscriminantNode({ propertyName: 'type', value: 'dog' })
854
- * // -> { type: 'object', properties: [{ name: 'type', required: true, schema: enum('dog') }] }
397
+ * const cache = new WeakMap<SchemaNode, Set<string>>()
398
+ * const getRefs = memoize(cache, (node) => collectRefs(node))
399
+ * ```
400
+ *
401
+ * @example Single Map key (primitive)
402
+ * ```ts
403
+ * const cache = new Map<string, Resolver>()
404
+ * const getResolver = memoize(cache, (name) => buildResolver(name))
405
+ * ```
406
+ *
407
+ * @example Two-level (object + primitive)
408
+ * ```ts
409
+ * const outer = new WeakMap<Params[], Map<string, Params[]>>()
410
+ * const fn = memoize(outer, (params) => memoize(new Map(), (key) => transform(params, key)))
411
+ * fn(params)('camelcase')
855
412
  * ```
856
413
  */
857
- function createDiscriminantNode({ propertyName, value }) {
858
- return createSchema({
859
- type: "object",
860
- primitive: "object",
861
- properties: [createProperty({
862
- name: propertyName,
863
- schema: createSchema({
864
- type: "enum",
865
- primitive: "string",
866
- enumValues: [value]
867
- }),
868
- required: true
869
- })]
870
- });
871
- }
872
- function resolveParamsType({ node, param, resolver }) {
873
- if (!resolver) return createParamsType({
874
- variant: "reference",
875
- name: param.schema.primitive ?? "unknown"
876
- });
877
- const individualName = resolver.resolveParamName(node, param);
878
- const groupLocation = param.in === "path" || param.in === "query" || param.in === "header" ? param.in : void 0;
879
- const groupResolvers = {
880
- path: resolver.resolvePathParamsName,
881
- query: resolver.resolveQueryParamsName,
882
- header: resolver.resolveHeaderParamsName
414
+ function memoize(store, factory) {
415
+ return (key) => {
416
+ if (store.has(key)) return store.get(key);
417
+ const value = factory(key);
418
+ store.set(key, value);
419
+ return value;
883
420
  };
884
- const groupName = groupLocation ? groupResolvers[groupLocation].call(resolver, node, param) : void 0;
885
- if (groupName && groupName !== individualName) return createParamsType({
886
- variant: "member",
887
- base: groupName,
888
- key: param.name
889
- });
890
- return createParamsType({
891
- variant: "reference",
892
- name: individualName
893
- });
894
421
  }
422
+ //#endregion
423
+ //#region src/utils/extractStringsFromNodes.ts
895
424
  /**
896
- * Converts an `OperationNode` into function parameters for code generation.
897
- *
898
- * Centralizes parameter grouping logic for all plugins. Provide a `resolver` for type name resolution
899
- * and `extraParams` for plugin-specific trailing parameters (e.g., `options` objects).
900
- * Supports three grouping modes: `object` (single destructured param), `inline` (separate params),
901
- * and `inlineSpread` (rest parameter). Use `CreateOperationParamsOptions` to fine-tune output.
902
- */
903
- function createOperationParams(node, options) {
904
- const { paramsType, pathParamsType, paramsCasing, resolver, pathParamsDefault, extraParams = [], paramNames, typeWrapper } = options;
905
- const dataName = paramNames?.data ?? "data";
906
- const paramsName = paramNames?.params ?? "params";
907
- const headersName = paramNames?.headers ?? "headers";
908
- const pathName = paramNames?.path ?? "pathParams";
909
- const wrapType = (type) => createParamsType({
910
- variant: "reference",
911
- name: typeWrapper ? typeWrapper(type) : type
912
- });
913
- const wrapTypeNode = (type) => type.kind === "ParamsType" && type.variant === "reference" ? wrapType(type.name) : type;
914
- const casedParams = caseParams(node.parameters, paramsCasing);
915
- const pathParams = casedParams.filter((p) => p.in === "path");
916
- const queryParams = casedParams.filter((p) => p.in === "query");
917
- const headerParams = casedParams.filter((p) => p.in === "header");
918
- const bodyType = node.requestBody?.content?.[0]?.schema ? wrapType(resolver?.resolveDataName(node) ?? "unknown") : void 0;
919
- const bodyRequired = node.requestBody?.required ?? false;
920
- const queryGroupType = resolver ? resolveGroupType({
921
- node,
922
- params: queryParams,
923
- groupMethod: resolver.resolveQueryParamsName,
924
- resolver
925
- }) : void 0;
926
- const headerGroupType = resolver ? resolveGroupType({
927
- node,
928
- params: headerParams,
929
- groupMethod: resolver.resolveHeaderParamsName,
930
- resolver
931
- }) : void 0;
932
- const params = [];
933
- if (paramsType === "object") {
934
- const children = [
935
- ...pathParams.map((p) => {
936
- const type = resolveParamsType({
937
- node,
938
- param: p,
939
- resolver
940
- });
941
- return createFunctionParameter({
942
- name: p.name,
943
- type: wrapTypeNode(type),
944
- optional: !p.required
945
- });
946
- }),
947
- ...bodyType ? [createFunctionParameter({
948
- name: dataName,
949
- type: bodyType,
950
- optional: !bodyRequired
951
- })] : [],
952
- ...buildGroupParam({
953
- name: paramsName,
954
- node,
955
- params: queryParams,
956
- groupType: queryGroupType,
957
- resolver,
958
- wrapType
959
- }),
960
- ...buildGroupParam({
961
- name: headersName,
962
- node,
963
- params: headerParams,
964
- groupType: headerGroupType,
965
- resolver,
966
- wrapType
967
- })
968
- ];
969
- if (children.length) params.push(createParameterGroup({
970
- properties: children,
971
- default: children.every((c) => c.optional) ? "{}" : void 0
972
- }));
973
- } else {
974
- if (pathParams.length) if (pathParamsType === "inlineSpread") {
975
- const spreadType = resolver?.resolvePathParamsName(node, pathParams[0]) ?? void 0;
976
- params.push(createFunctionParameter({
977
- name: pathName,
978
- type: spreadType ? wrapType(spreadType) : void 0,
979
- rest: true
980
- }));
981
- } else {
982
- const pathChildren = pathParams.map((p) => {
983
- const type = resolveParamsType({
984
- node,
985
- param: p,
986
- resolver
987
- });
988
- return createFunctionParameter({
989
- name: p.name,
990
- type: wrapTypeNode(type),
991
- optional: !p.required
992
- });
993
- });
994
- params.push(createParameterGroup({
995
- properties: pathChildren,
996
- inline: pathParamsType === "inline",
997
- default: pathParamsDefault ?? (pathChildren.every((c) => c.optional) ? "{}" : void 0)
998
- }));
425
+ * Extracts all string content from a `CodeNode` tree recursively.
426
+ *
427
+ * Collects text node values, identifier references in string fields (`params`, `generics`, `returnType`, `type`),
428
+ * and nested node content. Used to build the full source string for import filtering.
429
+ */
430
+ function extractStringsFromNodes(nodes) {
431
+ if (!nodes?.length) return "";
432
+ const collected = [];
433
+ for (const node of nodes) {
434
+ if (typeof node === "string") {
435
+ if (node) collected.push(node);
436
+ continue;
999
437
  }
1000
- if (bodyType) params.push(createFunctionParameter({
1001
- name: dataName,
1002
- type: bodyType,
1003
- optional: !bodyRequired
1004
- }));
1005
- params.push(...buildGroupParam({
1006
- name: paramsName,
1007
- node,
1008
- params: queryParams,
1009
- groupType: queryGroupType,
1010
- resolver,
1011
- wrapType
1012
- }));
1013
- params.push(...buildGroupParam({
1014
- name: headersName,
1015
- node,
1016
- params: headerParams,
1017
- groupType: headerGroupType,
1018
- resolver,
1019
- wrapType
1020
- }));
438
+ if (node.kind === "Text") {
439
+ if (node.value) collected.push(node.value);
440
+ continue;
441
+ }
442
+ if (node.kind === "Break") continue;
443
+ if (node.kind === "Jsx") {
444
+ if (node.value) collected.push(node.value);
445
+ continue;
446
+ }
447
+ const parts = [];
448
+ if ("params" in node && node.params) parts.push(node.params);
449
+ if ("generics" in node && node.generics) parts.push(Array.isArray(node.generics) ? node.generics.join(", ") : node.generics);
450
+ if ("returnType" in node && node.returnType) parts.push(node.returnType);
451
+ if ("type" in node && typeof node.type === "string") parts.push(node.type);
452
+ const nested = extractStringsFromNodes(node.nodes);
453
+ if (nested) parts.push(nested);
454
+ if (parts.length) collected.push(parts.join("\n"));
1021
455
  }
1022
- params.push(...extraParams);
1023
- return createFunctionParameters({ params });
1024
- }
1025
- /**
1026
- * Builds a single {@link FunctionParameterNode} for a query or header group.
1027
- * Returns an empty array when there are no params to emit.
1028
- *
1029
- * If a pre-resolved `groupType` is provided it emits `name: GroupType`.
1030
- * Otherwise, it builds an inline struct from the individual params.
1031
- */
1032
- function buildGroupParam({ name, node, params, groupType, resolver, wrapType }) {
1033
- if (groupType) return [createFunctionParameter({
1034
- name,
1035
- type: groupType.type.kind === "ParamsType" && groupType.type.variant === "reference" ? wrapType(groupType.type.name) : groupType.type,
1036
- optional: groupType.optional
1037
- })];
1038
- if (params.length) return [createFunctionParameter({
1039
- name,
1040
- type: toStructType({
1041
- node,
1042
- params,
1043
- resolver
1044
- }),
1045
- optional: params.every((p) => !p.required)
1046
- })];
1047
- return [];
456
+ return collected.join("\n");
1048
457
  }
458
+ //#endregion
459
+ //#region src/utils/combineFileMembers.ts
460
+ const IDENTIFIER_RUN = /[\w$]+/g;
1049
461
  /**
1050
- * Derives a {@link ParamGroupType} from the resolver's group method.
1051
- * Returns `undefined` when the group name equals the individual param name (no real group).
462
+ * How many imports a file needs before indexing the source beats scanning it once per name.
1052
463
  */
1053
- function resolveGroupType({ node, params, groupMethod, resolver }) {
1054
- if (!params.length) return;
1055
- const firstParam = params[0];
1056
- const groupName = groupMethod.call(resolver, node, firstParam);
1057
- if (groupName === resolver.resolveParamName(node, firstParam)) return;
1058
- const allOptional = params.every((p) => !p.required);
1059
- return {
1060
- type: createParamsType({
1061
- variant: "reference",
1062
- name: groupName
1063
- }),
1064
- optional: allOptional
1065
- };
1066
- }
464
+ const INDEX_ABOVE_IMPORTS = 128;
1067
465
  /**
1068
- * Builds a {@link TypeNode} with `variant: 'struct'` for an inline anonymous type grouping named fields.
1069
- *
1070
- * Used when query or header parameters have no dedicated group type name.
1071
- * Each language printer renders this appropriately (TypeScript: `{ petId: string; name?: string }`).
1072
- */
1073
- function toStructType({ node, params, resolver }) {
1074
- return createParamsType({
1075
- variant: "struct",
1076
- properties: params.map((p) => ({
1077
- name: p.name,
1078
- optional: !p.required,
1079
- type: resolveParamsType({
1080
- node,
1081
- param: p,
1082
- resolver
1083
- })
1084
- }))
1085
- });
466
+ * Every unbroken run of identifier characters in the source.
467
+ */
468
+ function collectIdentifiers(source) {
469
+ return new Set(source.match(IDENTIFIER_RUN));
1086
470
  }
1087
471
  function sourceKey(source) {
1088
472
  return `${source.name ?? extractStringsFromNodes(source.nodes)}:${source.isExportable ?? false}:${source.isTypeOnly ?? false}`;
@@ -1098,19 +482,19 @@ function importKey(path, name, isTypeOnly) {
1098
482
  }
1099
483
  /**
1100
484
  * Computes a multi-level sort key for exports and imports:
1101
- * non-array names first (wildcards/namespace aliases); type-only before value; alphabetical path; unnamed before named.
485
+ * non-array names first (wildcards/namespace aliases). Type-only before value. Alphabetical path. Unnamed before named.
1102
486
  */
1103
487
  function sortKey(node) {
1104
488
  const isArray = Array.isArray(node.name) ? "1" : "0";
1105
489
  const typeOnly = node.isTypeOnly ? "0" : "1";
1106
490
  const hasName = node.name != null ? "1" : "0";
1107
- const name = Array.isArray(node.name) ? [...node.name].sort().join("\0") : node.name ?? "";
491
+ const name = Array.isArray(node.name) ? node.name.toSorted().join("\0") : node.name ?? "";
1108
492
  return `${isArray}:${typeOnly}:${node.path}:${hasName}:${name}`;
1109
493
  }
1110
494
  /**
1111
- * Deduplicates and merges `SourceNode` objects by `name + isExportable + isTypeOnly`.
1112
- *
1113
- * Unnamed sources are deduplicated by object reference. Returns a deduplicated array in original order.
495
+ * Deduplicates `SourceNode` objects by `name + isExportable + isTypeOnly`, keeping the first of each
496
+ * key. Unnamed sources fall back to their extracted node strings as the name part of the key. Returns
497
+ * the deduplicated array in original order.
1114
498
  */
1115
499
  function combineSources(sources) {
1116
500
  const seen = /* @__PURE__ */ new Map();
@@ -1121,6 +505,16 @@ function combineSources(sources) {
1121
505
  return [...seen.values()];
1122
506
  }
1123
507
  /**
508
+ * Merges `incoming` names into `existing`, preserving order and dropping duplicates.
509
+ *
510
+ * Shared by `combineExports` and `combineImports` for the same-path name-merge case.
511
+ */
512
+ function mergeNameArrays(existing, incoming) {
513
+ const merged = new Set(existing);
514
+ for (const name of incoming) merged.add(name);
515
+ return [...merged];
516
+ }
517
+ /**
1124
518
  * Deduplicates and merges `ExportNode` objects by path and type.
1125
519
  *
1126
520
  * Named exports with the same path and `isTypeOnly` flag have their names merged into a single export.
@@ -1141,11 +535,8 @@ function combineExports(exports) {
1141
535
  if (!name.length) continue;
1142
536
  const key = pathTypeKey(path, isTypeOnly);
1143
537
  const existing = namedByPath.get(key);
1144
- if (existing && Array.isArray(existing.name)) {
1145
- const merged = new Set(existing.name);
1146
- for (const n of name) merged.add(n);
1147
- existing.name = [...merged];
1148
- } else {
538
+ if (existing && Array.isArray(existing.name)) existing.name = mergeNameArrays(existing.name, name);
539
+ else {
1149
540
  const newItem = {
1150
541
  ...curr,
1151
542
  name: [...new Set(name)]
@@ -1168,12 +559,11 @@ function combineExports(exports) {
1168
559
  *
1169
560
  * Retains imports that are referenced in `source` or re-exported. Imports with the same path and
1170
561
  * `isTypeOnly` flag have their names merged. Returns a sorted, deduplicated, filtered array.
1171
- *
1172
- * @note Use this when combining imports from multiple files to avoid duplicate declarations.
1173
562
  */
1174
563
  function combineImports(imports, exports, source) {
1175
564
  const exportedNames = new Set(exports.flatMap((e) => Array.isArray(e.name) ? e.name : e.name ? [e.name] : []));
1176
- const isUsed = (importName) => !source || source.includes(importName) || exportedNames.has(importName);
565
+ const identifiers = source && imports.length > INDEX_ABOVE_IMPORTS ? collectIdentifiers(source) : null;
566
+ const isUsed = (importName) => !source || identifiers?.has(importName) || source.includes(importName) || exportedNames.has(importName);
1177
567
  const importNameMemo = /* @__PURE__ */ new Map();
1178
568
  const canonicalizeName = (n) => {
1179
569
  if (typeof n === "string") return n;
@@ -1181,6 +571,11 @@ function combineImports(imports, exports, source) {
1181
571
  if (!importNameMemo.has(key)) importNameMemo.set(key, n);
1182
572
  return importNameMemo.get(key);
1183
573
  };
574
+ const pathsWithUsedNamedImport = /* @__PURE__ */ new Set();
575
+ for (const node of imports) {
576
+ if (!Array.isArray(node.name)) continue;
577
+ if (node.name.some((item) => typeof item === "string" ? isUsed(item) : isUsed(item.name ?? item.propertyName))) pathsWithUsedNamedImport.add(node.path);
578
+ }
1184
579
  const result = [];
1185
580
  const namedByPath = /* @__PURE__ */ new Map();
1186
581
  const seen = /* @__PURE__ */ new Set();
@@ -1198,11 +593,8 @@ function combineImports(imports, exports, source) {
1198
593
  if (!name.length) continue;
1199
594
  const key = pathTypeKey(path, isTypeOnly);
1200
595
  const existing = namedByPath.get(key);
1201
- if (existing && Array.isArray(existing.name)) {
1202
- const merged = new Set(existing.name);
1203
- for (const n of name) merged.add(n);
1204
- existing.name = [...merged];
1205
- } else {
596
+ if (existing && Array.isArray(existing.name)) existing.name = mergeNameArrays(existing.name, name);
597
+ else {
1206
598
  const newItem = {
1207
599
  ...curr,
1208
600
  name
@@ -1211,7 +603,7 @@ function combineImports(imports, exports, source) {
1211
603
  namedByPath.set(key, newItem);
1212
604
  }
1213
605
  } else {
1214
- if (name && !isUsed(name)) continue;
606
+ if (name && !isUsed(name) && !pathsWithUsedNamedImport.has(path)) continue;
1215
607
  const key = importKey(path, name, isTypeOnly);
1216
608
  if (!seen.has(key)) {
1217
609
  result.push(curr);
@@ -1221,505 +613,25 @@ function combineImports(imports, exports, source) {
1221
613
  }
1222
614
  return result;
1223
615
  }
616
+ //#endregion
617
+ //#region src/nodes/file.ts
1224
618
  /**
1225
- * Extracts all string content from a `CodeNode` tree recursively.
1226
- *
1227
- * Collects text node values, identifier references in string fields (`params`, `generics`, `returnType`, `type`),
1228
- * and nested node content. Used internally to build the full source string for import filtering.
619
+ * Definition for the {@link ImportNode}.
1229
620
  */
1230
- function extractStringsFromNodes(nodes) {
1231
- if (!nodes?.length) return "";
1232
- return nodes.map((node) => {
1233
- if (typeof node === "string") return node;
1234
- if (node.kind === "Text") return node.value;
1235
- if (node.kind === "Break") return "";
1236
- if (node.kind === "Jsx") return node.value;
1237
- const parts = [];
1238
- if ("params" in node && node.params) parts.push(node.params);
1239
- if ("generics" in node && node.generics) parts.push(Array.isArray(node.generics) ? node.generics.join(", ") : node.generics);
1240
- if ("returnType" in node && node.returnType) parts.push(node.returnType);
1241
- if ("type" in node && typeof node.type === "string") parts.push(node.type);
1242
- const nested = extractStringsFromNodes(node.nodes);
1243
- if (nested) parts.push(nested);
1244
- return parts.join("\n");
1245
- }).filter(Boolean).join("\n");
1246
- }
621
+ const importDef = defineNode({ kind: "Import" });
1247
622
  /**
1248
- * Resolves the schema name of a ref node, falling back through `ref` → `name` → nested `schema.name`.
1249
- *
1250
- * Returns `undefined` for non-ref nodes or when no name can be resolved. Use this to get a schema's
1251
- * identifier for type definitions or error messages.
1252
- *
1253
- * @example
1254
- * ```ts
1255
- * resolveRefName({ kind: 'Schema', type: 'ref', ref: '#/components/schemas/Pet' })
1256
- * // => 'Pet'
1257
- * ```
623
+ * Definition for the {@link ExportNode}.
1258
624
  */
1259
- function resolveRefName(node) {
1260
- if (!node || node.type !== "ref") return void 0;
1261
- if (node.ref) return extractRefName(node.ref) ?? node.name ?? node.schema?.name ?? void 0;
1262
- return node.name ?? node.schema?.name ?? void 0;
1263
- }
625
+ const exportDef = defineNode({ kind: "Export" });
1264
626
  /**
1265
- * Collects every named schema referenced (transitively) from a node via ref edges.
1266
- *
1267
- * Refs are followed by name only — the resolved `node.schema` is not traversed inline.
1268
- * Use this to determine schema dependencies, build reference graphs, or detect what schemas need to be emitted.
1269
- *
1270
- * @example Collect refs from a single schema
1271
- * ```ts
1272
- * const names = collectReferencedSchemaNames(petSchema)
1273
- * // → Set { 'Category', 'Tag' }
1274
- * ```
1275
- *
1276
- * @example Accumulate refs from multiple schemas into one set
1277
- * ```ts
1278
- * const out = new Set<string>()
1279
- * for (const schema of schemas) {
1280
- * collectReferencedSchemaNames(schema, out)
1281
- * }
1282
- * ```
1283
- */
1284
- function collectReferencedSchemaNames(node, out = /* @__PURE__ */ new Set()) {
1285
- if (!node) return out;
1286
- collect(node, { schema(child) {
1287
- if (child.type === "ref") {
1288
- const name = resolveRefName(child);
1289
- if (name) out.add(name);
1290
- }
1291
- } });
1292
- return out;
1293
- }
1294
- /**
1295
- * Collects the names of all top-level schemas transitively used by a set of operations.
1296
- *
1297
- * An operation uses a schema when any of its parameters, request body content, or responses
1298
- * reference it — directly or indirectly through other named schemas.
1299
- * The walk is iterative and safe against reference cycles.
1300
- *
1301
- * Use this together with `include` filters to determine which schemas from `components/schemas`
1302
- * are reachable from the allowed operations, so that schemas used only by excluded operations
1303
- * are not generated.
1304
- *
1305
- * @example Only generate schemas referenced by included operations
1306
- * ```ts
1307
- * const includedOps = inputNode.operations.filter(op => resolver.resolveOptions(op, { options, include }) !== null)
1308
- * const allowed = collectUsedSchemaNames(includedOps, inputNode.schemas)
1309
- *
1310
- * for (const schema of inputNode.schemas) {
1311
- * if (schema.name && !allowed.has(schema.name)) continue
1312
- * // … generate schema
1313
- * }
1314
- * ```
1315
- *
1316
- * @example Check whether a specific schema is needed
1317
- * ```ts
1318
- * const allowed = collectUsedSchemaNames(includedOps, inputNode.schemas)
1319
- * allowed.has('OrderStatus') // false when no included operation references OrderStatus
1320
- * ```
1321
- */
1322
- function collectUsedSchemaNames(operations, schemas) {
1323
- const schemaMap = /* @__PURE__ */ new Map();
1324
- for (const schema of schemas) if (schema.name) schemaMap.set(schema.name, schema);
1325
- const result = /* @__PURE__ */ new Set();
1326
- function visitSchema(schema) {
1327
- const directRefs = collectReferencedSchemaNames(schema);
1328
- for (const name of directRefs) if (!result.has(name)) {
1329
- result.add(name);
1330
- const namedSchema = schemaMap.get(name);
1331
- if (namedSchema) visitSchema(namedSchema);
1332
- }
1333
- }
1334
- for (const op of operations) for (const schema of collect(op, {
1335
- depth: "shallow",
1336
- schema: (node) => node
1337
- })) visitSchema(schema);
1338
- return result;
1339
- }
1340
- /**
1341
- * Identifies all schemas that participate in circular dependency chains, including direct self-loops.
1342
- *
1343
- * Returns a Set of schema names with circular dependencies. Use this to wrap recursive schema positions
1344
- * in deferred constructs (lazy getter, `z.lazy(() => …)`) to prevent infinite recursion when generated code runs.
1345
- * Refs are followed by name only, keeping the algorithm linear in the schema graph size.
1346
- *
1347
- * @note Call this once on the full schema graph, then use `containsCircularRef()` to check individual schemas.
1348
- */
1349
- function findCircularSchemas(schemas) {
1350
- const graph = /* @__PURE__ */ new Map();
1351
- for (const schema of schemas) {
1352
- if (!schema.name) continue;
1353
- graph.set(schema.name, collectReferencedSchemaNames(schema));
1354
- }
1355
- const circular = /* @__PURE__ */ new Set();
1356
- for (const start of graph.keys()) {
1357
- const visited = /* @__PURE__ */ new Set();
1358
- const stack = [...graph.get(start) ?? []];
1359
- while (stack.length > 0) {
1360
- const node = stack.pop();
1361
- if (node === start) {
1362
- circular.add(start);
1363
- break;
1364
- }
1365
- if (visited.has(node)) continue;
1366
- visited.add(node);
1367
- const next = graph.get(node);
1368
- if (next) for (const r of next) stack.push(r);
1369
- }
1370
- }
1371
- return circular;
1372
- }
1373
- /**
1374
- * Type guard returning `true` when a schema or anything nested within it contains a ref to a circular schema.
1375
- *
1376
- * Use `excludeName` to ignore refs to specific schemas (useful when self-references are handled separately).
1377
- * Commonly used with `findCircularSchemas()` to detect where lazy wrappers are needed in code generation.
1378
- *
1379
- * @note Returns `true` for the first matching circular ref found; use for fast dependency checks.
1380
- */
1381
- function containsCircularRef(node, { circularSchemas, excludeName }) {
1382
- if (!node || circularSchemas.size === 0) return false;
1383
- return collect(node, { schema(child) {
1384
- if (child.type !== "ref") return void 0;
1385
- const name = resolveRefName(child);
1386
- return name && name !== excludeName && circularSchemas.has(name) ? true : void 0;
1387
- } }).length > 0;
1388
- }
1389
- //#endregion
1390
- //#region src/factory.ts
1391
- /**
1392
- * Syncs property/parameter schema optionality flags from `required` and `schema.nullable`.
1393
- *
1394
- * - `optional` is set for non-required, non-nullable schemas.
1395
- * - `nullish` is set for non-required, nullable schemas.
1396
- */
1397
- function syncOptionality(schema, required) {
1398
- const nullable = schema.nullable ?? false;
1399
- return {
1400
- ...schema,
1401
- optional: !required && !nullable ? true : void 0,
1402
- nullish: !required && nullable ? true : void 0
1403
- };
1404
- }
1405
- /**
1406
- * Creates an `InputNode` with stable defaults for `schemas` and `operations`.
1407
- *
1408
- * @example
1409
- * ```ts
1410
- * const input = createInput()
1411
- * // { kind: 'Input', schemas: [], operations: [] }
1412
- * ```
1413
- *
1414
- * @example
1415
- * ```ts
1416
- * const input = createInput({ schemas: [petSchema] })
1417
- * // keeps default operations: []
1418
- * ```
1419
- */
1420
- function createInput(overrides = {}) {
1421
- return {
1422
- schemas: [],
1423
- operations: [],
1424
- ...overrides,
1425
- kind: "Input"
1426
- };
1427
- }
1428
- /**
1429
- * Creates an `OutputNode` with a stable default for `files`.
1430
- *
1431
- * @example
1432
- * ```ts
1433
- * const output = createOutput()
1434
- * // { kind: 'Output', files: [] }
1435
- * ```
1436
- *
1437
- * @example
1438
- * ```ts
1439
- * const output = createOutput({ files: [petFile] })
1440
- * ```
1441
- */
1442
- function createOutput(overrides = {}) {
1443
- return {
1444
- files: [],
1445
- ...overrides,
1446
- kind: "Output"
1447
- };
1448
- }
1449
- /**
1450
- * Creates an `OperationNode` with default empty arrays for `tags`, `parameters`, and `responses`.
1451
- *
1452
- * @example
1453
- * ```ts
1454
- * const operation = createOperation({
1455
- * operationId: 'getPetById',
1456
- * method: 'GET',
1457
- * path: '/pet/{petId}',
1458
- * })
1459
- * // tags, parameters, and responses are []
1460
- * ```
1461
- *
1462
- * @example
1463
- * ```ts
1464
- * const operation = createOperation({
1465
- * operationId: 'findPets',
1466
- * method: 'GET',
1467
- * path: '/pet/findByStatus',
1468
- * tags: ['pet'],
1469
- * })
1470
- * ```
1471
- */
1472
- function createOperation(props) {
1473
- return {
1474
- tags: [],
1475
- parameters: [],
1476
- responses: [],
1477
- ...props,
1478
- kind: "Operation"
1479
- };
1480
- }
1481
- /**
1482
- * Maps schema `type` to its underlying `primitive`.
1483
- * Primitive types map to themselves; special string formats map to `'string'`.
1484
- * Complex types (`ref`, `enum`, `union`, `intersection`, `tuple`, `blob`) are left unset.
1485
- */
1486
- const TYPE_TO_PRIMITIVE = {
1487
- string: "string",
1488
- number: "number",
1489
- integer: "integer",
1490
- bigint: "bigint",
1491
- boolean: "boolean",
1492
- null: "null",
1493
- any: "any",
1494
- unknown: "unknown",
1495
- void: "void",
1496
- never: "never",
1497
- object: "object",
1498
- array: "array",
1499
- date: "date",
1500
- uuid: "string",
1501
- email: "string",
1502
- url: "string",
1503
- datetime: "string",
1504
- time: "string"
1505
- };
1506
- function createSchema(props) {
1507
- const inferredPrimitive = TYPE_TO_PRIMITIVE[props.type];
1508
- if (props["type"] === "object") return {
1509
- properties: [],
1510
- primitive: "object",
1511
- ...props,
1512
- kind: "Schema"
1513
- };
1514
- return {
1515
- primitive: inferredPrimitive,
1516
- ...props,
1517
- kind: "Schema"
1518
- };
1519
- }
1520
- /**
1521
- * Creates a `PropertyNode`.
1522
- *
1523
- * `required` defaults to `false`.
1524
- * `schema.optional` and `schema.nullish` are derived from `required` and `schema.nullable`.
1525
- *
1526
- * @example
1527
- * ```ts
1528
- * const property = createProperty({
1529
- * name: 'status',
1530
- * schema: createSchema({ type: 'string' }),
1531
- * })
1532
- * // required=false, schema.optional=true
1533
- * ```
1534
- *
1535
- * @example
1536
- * ```ts
1537
- * const property = createProperty({
1538
- * name: 'status',
1539
- * required: true,
1540
- * schema: createSchema({ type: 'string', nullable: true }),
1541
- * })
1542
- * // required=true, no optional/nullish
1543
- * ```
1544
- */
1545
- function createProperty(props) {
1546
- const required = props.required ?? false;
1547
- return {
1548
- ...props,
1549
- kind: "Property",
1550
- required,
1551
- schema: syncOptionality(props.schema, required)
1552
- };
1553
- }
1554
- /**
1555
- * Creates a `ParameterNode`.
1556
- *
1557
- * `required` defaults to `false`.
1558
- * Nested schema flags are set from `required` and `schema.nullable`.
1559
- *
1560
- * @example
1561
- * ```ts
1562
- * const param = createParameter({
1563
- * name: 'petId',
1564
- * in: 'path',
1565
- * required: true,
1566
- * schema: createSchema({ type: 'string' }),
1567
- * })
1568
- * ```
1569
- *
1570
- * @example
1571
- * ```ts
1572
- * const param = createParameter({
1573
- * name: 'status',
1574
- * in: 'query',
1575
- * schema: createSchema({ type: 'string', nullable: true }),
1576
- * })
1577
- * // required=false, schema.nullish=true
1578
- * ```
1579
- */
1580
- function createParameter(props) {
1581
- const required = props.required ?? false;
1582
- return {
1583
- ...props,
1584
- kind: "Parameter",
1585
- required,
1586
- schema: syncOptionality(props.schema, required)
1587
- };
1588
- }
1589
- /**
1590
- * Creates a `ResponseNode`.
1591
- *
1592
- * @example
1593
- * ```ts
1594
- * const response = createResponse({
1595
- * statusCode: '200',
1596
- * description: 'Success',
1597
- * schema: createSchema({ type: 'object', properties: [] }),
1598
- * })
1599
- * ```
1600
- */
1601
- function createResponse(props) {
1602
- return {
1603
- ...props,
1604
- kind: "Response"
1605
- };
1606
- }
1607
- /**
1608
- * Creates a `FunctionParameterNode`.
1609
- *
1610
- * `optional` defaults to `false`.
1611
- *
1612
- * @example Required typed param
1613
- * ```ts
1614
- * createFunctionParameter({ name: 'petId', type: createParamsType({ variant: 'reference', name: 'string' }) })
1615
- * // → petId: string
1616
- * ```
1617
- *
1618
- * @example Optional param
1619
- * ```ts
1620
- * createFunctionParameter({ name: 'params', type: createParamsType({ variant: 'reference', name: 'QueryParams' }), optional: true })
1621
- * // → params?: QueryParams
1622
- * ```
1623
- *
1624
- * @example Param with default (implicitly optional; cannot combine with `optional: true`)
1625
- * ```ts
1626
- * createFunctionParameter({ name: 'config', type: createParamsType({ variant: 'reference', name: 'RequestConfig' }), default: '{}' })
1627
- * // → config: RequestConfig = {}
1628
- * ```
627
+ * Definition for the {@link SourceNode}.
1629
628
  */
1630
- function createFunctionParameter(props) {
1631
- return {
1632
- optional: false,
1633
- ...props,
1634
- kind: "FunctionParameter"
1635
- };
1636
- }
629
+ const sourceDef = defineNode({ kind: "Source" });
1637
630
  /**
1638
- * Creates a {@link TypeNode} representing a language-agnostic structured type expression.
1639
- *
1640
- * Use `variant: 'struct'` for inline anonymous types and `variant: 'member'` for a single
1641
- * named field accessed from a group type. Each language's printer renders the variant
1642
- * into its own syntax (TypeScript, Python, C#, Kotlin, …).
1643
- *
1644
- * @example Reference type (TypeScript: `QueryParams`)
1645
- * ```ts
1646
- * createParamsType({ variant: 'reference', name: 'QueryParams' })
1647
- * ```
1648
- *
1649
- * @example Struct type (TypeScript: `{ petId: string }`)
1650
- * ```ts
1651
- * createParamsType({ variant: 'struct', properties: [{ name: 'petId', optional: false, type: createParamsType({ variant: 'reference', name: 'string' }) }] })
1652
- * ```
1653
- *
1654
- * @example Member type (TypeScript: `DeletePetPathParams['petId']`)
1655
- * ```ts
1656
- * createParamsType({ variant: 'member', base: 'DeletePetPathParams', key: 'petId' })
1657
- * ```
631
+ * Definition for the {@link FileNode}. The fully resolved builder lives in
632
+ * `createFile`, so this definition only supplies the guard.
1658
633
  */
1659
- function createParamsType(props) {
1660
- return {
1661
- ...props,
1662
- kind: "ParamsType"
1663
- };
1664
- }
1665
- /**
1666
- * Creates a `ParameterGroupNode` representing a group of related parameters treated as a unit.
1667
- *
1668
- * @example Grouped param (TypeScript declaration)
1669
- * ```ts
1670
- * createParameterGroup({
1671
- * properties: [
1672
- * createFunctionParameter({ name: 'id', type: createParamsType({ variant: 'reference', name: 'string' }), optional: false }),
1673
- * createFunctionParameter({ name: 'name', type: createParamsType({ variant: 'reference', name: 'string' }), optional: true }),
1674
- * ],
1675
- * default: '{}',
1676
- * })
1677
- * // declaration → { id, name? }: { id: string; name?: string } = {}
1678
- * // call → { id, name }
1679
- * ```
1680
- *
1681
- * @example Inline (spread) — children emitted as individual top-level parameters
1682
- * ```ts
1683
- * createParameterGroup({
1684
- * properties: [createFunctionParameter({ name: 'petId', type: createParamsType({ variant: 'reference', name: 'string' }), optional: false })],
1685
- * inline: true,
1686
- * })
1687
- * // declaration → petId: string
1688
- * // call → petId
1689
- * ```
1690
- */
1691
- function createParameterGroup(props) {
1692
- return {
1693
- ...props,
1694
- kind: "ParameterGroup"
1695
- };
1696
- }
1697
- /**
1698
- * Creates a `FunctionParametersNode` from an ordered list of parameters.
1699
- *
1700
- * @example
1701
- * ```ts
1702
- * createFunctionParameters({
1703
- * params: [
1704
- * createFunctionParameter({ name: 'petId', type: createParamsType({ variant: 'reference', name: 'string' }), optional: false }),
1705
- * createFunctionParameter({ name: 'config', type: createParamsType({ variant: 'reference', name: 'RequestConfig' }), optional: false, default: '{}' }),
1706
- * ],
1707
- * })
1708
- * ```
1709
- *
1710
- * @example
1711
- * ```ts
1712
- * const empty = createFunctionParameters()
1713
- * // { kind: 'FunctionParameters', params: [] }
1714
- * ```
1715
- */
1716
- function createFunctionParameters(props = {}) {
1717
- return {
1718
- params: [],
1719
- ...props,
1720
- kind: "FunctionParameters"
1721
- };
1722
- }
634
+ const fileDef = defineNode({ kind: "File" });
1723
635
  /**
1724
636
  * Creates an `ImportNode` representing a language-agnostic import/dependency declaration.
1725
637
  *
@@ -1728,19 +640,8 @@ function createFunctionParameters(props = {}) {
1728
640
  * createImport({ name: ['useState'], path: 'react' })
1729
641
  * // import { useState } from 'react'
1730
642
  * ```
1731
- *
1732
- * @example Type-only import
1733
- * ```ts
1734
- * createImport({ name: ['FC'], path: 'react', isTypeOnly: true })
1735
- * // import type { FC } from 'react'
1736
- * ```
1737
643
  */
1738
- function createImport(props) {
1739
- return {
1740
- ...props,
1741
- kind: "Import"
1742
- };
1743
- }
644
+ const createImport = importDef.create;
1744
645
  /**
1745
646
  * Creates an `ExportNode` representing a language-agnostic export/public API declaration.
1746
647
  *
@@ -1749,19 +650,8 @@ function createImport(props) {
1749
650
  * createExport({ name: ['Pet'], path: './Pet' })
1750
651
  * // export { Pet } from './Pet'
1751
652
  * ```
1752
- *
1753
- * @example Wildcard export
1754
- * ```ts
1755
- * createExport({ path: './utils' })
1756
- * // export * from './utils'
1757
- * ```
1758
653
  */
1759
- function createExport(props) {
1760
- return {
1761
- ...props,
1762
- kind: "Export"
1763
- };
1764
- }
654
+ const createExport = exportDef.create;
1765
655
  /**
1766
656
  * Creates a `SourceNode` representing a fragment of source code within a file.
1767
657
  *
@@ -1770,19 +660,14 @@ function createExport(props) {
1770
660
  * createSource({ name: 'Pet', nodes: [createText('export type Pet = { id: number }')], isExportable: true })
1771
661
  * ```
1772
662
  */
1773
- function createSource(props) {
1774
- return {
1775
- ...props,
1776
- kind: "Source"
1777
- };
1778
- }
663
+ const createSource = sourceDef.create;
1779
664
  /**
1780
665
  * Creates a fully resolved `FileNode` from a file input descriptor.
1781
666
  *
1782
667
  * Computes:
1783
- * - `id` SHA256 hash of the file path
1784
- * - `name` `baseName` without extension
1785
- * - `extname` extension extracted from `baseName`
668
+ * - `id` SHA256 hash of the file path
669
+ * - `name` `baseName` without extension
670
+ * - `extname` extension extracted from `baseName`
1786
671
  *
1787
672
  * Deduplicates:
1788
673
  * - `sources` via `combineSources`
@@ -1804,18 +689,48 @@ function createSource(props) {
1804
689
  * // file.name = 'petStore'
1805
690
  * // file.extname = '.ts'
1806
691
  * ```
692
+ *
693
+ * @example Copy a real file into the output verbatim
694
+ * ```ts
695
+ * const file = createFile({
696
+ * baseName: 'client.ts',
697
+ * path: 'src/gen/client.ts',
698
+ * copy: '/abs/path/to/templates/client.ts',
699
+ * })
700
+ * ```
1807
701
  */
1808
702
  function createFile(input) {
1809
- const extname = node_path.default.extname(input.baseName) || (input.baseName.startsWith(".") ? input.baseName : "");
703
+ const extname = node_path.default.extname(input.baseName);
1810
704
  if (!extname) throw new Error(`No extname found for ${input.baseName}`);
1811
- const source = (input.sources ?? []).flatMap((item) => item.nodes ?? []).map((node) => extractStringsFromNodes([node])).filter(Boolean).join("\n\n");
1812
705
  const resolvedExports = input.exports?.length ? combineExports(input.exports) : [];
1813
- const resolvedImports = input.imports?.length ? combineImports(input.imports, resolvedExports, source || void 0) : [];
706
+ const resolvedImports = (() => {
707
+ if (!input.imports?.length) return [];
708
+ const sourceParts = [];
709
+ const localNames = /* @__PURE__ */ new Set();
710
+ for (const item of input.sources ?? []) {
711
+ const extracted = item.nodes && extractStringsFromNodes(item.nodes);
712
+ if (extracted) sourceParts.push(extracted);
713
+ if (item.name) localNames.add(item.name);
714
+ }
715
+ const source = sourceParts.join("\n") || void 0;
716
+ const combinedImports = combineImports(input.imports, resolvedExports, source);
717
+ const nameOf = (item) => typeof item === "string" ? item : item.name ?? item.propertyName;
718
+ return combinedImports.flatMap((imp) => {
719
+ if (imp.path === input.path) return [];
720
+ if (!Array.isArray(imp.name)) return typeof imp.name === "string" && localNames.has(imp.name) ? [] : [imp];
721
+ const kept = imp.name.filter((item) => !localNames.has(nameOf(item)));
722
+ if (!kept.length) return [];
723
+ return [kept.length === imp.name.length ? imp : {
724
+ ...imp,
725
+ name: kept
726
+ }];
727
+ });
728
+ })();
1814
729
  const resolvedSources = input.sources?.length ? combineSources(input.sources) : [];
1815
730
  return {
1816
731
  kind: "File",
1817
732
  ...input,
1818
- id: (0, node_crypto.createHash)("sha256").update(input.path).digest("hex"),
733
+ id: (0, node_crypto.hash)("sha256", input.path, "hex"),
1819
734
  name: trimExtName(input.baseName),
1820
735
  extname,
1821
736
  imports: resolvedImports,
@@ -1824,454 +739,945 @@ function createFile(input) {
1824
739
  meta: input.meta ?? {}
1825
740
  };
1826
741
  }
742
+ //#endregion
743
+ //#region src/nodes/input.ts
1827
744
  /**
1828
- * Creates a `ConstNode` representing a TypeScript `const` declaration.
745
+ * Definition for the {@link InputNode}.
746
+ */
747
+ const inputDef = defineNode({
748
+ kind: "Input",
749
+ defaults: {
750
+ schemas: [],
751
+ operations: [],
752
+ meta: {
753
+ circularNames: [],
754
+ enumNames: []
755
+ }
756
+ },
757
+ children: ["schemas", "operations"],
758
+ visitorKey: "input"
759
+ });
760
+ /**
761
+ * Creates an `InputNode`, defaulting `schemas`/`operations` to empty arrays and `meta` per
762
+ * {@link inputDef}.
1829
763
  *
1830
- * Mirrors the `Const` component from `@kubb/renderer-jsx`.
1831
- * The component's `children` are represented as `nodes`.
764
+ * @example
765
+ * ```ts
766
+ * const input = createInput()
767
+ * // { kind: 'Input', schemas: [], operations: [] }
768
+ * ```
769
+ */
770
+ function createInput(overrides = {}) {
771
+ return inputDef.create(overrides);
772
+ }
773
+ //#endregion
774
+ //#region src/nodes/requestBody.ts
775
+ /**
776
+ * Definition for the {@link RequestBodyNode}. Content entries are built upfront with
777
+ * {@link createContent}, mirroring how `parameters` and `responses` take prebuilt nodes.
778
+ */
779
+ const requestBodyDef = defineNode({
780
+ kind: "RequestBody",
781
+ children: ["content"]
782
+ });
783
+ /**
784
+ * Creates a `RequestBodyNode`.
785
+ */
786
+ const createRequestBody = requestBodyDef.create;
787
+ //#endregion
788
+ //#region src/nodes/operation.ts
789
+ /**
790
+ * Definition for the {@link OperationNode}. HTTP operations (those carrying both
791
+ * `method` and `path`) are tagged with `protocol: 'http'`, and the request body is
792
+ * normalized into a `RequestBodyNode`.
793
+ */
794
+ const operationDef = defineNode({
795
+ kind: "Operation",
796
+ build: (props) => {
797
+ const { requestBody, ...rest } = props;
798
+ const isHttp = rest.method !== void 0 && rest.path !== void 0;
799
+ return {
800
+ tags: [],
801
+ parameters: [],
802
+ responses: [],
803
+ ...rest,
804
+ ...isHttp ? { protocol: "http" } : {},
805
+ requestBody: requestBody ? createRequestBody(requestBody) : void 0
806
+ };
807
+ },
808
+ children: [
809
+ "parameters",
810
+ "requestBody",
811
+ "responses"
812
+ ],
813
+ visitorKey: "operation"
814
+ });
815
+ function createOperation(props) {
816
+ return operationDef.create(props);
817
+ }
818
+ //#endregion
819
+ //#region src/nodes/output.ts
820
+ /**
821
+ * Definition for the {@link OutputNode}.
822
+ */
823
+ const outputDef = defineNode({
824
+ kind: "Output",
825
+ defaults: { files: [] },
826
+ visitorKey: "output"
827
+ });
828
+ /**
829
+ * Creates an `OutputNode` with a stable default for `files`.
1832
830
  *
1833
- * @example Simple constant
831
+ * @example
1834
832
  * ```ts
1835
- * createConst({ name: 'pet' })
1836
- * // const pet = ...
833
+ * const output = createOutput()
834
+ * // { kind: 'Output', files: [] }
1837
835
  * ```
836
+ */
837
+ function createOutput(overrides = {}) {
838
+ return outputDef.create(overrides);
839
+ }
840
+ //#endregion
841
+ //#region src/optionality.ts
842
+ /**
843
+ * Generic JSON Schema optionality: a non-required field is optional, and a
844
+ * non-required nullable field is nullish.
845
+ */
846
+ function optionality(schema, required) {
847
+ const nullable = schema.nullable ?? false;
848
+ return {
849
+ ...schema,
850
+ optional: !required && !nullable ? true : void 0,
851
+ nullish: !required && nullable ? true : void 0
852
+ };
853
+ }
854
+ //#endregion
855
+ //#region src/nodes/parameter.ts
856
+ /**
857
+ * Definition for the {@link ParameterNode}. `required` defaults to `false`, and the schema's
858
+ * `optional`/`nullish` flags are derived from it through {@link optionality}.
859
+ */
860
+ const parameterDef = defineNode({
861
+ kind: "Parameter",
862
+ build: (props) => {
863
+ const required = props.required ?? false;
864
+ return {
865
+ ...props,
866
+ required,
867
+ schema: optionality(props.schema, required)
868
+ };
869
+ },
870
+ children: ["schema"],
871
+ visitorKey: "parameter"
872
+ });
873
+ /**
874
+ * Creates a `ParameterNode`.
1838
875
  *
1839
- * @example Exported constant with type and `as const`
876
+ * @example
1840
877
  * ```ts
1841
- * createConst({ name: 'pets', export: true, type: 'Pet[]', asConst: true })
1842
- * // export const pets: Pet[] = ... as const
878
+ * const param = createParameter({
879
+ * name: 'petId',
880
+ * in: 'path',
881
+ * required: true,
882
+ * schema: createSchema({ type: 'string' }),
883
+ * })
1843
884
  * ```
885
+ */
886
+ const createParameter = parameterDef.create;
887
+ //#endregion
888
+ //#region src/nodes/property.ts
889
+ /**
890
+ * Definition for the {@link PropertyNode}. `required` defaults to `false`, and the schema's
891
+ * `optional`/`nullish` flags are derived from it through {@link optionality}.
892
+ */
893
+ const propertyDef = defineNode({
894
+ kind: "Property",
895
+ build: (props) => {
896
+ const required = props.required ?? false;
897
+ return {
898
+ ...props,
899
+ required,
900
+ schema: optionality(props.schema, required)
901
+ };
902
+ },
903
+ children: ["schema"],
904
+ visitorKey: "property"
905
+ });
906
+ /**
907
+ * Creates a `PropertyNode`.
1844
908
  *
1845
- * @example With JSDoc and child nodes
909
+ * @example
1846
910
  * ```ts
1847
- * createConst({
1848
- * name: 'config',
1849
- * export: true,
1850
- * JSDoc: { comments: ['@description App configuration'] },
1851
- * nodes: [],
911
+ * const property = createProperty({
912
+ * name: 'status',
913
+ * required: true,
914
+ * schema: createSchema({ type: 'string', nullable: true }),
1852
915
  * })
916
+ * // required=true, no optional/nullish
1853
917
  * ```
1854
918
  */
1855
- function createConst(props) {
1856
- return {
1857
- ...props,
1858
- kind: "Const"
1859
- };
1860
- }
919
+ const createProperty = propertyDef.create;
920
+ //#endregion
921
+ //#region src/nodes/response.ts
922
+ /**
923
+ * Definition for the {@link ResponseNode}. A single legacy `schema` (with optional
924
+ * `mediaType`/`keysToOmit`) is normalized into one `content` entry.
925
+ */
926
+ const responseDef = defineNode({
927
+ kind: "Response",
928
+ build: (props) => {
929
+ const { schema, mediaType, keysToOmit, content, ...rest } = props;
930
+ const entries = content ?? (schema ? [createContent({
931
+ contentType: mediaType ?? "application/json",
932
+ schema,
933
+ keysToOmit: keysToOmit ?? null
934
+ })] : void 0);
935
+ return {
936
+ ...rest,
937
+ content: entries
938
+ };
939
+ },
940
+ children: ["content"],
941
+ visitorKey: "response"
942
+ });
1861
943
  /**
1862
- * Creates a `TypeNode` representing a TypeScript `type` alias declaration.
1863
- *
1864
- * Mirrors the `Type` component from `@kubb/renderer-jsx`.
1865
- * The component's `children` are represented as `nodes`.
1866
- *
1867
- * @example Simple type alias
1868
- * ```ts
1869
- * createType({ name: 'Pet' })
1870
- * // type Pet = ...
1871
- * ```
944
+ * Creates a `ResponseNode`.
1872
945
  *
1873
- * @example Exported type with JSDoc
946
+ * @example
1874
947
  * ```ts
1875
- * createType({
1876
- * name: 'PetStatus',
1877
- * export: true,
1878
- * JSDoc: { comments: ['@description Status of a pet'] },
948
+ * const response = createResponse({
949
+ * statusCode: '200',
950
+ * content: [createContent({ contentType: 'application/json', schema: createSchema({ type: 'object', properties: [] }) })],
1879
951
  * })
1880
- * // export type PetStatus = ...
1881
952
  * ```
1882
953
  */
1883
- function createType(props) {
1884
- return {
1885
- ...props,
1886
- kind: "Type"
1887
- };
954
+ const createResponse = responseDef.create;
955
+ //#endregion
956
+ //#region src/nodes/schema.ts
957
+ /**
958
+ * Maps schema `type` to its underlying `primitive`.
959
+ * Primitive types map to themselves and special string formats map to `'string'`.
960
+ * Any type not listed here (such as `ref`, `enum`, `union`, `intersection`, `tuple`, `ipv4`, `ipv6`, `blob`) has no `primitive`.
961
+ */
962
+ const TYPE_TO_PRIMITIVE = {
963
+ string: "string",
964
+ number: "number",
965
+ integer: "integer",
966
+ bigint: "bigint",
967
+ boolean: "boolean",
968
+ null: "null",
969
+ any: "any",
970
+ unknown: "unknown",
971
+ void: "void",
972
+ never: "never",
973
+ object: "object",
974
+ array: "array",
975
+ date: "date",
976
+ uuid: "string",
977
+ email: "string",
978
+ url: "string",
979
+ datetime: "string",
980
+ time: "string"
981
+ };
982
+ /**
983
+ * Definition for the {@link SchemaNode}. Object schemas default `properties` to an
984
+ * empty array, and `primitive` is inferred from `type` when not explicitly provided.
985
+ */
986
+ const schemaDef = defineNode({
987
+ kind: "Schema",
988
+ build: (props) => {
989
+ if (props.type === "object") return {
990
+ properties: [],
991
+ primitive: "object",
992
+ ...props
993
+ };
994
+ return {
995
+ primitive: TYPE_TO_PRIMITIVE[props.type],
996
+ ...props
997
+ };
998
+ },
999
+ children: [
1000
+ "properties",
1001
+ "items",
1002
+ "members",
1003
+ "additionalProperties"
1004
+ ],
1005
+ visitorKey: "schema"
1006
+ });
1007
+ function createSchema(props) {
1008
+ return schemaDef.create(props);
1888
1009
  }
1010
+ //#endregion
1011
+ //#region src/registry.ts
1012
+ /**
1013
+ * Every node definition. Adding a node means adding its `defineNode` to one
1014
+ * `nodes/*.ts` file and listing it here. The visitor tables in `visitor.ts` derive from it.
1015
+ */
1016
+ const nodeDefs = [
1017
+ inputDef,
1018
+ outputDef,
1019
+ operationDef,
1020
+ requestBodyDef,
1021
+ contentDef,
1022
+ responseDef,
1023
+ schemaDef,
1024
+ propertyDef,
1025
+ parameterDef,
1026
+ constDef,
1027
+ typeDef,
1028
+ functionDef,
1029
+ arrowFunctionDef,
1030
+ textDef,
1031
+ breakDef,
1032
+ jsxDef,
1033
+ importDef,
1034
+ exportDef,
1035
+ sourceDef,
1036
+ fileDef
1037
+ ];
1038
+ //#endregion
1039
+ //#region src/visitor.ts
1889
1040
  /**
1890
- * Creates a `FunctionNode` representing a TypeScript `function` declaration.
1891
- *
1892
- * Mirrors the `Function` component from `@kubb/renderer-jsx`.
1893
- * The component's `children` are represented as `nodes`.
1041
+ * Child node fields per node kind, in traversal order (Babel's `VISITOR_KEYS`).
1042
+ * Derived from each definition's `children`.
1043
+ */
1044
+ const VISITOR_KEYS = Object.fromEntries(nodeDefs.flatMap((def) => def.children ? [[def.kind, def.children]] : []));
1045
+ /**
1046
+ * Maps a node kind to the matching visitor callback name. Derived from each
1047
+ * definition's `visitorKey`.
1048
+ */
1049
+ const VISITOR_KEY_BY_KIND = Object.fromEntries(nodeDefs.flatMap((def) => def.visitorKey ? [[def.kind, def.visitorKey]] : []));
1050
+ const visitorKeysByKind = VISITOR_KEYS;
1051
+ /**
1052
+ * Returns `true` when `value` is an AST node (an object carrying a `kind`).
1053
+ */
1054
+ function isNode(value) {
1055
+ return typeof value === "object" && value !== null && typeof value.kind === "string";
1056
+ }
1057
+ /**
1058
+ * Returns the immediate traversable children of `node` based on {@link VISITOR_KEYS}.
1894
1059
  *
1895
- * @example Simple function
1896
- * ```ts
1897
- * createFunction({ name: 'getPet' })
1898
- * // function getPet() { ... }
1899
- * ```
1060
+ * `Schema` children are only included when `recurse` is `true`. Shallow mode skips them.
1900
1061
  *
1901
- * @example Exported async function with return type
1062
+ * @example
1902
1063
  * ```ts
1903
- * createFunction({ name: 'fetchPet', export: true, async: true, returnType: 'Pet' })
1904
- * // export async function fetchPet(): Promise<Pet> { ... }
1905
- * ```
1064
+ * const children = getChildren(operationNode, true)
1065
+ * // returns parameters, the request body, and responses
1066
+ * ```
1067
+ */
1068
+ function* getChildren(node, recurse) {
1069
+ if (node.kind === "Schema" && !recurse) return;
1070
+ const keys = visitorKeysByKind[node.kind];
1071
+ if (!keys) return;
1072
+ const record = node;
1073
+ for (const key of keys) {
1074
+ const value = record[key];
1075
+ if (Array.isArray(value)) {
1076
+ for (const item of value) if (isNode(item)) yield item;
1077
+ } else if (isNode(value)) yield value;
1078
+ }
1079
+ }
1080
+ /**
1081
+ * Runs the visitor callback that matches `node.kind` with the traversal
1082
+ * context. The result is a replacement node, a collected value, or `undefined`
1083
+ * when no callback is registered for the kind.
1906
1084
  *
1907
- * @example Function with generics and params
1908
- * ```ts
1909
- * createFunction({
1910
- * name: 'identity',
1911
- * export: true,
1912
- * generics: ['T'],
1913
- * params: 'value: T',
1914
- * returnType: 'T',
1915
- * })
1916
- * // export function identity<T>(value: T): T { ... }
1917
- * ```
1085
+ * Shared by `transform` and `collect` so node-kind dispatch lives in one place.
1086
+ * `TResult` is the caller's expected return: the same node type for `transform`,
1087
+ * the collected value type for `collect`.
1918
1088
  */
1919
- function createFunction(props) {
1089
+ function applyVisitor(node, visitor, parent) {
1090
+ const key = VISITOR_KEY_BY_KIND[node.kind];
1091
+ if (!key) return void 0;
1092
+ const fn = visitor[key];
1093
+ return fn?.(node, { parent });
1094
+ }
1095
+ function transform(node, options) {
1096
+ const { depth, parent, ...visitor } = options;
1097
+ return transformNode(node, visitor, (depth ?? visitorDepths.deep) === visitorDepths.deep, parent);
1098
+ }
1099
+ /**
1100
+ * Visits a single node, then immutably rebuilds its children. Returns the original
1101
+ * reference when neither the visitor nor the child rebuild changed anything, so callers
1102
+ * can detect "nothing changed" by identity and ancestors avoid reallocating.
1103
+ */
1104
+ function transformNode(node, visitor, recurse, parent) {
1105
+ return transformChildren(applyVisitor(node, visitor, parent) ?? node, visitor, recurse);
1106
+ }
1107
+ /**
1108
+ * Immutably rebuilds a node's children using {@link VISITOR_KEYS}, transforming
1109
+ * each child node and leaving non-node values (e.g. `additionalProperties: true`) intact.
1110
+ * `Schema` children are skipped in shallow mode.
1111
+ */
1112
+ function transformChildren(node, visitor, recurse) {
1113
+ if (node.kind === "Schema" && !recurse) return node;
1114
+ const keys = visitorKeysByKind[node.kind];
1115
+ if (!keys) return node;
1116
+ const record = node;
1117
+ let updates;
1118
+ for (const key of keys) {
1119
+ if (!(key in record)) continue;
1120
+ const value = record[key];
1121
+ if (Array.isArray(value)) {
1122
+ let mapped;
1123
+ for (const [i, item] of value.entries()) {
1124
+ const next = isNode(item) ? transformNode(item, visitor, recurse, node) : item;
1125
+ if (mapped) {
1126
+ mapped.push(next);
1127
+ continue;
1128
+ }
1129
+ if (next !== item) mapped = [...value.slice(0, i), next];
1130
+ }
1131
+ if (mapped) (updates ??= {})[key] = mapped;
1132
+ } else if (isNode(value)) {
1133
+ const next = transformNode(value, visitor, recurse, node);
1134
+ if (next !== value) (updates ??= {})[key] = next;
1135
+ }
1136
+ }
1137
+ if (!updates) return node;
1920
1138
  return {
1921
- ...props,
1922
- kind: "Function"
1139
+ ...node,
1140
+ ...updates
1923
1141
  };
1924
1142
  }
1925
1143
  /**
1926
- * Creates an `ArrowFunctionNode` representing a TypeScript arrow function.
1927
- *
1928
- * Mirrors the `Function.Arrow` component from `@kubb/renderer-jsx`.
1929
- * The component's `children` are represented as `nodes`.
1930
- *
1931
- * @example Simple arrow function
1932
- * ```ts
1933
- * createArrowFunction({ name: 'getPet' })
1934
- * // const getPet = () => { ... }
1935
- * ```
1144
+ * Lazy depth-first collection pass. Yields every non-null value returned by
1145
+ * the visitor callbacks. Use `collectSync` for the eager array form.
1936
1146
  *
1937
- * @example Single-line exported arrow function
1147
+ * @example Collect every operationId
1938
1148
  * ```ts
1939
- * createArrowFunction({ name: 'double', export: true, params: 'n: number', singleLine: true })
1940
- * // export const double = (n: number) => ...
1149
+ * const ids: string[] = []
1150
+ * for (const id of collect<string>(root, {
1151
+ * operation(node) {
1152
+ * return node.operationId
1153
+ * },
1154
+ * })) {
1155
+ * ids.push(id)
1156
+ * }
1941
1157
  * ```
1158
+ */
1159
+ function* collect(node, options) {
1160
+ const { depth, parent, ...visitor } = options;
1161
+ yield* collectNode(node, visitor, (depth ?? visitorDepths.deep) === visitorDepths.deep, parent);
1162
+ }
1163
+ function* collectNode(node, visitor, recurse, parent) {
1164
+ const v = applyVisitor(node, visitor, parent);
1165
+ if (v != null) yield v;
1166
+ for (const child of getChildren(node, recurse)) yield* collectNode(child, visitor, recurse, node);
1167
+ }
1168
+ /**
1169
+ * Eager depth-first collection pass. Gathers every non-null value the visitor
1170
+ * callbacks return into an array.
1942
1171
  *
1943
- * @example Async arrow function with generics
1172
+ * @example Collect every operationId
1944
1173
  * ```ts
1945
- * createArrowFunction({
1946
- * name: 'fetchPet',
1947
- * export: true,
1948
- * async: true,
1949
- * generics: ['T'],
1950
- * params: 'id: string',
1951
- * returnType: 'T',
1174
+ * const ids = collectSync<string>(root, {
1175
+ * operation(node) {
1176
+ * return node.operationId
1177
+ * },
1952
1178
  * })
1953
- * // export const fetchPet = async <T>(id: string): Promise<T> => { ... }
1954
1179
  * ```
1955
1180
  */
1956
- function createArrowFunction(props) {
1957
- return {
1958
- ...props,
1959
- kind: "ArrowFunction"
1960
- };
1181
+ function collectSync(node, options) {
1182
+ return Array.from(collect(node, options));
1961
1183
  }
1184
+ //#endregion
1185
+ //#region src/defineMacro.ts
1962
1186
  /**
1963
- * Creates a {@link TextNode} representing a raw string fragment in the source output.
1964
- *
1965
- * Use this instead of bare strings when building `nodes` arrays so that every
1966
- * entry in the array is a typed {@link CodeNode}.
1187
+ * Sort weight for an `enforce` hint. `pre` sorts before unmarked items and `post` after, so a plain
1188
+ * list keeps its authored order.
1189
+ */
1190
+ function enforceWeight(enforce) {
1191
+ if (enforce === "pre") return 0;
1192
+ if (enforce === "post") return 2;
1193
+ return 1;
1194
+ }
1195
+ /**
1196
+ * Types a macro for inference and a single construction site, mirroring `definePlugin`.
1197
+ * Adds no runtime behavior.
1967
1198
  *
1968
1199
  * @example
1969
1200
  * ```ts
1970
- * createText('return fetch(id)')
1971
- * // { kind: 'Text', value: 'return fetch(id)' }
1201
+ * const macroUntagged = defineMacro({
1202
+ * name: 'untagged',
1203
+ * operation(node) {
1204
+ * return node.tags?.length ? undefined : { ...node, tags: ['untagged'] }
1205
+ * },
1206
+ * })
1972
1207
  * ```
1973
1208
  */
1974
- function createText(value) {
1975
- return {
1976
- value,
1977
- kind: "Text"
1978
- };
1209
+ function defineMacro(macro) {
1210
+ return macro;
1979
1211
  }
1980
1212
  /**
1981
- * Creates a {@link BreakNode} representing a line break in the source output.
1982
- *
1983
- * Corresponds to `<br/>` in JSX components. Prints as an empty string which,
1984
- * when joined with `\n` by `printNodes`, produces a blank line.
1213
+ * Runs every macro's callback for one node kind in order, chaining the result so each macro sees
1214
+ * the previous macro's output. Returns `undefined` when nothing changed, so `transform` keeps the
1215
+ * original reference (structural sharing).
1216
+ */
1217
+ function chain({ macros, key, node, context }) {
1218
+ let current = node;
1219
+ for (const macro of macros) {
1220
+ const callback = macro[key];
1221
+ if (!callback) continue;
1222
+ if (macro.match && !macro.match(current)) continue;
1223
+ const next = callback(current, context);
1224
+ if (next != null) current = next;
1225
+ }
1226
+ return current === node ? void 0 : current;
1227
+ }
1228
+ /**
1229
+ * Folds an ordered list of macros into a single {@link Visitor} that `transform` (and the per-plugin
1230
+ * transform layer in `@kubb/core`) can run. Macros are stable-sorted by `enforce`, then applied
1231
+ * sequentially per node so later macros see earlier output. This differs from a plain visitor, which
1232
+ * has no names, ordering, or composition.
1985
1233
  *
1986
1234
  * @example
1987
1235
  * ```ts
1988
- * createBreak()
1989
- * // { kind: 'Break' }
1236
+ * const visitor = composeMacros([macroSimplifyUnion, macroDiscriminatorEnum])
1237
+ * const next = transform(root, visitor)
1990
1238
  * ```
1991
1239
  */
1992
- function createBreak() {
1993
- return { kind: "Break" };
1240
+ function composeMacros(macros) {
1241
+ const ordered = [...macros].sort((a, b) => enforceWeight(a.enforce) - enforceWeight(b.enforce));
1242
+ const visitor = {};
1243
+ for (const key of visitorKeys) {
1244
+ if (!ordered.some((macro) => typeof macro[key] === "function")) continue;
1245
+ const callback = (node, context) => chain({
1246
+ macros: ordered,
1247
+ key,
1248
+ node,
1249
+ context
1250
+ });
1251
+ visitor[key] = callback;
1252
+ }
1253
+ return visitor;
1994
1254
  }
1995
1255
  /**
1996
- * Creates a {@link JsxNode} representing a raw JSX fragment in the source output.
1997
- *
1998
- * Use this to embed JSX markup (including fragments `<>…</>`) directly in generated code.
1256
+ * Runs a list of macros over a node tree and returns the rewritten tree. Keeps `transform`'s
1257
+ * structural sharing, so an empty or no-op macro list returns the same reference. Pass
1258
+ * `depth: 'shallow'` to rewrite the root node only.
1999
1259
  *
2000
1260
  * @example
2001
1261
  * ```ts
2002
- * createJsx('<>\n <a href={href}>Open</a>\n</>')
2003
- * // { kind: 'Jsx', value: '<>\n <a href={href}>Open</a>\n</>' }
1262
+ * const next = applyMacros(root, [macroIntegerToString])
1263
+ * ```
1264
+ *
1265
+ * @example Apply to the root node only
1266
+ * ```ts
1267
+ * const named = applyMacros(node, [macroEnumName({ parentName, propName, enumSuffix })], { depth: 'shallow' })
2004
1268
  * ```
2005
1269
  */
2006
- function createJsx(value) {
2007
- return {
2008
- value,
2009
- kind: "Jsx"
2010
- };
1270
+ function applyMacros(root, macros, options) {
1271
+ if (macros.length === 0) return root;
1272
+ return transform(root, {
1273
+ ...composeMacros(macros),
1274
+ ...options
1275
+ });
2011
1276
  }
2012
1277
  //#endregion
2013
- //#region src/printer.ts
1278
+ //#region src/createPrinter.ts
2014
1279
  /**
2015
- * Creates a schema printer factory.
2016
- *
2017
- * This function wraps a builder and makes options optional at call sites.
1280
+ * Creates a schema printer: a function that takes a `SchemaNode` and emits
1281
+ * code in your target language. Each plugin that produces code from schemas
1282
+ * (TypeScript types, Zod schemas, Faker factories) ships a printer built
1283
+ * with this helper.
2018
1284
  *
2019
1285
  * The builder receives resolved options and returns:
2020
- * - `name` — a unique identifier for the printer
2021
- * - `options` — options stored on the returned printer instance
2022
- * - `nodes` — a map of `SchemaType` → handler functions that convert a `SchemaNode` to `TOutput`
2023
- * - `print` _(optional)_ — top-level override exposed as `printer.print`
2024
- * - Inside this function, use `this.transform(node)` to dispatch to the `nodes` map
2025
- * - This keeps recursion safe and avoids self-calls
2026
1286
  *
2027
- * When no `print` override is provided, `printer.print` falls back to `printer.transform` (the node-level dispatcher).
1287
+ * - `name` unique identifier for the printer.
1288
+ * - `options` stored on the returned printer instance.
1289
+ * - `nodes` map of `SchemaType` → handler. Handlers return the rendered
1290
+ * output (a string, a TypeScript AST node, ...) for that schema type.
1291
+ * - `overrides` (optional), user-supplied handlers that win over `nodes`.
1292
+ * An override can call `this.base(node)` to reuse the handler it replaced.
1293
+ * - `print` (optional), top-level override exposed as `printer.print`.
1294
+ * Use `this.transform(node)` inside it to dispatch to `nodes` recursively.
1295
+ *
1296
+ * Without a `print` override, `printer.print` falls back to `printer.transform`
1297
+ * (the node-level dispatcher).
2028
1298
  *
2029
- * @example Basic usage — Zod schema printer
1299
+ * @example Tiny Zod printer
2030
1300
  * ```ts
1301
+ * import { createPrinter, type PrinterFactoryOptions } from '@kubb/ast'
1302
+ *
2031
1303
  * type PrinterZod = PrinterFactoryOptions<'zod', { strict?: boolean }, string>
2032
1304
  *
2033
- * export const zodPrinter = definePrinter<PrinterZod>((options) => ({
1305
+ * export const zodPrinter = createPrinter<PrinterZod>((options) => ({
2034
1306
  * name: 'zod',
2035
1307
  * options: { strict: options.strict ?? true },
2036
1308
  * nodes: {
2037
1309
  * string: () => 'z.string()',
2038
1310
  * object(node) {
2039
- * const props = node.properties.map(p => `${p.name}: ${this.transform(p.schema)}`).join(', ')
1311
+ * const props = node.properties
1312
+ * .map((p) => `${p.name}: ${this.transform(p.schema)}`)
1313
+ * .join(', ')
2040
1314
  * return `z.object({ ${props} })`
2041
1315
  * },
2042
1316
  * },
2043
1317
  * }))
2044
1318
  * ```
2045
1319
  */
2046
- function definePrinter(build) {
2047
- return createPrinterFactory((node) => node.type)(build);
1320
+ function createPrinter(build) {
1321
+ return (options) => {
1322
+ const { name, options: resolvedOptions, nodes, overrides, print: printOverride } = build(options ?? {});
1323
+ const merged = overrides ? {
1324
+ ...nodes,
1325
+ ...overrides
1326
+ } : nodes;
1327
+ const context = {
1328
+ options: resolvedOptions,
1329
+ transform: (node) => {
1330
+ const handler = merged[node.type];
1331
+ if (!handler) return null;
1332
+ return handler.call(context, node);
1333
+ },
1334
+ base: (node) => {
1335
+ const handler = nodes[node.type];
1336
+ if (!handler) return null;
1337
+ return handler.call(context, node);
1338
+ }
1339
+ };
1340
+ return {
1341
+ name,
1342
+ options: resolvedOptions,
1343
+ transform: context.transform,
1344
+ print: printOverride ? printOverride.bind(context) : context.transform
1345
+ };
1346
+ };
2048
1347
  }
1348
+ //#endregion
1349
+ //#region src/utils/refs.ts
2049
1350
  /**
2050
- * Generic printer-factory function used by `definePrinter` and `defineFunctionPrinter`.
2051
- **
1351
+ * Resolves the emitted name of the schema a ref node points at. Prefers `targetName` (set when
1352
+ * the referenced schema was renamed, e.g. to break a collision), then the last segment of `ref`,
1353
+ * then `name`, then the nested `schema.name`.
1354
+ *
1355
+ * Returns `null` for non-ref nodes or when no name resolves.
1356
+ *
2052
1357
  * @example
2053
- * ```ts
2054
- * export const defineFunctionPrinter = createPrinterFactory<FunctionNode, FunctionNodeType, FunctionNodeByType>(
2055
- * (node) => kindToHandlerKey[node.kind],
2056
- * )
2057
- * ```
1358
+ * `resolveRefName({ kind: 'Schema', type: 'ref', ref: '#/components/schemas/Pet' }) // 'Pet'`
1359
+ *
1360
+ * @example Collision-renamed target
1361
+ * `resolveRefName({ kind: 'Schema', type: 'ref', ref: '#/components/schemas/Order', targetName: 'OrderSchema' }) // 'OrderSchema'`
2058
1362
  */
2059
- function createPrinterFactory(getKey) {
2060
- return function(build) {
2061
- return (options) => {
2062
- const { name, options: resolvedOptions, nodes, print: printOverride } = build(options ?? {});
2063
- const context = {
2064
- options: resolvedOptions,
2065
- transform: (node) => {
2066
- const key = getKey(node);
2067
- if (key === void 0) return null;
2068
- const handler = nodes[key];
2069
- if (!handler) return null;
2070
- return handler.call(context, node);
2071
- }
2072
- };
2073
- return {
2074
- name,
2075
- options: resolvedOptions,
2076
- transform: context.transform,
2077
- print: printOverride ? printOverride.bind(context) : context.transform
2078
- };
2079
- };
2080
- };
1363
+ function resolveRefName(node) {
1364
+ if (!node || node.type !== "ref") return null;
1365
+ if (node.targetName) return node.targetName;
1366
+ if (node.ref) return node.ref.split("/").at(-1) ?? node.ref;
1367
+ return node.name ?? node.schema?.name ?? null;
2081
1368
  }
2082
1369
  //#endregion
2083
- //#region src/resolvers.ts
2084
- function findDiscriminator(mapping, ref) {
2085
- if (!mapping || !ref) return null;
2086
- return Object.entries(mapping).find(([, value]) => value === ref)?.[0] ?? null;
2087
- }
2088
- function childName(parentName, propName) {
2089
- return parentName ? pascalCase([parentName, propName].join(" ")) : null;
2090
- }
2091
- function enumPropName(parentName, propName, enumSuffix) {
2092
- return pascalCase([
2093
- parentName,
2094
- propName,
2095
- enumSuffix
2096
- ].filter(Boolean).join(" "));
2097
- }
1370
+ //#region src/utils/schemaGraph.ts
2098
1371
  /**
2099
- * Collects import entries for all `ref` schema nodes in `node`.
1372
+ * Memoized inner pass that walks a single node and returns the names of every schema it references.
2100
1373
  */
2101
- function collectImports({ node, nameMapping, resolve }) {
2102
- return collect(node, { schema(schemaNode) {
2103
- const schemaRef = narrowSchema(schemaNode, "ref");
2104
- if (!schemaRef?.ref) return;
2105
- const rawName = extractRefName(schemaRef.ref);
2106
- const result = resolve(nameMapping.get(rawName) ?? rawName);
2107
- if (!result) return;
2108
- return result;
1374
+ const collectSchemaRefs = memoize(/* @__PURE__ */ new WeakMap(), (node) => {
1375
+ const refs = /* @__PURE__ */ new Set();
1376
+ collectSync(node, { schema(child) {
1377
+ if (child.type === "ref") {
1378
+ const name = resolveRefName(child);
1379
+ if (name) refs.add(name);
1380
+ }
2109
1381
  } });
1382
+ return refs;
1383
+ });
1384
+ /**
1385
+ * Collects the names of every ref found anywhere inside a node's own subtree.
1386
+ *
1387
+ * Each ref contributes its name only, so the schema it points to is never traversed here. Pass `out`
1388
+ * to accumulate names from several nodes into one set.
1389
+ *
1390
+ * @example Collect refs from a single schema
1391
+ * ```ts
1392
+ * const names = collectReferencedSchemaNames(petSchema)
1393
+ * // Set { 'Category', 'Tag' }
1394
+ * ```
1395
+ *
1396
+ * @example Accumulate refs from multiple schemas into one set
1397
+ * ```ts
1398
+ * const out = new Set<string>()
1399
+ * for (const schema of schemas) {
1400
+ * collectReferencedSchemaNames(schema, out)
1401
+ * }
1402
+ * ```
1403
+ */
1404
+ function collectReferencedSchemaNames(node, out = /* @__PURE__ */ new Set()) {
1405
+ if (!node) return out;
1406
+ for (const name of collectSchemaRefs(node)) out.add(name);
1407
+ return out;
2110
1408
  }
2111
- //#endregion
2112
- //#region src/transformers.ts
2113
1409
  /**
2114
- * Replaces a discriminator property's schema with a string enum of allowed values.
1410
+ * Collects the de-duplicated target names of every pointer-carrying ref in a node's subtree, in
1411
+ * first-occurrence order. The walk is memoized by node identity, so the subtree is scanned once and
1412
+ * `resolver.imports` reads the same result across the ts, zod, and faker plugins instead of
1413
+ * re-scanning the same schema per plugin.
2115
1414
  *
2116
- * If `node` is not an object schema, or if the property does not exist, the input
2117
- * node is returned as-is.
1415
+ * Only refs that carry a `$ref` pointer count, so a synthesized ref pointing at a sibling in the
1416
+ * same file (a union member created by name) is left out. That leaves exactly the set
1417
+ * `resolver.imports` emits. This is the ordered, import-facing counterpart to
1418
+ * {@link collectReferencedSchemaNames}, which returns an unordered set for graph analysis.
2118
1419
  *
2119
1420
  * @example
2120
1421
  * ```ts
2121
- * const schema = createSchema({
2122
- * type: 'object',
2123
- * properties: [createProperty({ name: 'type', required: true, schema: createSchema({ type: 'string' }) })],
2124
- * })
2125
- * const result = setDiscriminatorEnum({ node: schema, propertyName: 'type', values: ['dog', 'cat'] })
1422
+ * collectImportedRefNames(petSchema)
1423
+ * // ['Category', 'Tag']
2126
1424
  * ```
2127
1425
  */
2128
- function setDiscriminatorEnum({ node, propertyName, values, enumName }) {
2129
- const objectNode = narrowSchema(node, "object");
2130
- if (!objectNode?.properties?.length) return node;
2131
- if (!objectNode.properties.some((prop) => prop.name === propertyName)) return node;
2132
- return createSchema({
2133
- ...objectNode,
2134
- properties: objectNode.properties.map((prop) => {
2135
- if (prop.name !== propertyName) return prop;
2136
- return createProperty({
2137
- ...prop,
2138
- schema: createSchema({
2139
- type: "enum",
2140
- primitive: "string",
2141
- enumValues: values,
2142
- name: enumName,
2143
- readOnly: prop.schema.readOnly,
2144
- writeOnly: prop.schema.writeOnly
2145
- })
2146
- });
2147
- })
2148
- });
1426
+ const collectImportedRefNames = memoize(/* @__PURE__ */ new WeakMap(), (node) => {
1427
+ const seen = /* @__PURE__ */ new Set();
1428
+ const names = [];
1429
+ collectSync(node, { schema(child) {
1430
+ if (child.type !== "ref" || !child.ref) return;
1431
+ const name = resolveRefName(child);
1432
+ if (name && !seen.has(name)) {
1433
+ seen.add(name);
1434
+ names.push(name);
1435
+ }
1436
+ } });
1437
+ return names;
1438
+ });
1439
+ function computeUsedSchemaNames(operations, schemas) {
1440
+ const schemaMap = /* @__PURE__ */ new Map();
1441
+ for (const schema of schemas) if (schema.name) schemaMap.set(schema.name, schema);
1442
+ const result = /* @__PURE__ */ new Set();
1443
+ function visitSchema(schema) {
1444
+ const directRefs = collectReferencedSchemaNames(schema);
1445
+ for (const name of directRefs) if (!result.has(name)) {
1446
+ result.add(name);
1447
+ const namedSchema = schemaMap.get(name);
1448
+ if (namedSchema) visitSchema(namedSchema);
1449
+ }
1450
+ }
1451
+ for (const op of operations) for (const schema of collect(op, {
1452
+ depth: "shallow",
1453
+ schema: (node) => node
1454
+ })) visitSchema(schema);
1455
+ return result;
1456
+ }
1457
+ /**
1458
+ * Collects the names of all top-level schemas transitively used by a set of operations.
1459
+ *
1460
+ * An operation uses a schema when its parameters, request body, or responses reference it, directly
1461
+ * or through other named schemas. Once a name is added to the result it is not revisited, so
1462
+ * reference cycles terminate.
1463
+ *
1464
+ * Pair it with `include` filters so schemas reachable only from excluded operations stay ungenerated.
1465
+ *
1466
+ * @example Only generate schemas referenced by included operations
1467
+ * ```ts
1468
+ * const includedOps = operations.filter((op) => resolver.default.options(op, { options, include }) !== null)
1469
+ * const allowed = collectUsedSchemaNames(includedOps, schemas)
1470
+ *
1471
+ * for (const schema of schemas) {
1472
+ * if (schema.name && !allowed.has(schema.name)) continue
1473
+ * // generate schema
1474
+ * }
1475
+ * ```
1476
+ */
1477
+ function collectUsedSchemaNames(operations, schemas) {
1478
+ return computeUsedSchemaNames(operations, schemas);
2149
1479
  }
1480
+ const EMPTY_CIRCULAR_SET = /* @__PURE__ */ new Set();
2150
1481
  /**
2151
- * Merges adjacent anonymous object members into a single anonymous object member.
1482
+ * Finds every schema that takes part in a circular dependency chain in a schema dependency graph
1483
+ * that maps each schema name to the names it references directly.
1484
+ *
1485
+ * Use this when the graph was already collected during another pass (e.g. the adapter's convert
1486
+ * walk), so the schema nodes are not swept a second time. `findCircularSchemas` builds the graph
1487
+ * from schema nodes and delegates here.
2152
1488
  *
2153
1489
  * @example
2154
1490
  * ```ts
2155
- * const merged = mergeAdjacentObjects([
2156
- * createSchema({ type: 'object', properties: [createProperty({ name: 'a', schema: createSchema({ type: 'string' }) })] }),
2157
- * createSchema({ type: 'object', properties: [createProperty({ name: 'b', schema: createSchema({ type: 'number' }) })] }),
1491
+ * const graph = new Map([
1492
+ * ['Pet', new Set(['Category'])],
1493
+ * ['Category', new Set(['Pet'])],
2158
1494
  * ])
1495
+ * findCircularSchemasFromGraph(graph) // Set { 'Pet', 'Category' }
2159
1496
  * ```
2160
1497
  */
2161
- function mergeAdjacentObjects(members) {
2162
- return members.reduce((acc, member) => {
2163
- const objectMember = narrowSchema(member, "object");
2164
- if (objectMember && !objectMember.name) {
2165
- const previous = acc.at(-1);
2166
- const previousObject = previous ? narrowSchema(previous, "object") : void 0;
2167
- if (previousObject && !previousObject.name) {
2168
- acc[acc.length - 1] = createSchema({
2169
- ...previousObject,
2170
- properties: [...previousObject.properties ?? [], ...objectMember.properties ?? []]
2171
- });
2172
- return acc;
1498
+ function findCircularSchemasFromGraph(graph) {
1499
+ const circular = /* @__PURE__ */ new Set();
1500
+ for (const start of graph.keys()) {
1501
+ const visited = /* @__PURE__ */ new Set();
1502
+ const stack = [...graph.get(start) ?? []];
1503
+ while (stack.length > 0) {
1504
+ const node = stack.pop();
1505
+ if (node === start) {
1506
+ circular.add(start);
1507
+ break;
2173
1508
  }
1509
+ if (visited.has(node)) continue;
1510
+ visited.add(node);
1511
+ const next = graph.get(node);
1512
+ if (next) for (const r of next) stack.push(r);
2174
1513
  }
2175
- acc.push(member);
2176
- return acc;
2177
- }, []);
1514
+ }
1515
+ return circular;
2178
1516
  }
1517
+ const findCircularSchemasMemo = memoize(/* @__PURE__ */ new WeakMap(), (schemas) => {
1518
+ const graph = /* @__PURE__ */ new Map();
1519
+ for (const schema of schemas) {
1520
+ if (!schema.name) continue;
1521
+ graph.set(schema.name, collectReferencedSchemaNames(schema));
1522
+ }
1523
+ return findCircularSchemasFromGraph(graph);
1524
+ });
2179
1525
  /**
2180
- * Removes enum members that are covered by broader scalar primitives in the same union.
1526
+ * Finds every schema that takes part in a circular dependency chain, including direct self-loops.
1527
+ *
1528
+ * Wrap the returned schema positions in a deferred construct (a lazy getter or `z.lazy(() => …)`) so
1529
+ * the generated code does not recurse forever. Refs are followed by name only, so the walk stays
1530
+ * linear in the size of the schema graph.
1531
+ *
1532
+ * @note Call this once on the full graph, then check individual schemas with `containsCircularRef()`.
1533
+ */
1534
+ function findCircularSchemas(schemas) {
1535
+ if (schemas.length === 0) return EMPTY_CIRCULAR_SET;
1536
+ return findCircularSchemasMemo(schemas);
1537
+ }
1538
+ //#endregion
1539
+ //#region src/factory.ts
1540
+ var factory_exports = /* @__PURE__ */ __exportAll({
1541
+ createArrowFunction: () => createArrowFunction,
1542
+ createBreak: () => createBreak,
1543
+ createConst: () => createConst,
1544
+ createContent: () => createContent,
1545
+ createExport: () => createExport,
1546
+ createFile: () => createFile,
1547
+ createFunction: () => createFunction,
1548
+ createImport: () => createImport,
1549
+ createInput: () => createInput,
1550
+ createJsx: () => createJsx,
1551
+ createOperation: () => createOperation,
1552
+ createOutput: () => createOutput,
1553
+ createParameter: () => createParameter,
1554
+ createProperty: () => createProperty,
1555
+ createRequestBody: () => createRequestBody,
1556
+ createResponse: () => createResponse,
1557
+ createSchema: () => createSchema,
1558
+ createSource: () => createSource,
1559
+ createText: () => createText,
1560
+ createType: () => createType,
1561
+ update: () => update
1562
+ });
1563
+ /**
1564
+ * Identity-preserving node update: returns `node` unchanged when every field in
1565
+ * `changes` already equals (by reference) the current value, otherwise a new node
1566
+ * with the changes applied.
1567
+ *
1568
+ * Mirrors the TypeScript compiler's `factory.updateX` contract. Pair it with the
1569
+ * structural sharing in {@link transform} so a no-op rewrite does not allocate and
1570
+ * downstream passes can detect "nothing changed" by identity. Comparison is shallow,
1571
+ * so a structurally equal but newly allocated array or object counts as a change.
2181
1572
  *
2182
1573
  * @example
2183
1574
  * ```ts
2184
- * const simplified = simplifyUnion([
2185
- * createSchema({ type: 'enum', primitive: 'string', enumValues: ['active'] }),
2186
- * createSchema({ type: 'string' }),
2187
- * ])
2188
- * // keeps only string member
1575
+ * update(node, { name: node.name }) // -> same `node` reference
1576
+ * update(node, { name: 'renamed' }) // -> new node, `name` replaced
2189
1577
  * ```
2190
1578
  */
2191
- function simplifyUnion(members) {
2192
- const scalarPrimitives = new Set(members.filter((member) => isScalarPrimitive(member.type)).map((m) => m.type));
2193
- if (!scalarPrimitives.size) return members;
2194
- return members.filter((member) => {
2195
- const enumNode = narrowSchema(member, "enum");
2196
- if (!enumNode) return true;
2197
- const primitive = enumNode.primitive;
2198
- if (!primitive) return true;
2199
- if ((enumNode.namedEnumValues?.length ?? enumNode.enumValues?.length ?? 0) <= 1) return true;
2200
- if (scalarPrimitives.has(primitive)) return false;
2201
- if ((primitive === "integer" || primitive === "number") && (scalarPrimitives.has("integer") || scalarPrimitives.has("number"))) return false;
2202
- return true;
2203
- });
2204
- }
2205
- function setEnumName(propNode, parentName, propName, enumSuffix) {
2206
- const enumNode = narrowSchema(propNode, "enum");
2207
- if (enumNode?.primitive === "boolean") return {
2208
- ...propNode,
2209
- name: void 0
1579
+ function update(node, changes) {
1580
+ for (const key in changes) if (changes[key] !== node[key]) return {
1581
+ ...node,
1582
+ ...changes
2210
1583
  };
2211
- if (enumNode) return {
2212
- ...propNode,
2213
- name: enumPropName(parentName, propName, enumSuffix)
2214
- };
2215
- return propNode;
1584
+ return node;
2216
1585
  }
2217
1586
  //#endregion
2218
- exports.caseParams = caseParams;
2219
- exports.childName = childName;
1587
+ //#region src/exports.ts
1588
+ var exports_exports = /* @__PURE__ */ __exportAll({
1589
+ applyMacros: () => applyMacros,
1590
+ arrowFunctionDef: () => arrowFunctionDef,
1591
+ breakDef: () => breakDef,
1592
+ collect: () => collect,
1593
+ collectImportedRefNames: () => collectImportedRefNames,
1594
+ collectSync: () => collectSync,
1595
+ collectUsedSchemaNames: () => collectUsedSchemaNames,
1596
+ composeMacros: () => composeMacros,
1597
+ constDef: () => constDef,
1598
+ contentDef: () => contentDef,
1599
+ createPrinter: () => createPrinter,
1600
+ defineMacro: () => defineMacro,
1601
+ defineNode: () => defineNode,
1602
+ exportDef: () => exportDef,
1603
+ extractStringsFromNodes: () => extractStringsFromNodes,
1604
+ factory: () => factory_exports,
1605
+ fileDef: () => fileDef,
1606
+ findCircularSchemas: () => findCircularSchemas,
1607
+ findCircularSchemasFromGraph: () => findCircularSchemasFromGraph,
1608
+ functionDef: () => functionDef,
1609
+ importDef: () => importDef,
1610
+ inputDef: () => inputDef,
1611
+ isHttpOperationNode: () => isHttpOperationNode,
1612
+ jsxDef: () => jsxDef,
1613
+ narrowSchema: () => narrowSchema,
1614
+ nodeDefs: () => nodeDefs,
1615
+ operationDef: () => operationDef,
1616
+ optionality: () => optionality,
1617
+ outputDef: () => outputDef,
1618
+ parameterDef: () => parameterDef,
1619
+ propertyDef: () => propertyDef,
1620
+ requestBodyDef: () => requestBodyDef,
1621
+ resolveRefName: () => resolveRefName,
1622
+ responseDef: () => responseDef,
1623
+ schemaDef: () => schemaDef,
1624
+ schemaTypes: () => schemaTypes,
1625
+ sourceDef: () => sourceDef,
1626
+ textDef: () => textDef,
1627
+ transform: () => transform,
1628
+ typeDef: () => typeDef
1629
+ });
1630
+ //#endregion
1631
+ exports.applyMacros = applyMacros;
1632
+ exports.arrowFunctionDef = arrowFunctionDef;
1633
+ Object.defineProperty(exports, "ast", {
1634
+ enumerable: true,
1635
+ get: function() {
1636
+ return exports_exports;
1637
+ }
1638
+ });
1639
+ exports.breakDef = breakDef;
2220
1640
  exports.collect = collect;
2221
- exports.collectImports = collectImports;
2222
- exports.collectReferencedSchemaNames = collectReferencedSchemaNames;
1641
+ exports.collectImportedRefNames = collectImportedRefNames;
1642
+ exports.collectSync = collectSync;
2223
1643
  exports.collectUsedSchemaNames = collectUsedSchemaNames;
2224
- exports.containsCircularRef = containsCircularRef;
2225
- exports.createArrowFunction = createArrowFunction;
2226
- exports.createBreak = createBreak;
2227
- exports.createConst = createConst;
2228
- exports.createDiscriminantNode = createDiscriminantNode;
2229
- exports.createExport = createExport;
2230
- exports.createFile = createFile;
2231
- exports.createFunction = createFunction;
2232
- exports.createFunctionParameter = createFunctionParameter;
2233
- exports.createFunctionParameters = createFunctionParameters;
2234
- exports.createImport = createImport;
2235
- exports.createInput = createInput;
2236
- exports.createJsx = createJsx;
2237
- exports.createOperation = createOperation;
2238
- exports.createOperationParams = createOperationParams;
2239
- exports.createOutput = createOutput;
2240
- exports.createParameter = createParameter;
2241
- exports.createParameterGroup = createParameterGroup;
2242
- exports.createParamsType = createParamsType;
2243
- exports.createPrinterFactory = createPrinterFactory;
2244
- exports.createProperty = createProperty;
2245
- exports.createResponse = createResponse;
2246
- exports.createSchema = createSchema;
2247
- exports.createSource = createSource;
2248
- exports.createText = createText;
2249
- exports.createType = createType;
2250
- exports.definePrinter = definePrinter;
2251
- exports.enumPropName = enumPropName;
2252
- exports.extractRefName = extractRefName;
1644
+ exports.composeMacros = composeMacros;
1645
+ exports.constDef = constDef;
1646
+ exports.contentDef = contentDef;
1647
+ exports.createPrinter = createPrinter;
1648
+ exports.defineMacro = defineMacro;
1649
+ exports.defineNode = defineNode;
1650
+ exports.exportDef = exportDef;
2253
1651
  exports.extractStringsFromNodes = extractStringsFromNodes;
1652
+ Object.defineProperty(exports, "factory", {
1653
+ enumerable: true,
1654
+ get: function() {
1655
+ return factory_exports;
1656
+ }
1657
+ });
1658
+ exports.fileDef = fileDef;
2254
1659
  exports.findCircularSchemas = findCircularSchemas;
2255
- exports.findDiscriminator = findDiscriminator;
2256
- exports.httpMethods = httpMethods;
2257
- exports.isInputNode = isInputNode;
2258
- exports.isOperationNode = isOperationNode;
2259
- exports.isOutputNode = isOutputNode;
2260
- exports.isScalarPrimitive = isScalarPrimitive;
2261
- exports.isSchemaNode = isSchemaNode;
2262
- exports.isStringType = isStringType;
2263
- exports.mediaTypes = mediaTypes;
2264
- exports.mergeAdjacentObjects = mergeAdjacentObjects;
1660
+ exports.findCircularSchemasFromGraph = findCircularSchemasFromGraph;
1661
+ exports.functionDef = functionDef;
1662
+ exports.importDef = importDef;
1663
+ exports.inputDef = inputDef;
1664
+ exports.isHttpOperationNode = isHttpOperationNode;
1665
+ exports.jsxDef = jsxDef;
2265
1666
  exports.narrowSchema = narrowSchema;
2266
- exports.nodeKinds = nodeKinds;
1667
+ exports.nodeDefs = nodeDefs;
1668
+ exports.operationDef = operationDef;
1669
+ exports.optionality = optionality;
1670
+ exports.outputDef = outputDef;
1671
+ exports.parameterDef = parameterDef;
1672
+ exports.propertyDef = propertyDef;
1673
+ exports.requestBodyDef = requestBodyDef;
2267
1674
  exports.resolveRefName = resolveRefName;
1675
+ exports.responseDef = responseDef;
1676
+ exports.schemaDef = schemaDef;
2268
1677
  exports.schemaTypes = schemaTypes;
2269
- exports.setDiscriminatorEnum = setDiscriminatorEnum;
2270
- exports.setEnumName = setEnumName;
2271
- exports.simplifyUnion = simplifyUnion;
2272
- exports.syncOptionality = syncOptionality;
2273
- exports.syncSchemaRef = syncSchemaRef;
1678
+ exports.sourceDef = sourceDef;
1679
+ exports.textDef = textDef;
2274
1680
  exports.transform = transform;
2275
- exports.walk = walk;
1681
+ exports.typeDef = typeDef;
2276
1682
 
2277
1683
  //# sourceMappingURL=index.cjs.map