@kubb/ast 5.0.0-alpha.5 → 5.0.0-alpha.51
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 +24 -10
- package/dist/index.cjs +1643 -433
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3318 -21
- package/dist/index.js +1585 -423
- package/dist/index.js.map +1 -1
- package/package.json +23 -34
- package/src/constants.ts +126 -5
- package/src/factory.ts +680 -22
- package/src/guards.ts +77 -9
- package/src/index.ts +37 -6
- package/src/infer.ts +130 -0
- package/src/mocks.ts +101 -25
- package/src/nodes/base.ts +44 -4
- package/src/nodes/code.ts +304 -0
- package/src/nodes/file.ts +230 -0
- package/src/nodes/function.ts +223 -0
- package/src/nodes/http.ts +17 -5
- package/src/nodes/index.ts +47 -7
- package/src/nodes/operation.ts +69 -6
- package/src/nodes/output.ts +26 -0
- package/src/nodes/parameter.ts +27 -1
- package/src/nodes/property.ts +23 -1
- package/src/nodes/response.ts +29 -3
- package/src/nodes/root.ts +38 -12
- package/src/nodes/schema.ts +414 -42
- package/src/printer.ts +181 -60
- package/src/refs.ts +39 -7
- package/src/resolvers.ts +45 -0
- package/src/transformers.ts +159 -0
- package/src/types.ts +29 -4
- package/src/utils.ts +703 -10
- package/src/visitor.ts +408 -96
- package/dist/types.cjs +0 -0
- package/dist/types.d.ts +0 -2
- package/dist/types.js +0 -1
- package/dist/visitor-CE4-xBHW.d.ts +0 -674
package/dist/index.cjs
CHANGED
|
@@ -1,20 +1,64 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
|
|
2
|
+
//#region \0rolldown/runtime.js
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
13
|
+
get: ((k) => from[k]).bind(null, key),
|
|
14
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
20
|
+
value: mod,
|
|
21
|
+
enumerable: true
|
|
22
|
+
}) : target, mod));
|
|
3
23
|
//#endregion
|
|
4
|
-
let
|
|
24
|
+
let node_crypto = require("node:crypto");
|
|
25
|
+
let node_path = require("node:path");
|
|
26
|
+
node_path = __toESM(node_path, 1);
|
|
5
27
|
//#region src/constants.ts
|
|
6
28
|
const visitorDepths = {
|
|
7
29
|
shallow: "shallow",
|
|
8
30
|
deep: "deep"
|
|
9
31
|
};
|
|
10
32
|
const nodeKinds = {
|
|
11
|
-
|
|
33
|
+
input: "Input",
|
|
34
|
+
output: "Output",
|
|
12
35
|
operation: "Operation",
|
|
13
36
|
schema: "Schema",
|
|
14
37
|
property: "Property",
|
|
15
38
|
parameter: "Parameter",
|
|
16
|
-
response: "Response"
|
|
39
|
+
response: "Response",
|
|
40
|
+
functionParameter: "FunctionParameter",
|
|
41
|
+
parameterGroup: "ParameterGroup",
|
|
42
|
+
functionParameters: "FunctionParameters",
|
|
43
|
+
type: "Type",
|
|
44
|
+
file: "File",
|
|
45
|
+
import: "Import",
|
|
46
|
+
export: "Export",
|
|
47
|
+
source: "Source",
|
|
48
|
+
text: "Text",
|
|
49
|
+
break: "Break"
|
|
17
50
|
};
|
|
51
|
+
/**
|
|
52
|
+
* Canonical schema type strings used by AST schema nodes.
|
|
53
|
+
*
|
|
54
|
+
* These values are used across the AST as stable discriminators
|
|
55
|
+
* (for example `schema.type === schemaTypes.object`).
|
|
56
|
+
*
|
|
57
|
+
* The map is grouped by intent:
|
|
58
|
+
* - primitives (`string`, `number`, `boolean`, ...)
|
|
59
|
+
* - structural/composite (`object`, `array`, `union`, ...)
|
|
60
|
+
* - special OpenAPI-oriented types (`ref`, `datetime`, `uuid`, ...)
|
|
61
|
+
*/
|
|
18
62
|
const schemaTypes = {
|
|
19
63
|
string: "string",
|
|
20
64
|
number: "number",
|
|
@@ -38,9 +82,27 @@ const schemaTypes = {
|
|
|
38
82
|
uuid: "uuid",
|
|
39
83
|
email: "email",
|
|
40
84
|
url: "url",
|
|
85
|
+
ipv4: "ipv4",
|
|
86
|
+
ipv6: "ipv6",
|
|
41
87
|
blob: "blob",
|
|
42
88
|
never: "never"
|
|
43
89
|
};
|
|
90
|
+
/**
|
|
91
|
+
* Primitive scalar schema types used when simplifying union members.
|
|
92
|
+
*/
|
|
93
|
+
const SCALAR_PRIMITIVE_TYPES = new Set([
|
|
94
|
+
"string",
|
|
95
|
+
"number",
|
|
96
|
+
"integer",
|
|
97
|
+
"bigint",
|
|
98
|
+
"boolean"
|
|
99
|
+
]);
|
|
100
|
+
/**
|
|
101
|
+
* Returns `true` when `type` is a scalar primitive schema type.
|
|
102
|
+
*/
|
|
103
|
+
function isScalarPrimitive(type) {
|
|
104
|
+
return SCALAR_PRIMITIVE_TYPES.has(type);
|
|
105
|
+
}
|
|
44
106
|
const httpMethods = {
|
|
45
107
|
get: "GET",
|
|
46
108
|
post: "POST",
|
|
@@ -73,20 +135,725 @@ const mediaTypes = {
|
|
|
73
135
|
videoMp4: "video/mp4"
|
|
74
136
|
};
|
|
75
137
|
//#endregion
|
|
138
|
+
//#region ../../internals/utils/src/casing.ts
|
|
139
|
+
/**
|
|
140
|
+
* Shared implementation for camelCase and PascalCase conversion.
|
|
141
|
+
* Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
|
|
142
|
+
* and capitalizes each word according to `pascal`.
|
|
143
|
+
*
|
|
144
|
+
* When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
|
|
145
|
+
*/
|
|
146
|
+
function toCamelOrPascal(text, pascal) {
|
|
147
|
+
return text.trim().replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/(\d)([a-z])/g, "$1 $2").split(/[\s\-_./\\:]+/).filter(Boolean).map((word, i) => {
|
|
148
|
+
if (word.length > 1 && word === word.toUpperCase()) return word;
|
|
149
|
+
if (i === 0 && !pascal) return word.charAt(0).toLowerCase() + word.slice(1);
|
|
150
|
+
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
151
|
+
}).join("").replace(/[^a-zA-Z0-9]/g, "");
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Splits `text` on `.` and applies `transformPart` to each segment.
|
|
155
|
+
* The last segment receives `isLast = true`, all earlier segments receive `false`.
|
|
156
|
+
* Segments are joined with `/` to form a file path.
|
|
157
|
+
*
|
|
158
|
+
* Only splits on dots followed by a letter so that version numbers
|
|
159
|
+
* embedded in operationIds (e.g. `v2025.0`) are kept intact.
|
|
160
|
+
*
|
|
161
|
+
* Empty segments are filtered before joining. They arise when the text starts with
|
|
162
|
+
* a dot followed immediately by a letter (e.g. `..Schema` splits into `['..', 'Schema']`
|
|
163
|
+
* and `'..'` transforms to an empty string). Without this filter the join would produce
|
|
164
|
+
* a leading `/`, which `path.resolve` would interpret as an absolute path, allowing
|
|
165
|
+
* generated files to escape the configured output directory.
|
|
166
|
+
*/
|
|
167
|
+
function applyToFileParts(text, transformPart) {
|
|
168
|
+
const parts = text.split(/\.(?=[a-zA-Z])/);
|
|
169
|
+
return parts.map((part, i) => transformPart(part, i === parts.length - 1)).filter(Boolean).join("/");
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Converts `text` to camelCase.
|
|
173
|
+
* When `isFile` is `true`, dot-separated segments are each cased independently and joined with `/`.
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* camelCase('hello-world') // 'helloWorld'
|
|
177
|
+
* camelCase('pet.petId', { isFile: true }) // 'pet/petId'
|
|
178
|
+
*/
|
|
179
|
+
function camelCase(text, { isFile, prefix = "", suffix = "" } = {}) {
|
|
180
|
+
if (isFile) return applyToFileParts(text, (part, isLast) => camelCase(part, isLast ? {
|
|
181
|
+
prefix,
|
|
182
|
+
suffix
|
|
183
|
+
} : {}));
|
|
184
|
+
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Converts `text` to PascalCase.
|
|
188
|
+
* When `isFile` is `true`, the last dot-separated segment is PascalCased and earlier segments are camelCased.
|
|
189
|
+
*
|
|
190
|
+
* @example
|
|
191
|
+
* pascalCase('hello-world') // 'HelloWorld'
|
|
192
|
+
* pascalCase('pet.petId', { isFile: true }) // 'pet/PetId'
|
|
193
|
+
*/
|
|
194
|
+
function pascalCase(text, { isFile, prefix = "", suffix = "" } = {}) {
|
|
195
|
+
if (isFile) return applyToFileParts(text, (part, isLast) => isLast ? pascalCase(part, {
|
|
196
|
+
prefix,
|
|
197
|
+
suffix
|
|
198
|
+
}) : camelCase(part));
|
|
199
|
+
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
|
|
200
|
+
}
|
|
201
|
+
//#endregion
|
|
202
|
+
//#region ../../internals/utils/src/reserved.ts
|
|
203
|
+
/**
|
|
204
|
+
* Returns `true` when `name` is a syntactically valid JavaScript variable name.
|
|
205
|
+
*
|
|
206
|
+
* @example
|
|
207
|
+
* ```ts
|
|
208
|
+
* isValidVarName('status') // true
|
|
209
|
+
* isValidVarName('class') // false (reserved word)
|
|
210
|
+
* isValidVarName('42foo') // false (starts with digit)
|
|
211
|
+
* ```
|
|
212
|
+
*/
|
|
213
|
+
function isValidVarName(name) {
|
|
214
|
+
try {
|
|
215
|
+
new Function(`var ${name}`);
|
|
216
|
+
} catch {
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
//#endregion
|
|
222
|
+
//#region ../../internals/utils/src/string.ts
|
|
223
|
+
/**
|
|
224
|
+
* Strips the file extension from a path or file name.
|
|
225
|
+
* Only removes the last `.ext` segment when the dot is not part of a directory name.
|
|
226
|
+
*
|
|
227
|
+
* @example
|
|
228
|
+
* trimExtName('petStore.ts') // 'petStore'
|
|
229
|
+
* trimExtName('/src/models/pet.ts') // '/src/models/pet'
|
|
230
|
+
* trimExtName('/project.v2/gen/pet.ts') // '/project.v2/gen/pet'
|
|
231
|
+
* trimExtName('noExtension') // 'noExtension'
|
|
232
|
+
*/
|
|
233
|
+
function trimExtName(text) {
|
|
234
|
+
const dotIndex = text.lastIndexOf(".");
|
|
235
|
+
if (dotIndex > 0 && !text.includes("/", dotIndex)) return text.slice(0, dotIndex);
|
|
236
|
+
return text;
|
|
237
|
+
}
|
|
238
|
+
//#endregion
|
|
239
|
+
//#region src/guards.ts
|
|
240
|
+
/**
|
|
241
|
+
* Narrows a `SchemaNode` to the variant that matches `type`.
|
|
242
|
+
*
|
|
243
|
+
* @example
|
|
244
|
+
* ```ts
|
|
245
|
+
* const schema = createSchema({ type: 'string' })
|
|
246
|
+
* const stringNode = narrowSchema(schema, 'string') // StringSchemaNode | undefined
|
|
247
|
+
* ```
|
|
248
|
+
*/
|
|
249
|
+
function narrowSchema(node, type) {
|
|
250
|
+
return node?.type === type ? node : void 0;
|
|
251
|
+
}
|
|
252
|
+
function isKind(kind) {
|
|
253
|
+
return (node) => node.kind === kind;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Returns `true` when the input is an `InputNode`.
|
|
257
|
+
*
|
|
258
|
+
* @example
|
|
259
|
+
* ```ts
|
|
260
|
+
* if (isInputNode(node)) {
|
|
261
|
+
* console.log(node.schemas.length)
|
|
262
|
+
* }
|
|
263
|
+
* ```
|
|
264
|
+
*/
|
|
265
|
+
const isInputNode = isKind("Input");
|
|
266
|
+
/**
|
|
267
|
+
* Returns `true` when the input is an `OutputNode`.
|
|
268
|
+
*
|
|
269
|
+
* @example
|
|
270
|
+
* ```ts
|
|
271
|
+
* if (isOutputNode(node)) {
|
|
272
|
+
* console.log(node.files.length)
|
|
273
|
+
* }
|
|
274
|
+
* ```
|
|
275
|
+
*/
|
|
276
|
+
const isOutputNode = isKind("Output");
|
|
277
|
+
/**
|
|
278
|
+
* Returns `true` when the input is an `OperationNode`.
|
|
279
|
+
*
|
|
280
|
+
* @example
|
|
281
|
+
* ```ts
|
|
282
|
+
* if (isOperationNode(node)) {
|
|
283
|
+
* console.log(node.operationId)
|
|
284
|
+
* }
|
|
285
|
+
* ```
|
|
286
|
+
*/
|
|
287
|
+
const isOperationNode = isKind("Operation");
|
|
288
|
+
/**
|
|
289
|
+
* Returns `true` when the input is a `SchemaNode`.
|
|
290
|
+
*
|
|
291
|
+
* @example
|
|
292
|
+
* ```ts
|
|
293
|
+
* if (isSchemaNode(node)) {
|
|
294
|
+
* console.log(node.type)
|
|
295
|
+
* }
|
|
296
|
+
* ```
|
|
297
|
+
*/
|
|
298
|
+
const isSchemaNode = isKind("Schema");
|
|
299
|
+
isKind("Property");
|
|
300
|
+
isKind("Parameter");
|
|
301
|
+
isKind("Response");
|
|
302
|
+
isKind("FunctionParameter");
|
|
303
|
+
isKind("ParameterGroup");
|
|
304
|
+
isKind("FunctionParameters");
|
|
305
|
+
//#endregion
|
|
306
|
+
//#region src/utils.ts
|
|
307
|
+
const plainStringTypes = new Set([
|
|
308
|
+
"string",
|
|
309
|
+
"uuid",
|
|
310
|
+
"email",
|
|
311
|
+
"url",
|
|
312
|
+
"datetime"
|
|
313
|
+
]);
|
|
314
|
+
/**
|
|
315
|
+
* Returns a merged schema view for a ref node, combining the resolved `node.schema`
|
|
316
|
+
* (base from the referenced definition) with any usage-site sibling fields set directly
|
|
317
|
+
* on the ref node (description, readOnly, nullable, deprecated, etc.).
|
|
318
|
+
*
|
|
319
|
+
* Usage-site fields take precedence over the resolved schema's own fields when both are defined.
|
|
320
|
+
*
|
|
321
|
+
* For non-ref nodes the node itself is returned unchanged.
|
|
322
|
+
*/
|
|
323
|
+
function syncSchemaRef(node) {
|
|
324
|
+
const ref = narrowSchema(node, "ref");
|
|
325
|
+
if (!ref) return node;
|
|
326
|
+
if (!ref.schema) return node;
|
|
327
|
+
const { kind: _kind, type: _type, name: _name, ref: _ref, schema: _schema, ...overrides } = ref;
|
|
328
|
+
const definedOverrides = Object.fromEntries(Object.entries(overrides).filter(([, v]) => v !== void 0));
|
|
329
|
+
return createSchema({
|
|
330
|
+
...ref.schema,
|
|
331
|
+
...definedOverrides
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Returns `true` when a schema is emitted as a plain `string` type.
|
|
336
|
+
*
|
|
337
|
+
* - `string`, `uuid`, `email`, `url`, `datetime` are always plain strings.
|
|
338
|
+
* - `date` and `time` are plain strings when their `representation` is `'string'` rather than `'date'`.
|
|
339
|
+
*
|
|
340
|
+
* @example
|
|
341
|
+
* ```ts
|
|
342
|
+
* isStringType(createSchema({ type: 'uuid' })) // true
|
|
343
|
+
* isStringType(createSchema({ type: 'date', representation: 'date' })) // false
|
|
344
|
+
* ```
|
|
345
|
+
*/
|
|
346
|
+
function isStringType(node) {
|
|
347
|
+
if (plainStringTypes.has(node.type)) return true;
|
|
348
|
+
const temporal = narrowSchema(node, "date") ?? narrowSchema(node, "time");
|
|
349
|
+
if (temporal) return temporal.representation !== "date";
|
|
350
|
+
return false;
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Applies casing rules to parameter names and returns a new parameter array.
|
|
354
|
+
*
|
|
355
|
+
* The input array is not mutated.
|
|
356
|
+
* If `casing` is not set, the original array is returned unchanged.
|
|
357
|
+
*
|
|
358
|
+
* Use this before passing parameters to schema builders so that property keys
|
|
359
|
+
* in generated output match the desired casing while preserving
|
|
360
|
+
* `OperationNode.parameters` for other consumers.
|
|
361
|
+
*
|
|
362
|
+
* @example
|
|
363
|
+
* ```ts
|
|
364
|
+
* const params = [createParameter({ name: 'pet_id', in: 'query', schema: createSchema({ type: 'string' }) })]
|
|
365
|
+
* const cased = caseParams(params, 'camelcase')
|
|
366
|
+
* // cased[0].name === 'petId'
|
|
367
|
+
* ```
|
|
368
|
+
*/
|
|
369
|
+
function caseParams(params, casing) {
|
|
370
|
+
if (!casing) return params;
|
|
371
|
+
return params.map((param) => {
|
|
372
|
+
const transformed = casing === "camelcase" || !isValidVarName(param.name) ? camelCase(param.name) : param.name;
|
|
373
|
+
return {
|
|
374
|
+
...param,
|
|
375
|
+
name: transformed
|
|
376
|
+
};
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Creates a single-property object schema used as a discriminator literal.
|
|
381
|
+
*
|
|
382
|
+
* @example
|
|
383
|
+
* ```ts
|
|
384
|
+
* createDiscriminantNode({ propertyName: 'type', value: 'dog' })
|
|
385
|
+
* // -> { type: 'object', properties: [{ name: 'type', required: true, schema: enum('dog') }] }
|
|
386
|
+
* ```
|
|
387
|
+
*/
|
|
388
|
+
function createDiscriminantNode({ propertyName, value }) {
|
|
389
|
+
return createSchema({
|
|
390
|
+
type: "object",
|
|
391
|
+
primitive: "object",
|
|
392
|
+
properties: [createProperty({
|
|
393
|
+
name: propertyName,
|
|
394
|
+
schema: createSchema({
|
|
395
|
+
type: "enum",
|
|
396
|
+
primitive: "string",
|
|
397
|
+
enumValues: [value]
|
|
398
|
+
}),
|
|
399
|
+
required: true
|
|
400
|
+
})]
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
function resolveParamsType({ node, param, resolver }) {
|
|
404
|
+
if (!resolver) return createParamsType({
|
|
405
|
+
variant: "reference",
|
|
406
|
+
name: param.schema.primitive ?? "unknown"
|
|
407
|
+
});
|
|
408
|
+
const individualName = resolver.resolveParamName(node, param);
|
|
409
|
+
const groupLocation = param.in === "path" || param.in === "query" || param.in === "header" ? param.in : void 0;
|
|
410
|
+
const groupResolvers = {
|
|
411
|
+
path: resolver.resolvePathParamsName,
|
|
412
|
+
query: resolver.resolveQueryParamsName,
|
|
413
|
+
header: resolver.resolveHeaderParamsName
|
|
414
|
+
};
|
|
415
|
+
const groupName = groupLocation ? groupResolvers[groupLocation].call(resolver, node, param) : void 0;
|
|
416
|
+
if (groupName && groupName !== individualName) return createParamsType({
|
|
417
|
+
variant: "member",
|
|
418
|
+
base: groupName,
|
|
419
|
+
key: param.name
|
|
420
|
+
});
|
|
421
|
+
return createParamsType({
|
|
422
|
+
variant: "reference",
|
|
423
|
+
name: individualName
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Converts an {@link OperationNode} into a {@link FunctionParametersNode}.
|
|
428
|
+
*
|
|
429
|
+
* Centralizes the per-plugin `getParams()` pattern. Provide a `resolver` for
|
|
430
|
+
* type resolution and `extraParams` for plugin-specific trailing parameters.
|
|
431
|
+
*
|
|
432
|
+
* @example
|
|
433
|
+
* ```ts
|
|
434
|
+
* const params = createOperationParams(node, {
|
|
435
|
+
* paramsType: 'inline',
|
|
436
|
+
* pathParamsType: 'inline',
|
|
437
|
+
* resolver: tsResolver,
|
|
438
|
+
* extraParams: [createFunctionParameter({ name: 'options', type: createParamsType({ variant: 'reference', name: 'Partial<RequestOptions>' }), default: '{}' })],
|
|
439
|
+
* })
|
|
440
|
+
* ```
|
|
441
|
+
*/
|
|
442
|
+
function createOperationParams(node, options) {
|
|
443
|
+
const { paramsType, pathParamsType, paramsCasing, resolver, pathParamsDefault, extraParams = [], paramNames, typeWrapper } = options;
|
|
444
|
+
const dataName = paramNames?.data ?? "data";
|
|
445
|
+
const paramsName = paramNames?.params ?? "params";
|
|
446
|
+
const headersName = paramNames?.headers ?? "headers";
|
|
447
|
+
const pathName = paramNames?.path ?? "pathParams";
|
|
448
|
+
const wrapType = (type) => createParamsType({
|
|
449
|
+
variant: "reference",
|
|
450
|
+
name: typeWrapper ? typeWrapper(type) : type
|
|
451
|
+
});
|
|
452
|
+
const wrapTypeNode = (type) => type.kind === "ParamsType" && type.variant === "reference" ? wrapType(type.name) : type;
|
|
453
|
+
const casedParams = caseParams(node.parameters, paramsCasing);
|
|
454
|
+
const pathParams = casedParams.filter((p) => p.in === "path");
|
|
455
|
+
const queryParams = casedParams.filter((p) => p.in === "query");
|
|
456
|
+
const headerParams = casedParams.filter((p) => p.in === "header");
|
|
457
|
+
const bodyType = node.requestBody?.schema ? wrapType(resolver?.resolveDataName(node) ?? "unknown") : void 0;
|
|
458
|
+
const bodyRequired = node.requestBody?.required ?? false;
|
|
459
|
+
const queryGroupType = resolver ? resolveGroupType({
|
|
460
|
+
node,
|
|
461
|
+
params: queryParams,
|
|
462
|
+
groupMethod: resolver.resolveQueryParamsName,
|
|
463
|
+
resolver
|
|
464
|
+
}) : void 0;
|
|
465
|
+
const headerGroupType = resolver ? resolveGroupType({
|
|
466
|
+
node,
|
|
467
|
+
params: headerParams,
|
|
468
|
+
groupMethod: resolver.resolveHeaderParamsName,
|
|
469
|
+
resolver
|
|
470
|
+
}) : void 0;
|
|
471
|
+
const params = [];
|
|
472
|
+
if (paramsType === "object") {
|
|
473
|
+
const children = [
|
|
474
|
+
...pathParams.map((p) => {
|
|
475
|
+
const type = resolveParamsType({
|
|
476
|
+
node,
|
|
477
|
+
param: p,
|
|
478
|
+
resolver
|
|
479
|
+
});
|
|
480
|
+
return createFunctionParameter({
|
|
481
|
+
name: p.name,
|
|
482
|
+
type: wrapTypeNode(type),
|
|
483
|
+
optional: !p.required
|
|
484
|
+
});
|
|
485
|
+
}),
|
|
486
|
+
...bodyType ? [createFunctionParameter({
|
|
487
|
+
name: dataName,
|
|
488
|
+
type: bodyType,
|
|
489
|
+
optional: !bodyRequired
|
|
490
|
+
})] : [],
|
|
491
|
+
...buildGroupParam({
|
|
492
|
+
name: paramsName,
|
|
493
|
+
node,
|
|
494
|
+
params: queryParams,
|
|
495
|
+
groupType: queryGroupType,
|
|
496
|
+
resolver,
|
|
497
|
+
wrapType
|
|
498
|
+
}),
|
|
499
|
+
...buildGroupParam({
|
|
500
|
+
name: headersName,
|
|
501
|
+
node,
|
|
502
|
+
params: headerParams,
|
|
503
|
+
groupType: headerGroupType,
|
|
504
|
+
resolver,
|
|
505
|
+
wrapType
|
|
506
|
+
})
|
|
507
|
+
];
|
|
508
|
+
if (children.length) params.push(createParameterGroup({
|
|
509
|
+
properties: children,
|
|
510
|
+
default: children.every((c) => c.optional) ? "{}" : void 0
|
|
511
|
+
}));
|
|
512
|
+
} else {
|
|
513
|
+
if (pathParams.length) if (pathParamsType === "inlineSpread") {
|
|
514
|
+
const spreadType = resolver?.resolvePathParamsName(node, pathParams[0]) ?? void 0;
|
|
515
|
+
params.push(createFunctionParameter({
|
|
516
|
+
name: pathName,
|
|
517
|
+
type: spreadType ? wrapType(spreadType) : void 0,
|
|
518
|
+
rest: true
|
|
519
|
+
}));
|
|
520
|
+
} else {
|
|
521
|
+
const pathChildren = pathParams.map((p) => {
|
|
522
|
+
const type = resolveParamsType({
|
|
523
|
+
node,
|
|
524
|
+
param: p,
|
|
525
|
+
resolver
|
|
526
|
+
});
|
|
527
|
+
return createFunctionParameter({
|
|
528
|
+
name: p.name,
|
|
529
|
+
type: wrapTypeNode(type),
|
|
530
|
+
optional: !p.required
|
|
531
|
+
});
|
|
532
|
+
});
|
|
533
|
+
params.push(createParameterGroup({
|
|
534
|
+
properties: pathChildren,
|
|
535
|
+
inline: pathParamsType === "inline",
|
|
536
|
+
default: pathParamsDefault ?? (pathChildren.every((c) => c.optional) ? "{}" : void 0)
|
|
537
|
+
}));
|
|
538
|
+
}
|
|
539
|
+
if (bodyType) params.push(createFunctionParameter({
|
|
540
|
+
name: dataName,
|
|
541
|
+
type: bodyType,
|
|
542
|
+
optional: !bodyRequired
|
|
543
|
+
}));
|
|
544
|
+
params.push(...buildGroupParam({
|
|
545
|
+
name: paramsName,
|
|
546
|
+
node,
|
|
547
|
+
params: queryParams,
|
|
548
|
+
groupType: queryGroupType,
|
|
549
|
+
resolver,
|
|
550
|
+
wrapType
|
|
551
|
+
}));
|
|
552
|
+
params.push(...buildGroupParam({
|
|
553
|
+
name: headersName,
|
|
554
|
+
node,
|
|
555
|
+
params: headerParams,
|
|
556
|
+
groupType: headerGroupType,
|
|
557
|
+
resolver,
|
|
558
|
+
wrapType
|
|
559
|
+
}));
|
|
560
|
+
}
|
|
561
|
+
params.push(...extraParams);
|
|
562
|
+
return createFunctionParameters({ params });
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Builds a single {@link FunctionParameterNode} for a query or header group.
|
|
566
|
+
* Returns an empty array when there are no params to emit.
|
|
567
|
+
*
|
|
568
|
+
* If a pre-resolved `groupType` is provided it emits `name: GroupType`.
|
|
569
|
+
* Otherwise, it builds an inline struct from the individual params.
|
|
570
|
+
*/
|
|
571
|
+
function buildGroupParam({ name, node, params, groupType, resolver, wrapType }) {
|
|
572
|
+
if (groupType) return [createFunctionParameter({
|
|
573
|
+
name,
|
|
574
|
+
type: groupType.type.kind === "ParamsType" && groupType.type.variant === "reference" ? wrapType(groupType.type.name) : groupType.type,
|
|
575
|
+
optional: groupType.optional
|
|
576
|
+
})];
|
|
577
|
+
if (params.length) return [createFunctionParameter({
|
|
578
|
+
name,
|
|
579
|
+
type: toStructType({
|
|
580
|
+
node,
|
|
581
|
+
params,
|
|
582
|
+
resolver
|
|
583
|
+
}),
|
|
584
|
+
optional: params.every((p) => !p.required)
|
|
585
|
+
})];
|
|
586
|
+
return [];
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* Derives a {@link ParamGroupType} from the resolver's group method.
|
|
590
|
+
* Returns `undefined` when the group name equals the individual param name (no real group).
|
|
591
|
+
*/
|
|
592
|
+
function resolveGroupType({ node, params, groupMethod, resolver }) {
|
|
593
|
+
if (!params.length) return;
|
|
594
|
+
const firstParam = params[0];
|
|
595
|
+
const groupName = groupMethod.call(resolver, node, firstParam);
|
|
596
|
+
if (groupName === resolver.resolveParamName(node, firstParam)) return;
|
|
597
|
+
const allOptional = params.every((p) => !p.required);
|
|
598
|
+
return {
|
|
599
|
+
type: createParamsType({
|
|
600
|
+
variant: "reference",
|
|
601
|
+
name: groupName
|
|
602
|
+
}),
|
|
603
|
+
optional: allOptional
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* Builds a {@link TypeNode} with `variant: 'struct'` for an inline anonymous type grouping named fields.
|
|
608
|
+
*
|
|
609
|
+
* Used when query or header parameters have no dedicated group type name.
|
|
610
|
+
* Each language printer renders this appropriately (TypeScript: `{ petId: string; name?: string }`).
|
|
611
|
+
*/
|
|
612
|
+
function toStructType({ node, params, resolver }) {
|
|
613
|
+
return createParamsType({
|
|
614
|
+
variant: "struct",
|
|
615
|
+
properties: params.map((p) => ({
|
|
616
|
+
name: p.name,
|
|
617
|
+
optional: !p.required,
|
|
618
|
+
type: resolveParamsType({
|
|
619
|
+
node,
|
|
620
|
+
param: p,
|
|
621
|
+
resolver
|
|
622
|
+
})
|
|
623
|
+
}))
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
function sourceKey(source) {
|
|
627
|
+
return `${source.name ?? extractStringsFromNodes(source.nodes)}:${source.isExportable ?? false}:${source.isTypeOnly ?? false}`;
|
|
628
|
+
}
|
|
629
|
+
function pathTypeKey(path, isTypeOnly) {
|
|
630
|
+
return `${path}:${isTypeOnly ?? false}`;
|
|
631
|
+
}
|
|
632
|
+
function exportKey(path, name, isTypeOnly, asAlias) {
|
|
633
|
+
return `${path}:${name ?? ""}:${isTypeOnly ?? false}:${asAlias ?? ""}`;
|
|
634
|
+
}
|
|
635
|
+
function importKey(path, name, isTypeOnly) {
|
|
636
|
+
return `${path}:${name ?? ""}:${isTypeOnly ?? false}`;
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Computes a multi-level sort key for exports and imports:
|
|
640
|
+
* non-array names first (wildcards/namespace aliases); type-only before value; alphabetical path; unnamed before named.
|
|
641
|
+
*/
|
|
642
|
+
function sortKey(node) {
|
|
643
|
+
const isArray = Array.isArray(node.name) ? "1" : "0";
|
|
644
|
+
const typeOnly = node.isTypeOnly ? "0" : "1";
|
|
645
|
+
const hasName = node.name != null ? "1" : "0";
|
|
646
|
+
const name = Array.isArray(node.name) ? [...node.name].sort().join("\0") : node.name ?? "";
|
|
647
|
+
return `${isArray}:${typeOnly}:${node.path}:${hasName}:${name}`;
|
|
648
|
+
}
|
|
649
|
+
/**
|
|
650
|
+
* Deduplicates an array of `SourceNode` objects.
|
|
651
|
+
* Named sources are deduplicated by `name + isExportable + isTypeOnly`.
|
|
652
|
+
* Unnamed sources are deduplicated by object reference.
|
|
653
|
+
*/
|
|
654
|
+
function combineSources(sources) {
|
|
655
|
+
const seen = /* @__PURE__ */ new Map();
|
|
656
|
+
for (const source of sources) {
|
|
657
|
+
const key = sourceKey(source);
|
|
658
|
+
if (!seen.has(key)) seen.set(key, source);
|
|
659
|
+
}
|
|
660
|
+
return [...seen.values()];
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Deduplicates and merges an array of `ExportNode` objects.
|
|
664
|
+
* Exports with the same path and `isTypeOnly` flag have their names merged.
|
|
665
|
+
*/
|
|
666
|
+
function combineExports(exports) {
|
|
667
|
+
const result = [];
|
|
668
|
+
const namedByPath = /* @__PURE__ */ new Map();
|
|
669
|
+
const seen = /* @__PURE__ */ new Set();
|
|
670
|
+
const keyed = exports.map((node) => ({
|
|
671
|
+
node,
|
|
672
|
+
key: sortKey(node)
|
|
673
|
+
}));
|
|
674
|
+
keyed.sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0);
|
|
675
|
+
for (const { node: curr } of keyed) {
|
|
676
|
+
const { name, path, isTypeOnly, asAlias } = curr;
|
|
677
|
+
if (Array.isArray(name)) {
|
|
678
|
+
if (!name.length) continue;
|
|
679
|
+
const key = pathTypeKey(path, isTypeOnly);
|
|
680
|
+
const existing = namedByPath.get(key);
|
|
681
|
+
if (existing && Array.isArray(existing.name)) {
|
|
682
|
+
const merged = new Set(existing.name);
|
|
683
|
+
for (const n of name) merged.add(n);
|
|
684
|
+
existing.name = [...merged];
|
|
685
|
+
} else {
|
|
686
|
+
const newItem = {
|
|
687
|
+
...curr,
|
|
688
|
+
name: [...new Set(name)]
|
|
689
|
+
};
|
|
690
|
+
result.push(newItem);
|
|
691
|
+
namedByPath.set(key, newItem);
|
|
692
|
+
}
|
|
693
|
+
} else {
|
|
694
|
+
const key = exportKey(path, name, isTypeOnly, asAlias);
|
|
695
|
+
if (!seen.has(key)) {
|
|
696
|
+
result.push(curr);
|
|
697
|
+
seen.add(key);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
return result;
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* Deduplicates and merges an array of `ImportNode` objects.
|
|
705
|
+
* Filters out unused imports (names not referenced in `source` or re-exported).
|
|
706
|
+
* Imports with the same path and `isTypeOnly` flag have their names merged.
|
|
707
|
+
*/
|
|
708
|
+
function combineImports(imports, exports, source) {
|
|
709
|
+
const exportedNames = new Set(exports.flatMap((e) => Array.isArray(e.name) ? e.name : e.name ? [e.name] : []));
|
|
710
|
+
const isUsed = (importName) => !source || source.includes(importName) || exportedNames.has(importName);
|
|
711
|
+
const result = [];
|
|
712
|
+
const namedByPath = /* @__PURE__ */ new Map();
|
|
713
|
+
const seen = /* @__PURE__ */ new Set();
|
|
714
|
+
const keyed = imports.map((node) => ({
|
|
715
|
+
node,
|
|
716
|
+
key: sortKey(node)
|
|
717
|
+
}));
|
|
718
|
+
keyed.sort((a, b) => a.key < b.key ? -1 : a.key > b.key ? 1 : 0);
|
|
719
|
+
for (const { node: curr } of keyed) {
|
|
720
|
+
if (curr.path === curr.root) continue;
|
|
721
|
+
const { path, isTypeOnly } = curr;
|
|
722
|
+
let { name } = curr;
|
|
723
|
+
if (Array.isArray(name)) {
|
|
724
|
+
name = [...new Set(name)].filter((item) => typeof item === "string" ? isUsed(item) : isUsed(item.propertyName));
|
|
725
|
+
if (!name.length) continue;
|
|
726
|
+
const key = pathTypeKey(path, isTypeOnly);
|
|
727
|
+
const existing = namedByPath.get(key);
|
|
728
|
+
if (existing && Array.isArray(existing.name)) {
|
|
729
|
+
const merged = new Set(existing.name);
|
|
730
|
+
for (const n of name) merged.add(n);
|
|
731
|
+
existing.name = [...merged];
|
|
732
|
+
} else {
|
|
733
|
+
const newItem = {
|
|
734
|
+
...curr,
|
|
735
|
+
name
|
|
736
|
+
};
|
|
737
|
+
result.push(newItem);
|
|
738
|
+
namedByPath.set(key, newItem);
|
|
739
|
+
}
|
|
740
|
+
} else {
|
|
741
|
+
if (name && !isUsed(name)) continue;
|
|
742
|
+
const key = importKey(path, name, isTypeOnly);
|
|
743
|
+
if (!seen.has(key)) {
|
|
744
|
+
result.push(curr);
|
|
745
|
+
seen.add(key);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
return result;
|
|
750
|
+
}
|
|
751
|
+
/**
|
|
752
|
+
* Recursively extracts all string content embedded in a {@link CodeNode} tree.
|
|
753
|
+
*
|
|
754
|
+
* Includes text node values, and string attribute fields (`params`, `generics`,
|
|
755
|
+
* `returnType`, `type`) that may reference identifiers needing imports.
|
|
756
|
+
* Used by `createFile` to build the full source string for import filtering.
|
|
757
|
+
*/
|
|
758
|
+
function extractStringsFromNodes(nodes) {
|
|
759
|
+
if (!nodes?.length) return "";
|
|
760
|
+
return nodes.map((node) => {
|
|
761
|
+
if (typeof node === "string") return node;
|
|
762
|
+
if (node.kind === "Text") return node.value;
|
|
763
|
+
if (node.kind === "Break") return "";
|
|
764
|
+
if (node.kind === "Jsx") return node.value;
|
|
765
|
+
const parts = [];
|
|
766
|
+
if ("params" in node && node.params) parts.push(node.params);
|
|
767
|
+
if ("generics" in node && node.generics) parts.push(Array.isArray(node.generics) ? node.generics.join(", ") : node.generics);
|
|
768
|
+
if ("returnType" in node && node.returnType) parts.push(node.returnType);
|
|
769
|
+
if ("type" in node && typeof node.type === "string") parts.push(node.type);
|
|
770
|
+
const nested = extractStringsFromNodes(node.nodes);
|
|
771
|
+
if (nested) parts.push(nested);
|
|
772
|
+
return parts.join("\n");
|
|
773
|
+
}).filter(Boolean).join("\n");
|
|
774
|
+
}
|
|
775
|
+
//#endregion
|
|
76
776
|
//#region src/factory.ts
|
|
77
777
|
/**
|
|
78
|
-
*
|
|
778
|
+
* Syncs property/parameter schema optionality flags from `required` and `schema.nullable`.
|
|
779
|
+
*
|
|
780
|
+
* - `optional` is set for non-required, non-nullable schemas.
|
|
781
|
+
* - `nullish` is set for non-required, nullable schemas.
|
|
782
|
+
*/
|
|
783
|
+
function syncOptionality(schema, required) {
|
|
784
|
+
const nullable = schema.nullable ?? false;
|
|
785
|
+
return {
|
|
786
|
+
...schema,
|
|
787
|
+
optional: !required && !nullable ? true : void 0,
|
|
788
|
+
nullish: !required && nullable ? true : void 0
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
/**
|
|
792
|
+
* Creates an `InputNode` with stable defaults for `schemas` and `operations`.
|
|
793
|
+
*
|
|
794
|
+
* @example
|
|
795
|
+
* ```ts
|
|
796
|
+
* const input = createInput()
|
|
797
|
+
* // { kind: 'Input', schemas: [], operations: [] }
|
|
798
|
+
* ```
|
|
799
|
+
*
|
|
800
|
+
* @example
|
|
801
|
+
* ```ts
|
|
802
|
+
* const input = createInput({ schemas: [petSchema] })
|
|
803
|
+
* // keeps default operations: []
|
|
804
|
+
* ```
|
|
79
805
|
*/
|
|
80
|
-
function
|
|
806
|
+
function createInput(overrides = {}) {
|
|
81
807
|
return {
|
|
82
808
|
schemas: [],
|
|
83
809
|
operations: [],
|
|
84
810
|
...overrides,
|
|
85
|
-
kind: "
|
|
811
|
+
kind: "Input"
|
|
86
812
|
};
|
|
87
813
|
}
|
|
88
814
|
/**
|
|
89
|
-
* Creates an `
|
|
815
|
+
* Creates an `OutputNode` with a stable default for `files`.
|
|
816
|
+
*
|
|
817
|
+
* @example
|
|
818
|
+
* ```ts
|
|
819
|
+
* const output = createOutput()
|
|
820
|
+
* // { kind: 'Output', files: [] }
|
|
821
|
+
* ```
|
|
822
|
+
*
|
|
823
|
+
* @example
|
|
824
|
+
* ```ts
|
|
825
|
+
* const output = createOutput({ files: [petFile] })
|
|
826
|
+
* ```
|
|
827
|
+
*/
|
|
828
|
+
function createOutput(overrides = {}) {
|
|
829
|
+
return {
|
|
830
|
+
files: [],
|
|
831
|
+
...overrides,
|
|
832
|
+
kind: "Output"
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
/**
|
|
836
|
+
* Creates an `OperationNode` with default empty arrays for `tags`, `parameters`, and `responses`.
|
|
837
|
+
*
|
|
838
|
+
* @example
|
|
839
|
+
* ```ts
|
|
840
|
+
* const operation = createOperation({
|
|
841
|
+
* operationId: 'getPetById',
|
|
842
|
+
* method: 'GET',
|
|
843
|
+
* path: '/pet/{petId}',
|
|
844
|
+
* })
|
|
845
|
+
* // tags, parameters, and responses are []
|
|
846
|
+
* ```
|
|
847
|
+
*
|
|
848
|
+
* @example
|
|
849
|
+
* ```ts
|
|
850
|
+
* const operation = createOperation({
|
|
851
|
+
* operationId: 'findPets',
|
|
852
|
+
* method: 'GET',
|
|
853
|
+
* path: '/pet/findByStatus',
|
|
854
|
+
* tags: ['pet'],
|
|
855
|
+
* })
|
|
856
|
+
* ```
|
|
90
857
|
*/
|
|
91
858
|
function createOperation(props) {
|
|
92
859
|
return {
|
|
@@ -97,39 +864,125 @@ function createOperation(props) {
|
|
|
97
864
|
kind: "Operation"
|
|
98
865
|
};
|
|
99
866
|
}
|
|
867
|
+
/**
|
|
868
|
+
* Maps schema `type` to its underlying `primitive`.
|
|
869
|
+
* Primitive types map to themselves; special string formats map to `'string'`.
|
|
870
|
+
* Complex types (`ref`, `enum`, `union`, `intersection`, `tuple`, `blob`) are left unset.
|
|
871
|
+
*/
|
|
872
|
+
const TYPE_TO_PRIMITIVE = {
|
|
873
|
+
string: "string",
|
|
874
|
+
number: "number",
|
|
875
|
+
integer: "integer",
|
|
876
|
+
bigint: "bigint",
|
|
877
|
+
boolean: "boolean",
|
|
878
|
+
null: "null",
|
|
879
|
+
any: "any",
|
|
880
|
+
unknown: "unknown",
|
|
881
|
+
void: "void",
|
|
882
|
+
never: "never",
|
|
883
|
+
object: "object",
|
|
884
|
+
array: "array",
|
|
885
|
+
date: "date",
|
|
886
|
+
uuid: "string",
|
|
887
|
+
email: "string",
|
|
888
|
+
url: "string",
|
|
889
|
+
datetime: "string",
|
|
890
|
+
time: "string"
|
|
891
|
+
};
|
|
100
892
|
function createSchema(props) {
|
|
893
|
+
const inferredPrimitive = TYPE_TO_PRIMITIVE[props.type];
|
|
101
894
|
if (props["type"] === "object") return {
|
|
102
895
|
properties: [],
|
|
896
|
+
primitive: "object",
|
|
103
897
|
...props,
|
|
104
898
|
kind: "Schema"
|
|
105
899
|
};
|
|
106
900
|
return {
|
|
901
|
+
primitive: inferredPrimitive,
|
|
107
902
|
...props,
|
|
108
903
|
kind: "Schema"
|
|
109
904
|
};
|
|
110
905
|
}
|
|
111
906
|
/**
|
|
112
|
-
* Creates a `PropertyNode`.
|
|
907
|
+
* Creates a `PropertyNode`.
|
|
908
|
+
*
|
|
909
|
+
* `required` defaults to `false`.
|
|
910
|
+
* `schema.optional` and `schema.nullish` are derived from `required` and `schema.nullable`.
|
|
911
|
+
*
|
|
912
|
+
* @example
|
|
913
|
+
* ```ts
|
|
914
|
+
* const property = createProperty({
|
|
915
|
+
* name: 'status',
|
|
916
|
+
* schema: createSchema({ type: 'string' }),
|
|
917
|
+
* })
|
|
918
|
+
* // required=false, schema.optional=true
|
|
919
|
+
* ```
|
|
920
|
+
*
|
|
921
|
+
* @example
|
|
922
|
+
* ```ts
|
|
923
|
+
* const property = createProperty({
|
|
924
|
+
* name: 'status',
|
|
925
|
+
* required: true,
|
|
926
|
+
* schema: createSchema({ type: 'string', nullable: true }),
|
|
927
|
+
* })
|
|
928
|
+
* // required=true, no optional/nullish
|
|
929
|
+
* ```
|
|
113
930
|
*/
|
|
114
931
|
function createProperty(props) {
|
|
932
|
+
const required = props.required ?? false;
|
|
115
933
|
return {
|
|
116
|
-
required: false,
|
|
117
934
|
...props,
|
|
118
|
-
kind: "Property"
|
|
935
|
+
kind: "Property",
|
|
936
|
+
required,
|
|
937
|
+
schema: syncOptionality(props.schema, required)
|
|
119
938
|
};
|
|
120
939
|
}
|
|
121
940
|
/**
|
|
122
|
-
* Creates a `ParameterNode`.
|
|
941
|
+
* Creates a `ParameterNode`.
|
|
942
|
+
*
|
|
943
|
+
* `required` defaults to `false`.
|
|
944
|
+
* Nested schema flags are set from `required` and `schema.nullable`.
|
|
945
|
+
*
|
|
946
|
+
* @example
|
|
947
|
+
* ```ts
|
|
948
|
+
* const param = createParameter({
|
|
949
|
+
* name: 'petId',
|
|
950
|
+
* in: 'path',
|
|
951
|
+
* required: true,
|
|
952
|
+
* schema: createSchema({ type: 'string' }),
|
|
953
|
+
* })
|
|
954
|
+
* ```
|
|
955
|
+
*
|
|
956
|
+
* @example
|
|
957
|
+
* ```ts
|
|
958
|
+
* const param = createParameter({
|
|
959
|
+
* name: 'status',
|
|
960
|
+
* in: 'query',
|
|
961
|
+
* schema: createSchema({ type: 'string', nullable: true }),
|
|
962
|
+
* })
|
|
963
|
+
* // required=false, schema.nullish=true
|
|
964
|
+
* ```
|
|
123
965
|
*/
|
|
124
966
|
function createParameter(props) {
|
|
967
|
+
const required = props.required ?? false;
|
|
125
968
|
return {
|
|
126
|
-
required: false,
|
|
127
969
|
...props,
|
|
128
|
-
kind: "Parameter"
|
|
970
|
+
kind: "Parameter",
|
|
971
|
+
required,
|
|
972
|
+
schema: syncOptionality(props.schema, required)
|
|
129
973
|
};
|
|
130
974
|
}
|
|
131
975
|
/**
|
|
132
976
|
* Creates a `ResponseNode`.
|
|
977
|
+
*
|
|
978
|
+
* @example
|
|
979
|
+
* ```ts
|
|
980
|
+
* const response = createResponse({
|
|
981
|
+
* statusCode: '200',
|
|
982
|
+
* description: 'Success',
|
|
983
|
+
* schema: createSchema({ type: 'object', properties: [] }),
|
|
984
|
+
* })
|
|
985
|
+
* ```
|
|
133
986
|
*/
|
|
134
987
|
function createResponse(props) {
|
|
135
988
|
return {
|
|
@@ -137,420 +990,511 @@ function createResponse(props) {
|
|
|
137
990
|
kind: "Response"
|
|
138
991
|
};
|
|
139
992
|
}
|
|
140
|
-
//#endregion
|
|
141
|
-
//#region src/guards.ts
|
|
142
993
|
/**
|
|
143
|
-
*
|
|
994
|
+
* Creates a `FunctionParameterNode`.
|
|
995
|
+
*
|
|
996
|
+
* `optional` defaults to `false`.
|
|
997
|
+
*
|
|
998
|
+
* @example Required typed param
|
|
999
|
+
* ```ts
|
|
1000
|
+
* createFunctionParameter({ name: 'petId', type: createParamsType({ variant: 'reference', name: 'string' }) })
|
|
1001
|
+
* // → petId: string
|
|
1002
|
+
* ```
|
|
1003
|
+
*
|
|
1004
|
+
* @example Optional param
|
|
1005
|
+
* ```ts
|
|
1006
|
+
* createFunctionParameter({ name: 'params', type: createParamsType({ variant: 'reference', name: 'QueryParams' }), optional: true })
|
|
1007
|
+
* // → params?: QueryParams
|
|
1008
|
+
* ```
|
|
1009
|
+
*
|
|
1010
|
+
* @example Param with default (implicitly optional; cannot combine with `optional: true`)
|
|
1011
|
+
* ```ts
|
|
1012
|
+
* createFunctionParameter({ name: 'config', type: createParamsType({ variant: 'reference', name: 'RequestConfig' }), default: '{}' })
|
|
1013
|
+
* // → config: RequestConfig = {}
|
|
1014
|
+
* ```
|
|
144
1015
|
*/
|
|
145
|
-
function
|
|
146
|
-
return
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
1016
|
+
function createFunctionParameter(props) {
|
|
1017
|
+
return {
|
|
1018
|
+
optional: false,
|
|
1019
|
+
...props,
|
|
1020
|
+
kind: "FunctionParameter"
|
|
1021
|
+
};
|
|
150
1022
|
}
|
|
151
1023
|
/**
|
|
152
|
-
*
|
|
1024
|
+
* Creates a {@link TypeNode} representing a language-agnostic structured type expression.
|
|
1025
|
+
*
|
|
1026
|
+
* Use `variant: 'struct'` for inline anonymous types and `variant: 'member'` for a single
|
|
1027
|
+
* named field accessed from a group type. Each language's printer renders the variant
|
|
1028
|
+
* into its own syntax (TypeScript, Python, C#, Kotlin, …).
|
|
1029
|
+
*
|
|
1030
|
+
* @example Reference type (TypeScript: `QueryParams`)
|
|
1031
|
+
* ```ts
|
|
1032
|
+
* createParamsType({ variant: 'reference', name: 'QueryParams' })
|
|
1033
|
+
* ```
|
|
1034
|
+
*
|
|
1035
|
+
* @example Struct type (TypeScript: `{ petId: string }`)
|
|
1036
|
+
* ```ts
|
|
1037
|
+
* createParamsType({ variant: 'struct', properties: [{ name: 'petId', optional: false, type: createParamsType({ variant: 'reference', name: 'string' }) }] })
|
|
1038
|
+
* ```
|
|
1039
|
+
*
|
|
1040
|
+
* @example Member type (TypeScript: `DeletePetPathParams['petId']`)
|
|
1041
|
+
* ```ts
|
|
1042
|
+
* createParamsType({ variant: 'member', base: 'DeletePetPathParams', key: 'petId' })
|
|
1043
|
+
* ```
|
|
153
1044
|
*/
|
|
154
|
-
|
|
1045
|
+
function createParamsType(props) {
|
|
1046
|
+
return {
|
|
1047
|
+
...props,
|
|
1048
|
+
kind: "ParamsType"
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
155
1051
|
/**
|
|
156
|
-
*
|
|
1052
|
+
* Creates a `ParameterGroupNode` representing a group of related parameters treated as a unit.
|
|
1053
|
+
*
|
|
1054
|
+
* @example Grouped param (TypeScript declaration)
|
|
1055
|
+
* ```ts
|
|
1056
|
+
* createParameterGroup({
|
|
1057
|
+
* properties: [
|
|
1058
|
+
* createFunctionParameter({ name: 'id', type: createParamsType({ variant: 'reference', name: 'string' }), optional: false }),
|
|
1059
|
+
* createFunctionParameter({ name: 'name', type: createParamsType({ variant: 'reference', name: 'string' }), optional: true }),
|
|
1060
|
+
* ],
|
|
1061
|
+
* default: '{}',
|
|
1062
|
+
* })
|
|
1063
|
+
* // declaration → { id, name? }: { id: string; name?: string } = {}
|
|
1064
|
+
* // call → { id, name }
|
|
1065
|
+
* ```
|
|
1066
|
+
*
|
|
1067
|
+
* @example Inline (spread) — children emitted as individual top-level parameters
|
|
1068
|
+
* ```ts
|
|
1069
|
+
* createParameterGroup({
|
|
1070
|
+
* properties: [createFunctionParameter({ name: 'petId', type: createParamsType({ variant: 'reference', name: 'string' }), optional: false })],
|
|
1071
|
+
* inline: true,
|
|
1072
|
+
* })
|
|
1073
|
+
* // declaration → petId: string
|
|
1074
|
+
* // call → petId
|
|
1075
|
+
* ```
|
|
157
1076
|
*/
|
|
158
|
-
|
|
1077
|
+
function createParameterGroup(props) {
|
|
1078
|
+
return {
|
|
1079
|
+
...props,
|
|
1080
|
+
kind: "ParameterGroup"
|
|
1081
|
+
};
|
|
1082
|
+
}
|
|
159
1083
|
/**
|
|
160
|
-
*
|
|
1084
|
+
* Creates a `FunctionParametersNode` from an ordered list of parameters.
|
|
1085
|
+
*
|
|
1086
|
+
* @example
|
|
1087
|
+
* ```ts
|
|
1088
|
+
* createFunctionParameters({
|
|
1089
|
+
* params: [
|
|
1090
|
+
* createFunctionParameter({ name: 'petId', type: createParamsType({ variant: 'reference', name: 'string' }), optional: false }),
|
|
1091
|
+
* createFunctionParameter({ name: 'config', type: createParamsType({ variant: 'reference', name: 'RequestConfig' }), optional: false, default: '{}' }),
|
|
1092
|
+
* ],
|
|
1093
|
+
* })
|
|
1094
|
+
* ```
|
|
1095
|
+
*
|
|
1096
|
+
* @example
|
|
1097
|
+
* ```ts
|
|
1098
|
+
* const empty = createFunctionParameters()
|
|
1099
|
+
* // { kind: 'FunctionParameters', params: [] }
|
|
1100
|
+
* ```
|
|
161
1101
|
*/
|
|
162
|
-
|
|
1102
|
+
function createFunctionParameters(props = {}) {
|
|
1103
|
+
return {
|
|
1104
|
+
params: [],
|
|
1105
|
+
...props,
|
|
1106
|
+
kind: "FunctionParameters"
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
163
1109
|
/**
|
|
164
|
-
*
|
|
1110
|
+
* Creates an `ImportNode` representing a language-agnostic import/dependency declaration.
|
|
1111
|
+
*
|
|
1112
|
+
* @example Named import
|
|
1113
|
+
* ```ts
|
|
1114
|
+
* createImport({ name: ['useState'], path: 'react' })
|
|
1115
|
+
* // import { useState } from 'react'
|
|
1116
|
+
* ```
|
|
1117
|
+
*
|
|
1118
|
+
* @example Type-only import
|
|
1119
|
+
* ```ts
|
|
1120
|
+
* createImport({ name: ['FC'], path: 'react', isTypeOnly: true })
|
|
1121
|
+
* // import type { FC } from 'react'
|
|
1122
|
+
* ```
|
|
165
1123
|
*/
|
|
166
|
-
|
|
1124
|
+
function createImport(props) {
|
|
1125
|
+
return {
|
|
1126
|
+
...props,
|
|
1127
|
+
kind: "Import"
|
|
1128
|
+
};
|
|
1129
|
+
}
|
|
167
1130
|
/**
|
|
168
|
-
*
|
|
1131
|
+
* Creates an `ExportNode` representing a language-agnostic export/public API declaration.
|
|
1132
|
+
*
|
|
1133
|
+
* @example Named export
|
|
1134
|
+
* ```ts
|
|
1135
|
+
* createExport({ name: ['Pet'], path: './Pet' })
|
|
1136
|
+
* // export { Pet } from './Pet'
|
|
1137
|
+
* ```
|
|
1138
|
+
*
|
|
1139
|
+
* @example Wildcard export
|
|
1140
|
+
* ```ts
|
|
1141
|
+
* createExport({ path: './utils' })
|
|
1142
|
+
* // export * from './utils'
|
|
1143
|
+
* ```
|
|
169
1144
|
*/
|
|
170
|
-
|
|
1145
|
+
function createExport(props) {
|
|
1146
|
+
return {
|
|
1147
|
+
...props,
|
|
1148
|
+
kind: "Export"
|
|
1149
|
+
};
|
|
1150
|
+
}
|
|
171
1151
|
/**
|
|
172
|
-
*
|
|
1152
|
+
* Creates a `SourceNode` representing a fragment of source code within a file.
|
|
1153
|
+
*
|
|
1154
|
+
* @example
|
|
1155
|
+
* ```ts
|
|
1156
|
+
* createSource({ name: 'Pet', nodes: [createText('export type Pet = { id: number }')], isExportable: true })
|
|
1157
|
+
* ```
|
|
173
1158
|
*/
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
1159
|
+
function createSource(props) {
|
|
1160
|
+
return {
|
|
1161
|
+
...props,
|
|
1162
|
+
kind: "Source"
|
|
1163
|
+
};
|
|
1164
|
+
}
|
|
177
1165
|
/**
|
|
178
|
-
* Creates a
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
* nodes: {
|
|
192
|
-
* string(node) {
|
|
193
|
-
* return `z.string()`
|
|
194
|
-
* },
|
|
195
|
-
* object(node) {
|
|
196
|
-
* const props = node.properties
|
|
197
|
-
* ?.map(p => `${p.name}: ${this.print(p)}`)
|
|
198
|
-
* .join(', ') ?? ''
|
|
199
|
-
* return `z.object({ ${props} })`
|
|
200
|
-
* },
|
|
201
|
-
* },
|
|
202
|
-
* }
|
|
203
|
-
* })
|
|
1166
|
+
* Creates a fully resolved `FileNode` from a file input descriptor.
|
|
1167
|
+
*
|
|
1168
|
+
* Computes:
|
|
1169
|
+
* - `id` — SHA256 hash of the file path
|
|
1170
|
+
* - `name` — `baseName` without extension
|
|
1171
|
+
* - `extname` — extension extracted from `baseName`
|
|
1172
|
+
*
|
|
1173
|
+
* Deduplicates:
|
|
1174
|
+
* - `sources` via `combineSources`
|
|
1175
|
+
* - `exports` via `combineExports`
|
|
1176
|
+
* - `imports` via `combineImports` (also filters unused imports)
|
|
1177
|
+
*
|
|
1178
|
+
* @throws {Error} when `baseName` has no extension.
|
|
204
1179
|
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
*
|
|
1180
|
+
* @example
|
|
1181
|
+
* ```ts
|
|
1182
|
+
* const file = createFile({
|
|
1183
|
+
* baseName: 'petStore.ts',
|
|
1184
|
+
* path: 'src/models/petStore.ts',
|
|
1185
|
+
* sources: [createSource({ name: 'Pet', nodes: [createText('export type Pet = { id: number }')] })],
|
|
1186
|
+
* imports: [createImport({ name: ['z'], path: 'zod' })],
|
|
1187
|
+
* exports: [createExport({ name: ['Pet'], path: './petStore' })],
|
|
1188
|
+
* })
|
|
1189
|
+
* // file.id = SHA256 hash of 'src/models/petStore.ts'
|
|
1190
|
+
* // file.name = 'petStore'
|
|
1191
|
+
* // file.extname = '.ts'
|
|
209
1192
|
* ```
|
|
210
1193
|
*/
|
|
211
|
-
function
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
1194
|
+
function createFile(input) {
|
|
1195
|
+
const extname = node_path.default.extname(input.baseName) || (input.baseName.startsWith(".") ? input.baseName : "");
|
|
1196
|
+
if (!extname) throw new Error(`No extname found for ${input.baseName}`);
|
|
1197
|
+
const source = (input.sources ?? []).flatMap((item) => item.nodes ?? []).map((node) => extractStringsFromNodes([node])).filter(Boolean).join("\n\n");
|
|
1198
|
+
const resolvedExports = input.exports?.length ? combineExports(input.exports) : [];
|
|
1199
|
+
const resolvedImports = input.imports?.length ? combineImports(input.imports, resolvedExports, source || void 0) : [];
|
|
1200
|
+
const resolvedSources = input.sources?.length ? combineSources(input.sources) : [];
|
|
1201
|
+
return {
|
|
1202
|
+
kind: "File",
|
|
1203
|
+
...input,
|
|
1204
|
+
id: (0, node_crypto.createHash)("sha256").update(input.path).digest("hex"),
|
|
1205
|
+
name: trimExtName(input.baseName),
|
|
1206
|
+
extname,
|
|
1207
|
+
imports: resolvedImports,
|
|
1208
|
+
exports: resolvedExports,
|
|
1209
|
+
sources: resolvedSources,
|
|
1210
|
+
meta: input.meta ?? {}
|
|
227
1211
|
};
|
|
228
1212
|
}
|
|
229
|
-
//#endregion
|
|
230
|
-
//#region src/refs.ts
|
|
231
1213
|
/**
|
|
232
|
-
*
|
|
1214
|
+
* Creates a `ConstNode` representing a TypeScript `const` declaration.
|
|
1215
|
+
*
|
|
1216
|
+
* Mirrors the `Const` component from `@kubb/renderer-jsx`.
|
|
1217
|
+
* The component's `children` are represented as `nodes`.
|
|
1218
|
+
*
|
|
1219
|
+
* @example Simple constant
|
|
1220
|
+
* ```ts
|
|
1221
|
+
* createConst({ name: 'pet' })
|
|
1222
|
+
* // const pet = ...
|
|
1223
|
+
* ```
|
|
1224
|
+
*
|
|
1225
|
+
* @example Exported constant with type and `as const`
|
|
1226
|
+
* ```ts
|
|
1227
|
+
* createConst({ name: 'pets', export: true, type: 'Pet[]', asConst: true })
|
|
1228
|
+
* // export const pets: Pet[] = ... as const
|
|
1229
|
+
* ```
|
|
1230
|
+
*
|
|
1231
|
+
* @example With JSDoc and child nodes
|
|
1232
|
+
* ```ts
|
|
1233
|
+
* createConst({
|
|
1234
|
+
* name: 'config',
|
|
1235
|
+
* export: true,
|
|
1236
|
+
* JSDoc: { comments: ['@description App configuration'] },
|
|
1237
|
+
* nodes: [],
|
|
1238
|
+
* })
|
|
1239
|
+
* ```
|
|
233
1240
|
*/
|
|
234
|
-
function
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
1241
|
+
function createConst(props) {
|
|
1242
|
+
return {
|
|
1243
|
+
...props,
|
|
1244
|
+
kind: "Const"
|
|
1245
|
+
};
|
|
238
1246
|
}
|
|
239
1247
|
/**
|
|
240
|
-
*
|
|
1248
|
+
* Creates a `TypeNode` representing a TypeScript `type` alias declaration.
|
|
1249
|
+
*
|
|
1250
|
+
* Mirrors the `Type` component from `@kubb/renderer-jsx`.
|
|
1251
|
+
* The component's `children` are represented as `nodes`.
|
|
1252
|
+
*
|
|
1253
|
+
* @example Simple type alias
|
|
1254
|
+
* ```ts
|
|
1255
|
+
* createType({ name: 'Pet' })
|
|
1256
|
+
* // type Pet = ...
|
|
1257
|
+
* ```
|
|
1258
|
+
*
|
|
1259
|
+
* @example Exported type with JSDoc
|
|
1260
|
+
* ```ts
|
|
1261
|
+
* createType({
|
|
1262
|
+
* name: 'PetStatus',
|
|
1263
|
+
* export: true,
|
|
1264
|
+
* JSDoc: { comments: ['@description Status of a pet'] },
|
|
1265
|
+
* })
|
|
1266
|
+
* // export type PetStatus = ...
|
|
1267
|
+
* ```
|
|
241
1268
|
*/
|
|
242
|
-
function
|
|
243
|
-
return
|
|
1269
|
+
function createType(props) {
|
|
1270
|
+
return {
|
|
1271
|
+
...props,
|
|
1272
|
+
kind: "Type"
|
|
1273
|
+
};
|
|
244
1274
|
}
|
|
245
1275
|
/**
|
|
246
|
-
*
|
|
1276
|
+
* Creates a `FunctionNode` representing a TypeScript `function` declaration.
|
|
1277
|
+
*
|
|
1278
|
+
* Mirrors the `Function` component from `@kubb/renderer-jsx`.
|
|
1279
|
+
* The component's `children` are represented as `nodes`.
|
|
1280
|
+
*
|
|
1281
|
+
* @example Simple function
|
|
1282
|
+
* ```ts
|
|
1283
|
+
* createFunction({ name: 'getPet' })
|
|
1284
|
+
* // function getPet() { ... }
|
|
1285
|
+
* ```
|
|
1286
|
+
*
|
|
1287
|
+
* @example Exported async function with return type
|
|
1288
|
+
* ```ts
|
|
1289
|
+
* createFunction({ name: 'fetchPet', export: true, async: true, returnType: 'Pet' })
|
|
1290
|
+
* // export async function fetchPet(): Promise<Pet> { ... }
|
|
1291
|
+
* ```
|
|
1292
|
+
*
|
|
1293
|
+
* @example Function with generics and params
|
|
1294
|
+
* ```ts
|
|
1295
|
+
* createFunction({
|
|
1296
|
+
* name: 'identity',
|
|
1297
|
+
* export: true,
|
|
1298
|
+
* generics: ['T'],
|
|
1299
|
+
* params: 'value: T',
|
|
1300
|
+
* returnType: 'T',
|
|
1301
|
+
* })
|
|
1302
|
+
* // export function identity<T>(value: T): T { ... }
|
|
1303
|
+
* ```
|
|
247
1304
|
*/
|
|
248
|
-
function
|
|
249
|
-
return
|
|
1305
|
+
function createFunction(props) {
|
|
1306
|
+
return {
|
|
1307
|
+
...props,
|
|
1308
|
+
kind: "Function"
|
|
1309
|
+
};
|
|
250
1310
|
}
|
|
251
|
-
//#endregion
|
|
252
|
-
//#region ../../internals/utils/dist/index.js
|
|
253
1311
|
/**
|
|
254
|
-
*
|
|
255
|
-
* Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
|
|
256
|
-
* and capitalizes each word according to `pascal`.
|
|
1312
|
+
* Creates an `ArrowFunctionNode` representing a TypeScript arrow function.
|
|
257
1313
|
*
|
|
258
|
-
*
|
|
1314
|
+
* Mirrors the `Function.Arrow` component from `@kubb/renderer-jsx`.
|
|
1315
|
+
* The component's `children` are represented as `nodes`.
|
|
1316
|
+
*
|
|
1317
|
+
* @example Simple arrow function
|
|
1318
|
+
* ```ts
|
|
1319
|
+
* createArrowFunction({ name: 'getPet' })
|
|
1320
|
+
* // const getPet = () => { ... }
|
|
1321
|
+
* ```
|
|
1322
|
+
*
|
|
1323
|
+
* @example Single-line exported arrow function
|
|
1324
|
+
* ```ts
|
|
1325
|
+
* createArrowFunction({ name: 'double', export: true, params: 'n: number', singleLine: true })
|
|
1326
|
+
* // export const double = (n: number) => ...
|
|
1327
|
+
* ```
|
|
1328
|
+
*
|
|
1329
|
+
* @example Async arrow function with generics
|
|
1330
|
+
* ```ts
|
|
1331
|
+
* createArrowFunction({
|
|
1332
|
+
* name: 'fetchPet',
|
|
1333
|
+
* export: true,
|
|
1334
|
+
* async: true,
|
|
1335
|
+
* generics: ['T'],
|
|
1336
|
+
* params: 'id: string',
|
|
1337
|
+
* returnType: 'T',
|
|
1338
|
+
* })
|
|
1339
|
+
* // export const fetchPet = async <T>(id: string): Promise<T> => { ... }
|
|
1340
|
+
* ```
|
|
259
1341
|
*/
|
|
260
|
-
function
|
|
261
|
-
return
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
}).join("").replace(/[^a-zA-Z0-9]/g, "");
|
|
1342
|
+
function createArrowFunction(props) {
|
|
1343
|
+
return {
|
|
1344
|
+
...props,
|
|
1345
|
+
kind: "ArrowFunction"
|
|
1346
|
+
};
|
|
266
1347
|
}
|
|
267
1348
|
/**
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
*
|
|
1349
|
+
* Creates a {@link TextNode} representing a raw string fragment in the source output.
|
|
1350
|
+
*
|
|
1351
|
+
* Use this instead of bare strings when building `nodes` arrays so that every
|
|
1352
|
+
* entry in the array is a typed {@link CodeNode}.
|
|
1353
|
+
*
|
|
1354
|
+
* @example
|
|
1355
|
+
* ```ts
|
|
1356
|
+
* createText('return fetch(id)')
|
|
1357
|
+
* // { kind: 'Text', value: 'return fetch(id)' }
|
|
1358
|
+
* ```
|
|
271
1359
|
*/
|
|
272
|
-
function
|
|
273
|
-
|
|
274
|
-
|
|
1360
|
+
function createText(value) {
|
|
1361
|
+
return {
|
|
1362
|
+
value,
|
|
1363
|
+
kind: "Text"
|
|
1364
|
+
};
|
|
275
1365
|
}
|
|
276
1366
|
/**
|
|
277
|
-
*
|
|
278
|
-
*
|
|
1367
|
+
* Creates a {@link BreakNode} representing a line break in the source output.
|
|
1368
|
+
*
|
|
1369
|
+
* Corresponds to `<br/>` in JSX components. Prints as an empty string which,
|
|
1370
|
+
* when joined with `\n` by `printNodes`, produces a blank line.
|
|
279
1371
|
*
|
|
280
1372
|
* @example
|
|
281
|
-
*
|
|
282
|
-
*
|
|
1373
|
+
* ```ts
|
|
1374
|
+
* createBreak()
|
|
1375
|
+
* // { kind: 'Break' }
|
|
1376
|
+
* ```
|
|
283
1377
|
*/
|
|
284
|
-
function
|
|
285
|
-
|
|
286
|
-
prefix,
|
|
287
|
-
suffix
|
|
288
|
-
} : {}));
|
|
289
|
-
return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
|
|
290
|
-
}
|
|
291
|
-
/** Returns a `CLIAdapter` with type inference. Pass a different adapter to `createCLI` to swap the CLI engine. */
|
|
292
|
-
function defineCLIAdapter(adapter) {
|
|
293
|
-
return adapter;
|
|
1378
|
+
function createBreak() {
|
|
1379
|
+
return { kind: "Break" };
|
|
294
1380
|
}
|
|
295
1381
|
/**
|
|
296
|
-
*
|
|
297
|
-
*
|
|
1382
|
+
* Creates a {@link JsxNode} representing a raw JSX fragment in the source output.
|
|
1383
|
+
*
|
|
1384
|
+
* Use this to embed JSX markup (including fragments `<>…</>`) directly in generated code.
|
|
1385
|
+
*
|
|
1386
|
+
* @example
|
|
1387
|
+
* ```ts
|
|
1388
|
+
* createJsx('<>\n <a href={href}>Open</a>\n</>')
|
|
1389
|
+
* // { kind: 'Jsx', value: '<>\n <a href={href}>Open</a>\n</>' }
|
|
1390
|
+
* ```
|
|
298
1391
|
*/
|
|
299
|
-
function
|
|
300
|
-
return defs.map(serializeCommand);
|
|
301
|
-
}
|
|
302
|
-
function serializeCommand(def) {
|
|
1392
|
+
function createJsx(value) {
|
|
303
1393
|
return {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
arguments: def.arguments,
|
|
307
|
-
options: serializeOptions(def.options ?? {}),
|
|
308
|
-
subCommands: def.subCommands ? def.subCommands.map(serializeCommand) : []
|
|
309
|
-
};
|
|
310
|
-
}
|
|
311
|
-
function serializeOptions(options) {
|
|
312
|
-
return Object.entries(options).map(([name, opt]) => {
|
|
313
|
-
return {
|
|
314
|
-
name,
|
|
315
|
-
flags: `${opt.short ? `-${opt.short}, ` : ""}--${name}${opt.type === "string" ? ` <${opt.hint ?? name}>` : ""}`,
|
|
316
|
-
type: opt.type,
|
|
317
|
-
description: opt.description,
|
|
318
|
-
...opt.default !== void 0 ? { default: opt.default } : {},
|
|
319
|
-
...opt.hint ? { hint: opt.hint } : {},
|
|
320
|
-
...opt.enum ? { enum: opt.enum } : {},
|
|
321
|
-
...opt.required ? { required: opt.required } : {}
|
|
322
|
-
};
|
|
323
|
-
});
|
|
324
|
-
}
|
|
325
|
-
/** Prints formatted help output for a command using its `CommandDefinition`. */
|
|
326
|
-
function renderHelp(def, parentName) {
|
|
327
|
-
const schema = getCommandSchema([def])[0];
|
|
328
|
-
const programName = parentName ? `${parentName} ${schema.name}` : schema.name;
|
|
329
|
-
const argsPart = schema.arguments?.length ? ` ${schema.arguments.join(" ")}` : "";
|
|
330
|
-
const subCmdPart = schema.subCommands.length ? " <command>" : "";
|
|
331
|
-
console.log(`\n${(0, node_util.styleText)("bold", "Usage:")} ${programName}${argsPart}${subCmdPart} [options]\n`);
|
|
332
|
-
if (schema.description) console.log(` ${schema.description}\n`);
|
|
333
|
-
if (schema.subCommands.length) {
|
|
334
|
-
console.log((0, node_util.styleText)("bold", "Commands:"));
|
|
335
|
-
for (const sub of schema.subCommands) console.log(` ${(0, node_util.styleText)("cyan", sub.name.padEnd(16))}${sub.description}`);
|
|
336
|
-
console.log();
|
|
337
|
-
}
|
|
338
|
-
const options = [...schema.options, {
|
|
339
|
-
name: "help",
|
|
340
|
-
flags: "-h, --help",
|
|
341
|
-
type: "boolean",
|
|
342
|
-
description: "Show help"
|
|
343
|
-
}];
|
|
344
|
-
console.log((0, node_util.styleText)("bold", "Options:"));
|
|
345
|
-
for (const opt of options) {
|
|
346
|
-
const flags = (0, node_util.styleText)("cyan", opt.flags.padEnd(30));
|
|
347
|
-
const defaultPart = opt.default !== void 0 ? (0, node_util.styleText)("dim", ` (default: ${opt.default})`) : "";
|
|
348
|
-
console.log(` ${flags}${opt.description}${defaultPart}`);
|
|
349
|
-
}
|
|
350
|
-
console.log();
|
|
351
|
-
}
|
|
352
|
-
function buildParseOptions(def) {
|
|
353
|
-
const result = { help: {
|
|
354
|
-
type: "boolean",
|
|
355
|
-
short: "h"
|
|
356
|
-
} };
|
|
357
|
-
for (const [name, opt] of Object.entries(def.options ?? {})) result[name] = {
|
|
358
|
-
type: opt.type,
|
|
359
|
-
...opt.short ? { short: opt.short } : {},
|
|
360
|
-
...opt.default !== void 0 ? { default: opt.default } : {}
|
|
361
|
-
};
|
|
362
|
-
return result;
|
|
363
|
-
}
|
|
364
|
-
async function runCommand(def, argv, parentName) {
|
|
365
|
-
const parseOptions = buildParseOptions(def);
|
|
366
|
-
let parsed;
|
|
367
|
-
try {
|
|
368
|
-
const result = (0, node_util.parseArgs)({
|
|
369
|
-
args: argv,
|
|
370
|
-
options: parseOptions,
|
|
371
|
-
allowPositionals: true,
|
|
372
|
-
strict: false
|
|
373
|
-
});
|
|
374
|
-
parsed = {
|
|
375
|
-
values: result.values,
|
|
376
|
-
positionals: result.positionals
|
|
377
|
-
};
|
|
378
|
-
} catch {
|
|
379
|
-
renderHelp(def, parentName);
|
|
380
|
-
process.exit(1);
|
|
381
|
-
}
|
|
382
|
-
if (parsed.values["help"]) {
|
|
383
|
-
renderHelp(def, parentName);
|
|
384
|
-
process.exit(0);
|
|
385
|
-
}
|
|
386
|
-
for (const [name, opt] of Object.entries(def.options ?? {})) if (opt.required && parsed.values[name] === void 0) {
|
|
387
|
-
console.error((0, node_util.styleText)("red", `Error: --${name} is required`));
|
|
388
|
-
renderHelp(def, parentName);
|
|
389
|
-
process.exit(1);
|
|
390
|
-
}
|
|
391
|
-
if (!def.run) {
|
|
392
|
-
renderHelp(def, parentName);
|
|
393
|
-
process.exit(0);
|
|
394
|
-
}
|
|
395
|
-
try {
|
|
396
|
-
await def.run(parsed);
|
|
397
|
-
} catch (err) {
|
|
398
|
-
console.error((0, node_util.styleText)("red", `Error: ${err instanceof Error ? err.message : String(err)}`));
|
|
399
|
-
renderHelp(def, parentName);
|
|
400
|
-
process.exit(1);
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
function printRootHelp(programName, version, defs) {
|
|
404
|
-
console.log(`\n${(0, node_util.styleText)("bold", "Usage:")} ${programName} <command> [options]\n`);
|
|
405
|
-
console.log(` Kubb generation — v${version}\n`);
|
|
406
|
-
console.log((0, node_util.styleText)("bold", "Commands:"));
|
|
407
|
-
for (const def of defs) console.log(` ${(0, node_util.styleText)("cyan", def.name.padEnd(16))}${def.description}`);
|
|
408
|
-
console.log();
|
|
409
|
-
console.log((0, node_util.styleText)("bold", "Options:"));
|
|
410
|
-
console.log(` ${(0, node_util.styleText)("cyan", "-v, --version".padEnd(30))}Show version number`);
|
|
411
|
-
console.log(` ${(0, node_util.styleText)("cyan", "-h, --help".padEnd(30))}Show help`);
|
|
412
|
-
console.log();
|
|
413
|
-
console.log(`Run ${(0, node_util.styleText)("cyan", `${programName} <command> --help`)} for command-specific help.\n`);
|
|
414
|
-
}
|
|
415
|
-
defineCLIAdapter({
|
|
416
|
-
renderHelp(def, parentName) {
|
|
417
|
-
renderHelp(def, parentName);
|
|
418
|
-
},
|
|
419
|
-
async run(defs, argv, opts) {
|
|
420
|
-
const { programName, defaultCommandName, version } = opts;
|
|
421
|
-
const args = argv.length >= 2 && argv[0]?.includes("node") ? argv.slice(2) : argv;
|
|
422
|
-
if (args[0] === "--version" || args[0] === "-v") {
|
|
423
|
-
console.log(version);
|
|
424
|
-
process.exit(0);
|
|
425
|
-
}
|
|
426
|
-
if (args[0] === "--help" || args[0] === "-h") {
|
|
427
|
-
printRootHelp(programName, version, defs);
|
|
428
|
-
process.exit(0);
|
|
429
|
-
}
|
|
430
|
-
if (args.length === 0) {
|
|
431
|
-
const defaultDef = defs.find((d) => d.name === defaultCommandName);
|
|
432
|
-
if (defaultDef?.run) await runCommand(defaultDef, [], programName);
|
|
433
|
-
else printRootHelp(programName, version, defs);
|
|
434
|
-
return;
|
|
435
|
-
}
|
|
436
|
-
const [first, ...rest] = args;
|
|
437
|
-
const isKnownSubcommand = defs.some((d) => d.name === first);
|
|
438
|
-
let def;
|
|
439
|
-
let commandArgv;
|
|
440
|
-
let parentName;
|
|
441
|
-
if (isKnownSubcommand) {
|
|
442
|
-
def = defs.find((d) => d.name === first);
|
|
443
|
-
commandArgv = rest;
|
|
444
|
-
parentName = programName;
|
|
445
|
-
} else {
|
|
446
|
-
def = defs.find((d) => d.name === defaultCommandName);
|
|
447
|
-
commandArgv = args;
|
|
448
|
-
parentName = programName;
|
|
449
|
-
}
|
|
450
|
-
if (!def) {
|
|
451
|
-
console.error(`Unknown command: ${first}`);
|
|
452
|
-
printRootHelp(programName, version, defs);
|
|
453
|
-
process.exit(1);
|
|
454
|
-
}
|
|
455
|
-
if (def.subCommands?.length) {
|
|
456
|
-
const [subName, ...subRest] = commandArgv;
|
|
457
|
-
const subDef = def.subCommands.find((s) => s.name === subName);
|
|
458
|
-
if (subName === "--help" || subName === "-h") {
|
|
459
|
-
renderHelp(def, parentName);
|
|
460
|
-
process.exit(0);
|
|
461
|
-
}
|
|
462
|
-
if (!subDef) {
|
|
463
|
-
renderHelp(def, parentName);
|
|
464
|
-
process.exit(subName ? 1 : 0);
|
|
465
|
-
}
|
|
466
|
-
await runCommand(subDef, subRest, `${parentName} ${def.name}`);
|
|
467
|
-
return;
|
|
468
|
-
}
|
|
469
|
-
await runCommand(def, commandArgv, parentName);
|
|
470
|
-
}
|
|
471
|
-
});
|
|
472
|
-
/**
|
|
473
|
-
* Parses a CSS hex color string (`#RGB`) into its RGB channels.
|
|
474
|
-
* Falls back to `255` for any channel that cannot be parsed.
|
|
475
|
-
*/
|
|
476
|
-
function parseHex(color) {
|
|
477
|
-
const int = Number.parseInt(color.replace("#", ""), 16);
|
|
478
|
-
return Number.isNaN(int) ? {
|
|
479
|
-
r: 255,
|
|
480
|
-
g: 255,
|
|
481
|
-
b: 255
|
|
482
|
-
} : {
|
|
483
|
-
r: int >> 16 & 255,
|
|
484
|
-
g: int >> 8 & 255,
|
|
485
|
-
b: int & 255
|
|
1394
|
+
value,
|
|
1395
|
+
kind: "Jsx"
|
|
486
1396
|
};
|
|
487
1397
|
}
|
|
1398
|
+
//#endregion
|
|
1399
|
+
//#region src/printer.ts
|
|
488
1400
|
/**
|
|
489
|
-
*
|
|
490
|
-
*
|
|
1401
|
+
* Creates a schema printer factory.
|
|
1402
|
+
*
|
|
1403
|
+
* This function wraps a builder and makes options optional at call sites.
|
|
1404
|
+
*
|
|
1405
|
+
* The builder receives resolved options and returns:
|
|
1406
|
+
* - `name` — a unique identifier for the printer
|
|
1407
|
+
* - `options` — options stored on the returned printer instance
|
|
1408
|
+
* - `nodes` — a map of `SchemaType` → handler functions that convert a `SchemaNode` to `TOutput`
|
|
1409
|
+
* - `print` _(optional)_ — top-level override exposed as `printer.print`
|
|
1410
|
+
* - Inside this function, use `this.transform(node)` to dispatch to the `nodes` map
|
|
1411
|
+
* - This keeps recursion safe and avoids self-calls
|
|
1412
|
+
*
|
|
1413
|
+
* When no `print` override is provided, `printer.print` falls back to `printer.transform` (the node-level dispatcher).
|
|
1414
|
+
*
|
|
1415
|
+
* @example Basic usage — Zod schema printer
|
|
1416
|
+
* ```ts
|
|
1417
|
+
* type PrinterZod = PrinterFactoryOptions<'zod', { strict?: boolean }, string>
|
|
1418
|
+
*
|
|
1419
|
+
* export const zodPrinter = definePrinter<PrinterZod>((options) => ({
|
|
1420
|
+
* name: 'zod',
|
|
1421
|
+
* options: { strict: options.strict ?? true },
|
|
1422
|
+
* nodes: {
|
|
1423
|
+
* string: () => 'z.string()',
|
|
1424
|
+
* object(node) {
|
|
1425
|
+
* const props = node.properties.map(p => `${p.name}: ${this.transform(p.schema)}`).join(', ')
|
|
1426
|
+
* return `z.object({ ${props} })`
|
|
1427
|
+
* },
|
|
1428
|
+
* },
|
|
1429
|
+
* }))
|
|
1430
|
+
* ```
|
|
491
1431
|
*/
|
|
492
|
-
function
|
|
493
|
-
|
|
494
|
-
return (text) => `\x1b[38;2;${r};${g};${b}m${text}\x1b[0m`;
|
|
1432
|
+
function definePrinter(build) {
|
|
1433
|
+
return createPrinterFactory((node) => node.type)(build);
|
|
495
1434
|
}
|
|
496
|
-
hex("#F55A17"), hex("#F5A217"), hex("#F58517"), hex("#B45309"), hex("#FFFFFF"), hex("#adadc6"), hex("#FDA4AF");
|
|
497
1435
|
/**
|
|
498
|
-
*
|
|
1436
|
+
* Generic printer-factory function used by `definePrinter` and `defineFunctionPrinter`.
|
|
1437
|
+
**
|
|
1438
|
+
* @example
|
|
1439
|
+
* ```ts
|
|
1440
|
+
* export const defineFunctionPrinter = createPrinterFactory<FunctionNode, FunctionNodeType, FunctionNodeByType>(
|
|
1441
|
+
* (node) => kindToHandlerKey[node.kind],
|
|
1442
|
+
* )
|
|
1443
|
+
* ```
|
|
499
1444
|
*/
|
|
500
|
-
function
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
1445
|
+
function createPrinterFactory(getKey) {
|
|
1446
|
+
return function(build) {
|
|
1447
|
+
return (options) => {
|
|
1448
|
+
const { name, options: resolvedOptions, nodes, print: printOverride } = build(options ?? {});
|
|
1449
|
+
const context = {
|
|
1450
|
+
options: resolvedOptions,
|
|
1451
|
+
transform: (node) => {
|
|
1452
|
+
const key = getKey(node);
|
|
1453
|
+
if (key === void 0) return null;
|
|
1454
|
+
const handler = nodes[key];
|
|
1455
|
+
if (!handler) return null;
|
|
1456
|
+
return handler.call(context, node);
|
|
1457
|
+
}
|
|
1458
|
+
};
|
|
1459
|
+
return {
|
|
1460
|
+
name,
|
|
1461
|
+
options: resolvedOptions,
|
|
1462
|
+
transform: context.transform,
|
|
1463
|
+
print: printOverride ? printOverride.bind(context) : context.transform
|
|
1464
|
+
};
|
|
1465
|
+
};
|
|
1466
|
+
};
|
|
507
1467
|
}
|
|
508
1468
|
//#endregion
|
|
509
|
-
//#region src/
|
|
510
|
-
const plainStringTypes = new Set([
|
|
511
|
-
"string",
|
|
512
|
-
"uuid",
|
|
513
|
-
"email",
|
|
514
|
-
"url",
|
|
515
|
-
"datetime"
|
|
516
|
-
]);
|
|
517
|
-
/**
|
|
518
|
-
* Returns `true` when a schema node will be represented as a plain string in generated code.
|
|
519
|
-
*
|
|
520
|
-
* - `string`, `uuid`, `email`, `url`, `datetime` are always plain strings.
|
|
521
|
-
* - `date` and `time` are plain strings when their `representation` is `'string'` rather than `'date'`.
|
|
522
|
-
*/
|
|
523
|
-
function isPlainStringType(node) {
|
|
524
|
-
if (plainStringTypes.has(node.type)) return true;
|
|
525
|
-
const temporal = narrowSchema(node, "date") ?? narrowSchema(node, "time");
|
|
526
|
-
if (temporal) return temporal.representation !== "date";
|
|
527
|
-
return false;
|
|
528
|
-
}
|
|
1469
|
+
//#region src/refs.ts
|
|
529
1470
|
/**
|
|
530
|
-
*
|
|
1471
|
+
* Returns the last path segment of a reference string.
|
|
531
1472
|
*
|
|
532
|
-
*
|
|
533
|
-
* When no `casing` is provided the original array is returned as-is.
|
|
1473
|
+
* Example: `#/components/schemas/Pet` becomes `Pet`.
|
|
534
1474
|
*
|
|
535
|
-
*
|
|
536
|
-
*
|
|
537
|
-
*
|
|
1475
|
+
* @example
|
|
1476
|
+
* ```ts
|
|
1477
|
+
* extractRefName('#/components/schemas/Pet') // 'Pet'
|
|
1478
|
+
* ```
|
|
538
1479
|
*/
|
|
539
|
-
function
|
|
540
|
-
|
|
541
|
-
return params.map((param) => {
|
|
542
|
-
const transformed = casing === "camelcase" || !isValidVarName(param.name) ? camelCase(param.name) : param.name;
|
|
543
|
-
return {
|
|
544
|
-
...param,
|
|
545
|
-
name: transformed
|
|
546
|
-
};
|
|
547
|
-
});
|
|
1480
|
+
function extractRefName(ref) {
|
|
1481
|
+
return ref.split("/").at(-1) ?? ref;
|
|
548
1482
|
}
|
|
549
1483
|
//#endregion
|
|
550
1484
|
//#region src/visitor.ts
|
|
551
1485
|
/**
|
|
552
|
-
* Creates a
|
|
553
|
-
*
|
|
1486
|
+
* Creates a small async concurrency limiter.
|
|
1487
|
+
*
|
|
1488
|
+
* At most `concurrency` tasks are in flight at once. Extra tasks are queued.
|
|
1489
|
+
*
|
|
1490
|
+
* @example
|
|
1491
|
+
* ```ts
|
|
1492
|
+
* const limit = createLimit(2)
|
|
1493
|
+
* for (const task of [taskA, taskB, taskC]) {
|
|
1494
|
+
* await limit(() => task())
|
|
1495
|
+
* }
|
|
1496
|
+
* // only 2 tasks run at the same time
|
|
1497
|
+
* ```
|
|
554
1498
|
*/
|
|
555
1499
|
function createLimit(concurrency) {
|
|
556
1500
|
let active = 0;
|
|
@@ -576,15 +1520,23 @@ function createLimit(concurrency) {
|
|
|
576
1520
|
/**
|
|
577
1521
|
* Returns the immediate traversable children of `node`.
|
|
578
1522
|
*
|
|
579
|
-
* For `Schema` nodes, children (properties
|
|
580
|
-
*
|
|
1523
|
+
* For `Schema` nodes, children (`properties`, `items`, `members`, and non-boolean
|
|
1524
|
+
* `additionalProperties`) are only included
|
|
1525
|
+
* when `recurse` is `true`; shallow mode skips them.
|
|
1526
|
+
*
|
|
1527
|
+
* @example
|
|
1528
|
+
* ```ts
|
|
1529
|
+
* const children = getChildren(operationNode, true)
|
|
1530
|
+
* // returns parameters, requestBody schema (if present), and responses
|
|
1531
|
+
* ```
|
|
581
1532
|
*/
|
|
582
1533
|
function getChildren(node, recurse) {
|
|
583
1534
|
switch (node.kind) {
|
|
584
|
-
case "
|
|
1535
|
+
case "Input": return [...node.schemas, ...node.operations];
|
|
1536
|
+
case "Output": return [];
|
|
585
1537
|
case "Operation": return [
|
|
586
1538
|
...node.parameters,
|
|
587
|
-
...node.requestBody ? [node.requestBody] : [],
|
|
1539
|
+
...node.requestBody?.schema ? [node.requestBody.schema] : [],
|
|
588
1540
|
...node.responses
|
|
589
1541
|
];
|
|
590
1542
|
case "Schema": {
|
|
@@ -593,167 +1545,425 @@ function getChildren(node, recurse) {
|
|
|
593
1545
|
if ("properties" in node && node.properties.length > 0) children.push(...node.properties);
|
|
594
1546
|
if ("items" in node && node.items) children.push(...node.items);
|
|
595
1547
|
if ("members" in node && node.members) children.push(...node.members);
|
|
1548
|
+
if ("additionalProperties" in node && node.additionalProperties && node.additionalProperties !== true) children.push(node.additionalProperties);
|
|
596
1549
|
return children;
|
|
597
1550
|
}
|
|
598
1551
|
case "Property": return [node.schema];
|
|
599
1552
|
case "Parameter": return [node.schema];
|
|
600
1553
|
case "Response": return node.schema ? [node.schema] : [];
|
|
1554
|
+
case "FunctionParameter":
|
|
1555
|
+
case "ParameterGroup":
|
|
1556
|
+
case "FunctionParameters":
|
|
1557
|
+
case "Type": return [];
|
|
1558
|
+
default: return [];
|
|
601
1559
|
}
|
|
602
1560
|
}
|
|
603
1561
|
/**
|
|
604
1562
|
* Depth-first traversal for side effects. Visitor return values are ignored.
|
|
605
|
-
* Sibling nodes at each level are visited concurrently up to `options.concurrency`
|
|
1563
|
+
* Sibling nodes at each level are visited concurrently up to `options.concurrency`
|
|
1564
|
+
* (default: `WALK_CONCURRENCY`).
|
|
1565
|
+
*
|
|
1566
|
+
* @example
|
|
1567
|
+
* ```ts
|
|
1568
|
+
* await walk(root, {
|
|
1569
|
+
* operation(node) {
|
|
1570
|
+
* console.log(node.operationId)
|
|
1571
|
+
* },
|
|
1572
|
+
* })
|
|
1573
|
+
* ```
|
|
1574
|
+
*
|
|
1575
|
+
* @example
|
|
1576
|
+
* ```ts
|
|
1577
|
+
* // Visit only the current node
|
|
1578
|
+
* await walk(root, { depth: 'shallow', root: () => {} })
|
|
1579
|
+
* ```
|
|
606
1580
|
*/
|
|
607
|
-
async function walk(node,
|
|
608
|
-
return _walk(node,
|
|
1581
|
+
async function walk(node, options) {
|
|
1582
|
+
return _walk(node, options, (options.depth ?? visitorDepths.deep) === visitorDepths.deep, createLimit(options.concurrency ?? 30), void 0);
|
|
609
1583
|
}
|
|
610
|
-
|
|
611
|
-
* Internal recursive walk implementation — calls visitor then recurses into children.
|
|
612
|
-
*/
|
|
613
|
-
async function _walk(node, visitor, recurse, limit) {
|
|
1584
|
+
async function _walk(node, visitor, recurse, limit, parent) {
|
|
614
1585
|
switch (node.kind) {
|
|
615
|
-
case "
|
|
616
|
-
await limit(() => visitor.
|
|
1586
|
+
case "Input":
|
|
1587
|
+
await limit(() => visitor.input?.(node, { parent }));
|
|
1588
|
+
break;
|
|
1589
|
+
case "Output":
|
|
1590
|
+
await limit(() => visitor.output?.(node, { parent }));
|
|
617
1591
|
break;
|
|
618
1592
|
case "Operation":
|
|
619
|
-
await limit(() => visitor.operation?.(node));
|
|
1593
|
+
await limit(() => visitor.operation?.(node, { parent }));
|
|
620
1594
|
break;
|
|
621
1595
|
case "Schema":
|
|
622
|
-
await limit(() => visitor.schema?.(node));
|
|
1596
|
+
await limit(() => visitor.schema?.(node, { parent }));
|
|
623
1597
|
break;
|
|
624
1598
|
case "Property":
|
|
625
|
-
await limit(() => visitor.property?.(node));
|
|
1599
|
+
await limit(() => visitor.property?.(node, { parent }));
|
|
626
1600
|
break;
|
|
627
1601
|
case "Parameter":
|
|
628
|
-
await limit(() => visitor.parameter?.(node));
|
|
1602
|
+
await limit(() => visitor.parameter?.(node, { parent }));
|
|
629
1603
|
break;
|
|
630
1604
|
case "Response":
|
|
631
|
-
await limit(() => visitor.response?.(node));
|
|
1605
|
+
await limit(() => visitor.response?.(node, { parent }));
|
|
632
1606
|
break;
|
|
1607
|
+
case "FunctionParameter":
|
|
1608
|
+
case "ParameterGroup":
|
|
1609
|
+
case "FunctionParameters": break;
|
|
633
1610
|
}
|
|
634
1611
|
const children = getChildren(node, recurse);
|
|
635
|
-
|
|
1612
|
+
for (const child of children) await _walk(child, visitor, recurse, limit, node);
|
|
636
1613
|
}
|
|
637
|
-
function transform(node,
|
|
638
|
-
const
|
|
1614
|
+
function transform(node, options) {
|
|
1615
|
+
const { depth, parent, ...visitor } = options;
|
|
1616
|
+
const recurse = (depth ?? visitorDepths.deep) === visitorDepths.deep;
|
|
639
1617
|
switch (node.kind) {
|
|
640
|
-
case "
|
|
641
|
-
let
|
|
642
|
-
const replaced = visitor.
|
|
643
|
-
if (replaced)
|
|
1618
|
+
case "Input": {
|
|
1619
|
+
let input = node;
|
|
1620
|
+
const replaced = visitor.input?.(input, { parent });
|
|
1621
|
+
if (replaced) input = replaced;
|
|
644
1622
|
return {
|
|
645
|
-
...
|
|
646
|
-
schemas:
|
|
647
|
-
|
|
1623
|
+
...input,
|
|
1624
|
+
schemas: input.schemas.map((s) => transform(s, {
|
|
1625
|
+
...options,
|
|
1626
|
+
parent: input
|
|
1627
|
+
})),
|
|
1628
|
+
operations: input.operations.map((op) => transform(op, {
|
|
1629
|
+
...options,
|
|
1630
|
+
parent: input
|
|
1631
|
+
}))
|
|
648
1632
|
};
|
|
649
1633
|
}
|
|
1634
|
+
case "Output": {
|
|
1635
|
+
let output = node;
|
|
1636
|
+
const replaced = visitor.output?.(output, { parent });
|
|
1637
|
+
if (replaced) output = replaced;
|
|
1638
|
+
return output;
|
|
1639
|
+
}
|
|
650
1640
|
case "Operation": {
|
|
651
1641
|
let op = node;
|
|
652
|
-
const replaced = visitor.operation?.(op);
|
|
1642
|
+
const replaced = visitor.operation?.(op, { parent });
|
|
653
1643
|
if (replaced) op = replaced;
|
|
654
1644
|
return {
|
|
655
1645
|
...op,
|
|
656
|
-
parameters: op.parameters.map((p) => transform(p,
|
|
657
|
-
|
|
658
|
-
|
|
1646
|
+
parameters: op.parameters.map((p) => transform(p, {
|
|
1647
|
+
...options,
|
|
1648
|
+
parent: op
|
|
1649
|
+
})),
|
|
1650
|
+
requestBody: op.requestBody ? {
|
|
1651
|
+
...op.requestBody,
|
|
1652
|
+
schema: op.requestBody.schema ? transform(op.requestBody.schema, {
|
|
1653
|
+
...options,
|
|
1654
|
+
parent: op
|
|
1655
|
+
}) : void 0
|
|
1656
|
+
} : void 0,
|
|
1657
|
+
responses: op.responses.map((r) => transform(r, {
|
|
1658
|
+
...options,
|
|
1659
|
+
parent: op
|
|
1660
|
+
}))
|
|
659
1661
|
};
|
|
660
1662
|
}
|
|
661
1663
|
case "Schema": {
|
|
662
1664
|
let schema = node;
|
|
663
|
-
const replaced = visitor.schema?.(schema);
|
|
1665
|
+
const replaced = visitor.schema?.(schema, { parent });
|
|
664
1666
|
if (replaced) schema = replaced;
|
|
1667
|
+
const childOptions = {
|
|
1668
|
+
...options,
|
|
1669
|
+
parent: schema
|
|
1670
|
+
};
|
|
665
1671
|
return {
|
|
666
1672
|
...schema,
|
|
667
|
-
..."properties" in schema && recurse ? { properties: schema.properties.map((p) => transform(p,
|
|
668
|
-
..."items" in schema && recurse ? { items: schema.items?.map((i) => transform(i,
|
|
669
|
-
..."members" in schema && recurse ? { members: schema.members?.map((m) => transform(m,
|
|
1673
|
+
..."properties" in schema && recurse ? { properties: schema.properties.map((p) => transform(p, childOptions)) } : {},
|
|
1674
|
+
..."items" in schema && recurse ? { items: schema.items?.map((i) => transform(i, childOptions)) } : {},
|
|
1675
|
+
..."members" in schema && recurse ? { members: schema.members?.map((m) => transform(m, childOptions)) } : {},
|
|
1676
|
+
..."additionalProperties" in schema && recurse && schema.additionalProperties && schema.additionalProperties !== true ? { additionalProperties: transform(schema.additionalProperties, childOptions) } : {}
|
|
670
1677
|
};
|
|
671
1678
|
}
|
|
672
1679
|
case "Property": {
|
|
673
1680
|
let prop = node;
|
|
674
|
-
const replaced = visitor.property?.(prop);
|
|
1681
|
+
const replaced = visitor.property?.(prop, { parent });
|
|
675
1682
|
if (replaced) prop = replaced;
|
|
676
|
-
return {
|
|
1683
|
+
return createProperty({
|
|
677
1684
|
...prop,
|
|
678
|
-
schema: transform(prop.schema,
|
|
679
|
-
|
|
1685
|
+
schema: transform(prop.schema, {
|
|
1686
|
+
...options,
|
|
1687
|
+
parent: prop
|
|
1688
|
+
})
|
|
1689
|
+
});
|
|
680
1690
|
}
|
|
681
1691
|
case "Parameter": {
|
|
682
1692
|
let param = node;
|
|
683
|
-
const replaced = visitor.parameter?.(param);
|
|
1693
|
+
const replaced = visitor.parameter?.(param, { parent });
|
|
684
1694
|
if (replaced) param = replaced;
|
|
685
|
-
return {
|
|
1695
|
+
return createParameter({
|
|
686
1696
|
...param,
|
|
687
|
-
schema: transform(param.schema,
|
|
688
|
-
|
|
1697
|
+
schema: transform(param.schema, {
|
|
1698
|
+
...options,
|
|
1699
|
+
parent: param
|
|
1700
|
+
})
|
|
1701
|
+
});
|
|
689
1702
|
}
|
|
690
1703
|
case "Response": {
|
|
691
1704
|
let response = node;
|
|
692
|
-
const replaced = visitor.response?.(response);
|
|
1705
|
+
const replaced = visitor.response?.(response, { parent });
|
|
693
1706
|
if (replaced) response = replaced;
|
|
694
1707
|
return {
|
|
695
1708
|
...response,
|
|
696
|
-
schema:
|
|
1709
|
+
schema: transform(response.schema, {
|
|
1710
|
+
...options,
|
|
1711
|
+
parent: response
|
|
1712
|
+
})
|
|
697
1713
|
};
|
|
698
1714
|
}
|
|
1715
|
+
case "FunctionParameter":
|
|
1716
|
+
case "ParameterGroup":
|
|
1717
|
+
case "FunctionParameters":
|
|
1718
|
+
case "Type": return node;
|
|
1719
|
+
default: return node;
|
|
699
1720
|
}
|
|
700
1721
|
}
|
|
701
1722
|
/**
|
|
702
|
-
*
|
|
1723
|
+
* Runs a depth-first synchronous collection pass.
|
|
1724
|
+
*
|
|
1725
|
+
* Non-`undefined` values returned by visitor callbacks are appended to the result.
|
|
1726
|
+
*
|
|
1727
|
+
* @example
|
|
1728
|
+
* ```ts
|
|
1729
|
+
* const ids = collect(root, {
|
|
1730
|
+
* operation(node) {
|
|
1731
|
+
* return node.operationId
|
|
1732
|
+
* },
|
|
1733
|
+
* })
|
|
1734
|
+
* ```
|
|
1735
|
+
*
|
|
1736
|
+
* @example
|
|
1737
|
+
* ```ts
|
|
1738
|
+
* // Collect from only the current node
|
|
1739
|
+
* const values = collect(root, { depth: 'shallow', root: () => 'root' })
|
|
1740
|
+
* ```
|
|
703
1741
|
*/
|
|
704
|
-
function collect(node,
|
|
705
|
-
const
|
|
1742
|
+
function collect(node, options) {
|
|
1743
|
+
const { depth, parent, ...visitor } = options;
|
|
1744
|
+
const recurse = (depth ?? visitorDepths.deep) === visitorDepths.deep;
|
|
706
1745
|
const results = [];
|
|
707
1746
|
let v;
|
|
708
1747
|
switch (node.kind) {
|
|
709
|
-
case "
|
|
710
|
-
v = visitor.
|
|
1748
|
+
case "Input":
|
|
1749
|
+
v = visitor.input?.(node, { parent });
|
|
1750
|
+
break;
|
|
1751
|
+
case "Output":
|
|
1752
|
+
v = visitor.output?.(node, { parent });
|
|
711
1753
|
break;
|
|
712
1754
|
case "Operation":
|
|
713
|
-
v = visitor.operation?.(node);
|
|
1755
|
+
v = visitor.operation?.(node, { parent });
|
|
714
1756
|
break;
|
|
715
1757
|
case "Schema":
|
|
716
|
-
v = visitor.schema?.(node);
|
|
1758
|
+
v = visitor.schema?.(node, { parent });
|
|
717
1759
|
break;
|
|
718
1760
|
case "Property":
|
|
719
|
-
v = visitor.property?.(node);
|
|
1761
|
+
v = visitor.property?.(node, { parent });
|
|
720
1762
|
break;
|
|
721
1763
|
case "Parameter":
|
|
722
|
-
v = visitor.parameter?.(node);
|
|
1764
|
+
v = visitor.parameter?.(node, { parent });
|
|
723
1765
|
break;
|
|
724
1766
|
case "Response":
|
|
725
|
-
v = visitor.response?.(node);
|
|
1767
|
+
v = visitor.response?.(node, { parent });
|
|
726
1768
|
break;
|
|
1769
|
+
case "FunctionParameter":
|
|
1770
|
+
case "ParameterGroup":
|
|
1771
|
+
case "FunctionParameters": break;
|
|
727
1772
|
}
|
|
728
1773
|
if (v !== void 0) results.push(v);
|
|
729
|
-
for (const child of getChildren(node, recurse)) for (const item of collect(child,
|
|
1774
|
+
for (const child of getChildren(node, recurse)) for (const item of collect(child, {
|
|
1775
|
+
...options,
|
|
1776
|
+
parent: node
|
|
1777
|
+
})) results.push(item);
|
|
730
1778
|
return results;
|
|
731
1779
|
}
|
|
732
1780
|
//#endregion
|
|
733
|
-
|
|
734
|
-
|
|
1781
|
+
//#region src/resolvers.ts
|
|
1782
|
+
function findDiscriminator(mapping, ref) {
|
|
1783
|
+
if (!mapping || !ref) return null;
|
|
1784
|
+
return Object.entries(mapping).find(([, value]) => value === ref)?.[0] ?? null;
|
|
1785
|
+
}
|
|
1786
|
+
function childName(parentName, propName) {
|
|
1787
|
+
return parentName ? pascalCase([parentName, propName].join(" ")) : null;
|
|
1788
|
+
}
|
|
1789
|
+
function enumPropName(parentName, propName, enumSuffix) {
|
|
1790
|
+
return pascalCase([
|
|
1791
|
+
parentName,
|
|
1792
|
+
propName,
|
|
1793
|
+
enumSuffix
|
|
1794
|
+
].filter(Boolean).join(" "));
|
|
1795
|
+
}
|
|
1796
|
+
/**
|
|
1797
|
+
* Collects import entries for all `ref` schema nodes in `node`.
|
|
1798
|
+
*/
|
|
1799
|
+
function collectImports({ node, nameMapping, resolve }) {
|
|
1800
|
+
return collect(node, { schema(schemaNode) {
|
|
1801
|
+
const schemaRef = narrowSchema(schemaNode, "ref");
|
|
1802
|
+
if (!schemaRef?.ref) return;
|
|
1803
|
+
const rawName = extractRefName(schemaRef.ref);
|
|
1804
|
+
const result = resolve(nameMapping.get(rawName) ?? rawName);
|
|
1805
|
+
if (!result) return;
|
|
1806
|
+
return result;
|
|
1807
|
+
} });
|
|
1808
|
+
}
|
|
1809
|
+
//#endregion
|
|
1810
|
+
//#region src/transformers.ts
|
|
1811
|
+
/**
|
|
1812
|
+
* Replaces a discriminator property's schema with a string enum of allowed values.
|
|
1813
|
+
*
|
|
1814
|
+
* If `node` is not an object schema, or if the property does not exist, the input
|
|
1815
|
+
* node is returned as-is.
|
|
1816
|
+
*
|
|
1817
|
+
* @example
|
|
1818
|
+
* ```ts
|
|
1819
|
+
* const schema = createSchema({
|
|
1820
|
+
* type: 'object',
|
|
1821
|
+
* properties: [createProperty({ name: 'type', required: true, schema: createSchema({ type: 'string' }) })],
|
|
1822
|
+
* })
|
|
1823
|
+
* const result = setDiscriminatorEnum({ node: schema, propertyName: 'type', values: ['dog', 'cat'] })
|
|
1824
|
+
* ```
|
|
1825
|
+
*/
|
|
1826
|
+
function setDiscriminatorEnum({ node, propertyName, values, enumName }) {
|
|
1827
|
+
const objectNode = narrowSchema(node, "object");
|
|
1828
|
+
if (!objectNode?.properties?.length) return node;
|
|
1829
|
+
if (!objectNode.properties.some((prop) => prop.name === propertyName)) return node;
|
|
1830
|
+
return createSchema({
|
|
1831
|
+
...objectNode,
|
|
1832
|
+
properties: objectNode.properties.map((prop) => {
|
|
1833
|
+
if (prop.name !== propertyName) return prop;
|
|
1834
|
+
return createProperty({
|
|
1835
|
+
...prop,
|
|
1836
|
+
schema: createSchema({
|
|
1837
|
+
type: "enum",
|
|
1838
|
+
primitive: "string",
|
|
1839
|
+
enumValues: values,
|
|
1840
|
+
name: enumName,
|
|
1841
|
+
readOnly: prop.schema.readOnly,
|
|
1842
|
+
writeOnly: prop.schema.writeOnly
|
|
1843
|
+
})
|
|
1844
|
+
});
|
|
1845
|
+
})
|
|
1846
|
+
});
|
|
1847
|
+
}
|
|
1848
|
+
/**
|
|
1849
|
+
* Merges adjacent anonymous object members into a single anonymous object member.
|
|
1850
|
+
*
|
|
1851
|
+
* @example
|
|
1852
|
+
* ```ts
|
|
1853
|
+
* const merged = mergeAdjacentObjects([
|
|
1854
|
+
* createSchema({ type: 'object', properties: [createProperty({ name: 'a', schema: createSchema({ type: 'string' }) })] }),
|
|
1855
|
+
* createSchema({ type: 'object', properties: [createProperty({ name: 'b', schema: createSchema({ type: 'number' }) })] }),
|
|
1856
|
+
* ])
|
|
1857
|
+
* ```
|
|
1858
|
+
*/
|
|
1859
|
+
function mergeAdjacentObjects(members) {
|
|
1860
|
+
return members.reduce((acc, member) => {
|
|
1861
|
+
const objectMember = narrowSchema(member, "object");
|
|
1862
|
+
if (objectMember && !objectMember.name) {
|
|
1863
|
+
const previous = acc.at(-1);
|
|
1864
|
+
const previousObject = previous ? narrowSchema(previous, "object") : void 0;
|
|
1865
|
+
if (previousObject && !previousObject.name) {
|
|
1866
|
+
acc[acc.length - 1] = createSchema({
|
|
1867
|
+
...previousObject,
|
|
1868
|
+
properties: [...previousObject.properties ?? [], ...objectMember.properties ?? []]
|
|
1869
|
+
});
|
|
1870
|
+
return acc;
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
acc.push(member);
|
|
1874
|
+
return acc;
|
|
1875
|
+
}, []);
|
|
1876
|
+
}
|
|
1877
|
+
/**
|
|
1878
|
+
* Removes enum members that are covered by broader scalar primitives in the same union.
|
|
1879
|
+
*
|
|
1880
|
+
* @example
|
|
1881
|
+
* ```ts
|
|
1882
|
+
* const simplified = simplifyUnion([
|
|
1883
|
+
* createSchema({ type: 'enum', primitive: 'string', enumValues: ['active'] }),
|
|
1884
|
+
* createSchema({ type: 'string' }),
|
|
1885
|
+
* ])
|
|
1886
|
+
* // keeps only string member
|
|
1887
|
+
* ```
|
|
1888
|
+
*/
|
|
1889
|
+
function simplifyUnion(members) {
|
|
1890
|
+
const scalarPrimitives = new Set(members.filter((member) => isScalarPrimitive(member.type)).map((m) => m.type));
|
|
1891
|
+
if (!scalarPrimitives.size) return members;
|
|
1892
|
+
return members.filter((member) => {
|
|
1893
|
+
const enumNode = narrowSchema(member, "enum");
|
|
1894
|
+
if (!enumNode) return true;
|
|
1895
|
+
const primitive = enumNode.primitive;
|
|
1896
|
+
if (!primitive) return true;
|
|
1897
|
+
if ((enumNode.namedEnumValues?.length ?? enumNode.enumValues?.length ?? 0) <= 1) return true;
|
|
1898
|
+
if (scalarPrimitives.has(primitive)) return false;
|
|
1899
|
+
if ((primitive === "integer" || primitive === "number") && (scalarPrimitives.has("integer") || scalarPrimitives.has("number"))) return false;
|
|
1900
|
+
return true;
|
|
1901
|
+
});
|
|
1902
|
+
}
|
|
1903
|
+
function setEnumName(propNode, parentName, propName, enumSuffix) {
|
|
1904
|
+
const enumNode = narrowSchema(propNode, "enum");
|
|
1905
|
+
if (enumNode?.primitive === "boolean") return {
|
|
1906
|
+
...propNode,
|
|
1907
|
+
name: void 0
|
|
1908
|
+
};
|
|
1909
|
+
if (enumNode) return {
|
|
1910
|
+
...propNode,
|
|
1911
|
+
name: enumPropName(parentName, propName, enumSuffix)
|
|
1912
|
+
};
|
|
1913
|
+
return propNode;
|
|
1914
|
+
}
|
|
1915
|
+
//#endregion
|
|
1916
|
+
exports.caseParams = caseParams;
|
|
1917
|
+
exports.childName = childName;
|
|
735
1918
|
exports.collect = collect;
|
|
1919
|
+
exports.collectImports = collectImports;
|
|
1920
|
+
exports.createArrowFunction = createArrowFunction;
|
|
1921
|
+
exports.createBreak = createBreak;
|
|
1922
|
+
exports.createConst = createConst;
|
|
1923
|
+
exports.createDiscriminantNode = createDiscriminantNode;
|
|
1924
|
+
exports.createExport = createExport;
|
|
1925
|
+
exports.createFile = createFile;
|
|
1926
|
+
exports.createFunction = createFunction;
|
|
1927
|
+
exports.createFunctionParameter = createFunctionParameter;
|
|
1928
|
+
exports.createFunctionParameters = createFunctionParameters;
|
|
1929
|
+
exports.createImport = createImport;
|
|
1930
|
+
exports.createInput = createInput;
|
|
1931
|
+
exports.createJsx = createJsx;
|
|
736
1932
|
exports.createOperation = createOperation;
|
|
1933
|
+
exports.createOperationParams = createOperationParams;
|
|
1934
|
+
exports.createOutput = createOutput;
|
|
737
1935
|
exports.createParameter = createParameter;
|
|
1936
|
+
exports.createParameterGroup = createParameterGroup;
|
|
1937
|
+
exports.createParamsType = createParamsType;
|
|
1938
|
+
exports.createPrinterFactory = createPrinterFactory;
|
|
738
1939
|
exports.createProperty = createProperty;
|
|
739
1940
|
exports.createResponse = createResponse;
|
|
740
|
-
exports.createRoot = createRoot;
|
|
741
1941
|
exports.createSchema = createSchema;
|
|
1942
|
+
exports.createSource = createSource;
|
|
1943
|
+
exports.createText = createText;
|
|
1944
|
+
exports.createType = createType;
|
|
742
1945
|
exports.definePrinter = definePrinter;
|
|
1946
|
+
exports.enumPropName = enumPropName;
|
|
1947
|
+
exports.extractRefName = extractRefName;
|
|
1948
|
+
exports.extractStringsFromNodes = extractStringsFromNodes;
|
|
1949
|
+
exports.findDiscriminator = findDiscriminator;
|
|
743
1950
|
exports.httpMethods = httpMethods;
|
|
1951
|
+
exports.isInputNode = isInputNode;
|
|
744
1952
|
exports.isOperationNode = isOperationNode;
|
|
745
|
-
exports.
|
|
746
|
-
exports.
|
|
747
|
-
exports.isPropertyNode = isPropertyNode;
|
|
748
|
-
exports.isResponseNode = isResponseNode;
|
|
749
|
-
exports.isRootNode = isRootNode;
|
|
1953
|
+
exports.isOutputNode = isOutputNode;
|
|
1954
|
+
exports.isScalarPrimitive = isScalarPrimitive;
|
|
750
1955
|
exports.isSchemaNode = isSchemaNode;
|
|
1956
|
+
exports.isStringType = isStringType;
|
|
751
1957
|
exports.mediaTypes = mediaTypes;
|
|
1958
|
+
exports.mergeAdjacentObjects = mergeAdjacentObjects;
|
|
752
1959
|
exports.narrowSchema = narrowSchema;
|
|
753
1960
|
exports.nodeKinds = nodeKinds;
|
|
754
|
-
exports.refMapToObject = refMapToObject;
|
|
755
|
-
exports.resolveRef = resolveRef;
|
|
756
1961
|
exports.schemaTypes = schemaTypes;
|
|
1962
|
+
exports.setDiscriminatorEnum = setDiscriminatorEnum;
|
|
1963
|
+
exports.setEnumName = setEnumName;
|
|
1964
|
+
exports.simplifyUnion = simplifyUnion;
|
|
1965
|
+
exports.syncOptionality = syncOptionality;
|
|
1966
|
+
exports.syncSchemaRef = syncSchemaRef;
|
|
757
1967
|
exports.transform = transform;
|
|
758
1968
|
exports.walk = walk;
|
|
759
1969
|
|