@kubb/ast 5.18.5 → 5.18.6

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