@kubb/ast 5.18.5 → 5.18.7

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
@@ -2,18 +2,22 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  // \0rolldown/runtime.js
3
3
  var __create = Object.create;
4
4
  var __defProp = Object.defineProperty;
5
+ var __name = (target, value) => __defProp(target, "name", {
6
+ value,
7
+ configurable: true
8
+ });
5
9
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
10
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
11
  var __getProtoOf = Object.getPrototypeOf;
8
12
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
- var __exportAll = (all, no_symbols) => {
10
- let target = {};
13
+ var __exportAll = (all, flags) => {
14
+ let target = Object.setPrototypeOf({}, null);
11
15
  for (var name in all) __defProp(target, name, {
12
16
  get: all[name],
13
17
  enumerable: true
14
18
  });
15
- if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
16
- return target;
19
+ if (!(flags & 1)) __defProp(target, Symbol.toStringTag, { value: "Module" });
20
+ return flags & 2 ? target : Object.preventExtensions(target);
17
21
  };
18
22
  var __copyProps = (to, from, except, desc) => {
19
23
  if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
@@ -29,14 +33,29 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
29
33
  value: mod,
30
34
  enumerable: true
31
35
  }) : target, mod));
32
- let node_crypto = require("node:crypto");
33
36
  let node_path = require("node:path");
34
37
  node_path = __toESM(node_path, 1);
38
+ let node_crypto = require("node:crypto");
39
+ // ../../internals/utils/src/promise.ts
40
+ /**
41
+ * Returns the value cached under `key` in `store`, computing and storing it on the first call.
42
+ * Pass a `WeakMap` for object keys, so an entry goes away with its key, or a `Map` for primitive
43
+ * keys. A cached `null` or `undefined` counts as a hit, so `compute` runs once per key.
44
+ *
45
+ * @example
46
+ * `const refs = memo(refCache, node, () => collectRefs(node))`
47
+ */
48
+ function memo(store, key, compute) {
49
+ if (store.has(key)) return store.get(key);
50
+ const value = compute();
51
+ store.set(key, value);
52
+ return value;
53
+ }
35
54
  // src/constants.ts
36
55
  /**
37
56
  * Node kind discriminators used by every AST node (for example `node.kind === nodeKinds.text`).
38
57
  */
