@kubb/ast 5.18.5 → 5.18.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -8
- package/dist/index.cjs +1019 -628
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1496 -1317
- package/dist/index.d.ts +1496 -1317
- package/dist/index.js +1086 -755
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,21 +1,40 @@
|
|
|
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
|
|
6
|
-
|
|
5
|
+
var __name = (target, value) => __defProp(target, "name", {
|
|
6
|
+
value,
|
|
7
|
+
configurable: true
|
|
8
|
+
});
|
|
9
|
+
var __exportAll = (all, flags) => {
|
|
10
|
+
let target = Object.setPrototypeOf({}, null);
|
|
7
11
|
for (var name in all) __defProp(target, name, {
|
|
8
12
|
get: all[name],
|
|
9
13
|
enumerable: true
|
|
10
14
|
});
|
|
11
|
-
if (!
|
|
12
|
-
return target;
|
|
15
|
+
if (!(flags & 1)) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
16
|
+
return flags & 2 ? target : Object.preventExtensions(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 (
|
|
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
|
-
*
|
|
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 =
|
|
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
|
|
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/
|
|
681
|
+
// src/utils/refs.ts
|
|
480
682
|
/**
|
|
481
|
-
*
|
|
482
|
-
*
|
|
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
|
|
485
|
-
|
|
486
|
-
if (
|
|
487
|
-
|
|
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
|
-
*
|
|
491
|
-
*
|
|
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
|
-
* `
|
|
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
|
|
497
|
-
|
|
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
|
-
*
|
|
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
|
|
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
|
|
514
|
-
return
|
|
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
|
-
*
|
|
518
|
-
*
|
|
519
|
-
*
|
|
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
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
if (
|
|
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
|
|
772
|
+
return merged;
|
|
531
773
|
}
|
|
774
|
+
__name(mergeRefWithSchema$1, "mergeRefWithSchema");
|
|
532
775
|
/**
|
|
533
|
-
*
|
|
534
|
-
*
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
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
|
-
*
|
|
553
|
-
*
|
|
554
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
843
|
+
* collectImportedRefNames(petSchema)
|
|
844
|
+
* // ['Category', 'Tag']
|
|
564
845
|
* ```
|
|
565
846
|
*/
|
|
566
|
-
function
|
|
567
|
-
|
|
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
|
-
|
|
850
|
+
__name(collectImportedRefNames$1, "collectImportedRefNames");
|
|
574
851
|
/**
|
|
575
|
-
*
|
|
576
|
-
*
|
|
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
|
|
579
|
-
const
|
|
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
|
-
|
|
582
|
-
|
|
583
|
-
|
|
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 {
|
|
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
|
-
|
|
659
|
-
|
|
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
|
-
*
|
|
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
|
|
680
|
-
if (
|
|
681
|
-
|
|
682
|
-
|
|
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
|
-
*
|
|
704
|
-
*
|
|
705
|
-
*
|
|
706
|
-
*
|
|
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
|
-
|
|
1180
|
+
function compareMacroOrder(a, b) {
|
|
1181
|
+
return enforceWeight(a) - enforceWeight(b);
|
|
1182
|
+
}
|
|
709
1183
|
/**
|
|
710
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
1397
|
-
* `
|
|
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
|
|
1402
|
-
*
|
|
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
|
|
1406
|
-
const
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
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
|
-
*
|
|
1422
|
-
*
|
|
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
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
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
|
-
|
|
1432
|
-
const
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
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:
|
|
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:
|
|
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
|