39
- const nodeKinds = {
58
+ const nodeKinds$1 = {
40
59
  input: "Input",
41
60
  operation: "Operation",
42
61
  schema: "Schema",
@@ -62,7 +81,7 @@ const nodeKinds = {
62
81
  *
63
82
  * Each value is a stable discriminator across the AST (for example `schema.type === schemaTypes.object`).
64
83
  */
65
- const schemaTypes = {
84
+ const schemaTypes$1 = {
66
85
  /**
67
86
  * Text value.
68
87
  */
@@ -170,6 +189,19 @@ const schemaTypes = {
170
189
  */
171
190
  never: "never"
172
191
  };
192
+ // src/optionality.ts
193
+ /**
194
+ * Generic JSON Schema optionality: a non-required field is optional, and a
195
+ * non-required nullable field is nullish.
196
+ */
197
+ function applyOptionality(schema, required) {
198
+ const nullable = schema.nullable ?? false;
199
+ return {
200
+ ...schema,
201
+ optional: !required && !nullable ? true : void 0,
202
+ nullish: !required && nullable ? true : void 0
203
+ };
204
+ }
173
205
  // src/guards.ts
174
206
  /**
175
207
  * Narrows a `SchemaNode` to the variant that matches `type`.
@@ -180,22 +212,204 @@ const schemaTypes = {
180
212
  * const stringNode = narrowSchema(schema, 'string') // StringSchemaNode | null
181
213
  * ```
182
214
  */
183
- function narrowSchema(node, type) {
215
+ function narrowSchema$1(node, type) {
184
216
  return node?.type === type ? node : null;
185
217
  }
218
+ __name(narrowSchema$1, "narrowSchema");
186
219
  /**
187
220
  * Narrows an `OperationNode` to an `HttpOperationNode` so `method` and `path` are present.
188
221
  *
189
222
  * @example
190
223
  * ```ts
191
- * if (isHttpOperationNode(node)) {
224
+ * if (ast.http.isOperation(node)) {
192
225
  * console.log(node.method, node.path)
193
226
  * }
194
227
  * ```
195
228
  */
196
- function isHttpOperationNode(node) {
229
+ function isHttpOperationNode$1(node) {
197
230
  return node.protocol === "http" || node.method !== void 0 && node.path !== void 0;
198
231
  }
232
+ __name(isHttpOperationNode$1, "isHttpOperationNode");
233
+ // src/mediaType/index.ts
234
+ var mediaType_exports = /* @__PURE__ */ __exportAll({
235
+ getBase: () => getBase,
236
+ isJson: () => isJson,
237
+ isStream: () => isStream
238
+ });
239
+ const jsonMediaTypes = /* @__PURE__ */ new Set(["application/json", "text/json"]);
240
+ const streamMediaTypes = /* @__PURE__ */ new Set([
241
+ "application/jsonl",
242
+ "application/x-ndjson",
243
+ "application/json-seq",
244
+ "text/event-stream"
245
+ ]);
246
+ /** The media type without its parameters, in lower case: `Text/Plain; charset=utf-8` gives `text/plain`. */
247
+ function getBase(mediaType) {
248
+ return (typeof mediaType === "string" ? mediaType : mediaType.contentType).split(";")[0].trim().toLowerCase();
249
+ }
250
+ /** Whether the media type is `application/json`, `text/json`, or has a `+json` suffix. */
251
+ function isJson(mediaType) {
252
+ const baseType = getBase(mediaType);
253
+ return jsonMediaTypes.has(baseType) || baseType.endsWith("+json");
254
+ }
255
+ /** Whether the media type is a stream of items: SSE, JSON Lines, or a `+json-seq` sequence. */
256
+ function isStream(mediaType) {
257
+ const baseType = getBase(mediaType);
258
+ return streamMediaTypes.has(baseType) || baseType.startsWith("application/") && baseType.endsWith("+json-seq");
259
+ }
260
+ // src/utils/schemaProperties.ts
261
+ /**
262
+ * Finds every matching property exposed by an object, resolved reference, or intersection.
263
+ *
264
+ * References are followed through their parsed `schema`. Circular references terminate without
265
+ * revisiting a node. Unions are not traversed because a property on one branch does not describe
266
+ * every branch.
267
+ *
268
+ * @example Faker discriminator narrowing
269
+ * ```ts
270
+ * const values = resolveSchemaProperties({ node: member, propertyName: union.discriminatorPropertyName }).flatMap(({ schema }) =>
271
+ * getSchemaLiteralValues(schema),
272
+ * )
273
+ * const typeName = `Extract<Pet, { type: ${values.map(JSON.stringify).join(' | ')} }>`
274
+ * ```
275
+ *
276
+ * @example Zod discriminated unions
277
+ * ```ts
278
+ * const canDiscriminate = union.members?.every(
279
+ * (member) => resolveSchemaProperties({ node: member, propertyName: 'type' }).length > 0,
280
+ * )
281
+ * const expression = canDiscriminate ? `z.discriminatedUnion('type', members)` : `z.union(members)`
282
+ * ```
283
+ *
284
+ * @example TypeScript property lookup
285
+ * ```ts
286
+ * const properties = resolveSchemaProperties({ node: schema, propertyName: 'status' })
287
+ * const statusSchemas = properties.map((property) => print(property.schema))
288
+ * ```
289
+ */
290
+ function resolveSchemaProperties$1({ node, propertyName }) {
291
+ const properties = [];
292
+ walkSchema(node, "intersection", (current) => {
293
+ if (current.type !== "object") return;
294
+ const property = current.properties.find((candidate) => candidate.name === propertyName);
295
+ if (property) properties.push(property);
296
+ });
297
+ return properties;
298
+ }
299
+ __name(resolveSchemaProperties$1, "resolveSchemaProperties");
300
+ function walkSchema(node, through, visit) {
301
+ const visited = /* @__PURE__ */ new WeakSet();
302
+ function walk(current) {
303
+ if (!current || visited.has(current)) return;
304
+ visited.add(current);
305
+ visit(current);
306
+ if (current.type === "ref") return walk(current.schema);
307
+ if (current.type === through) for (const member of current.members ?? []) walk(member);
308
+ }
309
+ walk(node);
310
+ }
311
+ /**
312
+ * Reads the unique literal values represented by an enum, resolved reference, or union.
313
+ *
314
+ * Named enum values take precedence over simple enum values, matching how renderers consume
315
+ * `EnumSchemaNode`.
316
+ *
317
+ * @example
318
+ * ```ts
319
+ * const values = getSchemaLiteralValues(enumNode)
320
+ * // ['cat', 'dog']
321
+ * ```
322
+ */
323
+ function getSchemaLiteralValues$1(node) {
324
+ const values = /* @__PURE__ */ new Set();
325
+ walkSchema(node, "union", (current) => {
326
+ if (current.type !== "enum") return;
327
+ for (const { value } of current.namedEnumValues ?? current.enumValues?.map((value) => ({ value })) ?? []) values.add(value);
328
+ });
329
+ return [...values];
330
+ }
331
+ __name(getSchemaLiteralValues$1, "getSchemaLiteralValues");
332
+ function collectPropertyNames(node) {
333
+ const names = /* @__PURE__ */ new Set();
334
+ walkSchema(node, "intersection", (current) => {
335
+ if (current.type === "object") for (const property of current.properties) names.add(property.name);
336
+ });
337
+ return names;
338
+ }
339
+ /**
340
+ * Infers an implicit discriminator: a property every member exposes with a distinct single
341
+ * literal value, the same shape a declared OpenAPI `discriminator` already narrows on.
342
+ */
343
+ function inferDiscriminatorPropertyName$1(members) {
344
+ if (members.length < 2) return void 0;
345
+ let candidates;
346
+ for (const member of members) {
347
+ const names = collectPropertyNames(member);
348
+ candidates = candidates ? new Set([...candidates].filter((name) => names.has(name))) : names;
349
+ }
350
+ for (const name of candidates ?? []) {
351
+ const values = members.map((member) => {
352
+ const literals = new Set(resolveSchemaProperties$1({
353
+ node: member,
354
+ propertyName: name
355
+ }).flatMap((property) => getSchemaLiteralValues$1(property.schema)));
356
+ return literals.size === 1 ? [...literals][0] : void 0;
357
+ });
358
+ if (values.every((value) => value !== void 0) && new Set(values).size === members.length) return name;
359
+ }
360
+ }
361
+ __name(inferDiscriminatorPropertyName$1, "inferDiscriminatorPropertyName");
362
+ // src/schema/index.ts
363
+ var schema_exports = /* @__PURE__ */ __exportAll({
364
+ applyOptionality: () => applyOptionality,
365
+ find: () => findSchema$1,
366
+ findProperties: () => resolveSchemaProperties$1,
367
+ getEmbeddedJson: () => getEmbeddedJson,
368
+ getLiterals: () => getSchemaLiteralValues$1,
369
+ getNumberBounds: () => getNumberBounds,
370
+ inferDiscriminator: () => inferDiscriminatorPropertyName$1,
371
+ isString: () => isString,
372
+ narrow: () => narrowSchema$1,
373
+ types: () => schemaTypes$1
374
+ });
375
+ const plainStringTypes = /* @__PURE__ */ new Set([
376
+ "string",
377
+ "uuid",
378
+ "email",
379
+ "url",
380
+ "datetime"
381
+ ]);
382
+ /** The schema of the JSON document a string holds (`contentMediaType` with `contentSchema`), else `undefined`. */
383
+ function getEmbeddedJson({ content }) {
384
+ if (!content?.schema || content.encoding || !content.mediaType) return void 0;
385
+ return isJson(content.mediaType) ? content.schema : void 0;
386
+ }
387
+ /** Whether a schema prints as a plain `string`: string-like types, and `date` or `time` with a `'string'` representation. */
388
+ function isString(node) {
389
+ if (plainStringTypes.has(node.type)) return true;
390
+ const temporal = narrowSchema$1(node, "date") ?? narrowSchema$1(node, "time");
391
+ return temporal ? temporal.representation !== "date" : false;
392
+ }
393
+ /**
394
+ * The tightest inclusive bounds of a number schema. An exclusive bound moves to the next point of `step` (`1` for an
395
+ * integer), an integer bound rounds inward, and an integer bound past `Number.MAX_SAFE_INTEGER` is left out, since it
396
+ * prints as a rounded or invalid literal.
397
+ */
398
+ function getNumberBounds({ min, max, exclusive, integer = false, step }) {
399
+ const grid = step ?? (integer ? 1 : void 0);
400
+ const above = (bound) => grid ? (Math.floor(bound / grid) + 1) * grid : bound;
401
+ const below = (bound) => grid ? (Math.ceil(bound / grid) - 1) * grid : bound;
402
+ const tightest = (pick, bounds) => {
403
+ const usable = bounds.filter((bound) => bound !== void 0 && (!integer || Number.isSafeInteger(bound)));
404
+ return usable.length ? pick(...usable) : void 0;
405
+ };
406
+ const lower = tightest(Math.max, [min !== void 0 && integer ? Math.ceil(min) : min, exclusive?.min === void 0 ? void 0 : above(exclusive.min)]);
407
+ const upper = tightest(Math.min, [max !== void 0 && integer ? Math.floor(max) : max, exclusive?.max === void 0 ? void 0 : below(exclusive.max)]);
408
+ return {
409
+ ...lower !== void 0 ? { min: lower } : {},
410
+ ...upper !== void 0 ? { max: upper } : {}
411
+ };
412
+ }
199
413
  // src/visitor.ts
200
414
  /**
201
415
  * Child node fields per node kind, in traversal order (Babel's `VISITOR_KEYS`).
@@ -273,7 +487,7 @@ function isNodeRecord(value) {
273
487
  * ```
274
488
  */
275
489
  function getChildren(node, recurse) {
276
- if (node.kind === nodeKinds.schema && !recurse) return [];
490
+ if (node.kind === nodeKinds$1.schema && !recurse) return [];
277
491
  const children = [];
278
492
  visitChildNodes(node, (child) => {
279
493
  children.push(child);
@@ -281,12 +495,18 @@ function getChildren(node, recurse) {
281
495
  });
282
496
  return children;
283
497
  }
498
+ function isNeverPrinted({ node, key }) {
499
+ if (node.kind !== nodeKinds$1.schema) return false;
500
+ if (key === "contains" && (node.type === "array" || node.type === "tuple")) return node.contains?.min === 0 && node.contains.max === void 0;
501
+ if (key === "content" && node.type === "string") return !getEmbeddedJson(node);
502
+ return false;
503
+ }
284
504
  /**
285
505
  * Calls `visit` for each direct child node and stops at the first call that returns `true`.
286
- * With `checks: true`, a schema's {@link schemaCheckKeys} fields are walked too.
506
+ * With `checks: true`, a schema's {@link schemaCheckKeys} fields are walked too, except checks that cannot fail.
287
507
  */
288
508
  function visitChildNodes(node, visit, { checks = false } = {}) {
289
- const keys = checks && node.kind === nodeKinds.schema ? [...VISITOR_KEYS[node.kind] ?? [], ...schemaCheckKeys] : VISITOR_KEYS[node.kind];
509
+ const keys = checks && node.kind === nodeKinds$1.schema ? [...VISITOR_KEYS[node.kind] ?? [], ...schemaCheckKeys] : VISITOR_KEYS[node.kind];
290
510
  if (!keys) return false;
291
511
  const record = node;
292
512
  const seen = /* @__PURE__ */ new Set();
@@ -300,6 +520,10 @@ function visitChildNodes(node, visit, { checks = false } = {}) {
300
520
  return false;
301
521
  };
302
522
  for (const key of keys) {
523
+ if (checks && isNeverPrinted({
524
+ node,
525
+ key
526
+ })) continue;
303
527
  const value = record[key];
304
528
  if (Array.isArray(value)) {
305
529
  for (const item of value) if (isNode(item) ? visitOnce(item) : isNodeRecord(item) && visitRecord(item)) return true;
@@ -323,10 +547,10 @@ function visitChildNodes(node, visit, { checks = false } = {}) {
323
547
  * })
324
548
  * ```
325
549
  */
326
- function findSchema(node, { predicate, followRefs = false }) {
550
+ function findSchema$1(node, { predicate, followRefs = false }) {
327
551
  const seen = followRefs ? /* @__PURE__ */ new WeakSet() : void 0;
328
552
  const walk = (current) => {
329
- if (current.kind === nodeKinds.schema) {
553
+ if (current.kind === nodeKinds$1.schema) {
330
554
  if (predicate(current)) return current;
331
555
  if (seen) {
332
556
  if (seen.has(current)) return void 0;
@@ -346,6 +570,7 @@ function findSchema(node, { predicate, followRefs = false }) {
346
570
  };
347
571
  return walk(node);
348
572
  }
573
+ __name(findSchema$1, "findSchema");
349
574
  /**
350
575
  * Runs the visitor callback that matches `node.kind` with the traversal
351
576
  * context. The result is a replacement node, a collected value, or `undefined`
@@ -385,10 +610,11 @@ function applyVisitor(node, visitor, parent) {
385
610
  * })
386
611
  * ```
387
612
  */
388
- function transform(node, options) {
613
+ function transform$1(node, options) {
389
614
  const { depth, parent, ...visitor } = options;
390
615
  return transformNode(node, visitor, (depth ?? "deep") === "deep", parent);
391
616
  }
617
+ __name(transform$1, "transform");
392
618
  /**
393
619
  * Visits a single node, then immutably rebuilds its children. Returns the original
394
620
  * reference when neither the visitor nor the child rebuild changed anything, so callers
@@ -402,7 +628,7 @@ function transformNode(node, visitor, recurse, parent) {
402
628
  * (e.g. `additionalProperties: true`) intact.
403
629
  */
404
630
  function transformChildren(node, visitor, recurse) {
405
- const keys = node.kind === nodeKinds.schema && !recurse ? void 0 : VISITOR_KEYS[node.kind];
631
+ const keys = node.kind === nodeKinds$1.schema && !recurse ? void 0 : VISITOR_KEYS[node.kind];
406
632
  if (!keys) return node;
407
633
  const record = node;
408
634
  let updates;
@@ -449,161 +675,439 @@ function transformChildren(node, visitor, recurse) {
449
675
  };
450
676
  }
451
677
  /**
452
- * Lazy depth-first collection pass. Yields every non-null value returned by
453
- * the visitor callbacks. Use `collectSync` for the eager array form.
454
- *
455
- * @example Collect every operationId
456
- * ```ts
457
- * const ids: string[] = []
458
- * for (const id of collect<string>(root, {
459
- * operation(node) {
460
- * return node.operationId
461
- * },
462
- * })) {
463
- * ids.push(id)
464
- * }
465
- * ```
466
- */
467
- function* collect(node, options) {
468
- const { depth, parent, ...visitor } = options;
469
- yield* collectNode(node, visitor, (depth ?? "deep") === "deep", parent);
470
- }
471
- function* collectNode(node, visitor, recurse, parent) {
472
- const v = applyVisitor(node, visitor, parent);
473
- if (v != null) yield v;
474
- for (const child of getChildren(node, recurse)) yield* collectNode(child, visitor, recurse, node);
475
- }
476
- /**
477
- * Eager depth-first collection pass. Gathers every non-null value the visitor
678
+ * Depth-first collection pass. Gathers every non-null value the visitor
478
679
  * callbacks return into an array.
479
680
  *
480
681
  * @example Collect every operationId
481
682
  * ```ts
482
- * const ids = collectSync<string>(root, {
683
+ * const ids = collect<string>(root, {
483
684
  * operation(node) {
484
685
  * return node.operationId
485
686
  * },
486
687
  * })
487
688
  * ```
488
689
  */
489
- function collectSync(node, options) {
690
+ function collect$1(node, options) {
490
691
  const { depth, parent, ...visitor } = options;
491
692
  const result = [];
492
693
  collectInto(node, visitor, (depth ?? "deep") === "deep", parent, result);
493
694
  return result;
494
695
  }
696
+ __name(collect$1, "collect");
495
697
  function collectInto(node, visitor, recurse, parent, result) {
496
698
  const v = applyVisitor(node, visitor, parent);
497
699
  if (v != null) result.push(v);
498
700
  for (const child of getChildren(node, recurse)) collectInto(child, visitor, recurse, node, result);
499
701
  }
500
- // src/defineMacro.ts
702
+ // src/utils/refs.ts
501
703
  /**
502
- * Sort weight for an `enforce` hint. `pre` sorts before unmarked items and `post` after, so a plain
503
- * list keeps its authored order.
704
+ * Returns the last segment of a `$ref` pointer, for example `'Pet'` for `#/components/schemas/Pet`.
705
+ * The segment is decoded like the pointer resolver does: percent-escapes first, then `~1` and `~0`
706
+ * (RFC 6901), so `a~1b~0c` names the component `a/b~c`.
504
707
  */
505
- function enforceWeight(enforce) {
506
- if (enforce === "pre") return 0;
507
- if (enforce === "post") return 2;
508
- return 1;
708
+ function refName$1(ref) {
709
+ const cached = refNames.get(ref);
710
+ if (cached !== void 0) return cached;
711
+ const segment = ref.split("/").at(-1) ?? ref;
712
+ let decoded = segment;
713
+ try {
714
+ decoded = decodeURIComponent(segment);
715
+ } catch {}
716
+ const name = decoded.replaceAll("~1", "/").replaceAll("~0", "~");
717
+ if (refNames.size >= memoLimit) refNames.clear();
718
+ refNames.set(ref, name);
719
+ return name;
509
720
  }
721
+ __name(refName$1, "refName");
722
+ const refNames = /* @__PURE__ */ new Map();
723
+ const memoLimit = 1e5;
510
724
  /**
511
- * Compares two `enforce` hints for `Array.prototype.sort`: `pre` first, unmarked next, `post`
512
- * last. Equal hints compare as `0`, so a stable sort keeps authored order within a bucket.
725
+ * Resolves the emitted name of the schema a ref node points at. Prefers `targetName` (set when
726
+ * the referenced schema was renamed, e.g. to break a collision), then the last segment of `ref`,
727
+ * then `name`, then the nested `schema.name`.
728
+ *
729
+ * Returns `null` for non-ref nodes or when no name resolves.
513
730
  *
514
731
  * @example
515
- * `plugins.sort((a, b) => compareEnforce(a.enforce, b.enforce))`
732
+ * `resolveRefName({ kind: 'Schema', type: 'ref', ref: '#/components/schemas/Pet' }) // 'Pet'`
733
+ *
734
+ * @example Collision-renamed target
735
+ * `resolveRefName({ kind: 'Schema', type: 'ref', ref: '#/components/schemas/Order', targetName: 'OrderSchema' }) // 'OrderSchema'`
516
736
  */
517
- function compareEnforce(a, b) {
518
- return enforceWeight(a) - enforceWeight(b);
737
+ function resolveRefName$1(node) {
738
+ if (!node || node.type !== "ref") return null;
739
+ if (node.targetName) return node.targetName;
740
+ if (node.ref) return refName$1(node.ref);
741
+ return node.name ?? node.schema?.name ?? null;
519
742
  }
743
+ __name(resolveRefName$1, "resolveRefName");
520
744
  /**
521
- * Types a macro for inference and a single construction site, mirroring `defineExtension`.
522
- * Adds no runtime behavior.
745
+ * Follows a resolved ref chain to its target schema. Returns an unresolved or cyclic ref as is.
523
746
  *
524
747
  * @example
525
748
  * ```ts
526
- * const macroUntagged = defineMacro({
527
- * name: 'untagged',
528
- * operation(node) {
529
- * return node.tags?.length ? undefined : { ...node, tags: ['untagged'] }
530
- * },
531
- * })
749
+ * const target = resolveRef(refSchema)
532
750
  * ```
533
751
  */
534
- function defineMacro(macro) {
535
- return macro;
752
+ function resolveRef$1(node) {
753
+ if (node.type !== "ref" || !node.schema) return node;
754
+ const seen = /* @__PURE__ */ new WeakSet();
755
+ let current = node;
756
+ while (current.type === "ref" && current.schema) {
757
+ if (seen.has(current)) return current;
758
+ seen.add(current);
759
+ current = current.schema;
760
+ }
761
+ return current;
536
762
  }
763
+ __name(resolveRef$1, "resolveRef");
764
+ /** Fields of a ref node that describe the ref itself, so they never override the resolved schema. */
765
+ const REF_OWN_KEYS = /* @__PURE__ */ new Set([
766
+ "kind",
767
+ "type",
768
+ "name",
769
+ "ref",
770
+ "schema"
771
+ ]);
537
772
  /**
538
- * Runs every macro's callback for one node kind in order, chaining the result so each macro sees
539
- * the previous macro's output. Returns `undefined` when nothing changed, so `transform` keeps the
540
- * original reference (structural sharing).
773
+ * Merges a ref node with its resolved schema, giving the fields set at the usage site precedence.
774
+ *
775
+ * Every field on the ref except `kind`, `type`, `name`, `ref`, and `schema` overrides the same field
776
+ * of the resolved `schema` (for example `description`, `nullable`, `readOnly`, `deprecated`). Fields
777
+ * left `undefined` on the ref do not shadow the resolved schema. A ref without a resolved `schema`
778
+ * is returned as is.
779
+ *
780
+ * @example
781
+ * ```ts
782
+ * const ref = createSchema({ type: 'ref', ref: '#/components/schemas/Pet', description: 'A pet', schema: petSchema })
783
+ * mergeRefWithSchema(ref) // petSchema's fields with description 'A pet'
784
+ * ```
541
785
  */
542
- function chain({ macros, key, node, context }) {
543
- let current = node;
544
- for (const macro of macros) {
545
- const callback = macro[key];
546
- if (!callback) continue;
547
- if (macro.match && !macro.match(current)) continue;
548
- const next = callback(current, context);
549
- if (next != null) current = next;
786
+ function mergeRefWithSchema$1(node) {
787
+ if (!node.schema) return node;
788
+ const merged = { ...node.schema };
789
+ for (const key of Object.keys(node)) {
790
+ const value = node[key];
791
+ if (value !== void 0 && !REF_OWN_KEYS.has(key)) merged[key] = value;
550
792
  }
551
- return current === node ? void 0 : current;
793
+ return merged;
552
794
  }
795
+ __name(mergeRefWithSchema$1, "mergeRefWithSchema");
553
796
  /**
554
- * Folds an ordered list of macros into a single {@link Visitor} for `transform`. Macros are
555
- * stable-sorted by `enforce`, then applied sequentially per node so later macros see earlier output.
556
- */
557
- function composeMacros(macros) {
558
- const ordered = [...macros].sort((a, b) => compareEnforce(a.enforce, b.enforce));
559
- const visitor = {};
560
- for (const key of visitorKeys) {
561
- if (!ordered.some((macro) => typeof macro[key] === "function")) continue;
562
- const callback = (node, context) => chain({
563
- macros: ordered,
564
- key,
565
- node,
566
- context
567
- });
568
- visitor[key] = callback;
569
- }
570
- return visitor;
797
+ * Returns `true` when a schema is a bare ref without modifiers, metadata, or omit keys.
798
+ *
799
+ * Modifiers inherited from the resolved schema are included in the check because a ref emits the
800
+ * referenced schema variable with those modifiers already applied.
801
+ */
802
+ function isBareRef$1(node, keysToOmit) {
803
+ if (!node || node.type !== "ref") return false;
804
+ const meta = mergeRefWithSchema$1(node);
805
+ return !meta.nullable && !meta.optional && !meta.nullish && meta.default === void 0 && !meta.description && !meta.examples?.length && !keysToOmit?.length;
571
806
  }
807
+ __name(isBareRef$1, "isBareRef");
808
+ // src/utils/schemaGraph.ts
809
+ const refEntries = /* @__PURE__ */ new WeakMap();
810
+ const checkRefEntries = /* @__PURE__ */ new WeakMap();
811
+ const schemaRefs = /* @__PURE__ */ new WeakMap();
812
+ const importedRefNames = /* @__PURE__ */ new WeakMap();
813
+ const importedCheckRefNames = /* @__PURE__ */ new WeakMap();
572
814
  /**
573
- * Runs a list of macros over a node tree and returns the rewritten tree. Keeps `transform`'s
574
- * structural sharing, so an empty or no-op macro list returns the same reference. Pass
575
- * `depth: 'shallow'` to rewrite the root node only.
815
+ * Walks a node's subtree once and lists every ref it holds, in traversal order, as the resolved
816
+ * name plus whether the ref carries a `$ref` pointer. Memoized per node and per `checks` mode, so
817
+ * each helper below scans a schema once.
818
+ */
819
+ function collectRefEntries(node, { checks }) {
820
+ return memo(checks ? checkRefEntries : refEntries, node, () => {
821
+ const entries = [];
822
+ const walk = (current) => {
823
+ if (current.kind === nodeKinds$1.schema && current.type === "ref") {
824
+ const name = resolveRefName$1(current);
825
+ if (name) entries.push({
826
+ name,
827
+ hasPointer: Boolean(current.ref)
828
+ });
829
+ }
830
+ visitChildNodes(current, walk, { checks });
831
+ return false;
832
+ };
833
+ walk(node);
834
+ return entries;
835
+ });
836
+ }
837
+ /**
838
+ * Collects the names of every schema a node's subtree references, resolved with `ast.ref.getName`.
839
+ * Each ref contributes its name only, so the schema it points to is never traversed. Memoized by
840
+ * node identity, which lets `findCircularSchemas` and `ast.graph.containsCircular` share one scan per schema.
576
841
  *
577
842
  * @example
578
843
  * ```ts
579
- * const next = applyMacros(root, [macroIntegerToString])
844
+ * collectSchemaRefs(petSchema)
845
+ * // Set { 'Category', 'Tag' }
580
846
  * ```
847
+ */
848
+ function collectSchemaRefs$1(node) {
849
+ return memo(schemaRefs, node, () => new Set(collectRefEntries(node, { checks: true }).map((entry) => entry.name)));
850
+ }
851
+ __name(collectSchemaRefs$1, "collectSchemaRefs");
852
+ /**
853
+ * Collects the de-duplicated target names of every pointer-carrying ref in a node's subtree, in
854
+ * first-occurrence order. Memoized by node identity, so `resolver.imports` reads the same result
855
+ * across the ts, zod, and faker plugins instead of re-scanning the same schema per plugin.
581
856
  *
582
- * @example Apply to the root node only
857
+ * Only refs that carry a `$ref` pointer count, so a synthesized ref pointing at a sibling in the
858
+ * same file (a union member created by name) is left out. That leaves exactly the set
859
+ * `resolver.imports` emits. Refs inside `conditionals`, `contains` and `content` count only with
860
+ * `checks: true`, for a plugin that prints those checks.
861
+ *
862
+ * @example
583
863
  * ```ts
584
- * const named = applyMacros(node, [macroEnumName({ parentName, propName, enumSuffix })], { depth: 'shallow' })
864
+ * collectImportedRefNames(petSchema)
865
+ * // ['Category', 'Tag']
585
866
  * ```
586
867
  */
587
- function applyMacros(root, macros, options) {
588
- if (macros.length === 0) return root;
589
- return transform(root, {
590
- ...composeMacros(macros),
591
- ...options
592
- });
868
+ function collectImportedRefNames$1(node, { checks = false } = {}) {
869
+ return memo(checks ? importedCheckRefNames : importedRefNames, node, () => [...new Set(collectRefEntries(node, { checks }).flatMap((entry) => entry.hasPointer ? [entry.name] : []))]);
593
870
  }
594
- // src/optionality.ts
871
+ __name(collectImportedRefNames$1, "collectImportedRefNames");
595
872
  /**
596
- * Generic JSON Schema optionality: a non-required field is optional, and a
597
- * non-required nullable field is nullish.
873
+ * Collects the names of all top-level schemas transitively used by a set of operations.
874
+ *
875
+ * An operation uses a schema when its parameters, request body, or responses reference it, directly
876
+ * or through other named schemas. Once a name is added to the result it is not revisited, so
877
+ * reference cycles terminate.
878
+ *
879
+ * Pair it with `include` filters so schemas reachable only from excluded operations stay ungenerated.
880
+ *
881
+ * @example Only generate schemas referenced by included operations
882
+ * ```ts
883
+ * const includedOps = operations.filter((op) => resolver.default.options(op, { options, include }) !== null)
884
+ * const allowed = collectUsedSchemaNames(includedOps, schemas)
885
+ *
886
+ * for (const schema of schemas) {
887
+ * if (schema.name && !allowed.has(schema.name)) continue
888
+ * // generate schema
889
+ * }
890
+ * ```
598
891
  */
599
- function optionality(schema, required) {
600
- const nullable = schema.nullable ?? false;
892
+ function collectUsedSchemaNames$1(operations, schemas) {
893
+ const schemaMap = /* @__PURE__ */ new Map();
894
+ for (const schema of schemas) if (schema.name) schemaMap.set(schema.name, schema);
895
+ const result = /* @__PURE__ */ new Set();
896
+ function visitSchema(schema) {
897
+ for (const name of collectSchemaRefs$1(schema)) if (!result.has(name)) {
898
+ result.add(name);
899
+ const namedSchema = schemaMap.get(name);
900
+ if (namedSchema) visitSchema(namedSchema);
901
+ }
902
+ }
903
+ for (const op of operations) for (const schema of collect$1(op, {
904
+ depth: "shallow",
905
+ schema: (node) => node
906
+ })) visitSchema(schema);
907
+ return result;
908
+ }
909
+ __name(collectUsedSchemaNames$1, "collectUsedSchemaNames");
910
+ /**
911
+ * Finds every schema that takes part in a circular dependency chain, including direct self-loops.
912
+ *
913
+ * Wrap the returned schema positions in a deferred construct (a lazy getter or `z.lazy(() => …)`) so
914
+ * the generated code does not recurse forever. Refs are followed by name only, so the walk stays
915
+ * linear in the size of the schema graph.
916
+ *
917
+ * @note Call this once on the full graph, then check individual schemas with `ast.graph.containsCircular()`.
918
+ */
919
+ function findCircularSchemas$1(schemas) {
920
+ const graph = /* @__PURE__ */ new Map();
921
+ for (const schema of schemas) if (schema.name) graph.set(schema.name, collectSchemaRefs$1(schema));
922
+ const circular = /* @__PURE__ */ new Set();
923
+ for (const start of graph.keys()) {
924
+ const visited = /* @__PURE__ */ new Set();
925
+ const stack = [...graph.get(start) ?? []];
926
+ while (stack.length > 0) {
927
+ const node = stack.pop();
928
+ if (node === start) {
929
+ circular.add(start);
930
+ break;
931
+ }
932
+ if (visited.has(node)) continue;
933
+ visited.add(node);
934
+ const next = graph.get(node);
935
+ if (next) for (const r of next) stack.push(r);
936
+ }
937
+ }
938
+ return circular;
939
+ }
940
+ __name(findCircularSchemas$1, "findCircularSchemas");
941
+ // src/graph/index.ts
942
+ var graph_exports = /* @__PURE__ */ __exportAll({
943
+ containsCircular: () => containsCircular,
944
+ getCircularNames: () => findCircularSchemas$1,
945
+ getImportedNames: () => collectImportedRefNames$1,
946
+ getRefNames: () => collectSchemaRefs$1,
947
+ getUsedNames: () => collectUsedSchemaNames$1
948
+ });
949
+ /** Whether a schema, or anything inside it, refers to one of `circularSchemas`, other than `excludeName`. */
950
+ function containsCircular(node, { circularSchemas, excludeName }) {
951
+ if (!node || circularSchemas.size === 0) return false;
952
+ for (const name of collectSchemaRefs$1(node)) if (name !== excludeName && circularSchemas.has(name)) return true;
953
+ return false;
954
+ }
955
+ // src/http/requestBody.ts
956
+ var requestBody_exports = /* @__PURE__ */ __exportAll({ getSchema: () => getSchema });
957
+ /** The schema of the first request body entry, else `undefined` when the operation takes no body. */
958
+ function getSchema(node) {
959
+ return node.requestBody?.content?.[0]?.schema;
960
+ }
961
+ // src/http/status.ts
962
+ var status_exports = /* @__PURE__ */ __exportAll({
963
+ getClass: () => statusClass,
964
+ isError: () => isError,
965
+ isSuccess: () => isSuccess,
966
+ toCode: () => toCode
967
+ });
968
+ const classByRange = /* @__PURE__ */ new Map([
969
+ ["1xx", 1],
970
+ ["2xx", 2],
971
+ ["3xx", 3],
972
+ ["4xx", 4],
973
+ ["5xx", 5]
974
+ ]);
975
+ /** The class digit of a status code or range: `2` for `201` and `2xx`, `null` for `default`. */
976
+ function statusClass(input) {
977
+ const statusCode = typeof input === "object" ? input.statusCode : input;
978
+ const rangeClass = classByRange.get(String(statusCode));
979
+ if (rangeClass) return rangeClass;
980
+ const code = Number(statusCode);
981
+ return Number.isNaN(code) ? null : Math.floor(code / 100);
982
+ }
983
+ function isSuccess(statusCode) {
984
+ return statusClass(statusCode) === 2;
985
+ }
986
+ function isError(statusCode) {
987
+ const code = statusClass(statusCode);
988
+ return code !== null && code >= 4;
989
+ }
990
+ /** One concrete code for a status or range: `200` for `2xx` and `default`, the code itself otherwise. */
991
+ function toCode(input) {
992
+ const statusCode = typeof input === "object" ? input.statusCode : input;
993
+ const code = Number(statusCode);
994
+ return Number.isNaN(code) ? (statusClass(statusCode) ?? 2) * 100 : code;
995
+ }
996
+ // src/http/responses.ts
997
+ var responses_exports = /* @__PURE__ */ __exportAll({
998
+ filterError: () => filterError,
999
+ filterSuccess: () => filterSuccess,
1000
+ findPrimary: () => findPrimary,
1001
+ getImportedNames: () => getImportedNames
1002
+ });
1003
+ /** The `4xx` and `5xx` responses, ranges included. */
1004
+ function filterError(node) {
1005
+ return node.responses.filter((response) => isError(response.statusCode));
1006
+ }
1007
+ /** The ref names the response bodies import, in order, with repeats. */
1008
+ function getImportedNames(responses) {
1009
+ return responses.flatMap((response) => (response.content ?? []).flatMap((entry) => entry.schema ? collectImportedRefNames$1(entry.schema) : []));
1010
+ }
1011
+ function filterSuccess(node) {
1012
+ return node.responses.filter((response) => isSuccess(response.statusCode));
1013
+ }
1014
+ /** The success response a client returns: one with a body first, then an exact code over a `2xx` range. */
1015
+ function findPrimary(node) {
1016
+ const responses = filterSuccess(node);
1017
+ const withBody = responses.filter((response) => response.content?.some((entry) => entry.contentType));
1018
+ const candidates = withBody.length ? withBody : responses;
1019
+ return candidates.find((response) => !Number.isNaN(Number(response.statusCode))) ?? candidates[0] ?? null;
1020
+ }
1021
+ // src/http/index.ts
1022
+ var http_exports = /* @__PURE__ */ __exportAll({
1023
+ getBaseURL: () => getBaseURL,
1024
+ groupParameters: () => groupParameters,
1025
+ isOperation: () => isHttpOperationNode$1,
1026
+ requestBody: () => requestBody_exports,
1027
+ responses: () => responses_exports,
1028
+ status: () => status_exports
1029
+ });
1030
+ /** The server's URL when it is absolute and has no template variables, else `undefined`. */
1031
+ function getBaseURL(server) {
1032
+ if (!server || server.variables || server.url.includes("{") || !server.url.includes("://")) return void 0;
1033
+ return URL.canParse(server.url) ? server.url : void 0;
1034
+ }
1035
+ const parametersByNode = /* @__PURE__ */ new WeakMap();
1036
+ /** The operation's parameters by location, with a `querystring` parameter in `query`. A repeated name keeps its first parameter. */
1037
+ function groupParameters(node) {
1038
+ const cached = parametersByNode.get(node);
1039
+ if (cached) return cached;
1040
+ const group = (...locations) => {
1041
+ const seen = /* @__PURE__ */ new Set();
1042
+ return locations.flatMap((location) => node.parameters.filter((param) => {
1043
+ if (param.in !== location || seen.has(param.name)) return false;
1044
+ seen.add(param.name);
1045
+ return true;
1046
+ }));
1047
+ };
1048
+ const groups = {
1049
+ path: group("path"),
1050
+ query: group("query", "querystring"),
1051
+ header: group("header"),
1052
+ cookie: group("cookie")
1053
+ };
1054
+ parametersByNode.set(node, groups);
1055
+ return groups;
1056
+ }
1057
+ // src/nodes/schema.ts
1058
+ /**
1059
+ * Maps schema `type` to its underlying `primitive`.
1060
+ * Primitive types map to themselves and special string formats map to `'string'`.
1061
+ * Any type not listed here (such as `ref`, `enum`, `union`, `intersection`, `not`, `tuple`, `ipv4`, `ipv6`, `blob`) has no `primitive`.
1062
+ */
1063
+ const TYPE_TO_PRIMITIVE = {
1064
+ string: "string",
1065
+ number: "number",
1066
+ integer: "integer",
1067
+ bigint: "bigint",
1068
+ boolean: "boolean",
1069
+ null: "null",
1070
+ any: "any",
1071
+ unknown: "unknown",
1072
+ void: "void",
1073
+ never: "never",
1074
+ object: "object",
1075
+ array: "array",
1076
+ date: "date",
1077
+ uuid: "string",
1078
+ email: "string",
1079
+ url: "string",
1080
+ datetime: "string",
1081
+ time: "string"
1082
+ };
1083
+ function createSchema(props) {
1084
+ if (props.type === "object") return {
1085
+ properties: [],
1086
+ primitive: "object",
1087
+ ...props,
1088
+ kind: nodeKinds$1.schema
1089
+ };
601
1090
  return {
602
- ...schema,
603
- optional: !required && !nullable ? true : void 0,
604
- nullish: !required && nullable ? true : void 0
1091
+ primitive: TYPE_TO_PRIMITIVE[props.type],
1092
+ ...props,
1093
+ kind: nodeKinds$1.schema
605
1094
  };
606
1095
  }
1096
+ // src/ref/index.ts
1097
+ var ref_exports = /* @__PURE__ */ __exportAll({
1098
+ getName: () => getName,
1099
+ getSchema: () => resolveRef$1,
1100
+ isAlias: () => isBareRef$1,
1101
+ mergeSchema: () => mergeSchema
1102
+ });
1103
+ /** A resolved ref merged with its target schema, so usage-site fields such as `description` win. Other nodes come back unchanged. */
1104
+ function mergeSchema(node) {
1105
+ const ref = narrowSchema$1(node, "ref");
1106
+ return ref?.schema ? createSchema(mergeRefWithSchema$1(ref)) : node;
1107
+ }
1108
+ function getName(input) {
1109
+ return typeof input === "string" ? refName$1(input) : resolveRefName$1(input);
1110
+ }
607
1111
  // src/createPrinter.ts
608
1112
  /**
609
1113
  * Creates a schema printer: a function that takes a `SchemaNode` and emits
@@ -627,9 +1131,9 @@ function optionality(schema, required) {
627
1131
  *
628
1132
  * @example Tiny Zod printer
629
1133
  * ```ts
630
- * import { createPrinter, type PrinterFactoryOptions } from '@kubb/ast'
1134
+ * import { ast, createPrinter } from '@kubb/kit'
631
1135
  *
632
- * type PrinterZod = PrinterFactoryOptions<'zod', { strict?: boolean }, string>
1136
+ * type PrinterZod = ast.PrinterFactoryOptions<'zod', { strict?: boolean }, string>
633
1137
  *
634
1138
  * export const zodPrinter = createPrinter<PrinterZod>((options) => ({
635
1139
  * name: 'zod',
@@ -646,7 +1150,7 @@ function optionality(schema, required) {
646
1150
  * }))
647
1151
  * ```
648
1152
  */
649
- function createPrinter(build) {
1153
+ function createPrinter$1(build) {
650
1154
  return (options) => {
651
1155
  const { name, options: resolvedOptions, nodes, overrides, print: printOverride } = build(options ?? {});
652
1156
  const merged = overrides ? {
@@ -676,60 +1180,168 @@ function createPrinter(build) {
676
1180
  };
677
1181
  };
678
1182
  }
679
- // src/utils/extractStringsFromNodes.ts
680
- const CODE_NODE_TEXT_FIELDS = {
681
- Text: ["value"],
682
- Jsx: ["value"],
683
- Function: [
684
- "params",
685
- "generics",
686
- "returnType"
687
- ],
688
- ArrowFunction: [
689
- "params",
690
- "generics",
691
- "returnType"
692
- ],
693
- Const: ["type"]
694
- };
1183
+ __name(createPrinter$1, "createPrinter");
1184
+ // src/defineMacro.ts
695
1185
  /**
696
- * Extracts all string content from a `CodeNode` tree recursively.
697
- *
698
- * Collects the text fields and nested node content, to build the source string for import filtering.
1186
+ * Sort weight for an `enforce` hint. `pre` sorts before unmarked items and `post` after, so a plain
1187
+ * list keeps its authored order.
699
1188
  */
700
- function extractStringsFromNodes(nodes) {
701
- if (!nodes?.length) return "";
702
- const collected = [];
703
- for (const node of nodes) {
704
- if (typeof node === "string") {
705
- if (node) collected.push(node);
706
- continue;
707
- }
708
- if (node.kind === nodeKinds.break) continue;
709
- const parts = [];
710
- const record = node;
711
- for (const field of CODE_NODE_TEXT_FIELDS[node.kind] ?? []) {
712
- const value = record[field];
713
- if (Array.isArray(value)) parts.push(value.join(", "));
714
- if (typeof value === "string" && value) parts.push(value);
715
- }
716
- const nested = "nodes" in node ? extractStringsFromNodes(node.nodes) : "";
717
- if (nested) parts.push(nested);
718
- if (parts.length) collected.push(parts.join("\n"));
719
- }
720
- return collected.join("\n");
1189
+ function enforceWeight(enforce) {
1190
+ if (enforce === "pre") return 0;
1191
+ if (enforce === "post") return 2;
1192
+ return 1;
721
1193
  }
722
- // src/utils/combineFileMembers.ts
723
1194
  /**
724
- * File-member merging. `combineImports` and `combineExports` deduplicate and sort the import and
725
- * export entries of one file, while `combineSources` deduplicates source entries in original order.
726
- * `combineImports` also drops imports nothing references. This works on a file's members, not on
727
- * schema content.
1195
+ * Compares two `enforce` hints for `Array.prototype.sort`: `pre` first, unmarked next, `post`
1196
+ * last. Equal hints compare as `0`, so a stable sort keeps authored order within a bucket.
1197
+ *
1198
+ * @example
1199
+ * `plugins.sort((a, b) => compareMacroOrder(a.enforce, b.enforce))`
728
1200
  */
729
- const IDENTIFIER_RUN = /[\w$]+/g;
1201
+ function compareMacroOrder(a, b) {
1202
+ return enforceWeight(a) - enforceWeight(b);
1203
+ }
730
1204
  /**
731
- * How many imports a file needs before indexing the source beats scanning it once per name.
732
- */
1205
+ * Types a macro for inference and a single construction site, mirroring `defineExtension`.
1206
+ * Adds no runtime behavior.
1207
+ *
1208
+ * @example
1209
+ * ```ts
1210
+ * const macroUntagged = defineMacro({
1211
+ * name: 'untagged',
1212
+ * operation(node) {
1213
+ * return node.tags?.length ? undefined : { ...node, tags: ['untagged'] }
1214
+ * },
1215
+ * })
1216
+ * ```
1217
+ */
1218
+ function defineMacro$1(macro) {
1219
+ return macro;
1220
+ }
1221
+ __name(defineMacro$1, "defineMacro");
1222
+ /**
1223
+ * Runs every macro's callback for one node kind in order, chaining the result so each macro sees
1224
+ * the previous macro's output. Returns `undefined` when nothing changed, so `transform` keeps the
1225
+ * original reference (structural sharing).
1226
+ */
1227
+ function chain({ macros, key, node, context }) {
1228
+ let current = node;
1229
+ for (const macro of macros) {
1230
+ const callback = macro[key];
1231
+ if (!callback) continue;
1232
+ if (macro.match && !macro.match(current)) continue;
1233
+ const next = callback(current, context);
1234
+ if (next != null) current = next;
1235
+ }
1236
+ return current === node ? void 0 : current;
1237
+ }
1238
+ /**
1239
+ * Folds an ordered list of macros into a single {@link Visitor} for `transform`. Macros are
1240
+ * stable-sorted by `enforce`, then applied sequentially per node so later macros see earlier output.
1241
+ */
1242
+ function composeMacros(macros) {
1243
+ const ordered = [...macros].sort((a, b) => compareMacroOrder(a.enforce, b.enforce));
1244
+ const visitor = {};
1245
+ for (const key of visitorKeys) {
1246
+ if (!ordered.some((macro) => typeof macro[key] === "function")) continue;
1247
+ const callback = (node, context) => chain({
1248
+ macros: ordered,
1249
+ key,
1250
+ node,
1251
+ context
1252
+ });
1253
+ visitor[key] = callback;
1254
+ }
1255
+ return visitor;
1256
+ }
1257
+ /**
1258
+ * Runs a list of macros over a node tree and returns the rewritten tree. Keeps `transform`'s
1259
+ * structural sharing, so an empty or no-op macro list returns the same reference. Pass
1260
+ * `depth: 'shallow'` to rewrite the root node only.
1261
+ *
1262
+ * @example
1263
+ * ```ts
1264
+ * const next = applyMacros(root, [macroIntegerToString])
1265
+ * ```
1266
+ *
1267
+ * @example Apply to the root node only
1268
+ * ```ts
1269
+ * const named = applyMacros(node, [macroEnumName({ parentName, propName, enumSuffix })], { depth: 'shallow' })
1270
+ * ```
1271
+ */
1272
+ function applyMacros$1(root, macros, options) {
1273
+ if (macros.length === 0) return root;
1274
+ return transform$1(root, {
1275
+ ...composeMacros(macros),
1276
+ ...options
1277
+ });
1278
+ }
1279
+ __name(applyMacros$1, "applyMacros");
1280
+ // src/macro/index.ts
1281
+ var macro_exports = /* @__PURE__ */ __exportAll({
1282
+ apply: () => applyMacros$1,
1283
+ compareOrder: () => compareMacroOrder
1284
+ });
1285
+ // src/utils/nodeText.ts
1286
+ const CODE_NODE_TEXT_FIELDS = {
1287
+ Text: ["value"],
1288
+ Jsx: ["value"],
1289
+ Function: [
1290
+ "params",
1291
+ "generics",
1292
+ "returnType"
1293
+ ],
1294
+ ArrowFunction: [
1295
+ "params",
1296
+ "generics",
1297
+ "returnType"
1298
+ ],
1299
+ Const: ["type"]
1300
+ };
1301
+ /**
1302
+ * Extracts all string content from a `CodeNode` tree recursively.
1303
+ * Collects the text fields and nested node content, to build the source string for import filtering.
1304
+ */
1305
+ function getNodeText(nodes) {
1306
+ if (!nodes?.length) return "";
1307
+ const collected = [];
1308
+ for (const node of nodes) {
1309
+ if (typeof node === "string") {
1310
+ if (node) collected.push(node);
1311
+ continue;
1312
+ }
1313
+ if (node.kind === nodeKinds$1.break) continue;
1314
+ const parts = [];
1315
+ const record = node;
1316
+ for (const field of CODE_NODE_TEXT_FIELDS[node.kind] ?? []) {
1317
+ const value = record[field];
1318
+ if (Array.isArray(value)) parts.push(value.join(", "));
1319
+ if (typeof value === "string" && value) parts.push(value);
1320
+ }
1321
+ const nested = "nodes" in node ? getNodeText(node.nodes) : "";
1322
+ if (nested) parts.push(nested);
1323
+ if (parts.length) collected.push(parts.join("\n"));
1324
+ }
1325
+ return collected.join("\n");
1326
+ }
1327
+ // src/node/index.ts
1328
+ var node_exports = /* @__PURE__ */ __exportAll({
1329
+ collect: () => collect$1,
1330
+ getText: () => getNodeText,
1331
+ kinds: () => nodeKinds$1,
1332
+ transform: () => transform$1
1333
+ });
1334
+ // src/utils/combineFileMembers.ts
1335
+ /**
1336
+ * File-member merging. `combineImports` and `combineExports` deduplicate and sort the import and
1337
+ * export entries of one file, while `combineSources` deduplicates source entries in original order.
1338
+ * `combineImports` also drops imports nothing references. This works on a file's members, not on
1339
+ * schema content.
1340
+ */
1341
+ const IDENTIFIER_RUN = /[\w$]+/g;
1342
+ /**
1343
+ * How many imports a file needs before indexing the source beats scanning it once per name.
1344
+ */
733
1345
  const INDEX_ABOVE_IMPORTS = 128;
734
1346
  /**
735
1347
  * Returns the local binding an import name item introduces: the alias when there is one, else the
@@ -898,7 +1510,7 @@ function nodeFactory(kind) {
898
1510
  * // import { useState } from 'react'
899
1511
  * ```
900
1512
  */
901
- const createImport = nodeFactory(nodeKinds.import);
1513
+ const createImport = nodeFactory(nodeKinds$1.import);
902
1514
  /**
903
1515
  * Creates an `ExportNode` representing a language-agnostic export/public API declaration.
904
1516
  *
@@ -908,7 +1520,7 @@ const createImport = nodeFactory(nodeKinds.import);
908
1520
  * // export { Pet } from './Pet'
909
1521
  * ```
910
1522
  */
911
- const createExport = nodeFactory(nodeKinds.export);
1523
+ const createExport = nodeFactory(nodeKinds$1.export);
912
1524
  /**
913
1525
  * Creates a `SourceNode` representing a fragment of source code within a file.
914
1526
  *
@@ -917,7 +1529,7 @@ const createExport = nodeFactory(nodeKinds.export);
917
1529
  * createSource({ name: 'Pet', nodes: [createText('export type Pet = { id: number }')], isExportable: true })
918
1530
  * ```
919
1531
  */
920
- const createSource = nodeFactory(nodeKinds.source);
1532
+ const createSource = nodeFactory(nodeKinds$1.source);
921
1533
  /**
922
1534
  * Creates a fully resolved `FileNode` from a file input descriptor.
923
1535
  *
@@ -966,7 +1578,7 @@ function createFile(input) {
966
1578
  const sourceParts = [];
967
1579
  const localNames = /* @__PURE__ */ new Set();
968
1580
  for (const item of resolvedSources) {
969
- const extracted = item.nodes && extractStringsFromNodes(item.nodes);
1581
+ const extracted = item.nodes && getNodeText(item.nodes);
970
1582
  if (extracted) sourceParts.push(extracted);
971
1583
  if (item.name) localNames.add(item.name);
972
1584
  }
@@ -984,7 +1596,7 @@ function createFile(input) {
984
1596
  })();
985
1597
  return {
986
1598
  ...input,
987
- kind: nodeKinds.file,
1599
+ kind: nodeKinds$1.file,
988
1600
  id: (0, node_crypto.hash)("sha256", input.path, "hex"),
989
1601
  name: node_path.default.basename(input.baseName, extname),
990
1602
  extname,
@@ -999,7 +1611,7 @@ function createFile(input) {
999
1611
  * Replaces every name a file imports from a virtual file (one whose sources are all inline, so it is never written) with that name's
1000
1612
  * `inline.expression`, until no inline names are left. The definitions' own imports move to the importing file.
1001
1613
  */
1002
- function inlineFiles(files, { language }) {
1614
+ function inlineFiles$1(files, { language }) {
1003
1615
  const virtualFiles = new Map(files.filter((file) => file.sources.length > 0 && file.sources.every((source) => source.inline)).map((file) => [file.path, file]));
1004
1616
  const result = {
1005
1617
  files: [],
@@ -1050,415 +1662,148 @@ function inlineFiles(files, { language }) {
1050
1662
  }
1051
1663
  imports.push(...definition.imports);
1052
1664
  return lang.wrap(definition.expression);
1053
- }),
1054
- used
1055
- };
1056
- }
1057
- function findTarget(file, local) {
1058
- if (virtualFiles.has(file.path) && file.sources.some((source) => source.name === local)) return {
1059
- path: file.path,
1060
- name: local
1061
- };
1062
- for (const imp of file.imports) {
1063
- const item = virtualFiles.has(imp.path) && [imp.name].flat().find((item) => importLocalName(item) === local);
1064
- if (item) return {
1065
- path: imp.path,
1066
- name: typeof item === "string" ? item : item.propertyName
1067
- };
1068
- }
1069
- }
1070
- for (const file of files) {
1071
- if (virtualFiles.has(file.path) || !file.imports.some((imp) => virtualFiles.has(imp.path))) continue;
1072
- const lang = language(file);
1073
- if (!lang) {
1074
- result.problems.push({
1075
- path: file.path,
1076
- message: `No parser for "${file.extname}" files supports inline sources.`
1077
- });
1078
- continue;
1079
- }
1080
- const carried = [];
1081
- const sources = file.sources.map((source) => ({
1082
- ...source,
1083
- nodes: rewriteNodes(source.nodes, (text) => replaceNames(text, file, lang, carried).text)
1084
- }));
1085
- const imports = [...file.imports.filter((imp) => !virtualFiles.has(imp.path)), ...carried.map((imp) => imp.root ? {
1086
- ...imp,
1087
- root: file.path
1088
- } : imp)];
1089
- result.files.push(createFile({
1090
- ...file,
1091
- sources,
1092
- imports
1093
- }));
1094
- }
1095
- return result;
1096
- }
1097
- function pickNames(imp, used) {
1098
- if (!Array.isArray(imp.name)) return used.has(imp.name) ? [imp] : [];
1099
- const name = imp.name.filter((item) => used.has(importLocalName(item)));
1100
- return name.length > 0 ? [{
1101
- ...imp,
1102
- name
1103
- }] : [];
1104
- }
1105
- function rewriteNodes(nodes, rewrite) {
1106
- return nodes?.map((node) => {
1107
- if (typeof node === "string") return rewrite(node);
1108
- if (node.kind === nodeKinds.break) return node;
1109
- const next = { ...node };
1110
- for (const field of CODE_NODE_TEXT_FIELDS[node.kind] ?? []) {
1111
- const value = next[field];
1112
- if (Array.isArray(value)) next[field] = value.map(rewrite);
1113
- if (typeof value === "string" && value) next[field] = rewrite(value);
1114
- }
1115
- if ("nodes" in node && node.nodes) next.nodes = rewriteNodes(node.nodes, rewrite);
1116
- return next;
1117
- });
1118
- }
1119
- // src/utils/refs.ts
1120
- /**
1121
- * Returns the last segment of a `$ref` pointer, for example `'Pet'` for `#/components/schemas/Pet`.
1122
- * The segment is decoded like the pointer resolver does: percent-escapes first, then `~1` and `~0`
1123
- * (RFC 6901), so `a~1b~0c` names the component `a/b~c`.
1124
- */
1125
- function refName(ref) {
1126
- const cached = refNames.get(ref);
1127
- if (cached !== void 0) return cached;
1128
- const segment = ref.split("/").at(-1) ?? ref;
1129
- let decoded = segment;
1130
- try {
1131
- decoded = decodeURIComponent(segment);
1132
- } catch {}
1133
- const name = decoded.replaceAll("~1", "/").replaceAll("~0", "~");
1134
- if (refNames.size >= memoLimit) refNames.clear();
1135
- refNames.set(ref, name);
1136
- return name;
1137
- }
1138
- const refNames = /* @__PURE__ */ new Map();
1139
- const memoLimit = 1e5;
1140
- /**
1141
- * Resolves the emitted name of the schema a ref node points at. Prefers `targetName` (set when
1142
- * the referenced schema was renamed, e.g. to break a collision), then the last segment of `ref`,
1143
- * then `name`, then the nested `schema.name`.
1144
- *
1145
- * Returns `null` for non-ref nodes or when no name resolves.
1146
- *
1147
- * @example
1148
- * `resolveRefName({ kind: 'Schema', type: 'ref', ref: '#/components/schemas/Pet' }) // 'Pet'`
1149
- *
1150
- * @example Collision-renamed target
1151
- * `resolveRefName({ kind: 'Schema', type: 'ref', ref: '#/components/schemas/Order', targetName: 'OrderSchema' }) // 'OrderSchema'`
1152
- */
1153
- function resolveRefName(node) {
1154
- if (!node || node.type !== "ref") return null;
1155
- if (node.targetName) return node.targetName;
1156
- if (node.ref) return refName(node.ref);
1157
- return node.name ?? node.schema?.name ?? null;
1158
- }
1159
- /**
1160
- * Follows a resolved ref chain to its target schema. Returns an unresolved or cyclic ref as is.
1161
- *
1162
- * @example
1163
- * ```ts
1164
- * const target = resolveRef(refSchema)
1165
- * ```
1166
- */
1167
- function resolveRef(node) {
1168
- if (node.type !== "ref" || !node.schema) return node;
1169
- const seen = /* @__PURE__ */ new WeakSet();
1170
- let current = node;
1171
- while (current.type === "ref" && current.schema) {
1172
- if (seen.has(current)) return current;
1173
- seen.add(current);
1174
- current = current.schema;
1175
- }
1176
- return current;
1177
- }
1178
- /** Fields of a ref node that describe the ref itself, so they never override the resolved schema. */
1179
- const REF_OWN_KEYS = /* @__PURE__ */ new Set([
1180
- "kind",
1181
- "type",
1182
- "name",
1183
- "ref",
1184
- "schema"
1185
- ]);
1186
- /**
1187
- * Merges a ref node with its resolved schema, giving the fields set at the usage site precedence.
1188
- *
1189
- * Every field on the ref except `kind`, `type`, `name`, `ref`, and `schema` overrides the same field
1190
- * of the resolved `schema` (for example `description`, `nullable`, `readOnly`, `deprecated`). Fields
1191
- * left `undefined` on the ref do not shadow the resolved schema. A ref without a resolved `schema`
1192
- * is returned as is.
1193
- *
1194
- * @example
1195
- * ```ts
1196
- * const ref = createSchema({ type: 'ref', ref: '#/components/schemas/Pet', description: 'A pet', schema: petSchema })
1197
- * mergeRefWithSchema(ref) // petSchema's fields with description 'A pet'
1198
- * ```
1199
- */
1200
- function mergeRefWithSchema(node) {
1201
- if (!node.schema) return node;
1202
- const merged = { ...node.schema };
1203
- for (const key of Object.keys(node)) {
1204
- const value = node[key];
1205
- if (value !== void 0 && !REF_OWN_KEYS.has(key)) merged[key] = value;
1206
- }
1207
- return merged;
1208
- }
1209
- /**
1210
- * Returns `true` when a schema is a bare ref without modifiers, metadata, or omit keys.
1211
- *
1212
- * Modifiers inherited from the resolved schema are included in the check because a ref emits the
1213
- * referenced schema variable with those modifiers already applied.
1214
- */
1215
- function isBareRef(node, keysToOmit) {
1216
- if (!node || node.type !== "ref") return false;
1217
- const meta = mergeRefWithSchema(node);
1218
- return !meta.nullable && !meta.optional && !meta.nullish && meta.default === void 0 && !meta.description && !meta.examples?.length && !keysToOmit?.length;
1219
- }
1220
- // ../../internals/utils/src/promise.ts
1221
- /**
1222
- * Returns the value cached under `key` in `store`, computing and storing it on the first call.
1223
- * Pass a `WeakMap` for object keys, so an entry goes away with its key, or a `Map` for primitive
1224
- * keys. A cached `null` or `undefined` counts as a hit, so `compute` runs once per key.
1225
- *
1226
- * @example
1227
- * `const refs = memo(refCache, node, () => collectRefs(node))`
1228
- */
1229
- function memo(store, key, compute) {
1230
- if (store.has(key)) return store.get(key);
1231
- const value = compute();
1232
- store.set(key, value);
1233
- return value;
1234
- }
1235
- // src/utils/schemaGraph.ts
1236
- const refEntries = /* @__PURE__ */ new WeakMap();
1237
- const checkRefEntries = /* @__PURE__ */ new WeakMap();
1238
- const schemaRefs = /* @__PURE__ */ new WeakMap();
1239
- const importedRefNames = /* @__PURE__ */ new WeakMap();
1240
- const importedCheckRefNames = /* @__PURE__ */ new WeakMap();
1241
- /**
1242
- * Walks a node's subtree once and lists every ref it holds, in traversal order, as the resolved
1243
- * name plus whether the ref carries a `$ref` pointer. Memoized per node and per `checks` mode, so
1244
- * each helper below scans a schema once.
1245
- */
1246
- function collectRefEntries(node, { checks }) {
1247
- return memo(checks ? checkRefEntries : refEntries, node, () => {
1248
- const entries = [];
1249
- const walk = (current) => {
1250
- if (current.kind === nodeKinds.schema && current.type === "ref") {
1251
- const name = resolveRefName(current);
1252
- if (name) entries.push({
1253
- name,
1254
- hasPointer: Boolean(current.ref)
1255
- });
1256
- }
1257
- visitChildNodes(current, walk, { checks });
1258
- return false;
1259
- };
1260
- walk(node);
1261
- return entries;
1262
- });
1263
- }
1264
- /**
1265
- * Collects the names of every schema a node's subtree references, resolved with `resolveRefName`.
1266
- * Each ref contributes its name only, so the schema it points to is never traversed. Memoized by
1267
- * node identity, which lets `findCircularSchemas` and `containsCircularRef` share one scan per schema.
1268
- *
1269
- * @example
1270
- * ```ts
1271
- * collectSchemaRefs(petSchema)
1272
- * // Set { 'Category', 'Tag' }
1273
- * ```
1274
- */
1275
- function collectSchemaRefs(node) {
1276
- return memo(schemaRefs, node, () => new Set(collectRefEntries(node, { checks: true }).map((entry) => entry.name)));
1277
- }
1278
- /**
1279
- * Collects the de-duplicated target names of every pointer-carrying ref in a node's subtree, in
1280
- * first-occurrence order. Memoized by node identity, so `resolver.imports` reads the same result
1281
- * across the ts, zod, and faker plugins instead of re-scanning the same schema per plugin.
1282
- *
1283
- * Only refs that carry a `$ref` pointer count, so a synthesized ref pointing at a sibling in the
1284
- * same file (a union member created by name) is left out. That leaves exactly the set
1285
- * `resolver.imports` emits. Refs inside `conditionals`, `contains` and `content` count only with
1286
- * `checks: true`, for a plugin that prints those checks.
1287
- *
1288
- * @example
1289
- * ```ts
1290
- * collectImportedRefNames(petSchema)
1291
- * // ['Category', 'Tag']
1292
- * ```
1293
- */
1294
- function collectImportedRefNames(node, { checks = false } = {}) {
1295
- return memo(checks ? importedCheckRefNames : importedRefNames, node, () => [...new Set(collectRefEntries(node, { checks }).flatMap((entry) => entry.hasPointer ? [entry.name] : []))]);
1296
- }
1297
- /**
1298
- * Collects the names of all top-level schemas transitively used by a set of operations.
1299
- *
1300
- * An operation uses a schema when its parameters, request body, or responses reference it, directly
1301
- * or through other named schemas. Once a name is added to the result it is not revisited, so
1302
- * reference cycles terminate.
1303
- *
1304
- * Pair it with `include` filters so schemas reachable only from excluded operations stay ungenerated.
1305
- *
1306
- * @example Only generate schemas referenced by included operations
1307
- * ```ts
1308
- * const includedOps = operations.filter((op) => resolver.default.options(op, { options, include }) !== null)
1309
- * const allowed = collectUsedSchemaNames(includedOps, schemas)
1310
- *
1311
- * for (const schema of schemas) {
1312
- * if (schema.name && !allowed.has(schema.name)) continue
1313
- * // generate schema
1314
- * }
1315
- * ```
1316
- */
1317
- function collectUsedSchemaNames(operations, schemas) {
1318
- const schemaMap = /* @__PURE__ */ new Map();
1319
- for (const schema of schemas) if (schema.name) schemaMap.set(schema.name, schema);
1320
- const result = /* @__PURE__ */ new Set();
1321
- function visitSchema(schema) {
1322
- for (const name of collectSchemaRefs(schema)) if (!result.has(name)) {
1323
- result.add(name);
1324
- const namedSchema = schemaMap.get(name);
1325
- if (namedSchema) visitSchema(namedSchema);
1326
- }
1665
+ }),
1666
+ used
1667
+ };
1327
1668
  }
1328
- for (const op of operations) for (const schema of collect(op, {
1329
- depth: "shallow",
1330
- schema: (node) => node
1331
- })) visitSchema(schema);
1332
- return result;
1333
- }
1334
- /**
1335
- * Finds every schema that takes part in a circular dependency chain, including direct self-loops.
1336
- *
1337
- * Wrap the returned schema positions in a deferred construct (a lazy getter or `z.lazy(() => …)`) so
1338
- * the generated code does not recurse forever. Refs are followed by name only, so the walk stays
1339
- * linear in the size of the schema graph.
1340
- *
1341
- * @note Call this once on the full graph, then check individual schemas with `containsCircularRef()`.
1342
- */
1343
- function findCircularSchemas(schemas) {
1344
- const graph = /* @__PURE__ */ new Map();
1345
- for (const schema of schemas) if (schema.name) graph.set(schema.name, collectSchemaRefs(schema));
1346
- const circular = /* @__PURE__ */ new Set();
1347
- for (const start of graph.keys()) {
1348
- const visited = /* @__PURE__ */ new Set();
1349
- const stack = [...graph.get(start) ?? []];
1350
- while (stack.length > 0) {
1351
- const node = stack.pop();
1352
- if (node === start) {
1353
- circular.add(start);
1354
- break;
1355
- }
1356
- if (visited.has(node)) continue;
1357
- visited.add(node);
1358
- const next = graph.get(node);
1359
- if (next) for (const r of next) stack.push(r);
1669
+ function findTarget(file, local) {
1670
+ if (virtualFiles.has(file.path) && file.sources.some((source) => source.name === local)) return {
1671
+ path: file.path,
1672
+ name: local
1673
+ };
1674
+ for (const imp of file.imports) {
1675
+ const item = virtualFiles.has(imp.path) && [imp.name].flat().find((item) => importLocalName(item) === local);
1676
+ if (item) return {
1677
+ path: imp.path,
1678
+ name: typeof item === "string" ? item : item.propertyName
1679
+ };
1360
1680
  }
1361
1681
  }
1362
- return circular;
1363
- }
1364
- // src/utils/schemaProperties.ts
1365
- /**
1366
- * Finds every matching property exposed by an object, resolved reference, or intersection.
1367
- *
1368
- * References are followed through their parsed `schema`. Circular references terminate without
1369
- * revisiting a node. Unions are not traversed because a property on one branch does not describe
1370
- * every branch.
1371
- *
1372
- * @example Faker discriminator narrowing
1373
- * ```ts
1374
- * const values = resolveSchemaProperties({ node: member, propertyName: union.discriminatorPropertyName }).flatMap(({ schema }) =>
1375
- * getSchemaLiteralValues(schema),
1376
- * )
1377
- * const typeName = `Extract<Pet, { type: ${values.map(JSON.stringify).join(' | ')} }>`
1378
- * ```
1379
- *
1380
- * @example Zod discriminated unions
1381
- * ```ts
1382
- * const canDiscriminate = union.members?.every(
1383
- * (member) => resolveSchemaProperties({ node: member, propertyName: 'type' }).length > 0,
1384
- * )
1385
- * const expression = canDiscriminate ? `z.discriminatedUnion('type', members)` : `z.union(members)`
1386
- * ```
1387
- *
1388
- * @example TypeScript property lookup
1389
- * ```ts
1390
- * const properties = resolveSchemaProperties({ node: schema, propertyName: 'status' })
1391
- * const statusSchemas = properties.map((property) => print(property.schema))
1392
- * ```
1393
- */
1394
- function resolveSchemaProperties({ node, propertyName }) {
1395
- const properties = [];
1396
- walkSchema(node, "intersection", (current) => {
1397
- if (current.type !== "object") return;
1398
- const property = current.properties.find((candidate) => candidate.name === propertyName);
1399
- if (property) properties.push(property);
1400
- });
1401
- return properties;
1402
- }
1403
- function walkSchema(node, through, visit) {
1404
- const visited = /* @__PURE__ */ new WeakSet();
1405
- function walk(current) {
1406
- if (!current || visited.has(current)) return;
1407
- visited.add(current);
1408
- visit(current);
1409
- if (current.type === "ref") return walk(current.schema);
1410
- if (current.type === through) for (const member of current.members ?? []) walk(member);
1682
+ for (const file of files) {
1683
+ if (virtualFiles.has(file.path) || !file.imports.some((imp) => virtualFiles.has(imp.path))) continue;
1684
+ const lang = language(file);
1685
+ if (!lang) {
1686
+ result.problems.push({
1687
+ path: file.path,
1688
+ message: `No parser for "${file.extname}" files supports inline sources.`
1689
+ });
1690
+ continue;
1691
+ }
1692
+ const carried = [];
1693
+ const sources = file.sources.map((source) => ({
1694
+ ...source,
1695
+ nodes: rewriteNodes(source.nodes, (text) => replaceNames(text, file, lang, carried).text)
1696
+ }));
1697
+ const imports = [...file.imports.filter((imp) => !virtualFiles.has(imp.path)), ...carried.map((imp) => imp.root ? {
1698
+ ...imp,
1699
+ root: file.path
1700
+ } : imp)];
1701
+ result.files.push(createFile({
1702
+ ...file,
1703
+ sources,
1704
+ imports
1705
+ }));
1411
1706
  }
1412
- walk(node);
1707
+ return result;
1413
1708
  }
1414
- /**
1415
- * Reads the unique literal values represented by an enum, resolved reference, or union.
1416
- *
1417
- * Named enum values take precedence over simple enum values, matching how renderers consume
1418
- * `EnumSchemaNode`.
1419
- *
1420
- * @example
1421
- * ```ts
1422
- * const values = getSchemaLiteralValues(enumNode)
1423
- * // ['cat', 'dog']
1424
- * ```
1425
- */
1426
- function getSchemaLiteralValues(node) {
1427
- const values = /* @__PURE__ */ new Set();
1428
- walkSchema(node, "union", (current) => {
1429
- if (current.type !== "enum") return;
1430
- for (const { value } of current.namedEnumValues ?? current.enumValues?.map((value) => ({ value })) ?? []) values.add(value);
1431
- });
1432
- return [...values];
1709
+ __name(inlineFiles$1, "inlineFiles");
1710
+ function pickNames(imp, used) {
1711
+ if (!Array.isArray(imp.name)) return used.has(imp.name) ? [imp] : [];
1712
+ const name = imp.name.filter((item) => used.has(importLocalName(item)));
1713
+ return name.length > 0 ? [{
1714
+ ...imp,
1715
+ name
1716
+ }] : [];
1433
1717
  }
1434
- function collectPropertyNames(node) {
1435
- const names = /* @__PURE__ */ new Set();
1436
- walkSchema(node, "intersection", (current) => {
1437
- if (current.type === "object") for (const property of current.properties) names.add(property.name);
1718
+ function rewriteNodes(nodes, rewrite) {
1719
+ return nodes?.map((node) => {
1720
+ if (typeof node === "string") return rewrite(node);
1721
+ if (node.kind === nodeKinds$1.break) return node;
1722
+ const next = { ...node };
1723
+ for (const field of CODE_NODE_TEXT_FIELDS[node.kind] ?? []) {
1724
+ const value = next[field];
1725
+ if (Array.isArray(value)) next[field] = value.map(rewrite);
1726
+ if (typeof value === "string" && value) next[field] = rewrite(value);
1727
+ }
1728
+ if ("nodes" in node && node.nodes) next.nodes = rewriteNodes(node.nodes, rewrite);
1729
+ return next;
1438
1730
  });
1439
- return names;
1440
1731
  }
1441
- /**
1442
- * Infers an implicit discriminator: a property every member exposes with a distinct single
1443
- * literal value, the same shape a declared OpenAPI `discriminator` already narrows on.
1444
- */
1445
- function inferDiscriminatorPropertyName(members) {
1446
- if (members.length < 2) return void 0;
1447
- let candidates;
1448
- for (const member of members) {
1449
- const names = collectPropertyNames(member);
1450
- candidates = candidates ? new Set([...candidates].filter((name) => names.has(name))) : names;
1451
- }
1452
- for (const name of candidates ?? []) {
1453
- const values = members.map((member) => {
1454
- const literals = new Set(resolveSchemaProperties({
1455
- node: member,
1456
- propertyName: name
1457
- }).flatMap((property) => getSchemaLiteralValues(property.schema)));
1458
- return literals.size === 1 ? [...literals][0] : void 0;
1459
- });
1460
- if (values.every((value) => value !== void 0) && new Set(values).size === members.length) return name;
1732
+ // src/file/index.ts
1733
+ var file_exports = /* @__PURE__ */ __exportAll({ inline: () => inlineFiles$1 });
1734
+ // src/deprecated.ts
1735
+ /** @deprecated Use `ast.ref.isAlias`. Removed in Kubb v6. */
1736
+ const isBareRef = isBareRef$1;
1737
+ /** @deprecated Use `ast.ref.mergeSchema`. Removed in Kubb v6. */
1738
+ const mergeRefWithSchema = mergeRefWithSchema$1;
1739
+ /** @deprecated Use `ast.ref.getName`. Removed in Kubb v6. */
1740
+ const refName = getName;
1741
+ /** @deprecated Use `ast.ref.getSchema`. Removed in Kubb v6. */
1742
+ const resolveRef = resolveRef$1;
1743
+ /** @deprecated Use `ast.ref.getName`. Removed in Kubb v6. */
1744
+ const resolveRefName = getName;
1745
+ /** @deprecated Use `ast.graph.getImportedNames`. Removed in Kubb v6. */
1746
+ const collectImportedRefNames = collectImportedRefNames$1;
1747
+ /** @deprecated Use `ast.graph.getRefNames`. Removed in Kubb v6. */
1748
+ const collectSchemaRefs = collectSchemaRefs$1;
1749
+ /** @deprecated Use `ast.graph.getUsedNames`. Removed in Kubb v6. */
1750
+ const collectUsedSchemaNames = collectUsedSchemaNames$1;
1751
+ /** @deprecated Use `ast.graph.getCircularNames`. Removed in Kubb v6. */
1752
+ const findCircularSchemas = findCircularSchemas$1;
1753
+ /** @deprecated Use `ast.schema.getLiterals`. Removed in Kubb v6. */
1754
+ const getSchemaLiteralValues = getSchemaLiteralValues$1;
1755
+ /** @deprecated Use `ast.schema.inferDiscriminator`. Removed in Kubb v6. */
1756
+ const inferDiscriminatorPropertyName = inferDiscriminatorPropertyName$1;
1757
+ /** @deprecated Use `ast.schema.findProperties`. Removed in Kubb v6. */
1758
+ const resolveSchemaProperties = resolveSchemaProperties$1;
1759
+ /** @deprecated Use `ast.http.isOperation`. Removed in Kubb v6. */
1760
+ const isHttpOperationNode = isHttpOperationNode$1;
1761
+ /** @deprecated Use `ast.node.collect`, which returns the same array. Removed in Kubb v6. */
1762
+ const collectSync = collect$1;
1763
+ /** @deprecated Use `ast.schema.applyOptionality`. Removed in Kubb v6. */
1764
+ const optionality = applyOptionality;
1765
+ /** @deprecated Use `ast.node.getText`. Removed in Kubb v6. */
1766
+ const extractStringsFromNodes = getNodeText;
1767
+ /** @deprecated Use `ast.macro.compareOrder`. Removed in Kubb v6. */
1768
+ const compareEnforce = compareMacroOrder;
1769
+ /** @deprecated Use `createPrinter` from `@kubb/kit`. Removed in Kubb v6. */
1770
+ const createPrinter = createPrinter$1;
1771
+ /** @deprecated Use `defineMacro` from `@kubb/kit`. Removed in Kubb v6. */
1772
+ const defineMacro = defineMacro$1;
1773
+ /** @deprecated Use `ast.macro.apply`. Removed in Kubb v6. */
1774
+ const applyMacros = applyMacros$1;
1775
+ /** @deprecated Use `ast.node.kinds`. Removed in Kubb v6. */
1776
+ const nodeKinds = nodeKinds$1;
1777
+ /** @deprecated Use `ast.schema.types`. Removed in Kubb v6. */
1778
+ const schemaTypes = schemaTypes$1;
1779
+ /** @deprecated Use `ast.schema.find`. Removed in Kubb v6. */
1780
+ const findSchema = findSchema$1;
1781
+ /** @deprecated Use `ast.schema.narrow`. Removed in Kubb v6. */
1782
+ const narrowSchema = narrowSchema$1;
1783
+ /** @deprecated Use `ast.node.transform`. Removed in Kubb v6. */
1784
+ const transform = transform$1;
1785
+ /** @deprecated Use `ast.node.collect`. Removed in Kubb v6. */
1786
+ const collect = collect$1;
1787
+ /** @deprecated Use `ast.file.inline`. Removed in Kubb v6. */
1788
+ const inlineFiles = inlineFiles$1;
1789
+ // src/auth/index.ts
1790
+ var auth_exports = /* @__PURE__ */ __exportAll({ getRequirements: () => getRequirements });
1791
+ /** Joins each requirement with its schemes. Drops undeclared and repeated requirements, and keeps an empty one (no auth needed). */
1792
+ function getRequirements({ auth, schemes }) {
1793
+ const credentials = new Map(schemes?.map((scheme) => [scheme.name, scheme.credential]));
1794
+ const seen = /* @__PURE__ */ new Set();
1795
+ const resolved = [];
1796
+ for (const requirement of auth ?? []) {
1797
+ const key = requirement.map(({ scheme, scopes }) => [scheme, ...scopes ?? []].join(" ")).join("\0");
1798
+ if (seen.has(key) || !requirement.every(({ scheme }) => credentials.has(scheme))) continue;
1799
+ seen.add(key);
1800
+ resolved.push(requirement.map(({ scheme, scopes }) => ({
1801
+ name: scheme,
1802
+ credential: credentials.get(scheme),
1803
+ scopes: scopes ?? []
1804
+ })));
1461
1805
  }
1806
+ return resolved;
1462
1807
  }
1463
1808
  // src/nodes/code.ts
1464
1809
  /**
@@ -1470,7 +1815,7 @@ function inferDiscriminatorPropertyName(members) {
1470
1815
  * // export const pets: Pet[] = ... as const
1471
1816
  * ```
1472
1817
  */
1473
- const createConst = nodeFactory(nodeKinds.const);
1818
+ const createConst = nodeFactory(nodeKinds$1.const);
1474
1819
  /**
1475
1820
  * Creates a `TypeNode` representing a TypeScript `type` alias declaration.
1476
1821
  *
@@ -1480,7 +1825,7 @@ const createConst = nodeFactory(nodeKinds.const);
1480
1825
  * // export type Pet = ...
1481
1826
  * ```
1482
1827
  */
1483
- const createType = nodeFactory(nodeKinds.type);
1828
+ const createType = nodeFactory(nodeKinds$1.type);
1484
1829
  /**
1485
1830
  * Creates a `FunctionNode` representing a TypeScript `function` declaration.
1486
1831
  *
@@ -1490,7 +1835,7 @@ const createType = nodeFactory(nodeKinds.type);
1490
1835
  * // export async function fetchPet(): Promise<Pet> { ... }
1491
1836
  * ```
1492
1837
  */
1493
- const createFunction = nodeFactory(nodeKinds.function);
1838
+ const createFunction = nodeFactory(nodeKinds$1.function);
1494
1839
  /**
1495
1840
  * Creates an `ArrowFunctionNode` representing a TypeScript arrow function.
1496
1841
  *
@@ -1500,7 +1845,7 @@ const createFunction = nodeFactory(nodeKinds.function);
1500
1845
  * // export const double = (n: number) => ...
1501
1846
  * ```
1502
1847
  */
1503
- const createArrowFunction = nodeFactory(nodeKinds.arrowFunction);
1848
+ const createArrowFunction = nodeFactory(nodeKinds$1.arrowFunction);
1504
1849
  /**
1505
1850
  * Creates a {@link TextNode} representing a raw string fragment in the source output.
1506
1851
  *
@@ -1512,7 +1857,7 @@ const createArrowFunction = nodeFactory(nodeKinds.arrowFunction);
1512
1857
  */
1513
1858
  function createText(value) {
1514
1859
  return {
1515
- kind: nodeKinds.text,
1860
+ kind: nodeKinds$1.text,
1516
1861
  value
1517
1862
  };
1518
1863
  }
@@ -1526,7 +1871,7 @@ function createText(value) {
1526
1871
  * ```
1527
1872
  */
1528
1873
  function createBreak() {
1529
- return { kind: nodeKinds.break };
1874
+ return { kind: nodeKinds$1.break };
1530
1875
  }
1531
1876
  /**
1532
1877
  * Creates a {@link JsxNode} representing a raw JSX fragment in the source output.
@@ -1539,7 +1884,7 @@ function createBreak() {
1539
1884
  */
1540
1885
  function createJsx(value) {
1541
1886
  return {
1542
- kind: nodeKinds.jsx,
1887
+ kind: nodeKinds$1.jsx,
1543
1888
  value
1544
1889
  };
1545
1890
  }
@@ -1547,7 +1892,7 @@ function createJsx(value) {
1547
1892
  /**
1548
1893
  * Creates a `ContentNode` for a single request-body or response content type.
1549
1894
  */
1550
- const createContent = nodeFactory(nodeKinds.content);
1895
+ const createContent = nodeFactory(nodeKinds$1.content);
1551
1896
  // src/nodes/input.ts
1552
1897
  /**
1553
1898
  * Creates an `InputNode`, defaulting `schemas`/`operations` to empty arrays and `meta` to an empty
@@ -1565,14 +1910,14 @@ function createInput(overrides = {}) {
1565
1910
  operations: [],
1566
1911
  meta: {},
1567
1912
  ...overrides,
1568
- kind: nodeKinds.input
1913
+ kind: nodeKinds$1.input
1569
1914
  };
1570
1915
  }
1571
1916
  // src/nodes/requestBody.ts
1572
1917
  /**
1573
1918
  * Creates a `RequestBodyNode`.
1574
1919
  */
1575
- const createRequestBody = nodeFactory(nodeKinds.requestBody);
1920
+ const createRequestBody = nodeFactory(nodeKinds$1.requestBody);
1576
1921
  // src/nodes/operation.ts
1577
1922
  function createOperation(props) {
1578
1923
  const { requestBody, ...rest } = props;
@@ -1582,7 +1927,7 @@ function createOperation(props) {
1582
1927
  parameters: [],
1583
1928
  responses: [],
1584
1929
  ...rest,
1585
- kind: nodeKinds.operation,
1930
+ kind: nodeKinds$1.operation,
1586
1931
  ...isHttp ? { protocol: "http" } : {},
1587
1932
  requestBody: requestBody ? createRequestBody(requestBody) : void 0
1588
1933
  };
@@ -1607,8 +1952,8 @@ function createParameter(input) {
1607
1952
  return {
1608
1953
  ...input,
1609
1954
  required,
1610
- schema: optionality(input.schema, required),
1611
- kind: nodeKinds.parameter
1955
+ schema: applyOptionality(input.schema, required),
1956
+ kind: nodeKinds$1.parameter
1612
1957
  };
1613
1958
  }
1614
1959
  // src/nodes/property.ts
@@ -1631,8 +1976,8 @@ function createProperty(input) {
1631
1976
  return {
1632
1977
  ...input,
1633
1978
  required,
1634
- schema: optionality(input.schema, required),
1635
- kind: nodeKinds.property
1979
+ schema: applyOptionality(input.schema, required),
1980
+ kind: nodeKinds$1.property
1636
1981
  };
1637
1982
  }
1638
1983
  // src/nodes/response.ts
@@ -1658,46 +2003,7 @@ function createResponse(props) {
1658
2003
  return {
1659
2004
  ...rest,
1660
2005
  content: entries,
1661
- kind: nodeKinds.response
1662
- };
1663
- }
1664
- // src/nodes/schema.ts
1665
- /**
1666
- * Maps schema `type` to its underlying `primitive`.
1667
- * Primitive types map to themselves and special string formats map to `'string'`.
1668
- * Any type not listed here (such as `ref`, `enum`, `union`, `intersection`, `not`, `tuple`, `ipv4`, `ipv6`, `blob`) has no `primitive`.
1669
- */
1670
- const TYPE_TO_PRIMITIVE = {
1671
- string: "string",
1672
- number: "number",
1673
- integer: "integer",
1674
- bigint: "bigint",
1675
- boolean: "boolean",
1676
- null: "null",
1677
- any: "any",
1678
- unknown: "unknown",
1679
- void: "void",
1680
- never: "never",
1681
- object: "object",
1682
- array: "array",
1683
- date: "date",
1684
- uuid: "string",
1685
- email: "string",
1686
- url: "string",
1687
- datetime: "string",
1688
- time: "string"
1689
- };
1690
- function createSchema(props) {
1691
- if (props.type === "object") return {
1692
- properties: [],
1693
- primitive: "object",
1694
- ...props,
1695
- kind: nodeKinds.schema
1696
- };
1697
- return {
1698
- primitive: TYPE_TO_PRIMITIVE[props.type],
1699
- ...props,
1700
- kind: nodeKinds.schema
2006
+ kind: nodeKinds$1.response
1701
2007
  };
1702
2008
  }
1703
2009
  // src/nodes/index.ts
@@ -1722,10 +2028,26 @@ var nodes_exports = /* @__PURE__ */ __exportAll({
1722
2028
  createText: () => createText,
1723
2029
  createType: () => createType
1724
2030
  });
2031
+ // src/server/index.ts
2032
+ var server_exports = /* @__PURE__ */ __exportAll({
2033
+ get: () => get,
2034
+ groupByName: () => groupByName
2035
+ });
2036
+ /** The server at `index`, else the first one. */
2037
+ function get({ servers, index = 0 }) {
2038
+ return servers?.[index] ?? servers?.[0];
2039
+ }
2040
+ /** Named servers by name. The first server with a name wins. */
2041
+ function groupByName(servers) {
2042
+ const named = /* @__PURE__ */ new Map();
2043
+ for (const server of servers ?? []) if (server.name && !named.has(server.name)) named.set(server.name, server);
2044
+ return named;
2045
+ }
1725
2046
  // src/index.ts
1726
2047
  var src_exports = /* @__PURE__ */ __exportAll({
1727
2048
  applyMacros: () => applyMacros,
1728
2049
  ast: () => src_exports,
2050
+ auth: () => auth_exports,
1729
2051
  collect: () => collect,
1730
2052
  collectImportedRefNames: () => collectImportedRefNames,
1731
2053
  collectSchemaRefs: () => collectSchemaRefs,
@@ -1736,22 +2058,31 @@ var src_exports = /* @__PURE__ */ __exportAll({
1736
2058
  defineMacro: () => defineMacro,
1737
2059
  extractStringsFromNodes: () => extractStringsFromNodes,
1738
2060
  factory: () => nodes_exports,
2061
+ file: () => file_exports,
1739
2062
  findCircularSchemas: () => findCircularSchemas,
1740
2063
  findSchema: () => findSchema,
1741
2064
  getSchemaLiteralValues: () => getSchemaLiteralValues,
2065
+ graph: () => graph_exports,
2066
+ http: () => http_exports,
1742
2067
  inferDiscriminatorPropertyName: () => inferDiscriminatorPropertyName,
1743
2068
  inlineFiles: () => inlineFiles,
1744
2069
  isBareRef: () => isBareRef,
1745
2070
  isHttpOperationNode: () => isHttpOperationNode,
2071
+ macro: () => macro_exports,
2072
+ mediaType: () => mediaType_exports,
1746
2073
  mergeRefWithSchema: () => mergeRefWithSchema,
1747
2074
  narrowSchema: () => narrowSchema,
2075
+ node: () => node_exports,
1748
2076
  nodeKinds: () => nodeKinds,
1749
2077
  optionality: () => optionality,
2078
+ ref: () => ref_exports,
1750
2079
  refName: () => refName,
1751
2080
  resolveRef: () => resolveRef,
1752
2081
  resolveRefName: () => resolveRefName,
1753
2082
  resolveSchemaProperties: () => resolveSchemaProperties,
2083
+ schema: () => schema_exports,
1754
2084
  schemaTypes: () => schemaTypes,
2085
+ server: () => server_exports,
1755
2086
  transform: () => transform
1756
2087
  });
1757
2088
  exports.applyMacros = applyMacros;
@@ -1761,6 +2092,12 @@ Object.defineProperty(exports, "ast", {
1761
2092
  return src_exports;
1762
2093
  }
1763
2094
  });
2095
+ Object.defineProperty(exports, "auth", {
2096
+ enumerable: true,
2097
+ get: function() {
2098
+ return auth_exports;
2099
+ }
2100
+ });
1764
2101
  exports.collect = collect;
1765
2102
  exports.collectImportedRefNames = collectImportedRefNames;
1766
2103
  exports.collectSchemaRefs = collectSchemaRefs;
@@ -1776,22 +2113,76 @@ Object.defineProperty(exports, "factory", {
1776
2113
  return nodes_exports;
1777
2114
  }
1778
2115
  });
2116
+ Object.defineProperty(exports, "file", {
2117
+ enumerable: true,
2118
+ get: function() {
2119
+ return file_exports;
2120
+ }
2121
+ });
1779
2122
  exports.findCircularSchemas = findCircularSchemas;
1780
2123
  exports.findSchema = findSchema;
1781
2124
  exports.getSchemaLiteralValues = getSchemaLiteralValues;
2125
+ Object.defineProperty(exports, "graph", {
2126
+ enumerable: true,
2127
+ get: function() {
2128
+ return graph_exports;
2129
+ }
2130
+ });
2131
+ Object.defineProperty(exports, "http", {
2132
+ enumerable: true,
2133
+ get: function() {
2134
+ return http_exports;
2135
+ }
2136
+ });
1782
2137
  exports.inferDiscriminatorPropertyName = inferDiscriminatorPropertyName;
1783
2138
  exports.inlineFiles = inlineFiles;
1784
2139
  exports.isBareRef = isBareRef;
1785
2140
  exports.isHttpOperationNode = isHttpOperationNode;
2141
+ Object.defineProperty(exports, "macro", {
2142
+ enumerable: true,
2143
+ get: function() {
2144
+ return macro_exports;
2145
+ }
2146
+ });
2147
+ Object.defineProperty(exports, "mediaType", {
2148
+ enumerable: true,
2149
+ get: function() {
2150
+ return mediaType_exports;
2151
+ }
2152
+ });
1786
2153
  exports.mergeRefWithSchema = mergeRefWithSchema;
1787
2154
  exports.narrowSchema = narrowSchema;
2155
+ Object.defineProperty(exports, "node", {
2156
+ enumerable: true,
2157
+ get: function() {
2158
+ return node_exports;
2159
+ }
2160
+ });
1788
2161
  exports.nodeKinds = nodeKinds;
1789
2162
  exports.optionality = optionality;
2163
+ Object.defineProperty(exports, "ref", {
2164
+ enumerable: true,
2165
+ get: function() {
2166
+ return ref_exports;
2167
+ }
2168
+ });
1790
2169
  exports.refName = refName;
1791
2170
  exports.resolveRef = resolveRef;
1792
2171
  exports.resolveRefName = resolveRefName;
1793
2172
  exports.resolveSchemaProperties = resolveSchemaProperties;
2173
+ Object.defineProperty(exports, "schema", {
2174
+ enumerable: true,
2175
+ get: function() {
2176
+ return schema_exports;
2177
+ }
2178
+ });
1794
2179
  exports.schemaTypes = schemaTypes;
2180
+ Object.defineProperty(exports, "server", {
2181
+ enumerable: true,
2182
+ get: function() {
2183
+ return server_exports;
2184
+ }
2185
+ });
1795
2186
  exports.transform = transform;
1796
2187
 
1797
2188
  //# sourceMappingURL=index.cjs.map