ng-openapi 0.3.3 → 0.4.0
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/cli.cjs +1412 -340
- package/index.d.ts +368 -28
- package/index.js +1491 -330
- package/package.json +1 -1
package/cli.cjs
CHANGED
|
@@ -42,6 +42,28 @@ function isUrl(input) {
|
|
|
42
42
|
__name(isUrl, "isUrl");
|
|
43
43
|
|
|
44
44
|
// ../shared/src/errors.ts
|
|
45
|
+
var NG_OPENAPI_ERROR_BRAND = "__ngOpenApiError";
|
|
46
|
+
var ERROR_LINEAGE = /* @__PURE__ */ new WeakMap();
|
|
47
|
+
var FALLBACK_LINEAGE = Object.freeze([
|
|
48
|
+
"NgOpenApiError"
|
|
49
|
+
]);
|
|
50
|
+
function registerError(cls, lineage) {
|
|
51
|
+
ERROR_LINEAGE.set(cls, Object.freeze([
|
|
52
|
+
...lineage
|
|
53
|
+
]));
|
|
54
|
+
return cls;
|
|
55
|
+
}
|
|
56
|
+
__name(registerError, "registerError");
|
|
57
|
+
function inheritedLineage(cls) {
|
|
58
|
+
for (let current = cls; typeof current === "function"; current = Object.getPrototypeOf(current)) {
|
|
59
|
+
const lineage = ERROR_LINEAGE.get(current);
|
|
60
|
+
if (lineage) {
|
|
61
|
+
return lineage;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return FALLBACK_LINEAGE;
|
|
65
|
+
}
|
|
66
|
+
__name(inheritedLineage, "inheritedLineage");
|
|
45
67
|
var NgOpenApiError = class extends Error {
|
|
46
68
|
static {
|
|
47
69
|
__name(this, "NgOpenApiError");
|
|
@@ -50,10 +72,73 @@ var NgOpenApiError = class extends Error {
|
|
|
50
72
|
cause;
|
|
51
73
|
constructor(message, cause) {
|
|
52
74
|
super(message);
|
|
53
|
-
|
|
75
|
+
const lineage = inheritedLineage(new.target);
|
|
76
|
+
this.name = lineage[0];
|
|
54
77
|
this.cause = cause;
|
|
78
|
+
Object.defineProperty(this, NG_OPENAPI_ERROR_BRAND, {
|
|
79
|
+
value: lineage,
|
|
80
|
+
enumerable: false,
|
|
81
|
+
writable: false,
|
|
82
|
+
configurable: false
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* `message` and `name` are non-enumerable on Error, so the default
|
|
87
|
+
* JSON.stringify dropped both — the least useful possible serialization for
|
|
88
|
+
* something that exists to be logged.
|
|
89
|
+
*
|
|
90
|
+
* The payload is spread rather than enumerated: `source`, `issues`,
|
|
91
|
+
* `operation`, `names` and `placeholders` are why these classes are typed
|
|
92
|
+
* in the first place, and listing fields by hand silently drops whichever
|
|
93
|
+
* ones a later subclass adds.
|
|
94
|
+
*/
|
|
95
|
+
toJSON() {
|
|
96
|
+
const cause = this.cause instanceof Error ? this.cause.message : this.cause;
|
|
97
|
+
return {
|
|
98
|
+
name: this.name,
|
|
99
|
+
message: this.message,
|
|
100
|
+
...cause === void 0 ? {} : {
|
|
101
|
+
cause
|
|
102
|
+
},
|
|
103
|
+
// Own enumerable properties: the brand is deliberately not one.
|
|
104
|
+
...Object.fromEntries(Object.entries(this).filter(([key]) => key !== "cause"))
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Recognizes branded errors from another bundled copy of this module, so
|
|
109
|
+
* `error instanceof SpecLoadError` works for a plugin-thrown error too.
|
|
110
|
+
* The prototype chain is checked first, so a caller's own subclass of these
|
|
111
|
+
* classes still matches even though it carries no lineage entry.
|
|
112
|
+
*
|
|
113
|
+
* Never throws and never runs foreign code: both reflective reads sit
|
|
114
|
+
* inside the try, because a Proxy traps `getPrototypeOf` just as readily as
|
|
115
|
+
* `getOwnPropertyDescriptor`.
|
|
116
|
+
*/
|
|
117
|
+
static [Symbol.hasInstance](value) {
|
|
118
|
+
if (typeof value !== "object" || value === null) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
try {
|
|
122
|
+
if (Object.prototype.isPrototypeOf.call(this.prototype, value)) {
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
const expected = ERROR_LINEAGE.get(this)?.[0];
|
|
126
|
+
if (expected === void 0) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, NG_OPENAPI_ERROR_BRAND);
|
|
130
|
+
if (!descriptor || descriptor.enumerable || !("value" in descriptor)) {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
return Array.isArray(descriptor.value) && descriptor.value.includes(expected);
|
|
134
|
+
} catch {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
55
137
|
}
|
|
56
138
|
};
|
|
139
|
+
registerError(NgOpenApiError, [
|
|
140
|
+
"NgOpenApiError"
|
|
141
|
+
]);
|
|
57
142
|
var SpecLoadError = class extends NgOpenApiError {
|
|
58
143
|
static {
|
|
59
144
|
__name(this, "SpecLoadError");
|
|
@@ -65,6 +150,10 @@ var SpecLoadError = class extends NgOpenApiError {
|
|
|
65
150
|
this.source = source;
|
|
66
151
|
}
|
|
67
152
|
};
|
|
153
|
+
registerError(SpecLoadError, [
|
|
154
|
+
"SpecLoadError",
|
|
155
|
+
"NgOpenApiError"
|
|
156
|
+
]);
|
|
68
157
|
var SpecParseError = class extends NgOpenApiError {
|
|
69
158
|
static {
|
|
70
159
|
__name(this, "SpecParseError");
|
|
@@ -76,6 +165,113 @@ var SpecParseError = class extends NgOpenApiError {
|
|
|
76
165
|
this.source = source;
|
|
77
166
|
}
|
|
78
167
|
};
|
|
168
|
+
registerError(SpecParseError, [
|
|
169
|
+
"SpecParseError",
|
|
170
|
+
"NgOpenApiError"
|
|
171
|
+
]);
|
|
172
|
+
function describeOperation(operation) {
|
|
173
|
+
const location = `(${operation.method}) ${operation.path}`;
|
|
174
|
+
return operation.operationId ? `${operation.operationId} (${location})` : location;
|
|
175
|
+
}
|
|
176
|
+
__name(describeOperation, "describeOperation");
|
|
177
|
+
function captureOperation(operation) {
|
|
178
|
+
return Object.freeze({
|
|
179
|
+
operationId: operation.operationId,
|
|
180
|
+
method: operation.method,
|
|
181
|
+
path: operation.path
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
__name(captureOperation, "captureOperation");
|
|
185
|
+
var InvalidIdentifierError = class extends NgOpenApiError {
|
|
186
|
+
static {
|
|
187
|
+
__name(this, "InvalidIdentifierError");
|
|
188
|
+
}
|
|
189
|
+
/** The rejected name, verbatim; absent when no name could be derived. */
|
|
190
|
+
identifier;
|
|
191
|
+
/** The operation whose name was being derived. */
|
|
192
|
+
operation;
|
|
193
|
+
constructor(message, operation, identifier) {
|
|
194
|
+
super(message);
|
|
195
|
+
this.operation = captureOperation(operation);
|
|
196
|
+
this.identifier = identifier;
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
registerError(InvalidIdentifierError, [
|
|
200
|
+
"InvalidIdentifierError",
|
|
201
|
+
"NgOpenApiError"
|
|
202
|
+
]);
|
|
203
|
+
var DuplicateGeneratedNameError = class extends NgOpenApiError {
|
|
204
|
+
static {
|
|
205
|
+
__name(this, "DuplicateGeneratedNameError");
|
|
206
|
+
}
|
|
207
|
+
/** The colliding generated names. */
|
|
208
|
+
names;
|
|
209
|
+
/** The operations that produced them, when known. */
|
|
210
|
+
operations;
|
|
211
|
+
constructor(message, names, operations = []) {
|
|
212
|
+
super(message);
|
|
213
|
+
this.names = Object.freeze([
|
|
214
|
+
...names
|
|
215
|
+
]);
|
|
216
|
+
this.operations = Object.freeze(operations.map(captureOperation));
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
registerError(DuplicateGeneratedNameError, [
|
|
220
|
+
"DuplicateGeneratedNameError",
|
|
221
|
+
"NgOpenApiError"
|
|
222
|
+
]);
|
|
223
|
+
var UnresolvedPathTemplateError = class extends NgOpenApiError {
|
|
224
|
+
static {
|
|
225
|
+
__name(this, "UnresolvedPathTemplateError");
|
|
226
|
+
}
|
|
227
|
+
/** The path as written in the spec. */
|
|
228
|
+
path;
|
|
229
|
+
/** Placeholder names with no declared parameter. */
|
|
230
|
+
placeholders;
|
|
231
|
+
constructor(message, path15, placeholders) {
|
|
232
|
+
super(message);
|
|
233
|
+
this.path = path15;
|
|
234
|
+
this.placeholders = Object.freeze([
|
|
235
|
+
...placeholders
|
|
236
|
+
]);
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
registerError(UnresolvedPathTemplateError, [
|
|
240
|
+
"UnresolvedPathTemplateError",
|
|
241
|
+
"NgOpenApiError"
|
|
242
|
+
]);
|
|
243
|
+
var ConfigValidationError = class extends NgOpenApiError {
|
|
244
|
+
static {
|
|
245
|
+
__name(this, "ConfigValidationError");
|
|
246
|
+
}
|
|
247
|
+
issues;
|
|
248
|
+
constructor(issues) {
|
|
249
|
+
super(`Invalid ng-openapi configuration:
|
|
250
|
+
${issues.map((issue) => ` - ${issue}`).join("\n")}`);
|
|
251
|
+
this.issues = Object.freeze([
|
|
252
|
+
...issues
|
|
253
|
+
]);
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
registerError(ConfigValidationError, [
|
|
257
|
+
"ConfigValidationError",
|
|
258
|
+
"NgOpenApiError"
|
|
259
|
+
]);
|
|
260
|
+
var ConfigLoadError = class extends NgOpenApiError {
|
|
261
|
+
static {
|
|
262
|
+
__name(this, "ConfigLoadError");
|
|
263
|
+
}
|
|
264
|
+
/** Path of the config file that failed to load. */
|
|
265
|
+
source;
|
|
266
|
+
constructor(message, source, cause) {
|
|
267
|
+
super(message, cause);
|
|
268
|
+
this.source = source;
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
registerError(ConfigLoadError, [
|
|
272
|
+
"ConfigLoadError",
|
|
273
|
+
"NgOpenApiError"
|
|
274
|
+
]);
|
|
79
275
|
|
|
80
276
|
// ../shared/src/core/spec-loader.ts
|
|
81
277
|
async function loadSpecContent(pathOrUrl) {
|
|
@@ -177,6 +373,430 @@ function detectFormat(content) {
|
|
|
177
373
|
}
|
|
178
374
|
__name(detectFormat, "detectFormat");
|
|
179
375
|
|
|
376
|
+
// ../shared/src/utils/string.utils.ts
|
|
377
|
+
var IDENTIFIER_SEPARATORS = /(?:_|[^\p{ID_Continue}$])+(.)?/gu;
|
|
378
|
+
var INVALID_IDENTIFIER_START = /^[^\p{ID_Start}$_]/u;
|
|
379
|
+
function toIdentifier(converted) {
|
|
380
|
+
if (converted === "") {
|
|
381
|
+
return "_";
|
|
382
|
+
}
|
|
383
|
+
return converted.replace(INVALID_IDENTIFIER_START, (char) => `_${char}`);
|
|
384
|
+
}
|
|
385
|
+
__name(toIdentifier, "toIdentifier");
|
|
386
|
+
function isValidIdentifier(name) {
|
|
387
|
+
return /^[\p{ID_Start}$_][\p{ID_Continue}$]*$/u.test(name);
|
|
388
|
+
}
|
|
389
|
+
__name(isValidIdentifier, "isValidIdentifier");
|
|
390
|
+
function camelCase(str) {
|
|
391
|
+
const converted = str.replace(IDENTIFIER_SEPARATORS, (_, char) => char ? char.toUpperCase() : "").replace(/^./u, (char) => char.toLowerCase());
|
|
392
|
+
return toIdentifier(converted);
|
|
393
|
+
}
|
|
394
|
+
__name(camelCase, "camelCase");
|
|
395
|
+
function kebabCase(str) {
|
|
396
|
+
return str.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[-_\s]+/g, "-").toLowerCase();
|
|
397
|
+
}
|
|
398
|
+
__name(kebabCase, "kebabCase");
|
|
399
|
+
function pascalCase(str) {
|
|
400
|
+
const converted = str.replace(IDENTIFIER_SEPARATORS, (_, char) => char ? char.toUpperCase() : "").replace(/^./u, (char) => char.toUpperCase());
|
|
401
|
+
return toIdentifier(converted);
|
|
402
|
+
}
|
|
403
|
+
__name(pascalCase, "pascalCase");
|
|
404
|
+
function pascalCaseForEnums(str) {
|
|
405
|
+
const converted = str.replace(/[^a-zA-Z0-9]/g, "_").replace(/(?:^|_)([a-z])/g, (_, char) => char.toUpperCase()).replace(/^([0-9])/, "_$1");
|
|
406
|
+
return converted === "" ? "_" : converted;
|
|
407
|
+
}
|
|
408
|
+
__name(pascalCaseForEnums, "pascalCaseForEnums");
|
|
409
|
+
function capitalizeFirst(str) {
|
|
410
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
411
|
+
}
|
|
412
|
+
__name(capitalizeFirst, "capitalizeFirst");
|
|
413
|
+
|
|
414
|
+
// ../shared/src/core/inline-nested-refs.ts
|
|
415
|
+
function inlineNestedRefs(spec, onWarning) {
|
|
416
|
+
const ctx = {
|
|
417
|
+
root: spec,
|
|
418
|
+
onWarning,
|
|
419
|
+
warned: /* @__PURE__ */ new Set(),
|
|
420
|
+
emittedPerCause: /* @__PURE__ */ new Map(),
|
|
421
|
+
deferred: /* @__PURE__ */ new Map(),
|
|
422
|
+
budget: MAX_INLINED_NODES,
|
|
423
|
+
budgetExhausted: false
|
|
424
|
+
};
|
|
425
|
+
const result = transform(spec, ctx, [], false);
|
|
426
|
+
flushDeferredWarnings(ctx);
|
|
427
|
+
return result;
|
|
428
|
+
}
|
|
429
|
+
__name(inlineNestedRefs, "inlineNestedRefs");
|
|
430
|
+
var MAX_REF_DEPTH = 64;
|
|
431
|
+
var MAX_INLINED_NODES = 1e5;
|
|
432
|
+
var MAX_WARNINGS_PER_CAUSE = 10;
|
|
433
|
+
var WARNING_CAUSE_LABELS = {
|
|
434
|
+
cyclic: "cyclic nested $ref(s) left in place",
|
|
435
|
+
unresolvable: "unresolvable nested $ref(s) left in place",
|
|
436
|
+
"not-a-schema": "nested $ref(s) resolving to a non-schema value",
|
|
437
|
+
"not-a-schema-position": "nested $ref(s) that do not address a schema position",
|
|
438
|
+
depth: "nested $ref(s) past the pointer-hop limit",
|
|
439
|
+
budget: "nested $ref(s) left uninlined after the expansion budget ran out",
|
|
440
|
+
"sibling-conflict": "nested $ref site(s) whose siblings overrode the target",
|
|
441
|
+
"sibling-on-boolean": "nested $ref site(s) whose siblings were dropped on a boolean target",
|
|
442
|
+
"malformed-ref": "non-string $ref value(s)",
|
|
443
|
+
"unsupported-pointer-root": "deep $ref(s) into a root this pass does not inline"
|
|
444
|
+
};
|
|
445
|
+
var REFUSED = /* @__PURE__ */ Symbol("refused");
|
|
446
|
+
function transform(node, ctx, chain, keysAreNames) {
|
|
447
|
+
if (Array.isArray(node)) {
|
|
448
|
+
let changed2 = false;
|
|
449
|
+
const items = node.map((item) => {
|
|
450
|
+
const next = transform(item, ctx, chain, false);
|
|
451
|
+
changed2 ||= next !== item;
|
|
452
|
+
return next;
|
|
453
|
+
});
|
|
454
|
+
return changed2 ? items : node;
|
|
455
|
+
}
|
|
456
|
+
if (!isPlainObject(node)) {
|
|
457
|
+
return node;
|
|
458
|
+
}
|
|
459
|
+
const ref = keysAreNames ? void 0 : node["$ref"];
|
|
460
|
+
if (typeof ref === "string") {
|
|
461
|
+
if (isDeepSchemaPointer(ref)) {
|
|
462
|
+
const inlined = inlineTarget(node, ref, ctx, chain);
|
|
463
|
+
if (inlined !== REFUSED) {
|
|
464
|
+
return inlined;
|
|
465
|
+
}
|
|
466
|
+
} else if (isDeepPointerIntoUnsupportedRoot(ref)) {
|
|
467
|
+
const emittedName = pascalCase(ref.split("/").pop() ?? "");
|
|
468
|
+
warnOnce(ctx, "unsupported-pointer-root", ref, `Nested $ref "${ref}" points into a part of the document this generator does not inline (only #/components/schemas/\u2026 and #/definitions/\u2026 are) \u2014 it will be emitted as the type "${emittedName}", which nothing defines. Promote the target to a named schema and reference that. ${WRONG_TYPE_SUFFIX}`);
|
|
469
|
+
}
|
|
470
|
+
} else if (ref !== void 0) {
|
|
471
|
+
warnOnce(ctx, "malformed-ref", safeStringify(ref), `Ignoring a "$ref" whose value is ${describeKind(ref)}, not a string (${safeStringify(ref)}) \u2014 a $ref must be a JSON pointer string.`);
|
|
472
|
+
}
|
|
473
|
+
let changed = false;
|
|
474
|
+
const entries = Object.entries(node).map(([key, value]) => {
|
|
475
|
+
const next = transformEntry(key, value, ctx, chain, keysAreNames);
|
|
476
|
+
changed ||= next !== value;
|
|
477
|
+
return [
|
|
478
|
+
key,
|
|
479
|
+
next
|
|
480
|
+
];
|
|
481
|
+
});
|
|
482
|
+
return changed ? Object.fromEntries(entries) : node;
|
|
483
|
+
}
|
|
484
|
+
__name(transform, "transform");
|
|
485
|
+
function transformEntry(key, value, ctx, chain, keysAreNames) {
|
|
486
|
+
if (!keysAreNames && PAYLOAD_KEYS.has(key)) {
|
|
487
|
+
return value;
|
|
488
|
+
}
|
|
489
|
+
return transform(value, ctx, chain, !keysAreNames && NAME_KEYED_CONTAINERS.has(key));
|
|
490
|
+
}
|
|
491
|
+
__name(transformEntry, "transformEntry");
|
|
492
|
+
function inlineTarget(node, ref, ctx, chain) {
|
|
493
|
+
if (chain.includes(ref)) {
|
|
494
|
+
warnOnce(ctx, "cyclic", ref, `Nested $ref "${ref}" is cyclic and cannot be inlined finitely \u2014 restructure the target as a named definition. ${WRONG_TYPE_SUFFIX}`);
|
|
495
|
+
return REFUSED;
|
|
496
|
+
}
|
|
497
|
+
const target = resolvePointer(ctx.root, ref);
|
|
498
|
+
if (target === void 0) {
|
|
499
|
+
warnOnce(ctx, "unresolvable", ref, `Could not resolve nested $ref "${ref}". ${WRONG_TYPE_SUFFIX}`);
|
|
500
|
+
return REFUSED;
|
|
501
|
+
}
|
|
502
|
+
if (!isPlainObject(target) && typeof target !== "boolean") {
|
|
503
|
+
warnOnce(ctx, "not-a-schema", ref, `Nested $ref "${ref}" resolves to ${describeKind(target)}, not a schema \u2014 point it at a schema object. ${WRONG_TYPE_SUFFIX}`);
|
|
504
|
+
return REFUSED;
|
|
505
|
+
}
|
|
506
|
+
const nonSchemaSegment = firstNonSchemaSegment(ref);
|
|
507
|
+
if (nonSchemaSegment !== void 0) {
|
|
508
|
+
warnOnce(ctx, "not-a-schema-position", ref, `Nested $ref "${ref}" does not address a schema \u2014 its "${nonSchemaSegment}" segment is not a schema-valued position (expected one of properties/<name>, items, additionalProperties, allOf|anyOf|oneOf/<index>, not, $defs/<name>). ${WRONG_TYPE_SUFFIX}`);
|
|
509
|
+
return REFUSED;
|
|
510
|
+
}
|
|
511
|
+
if (chain.length >= MAX_REF_DEPTH) {
|
|
512
|
+
warnOnce(ctx, "depth", ref, `Nested $ref "${ref}" is more than ${MAX_REF_DEPTH} pointer hops deep and was left uninlined \u2014 restructure the target as a named definition. ${WRONG_TYPE_SUFFIX}`);
|
|
513
|
+
return REFUSED;
|
|
514
|
+
}
|
|
515
|
+
if (ctx.budgetExhausted) {
|
|
516
|
+
warnOnce(ctx, "budget", ref, `Nested $ref "${ref}" was left uninlined: the ${MAX_INLINED_NODES}-node expansion budget was already exhausted earlier in this spec. ${WRONG_TYPE_SUFFIX}`);
|
|
517
|
+
return REFUSED;
|
|
518
|
+
}
|
|
519
|
+
const cost = countNodes(target);
|
|
520
|
+
if (cost > ctx.budget) {
|
|
521
|
+
ctx.budgetExhausted = true;
|
|
522
|
+
warnOnce(ctx, "budget", ref, `Inlining nested $refs exceeded the ${MAX_INLINED_NODES}-node expansion budget at "${ref}" \u2014 it and every deep $ref after it were left uninlined. The spec expands combinatorially; restructure the repeated targets as named definitions. ${WRONG_TYPE_SUFFIX}`);
|
|
523
|
+
return REFUSED;
|
|
524
|
+
}
|
|
525
|
+
ctx.budget -= cost;
|
|
526
|
+
const inlined = transform(structuredClone(target), ctx, [
|
|
527
|
+
...chain,
|
|
528
|
+
ref
|
|
529
|
+
], false);
|
|
530
|
+
return mergeSiblings(node, inlined, ref, ctx, chain);
|
|
531
|
+
}
|
|
532
|
+
__name(inlineTarget, "inlineTarget");
|
|
533
|
+
var WRONG_TYPE_SUFFIX = "The generated type will not match the spec (generated files ship @ts-nocheck, so this surfaces as a silently wrong type rather than a compile error).";
|
|
534
|
+
var PAYLOAD_KEYS = /* @__PURE__ */ new Set([
|
|
535
|
+
"example",
|
|
536
|
+
"examples",
|
|
537
|
+
"default",
|
|
538
|
+
"enum",
|
|
539
|
+
"const"
|
|
540
|
+
]);
|
|
541
|
+
var NAME_KEYED_CONTAINERS = /* @__PURE__ */ new Set([
|
|
542
|
+
// Schema keyword maps
|
|
543
|
+
"properties",
|
|
544
|
+
"patternProperties",
|
|
545
|
+
"dependentSchemas",
|
|
546
|
+
"$defs",
|
|
547
|
+
"definitions",
|
|
548
|
+
// Document-level maps keyed by name, status code, path or media type
|
|
549
|
+
"schemas",
|
|
550
|
+
"responses",
|
|
551
|
+
"parameters",
|
|
552
|
+
"requestBodies",
|
|
553
|
+
"headers",
|
|
554
|
+
"securitySchemes",
|
|
555
|
+
"links",
|
|
556
|
+
"callbacks",
|
|
557
|
+
"encoding",
|
|
558
|
+
"variables",
|
|
559
|
+
"content",
|
|
560
|
+
"paths",
|
|
561
|
+
"webhooks",
|
|
562
|
+
"pathItems"
|
|
563
|
+
]);
|
|
564
|
+
function mergeSiblings(node, inlined, ref, ctx, chain) {
|
|
565
|
+
const siblings = Object.entries(node).filter(([key]) => key !== "$ref");
|
|
566
|
+
if (siblings.length === 0) {
|
|
567
|
+
return inlined;
|
|
568
|
+
}
|
|
569
|
+
if (!isPlainObject(inlined)) {
|
|
570
|
+
const dropped = siblings.map(([key]) => key);
|
|
571
|
+
warnOnce(ctx, "sibling-on-boolean", ref, `Nested $ref "${ref}" resolves to a boolean schema, so the keys next to it (${dropped.join(", ")}) were dropped.`, dropped);
|
|
572
|
+
return inlined;
|
|
573
|
+
}
|
|
574
|
+
const conflicts = siblings.filter(([key, value]) => !ANNOTATION_ONLY_KEYS.has(key) && Object.prototype.hasOwnProperty.call(inlined, key) && !sameValue(value, inlined[key])).map(([key]) => key);
|
|
575
|
+
if (conflicts.length > 0) {
|
|
576
|
+
warnOnce(ctx, "sibling-conflict", ref, `Nested $ref "${ref}" sits next to key(s) (${conflicts.join(", ")}) that its target also defines \u2014 the local value won and the target's was dropped, per JSON Schema 2020-12, where $ref composes with its siblings; OpenAPI 3.0 would have ignored the local one instead. The generated type follows the sibling, not the target \u2014 drop it if the target's value was meant to apply.`, conflicts);
|
|
577
|
+
}
|
|
578
|
+
return Object.fromEntries([
|
|
579
|
+
...Object.entries(inlined),
|
|
580
|
+
// The ref node is a schema, so its keys are keywords: a payload sibling
|
|
581
|
+
// stays verbatim, exactly as it would one level up.
|
|
582
|
+
...siblings.map(([key, value]) => [
|
|
583
|
+
key,
|
|
584
|
+
transformEntry(key, value, ctx, chain, false)
|
|
585
|
+
])
|
|
586
|
+
]);
|
|
587
|
+
}
|
|
588
|
+
__name(mergeSiblings, "mergeSiblings");
|
|
589
|
+
var ANNOTATION_ONLY_KEYS = /* @__PURE__ */ new Set([
|
|
590
|
+
"description",
|
|
591
|
+
"title",
|
|
592
|
+
"summary",
|
|
593
|
+
"example",
|
|
594
|
+
"examples",
|
|
595
|
+
"externalDocs",
|
|
596
|
+
"deprecated",
|
|
597
|
+
"xml",
|
|
598
|
+
"$comment"
|
|
599
|
+
]);
|
|
600
|
+
function sameValue(a, b) {
|
|
601
|
+
if (a === b) {
|
|
602
|
+
return true;
|
|
603
|
+
}
|
|
604
|
+
if (Array.isArray(a) || Array.isArray(b)) {
|
|
605
|
+
return Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.every((item, index) => sameValue(item, b[index]));
|
|
606
|
+
}
|
|
607
|
+
if (isPlainObject(a) && isPlainObject(b)) {
|
|
608
|
+
const keys = Object.keys(a);
|
|
609
|
+
return keys.length === Object.keys(b).length && keys.every((key) => Object.prototype.hasOwnProperty.call(b, key) && sameValue(a[key], b[key]));
|
|
610
|
+
}
|
|
611
|
+
return typeof a === "object" && typeof b === "object" && safeStringify(a) === safeStringify(b);
|
|
612
|
+
}
|
|
613
|
+
__name(sameValue, "sameValue");
|
|
614
|
+
function warnOnce(ctx, cause, ref, message, site) {
|
|
615
|
+
const key = site === void 0 ? `${cause}:${ref}` : `${cause}:${ref}|${[
|
|
616
|
+
...site
|
|
617
|
+
].sort().join(",")}`;
|
|
618
|
+
if (ctx.warned.has(key)) {
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
ctx.warned.add(key);
|
|
622
|
+
const emitted = ctx.emittedPerCause.get(cause) ?? 0;
|
|
623
|
+
if (emitted >= MAX_WARNINGS_PER_CAUSE) {
|
|
624
|
+
const refs = ctx.deferred.get(cause) ?? /* @__PURE__ */ new Set();
|
|
625
|
+
refs.add(ref);
|
|
626
|
+
ctx.deferred.set(cause, refs);
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
ctx.emittedPerCause.set(cause, emitted + 1);
|
|
630
|
+
ctx.onWarning?.(message);
|
|
631
|
+
}
|
|
632
|
+
__name(warnOnce, "warnOnce");
|
|
633
|
+
function flushDeferredWarnings(ctx) {
|
|
634
|
+
for (const [cause, refs] of ctx.deferred) {
|
|
635
|
+
ctx.onWarning?.(`\u2026and ${refs.size} more ${WARNING_CAUSE_LABELS[cause]} (only the first ${MAX_WARNINGS_PER_CAUSE} are reported in full): ${[
|
|
636
|
+
...refs
|
|
637
|
+
].join(", ")}.`);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
__name(flushDeferredWarnings, "flushDeferredWarnings");
|
|
641
|
+
function describeKind(value) {
|
|
642
|
+
if (value === null) {
|
|
643
|
+
return "null";
|
|
644
|
+
}
|
|
645
|
+
if (Array.isArray(value)) {
|
|
646
|
+
return "an array";
|
|
647
|
+
}
|
|
648
|
+
if (isPlainObject(value)) {
|
|
649
|
+
return "an object";
|
|
650
|
+
}
|
|
651
|
+
if (typeof value === "object") {
|
|
652
|
+
const className = Object.getPrototypeOf(value)?.constructor?.name;
|
|
653
|
+
return typeof className === "string" ? `a ${className}` : "a non-plain object";
|
|
654
|
+
}
|
|
655
|
+
return `a ${typeof value}`;
|
|
656
|
+
}
|
|
657
|
+
__name(describeKind, "describeKind");
|
|
658
|
+
function safeStringify(value) {
|
|
659
|
+
try {
|
|
660
|
+
return JSON.stringify(value) ?? String(value);
|
|
661
|
+
} catch {
|
|
662
|
+
return String(value);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
__name(safeStringify, "safeStringify");
|
|
666
|
+
function countNodes(value) {
|
|
667
|
+
if (Array.isArray(value)) {
|
|
668
|
+
return value.reduce((total, item) => total + countNodes(item), 1);
|
|
669
|
+
}
|
|
670
|
+
if (!isPlainObject(value)) {
|
|
671
|
+
return 1;
|
|
672
|
+
}
|
|
673
|
+
return Object.values(value).reduce((total, item) => total + countNodes(item), 1);
|
|
674
|
+
}
|
|
675
|
+
__name(countNodes, "countNodes");
|
|
676
|
+
function isDeepSchemaPointer(ref) {
|
|
677
|
+
if (!ref.startsWith("#/")) {
|
|
678
|
+
return false;
|
|
679
|
+
}
|
|
680
|
+
const segments = ref.slice(2).split("/");
|
|
681
|
+
if (segments[0] === "components" && segments[1] === "schemas") {
|
|
682
|
+
return segments.length > 3;
|
|
683
|
+
}
|
|
684
|
+
if (segments[0] === "definitions") {
|
|
685
|
+
return segments.length > 2;
|
|
686
|
+
}
|
|
687
|
+
return false;
|
|
688
|
+
}
|
|
689
|
+
__name(isDeepSchemaPointer, "isDeepSchemaPointer");
|
|
690
|
+
function isDeepPointerIntoUnsupportedRoot(ref) {
|
|
691
|
+
if (!ref.startsWith("#/") || isDeepSchemaPointer(ref)) {
|
|
692
|
+
return false;
|
|
693
|
+
}
|
|
694
|
+
const segments = ref.slice(2).split("/");
|
|
695
|
+
const wholeEntryDepth = segments[0] === "components" ? 3 : 2;
|
|
696
|
+
return segments.length > wholeEntryDepth;
|
|
697
|
+
}
|
|
698
|
+
__name(isDeepPointerIntoUnsupportedRoot, "isDeepPointerIntoUnsupportedRoot");
|
|
699
|
+
var SCHEMA_KEYWORDS = /* @__PURE__ */ new Set([
|
|
700
|
+
"not",
|
|
701
|
+
"additionalProperties",
|
|
702
|
+
"unevaluatedItems",
|
|
703
|
+
"unevaluatedProperties",
|
|
704
|
+
"contains",
|
|
705
|
+
"propertyNames",
|
|
706
|
+
"contentSchema",
|
|
707
|
+
"if",
|
|
708
|
+
"then",
|
|
709
|
+
"else"
|
|
710
|
+
]);
|
|
711
|
+
var SCHEMA_MAP_KEYWORDS = /* @__PURE__ */ new Set([
|
|
712
|
+
"properties",
|
|
713
|
+
"patternProperties",
|
|
714
|
+
"dependentSchemas",
|
|
715
|
+
"$defs",
|
|
716
|
+
"definitions"
|
|
717
|
+
]);
|
|
718
|
+
var SCHEMA_LIST_KEYWORDS = /* @__PURE__ */ new Set([
|
|
719
|
+
"allOf",
|
|
720
|
+
"anyOf",
|
|
721
|
+
"oneOf",
|
|
722
|
+
"prefixItems"
|
|
723
|
+
]);
|
|
724
|
+
var SCHEMA_OR_LIST_KEYWORDS = /* @__PURE__ */ new Set([
|
|
725
|
+
"items",
|
|
726
|
+
"additionalItems"
|
|
727
|
+
]);
|
|
728
|
+
function firstNonSchemaSegment(ref) {
|
|
729
|
+
const segments = pointerSegments(ref);
|
|
730
|
+
const rest = segments.slice(segments[0] === "definitions" ? 2 : 3);
|
|
731
|
+
let position = "schema";
|
|
732
|
+
for (const segment of rest) {
|
|
733
|
+
if (position === "schema-map") {
|
|
734
|
+
position = "schema";
|
|
735
|
+
continue;
|
|
736
|
+
}
|
|
737
|
+
if (position === "schema-list") {
|
|
738
|
+
if (toArrayIndex(segment) === void 0) {
|
|
739
|
+
return segment;
|
|
740
|
+
}
|
|
741
|
+
position = "schema";
|
|
742
|
+
continue;
|
|
743
|
+
}
|
|
744
|
+
if (position === "schema-or-list" && toArrayIndex(segment) !== void 0) {
|
|
745
|
+
position = "schema";
|
|
746
|
+
continue;
|
|
747
|
+
}
|
|
748
|
+
if (SCHEMA_KEYWORDS.has(segment)) {
|
|
749
|
+
position = "schema";
|
|
750
|
+
} else if (SCHEMA_MAP_KEYWORDS.has(segment)) {
|
|
751
|
+
position = "schema-map";
|
|
752
|
+
} else if (SCHEMA_LIST_KEYWORDS.has(segment)) {
|
|
753
|
+
position = "schema-list";
|
|
754
|
+
} else if (SCHEMA_OR_LIST_KEYWORDS.has(segment)) {
|
|
755
|
+
position = "schema-or-list";
|
|
756
|
+
} else {
|
|
757
|
+
return segment;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
return position === "schema" || position === "schema-or-list" ? void 0 : rest[rest.length - 1];
|
|
761
|
+
}
|
|
762
|
+
__name(firstNonSchemaSegment, "firstNonSchemaSegment");
|
|
763
|
+
function pointerSegments(ref) {
|
|
764
|
+
return ref.slice(2).split("/").map((segment) => segment.replace(/~1/g, "/").replace(/~0/g, "~"));
|
|
765
|
+
}
|
|
766
|
+
__name(pointerSegments, "pointerSegments");
|
|
767
|
+
function resolvePointer(root, ref) {
|
|
768
|
+
let current = root;
|
|
769
|
+
const segments = pointerSegments(ref);
|
|
770
|
+
for (const segment of segments) {
|
|
771
|
+
if (Array.isArray(current)) {
|
|
772
|
+
const index = toArrayIndex(segment);
|
|
773
|
+
if (index === void 0 || index >= current.length) {
|
|
774
|
+
return void 0;
|
|
775
|
+
}
|
|
776
|
+
current = current[index];
|
|
777
|
+
continue;
|
|
778
|
+
}
|
|
779
|
+
if (!isPlainObject(current) || !Object.prototype.hasOwnProperty.call(current, segment)) {
|
|
780
|
+
return void 0;
|
|
781
|
+
}
|
|
782
|
+
current = current[segment];
|
|
783
|
+
}
|
|
784
|
+
return current;
|
|
785
|
+
}
|
|
786
|
+
__name(resolvePointer, "resolvePointer");
|
|
787
|
+
function toArrayIndex(segment) {
|
|
788
|
+
return /^(?:0|[1-9][0-9]*)$/.test(segment) ? Number(segment) : void 0;
|
|
789
|
+
}
|
|
790
|
+
__name(toArrayIndex, "toArrayIndex");
|
|
791
|
+
function isPlainObject(value) {
|
|
792
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
793
|
+
return false;
|
|
794
|
+
}
|
|
795
|
+
const proto = Object.getPrototypeOf(value);
|
|
796
|
+
return proto === Object.prototype || proto === null;
|
|
797
|
+
}
|
|
798
|
+
__name(isPlainObject, "isPlainObject");
|
|
799
|
+
|
|
180
800
|
// ../shared/src/utils/content-types.constants.ts
|
|
181
801
|
var CONTENT_TYPES = {
|
|
182
802
|
MULTIPART: "multipart/form-data",
|
|
@@ -193,7 +813,7 @@ function extractPaths(swaggerPaths = {}, methods = [
|
|
|
193
813
|
"delete",
|
|
194
814
|
"options",
|
|
195
815
|
"head"
|
|
196
|
-
]) {
|
|
816
|
+
], onWarning, resolveParameter) {
|
|
197
817
|
const paths = [];
|
|
198
818
|
Object.entries(swaggerPaths).forEach(([path15, pathItem]) => {
|
|
199
819
|
methods.forEach((method) => {
|
|
@@ -202,11 +822,11 @@ function extractPaths(swaggerPaths = {}, methods = [
|
|
|
202
822
|
paths.push({
|
|
203
823
|
path: path15,
|
|
204
824
|
method: method.toUpperCase(),
|
|
205
|
-
operationId: operation.operationId,
|
|
825
|
+
operationId: asName(operation.operationId),
|
|
206
826
|
summary: operation.summary,
|
|
207
827
|
description: operation.description,
|
|
208
|
-
tags: operation.tags
|
|
209
|
-
parameters: parseParameters(operation.parameters || [], pathItem.parameters || []),
|
|
828
|
+
tags: Array.isArray(operation.tags) ? operation.tags.flatMap(nameOrNothing) : [],
|
|
829
|
+
parameters: parseParameters(operation.parameters || [], pathItem.parameters || [], `(${method.toUpperCase()}) ${path15}`, onWarning, resolveParameter),
|
|
210
830
|
requestBody: operation.requestBody,
|
|
211
831
|
responses: operation.responses || {}
|
|
212
832
|
});
|
|
@@ -216,13 +836,54 @@ function extractPaths(swaggerPaths = {}, methods = [
|
|
|
216
836
|
return paths;
|
|
217
837
|
}
|
|
218
838
|
__name(extractPaths, "extractPaths");
|
|
219
|
-
function
|
|
839
|
+
function asName(value) {
|
|
840
|
+
if (typeof value === "string") {
|
|
841
|
+
return value;
|
|
842
|
+
}
|
|
843
|
+
return typeof value === "number" && Number.isFinite(value) ? String(value) : void 0;
|
|
844
|
+
}
|
|
845
|
+
__name(asName, "asName");
|
|
846
|
+
function nameOrNothing(value) {
|
|
847
|
+
const name = asName(value);
|
|
848
|
+
return name === void 0 ? [] : [
|
|
849
|
+
name
|
|
850
|
+
];
|
|
851
|
+
}
|
|
852
|
+
__name(nameOrNothing, "nameOrNothing");
|
|
853
|
+
function parseParameters(operationParams, pathParams, location, onWarning, resolveParameter) {
|
|
220
854
|
const allParams = [
|
|
221
855
|
...pathParams,
|
|
222
856
|
...operationParams
|
|
223
|
-
]
|
|
224
|
-
|
|
225
|
-
|
|
857
|
+
].flatMap((param) => {
|
|
858
|
+
const ref = param.$ref;
|
|
859
|
+
if (typeof ref !== "string") {
|
|
860
|
+
return [
|
|
861
|
+
param
|
|
862
|
+
];
|
|
863
|
+
}
|
|
864
|
+
const resolution = resolveParameter?.(ref) ?? {
|
|
865
|
+
problem: "cannot be resolved here: no reusable parameters were provided"
|
|
866
|
+
};
|
|
867
|
+
if ("parameter" in resolution) {
|
|
868
|
+
return [
|
|
869
|
+
resolution.parameter
|
|
870
|
+
];
|
|
871
|
+
}
|
|
872
|
+
onWarning?.(`A parameter of ${location} references "${ref}", which ${resolution.problem}. The parameter was skipped.`);
|
|
873
|
+
return [];
|
|
874
|
+
});
|
|
875
|
+
const named = allParams.filter((param) => {
|
|
876
|
+
const name = asName(param.name);
|
|
877
|
+
if (name !== void 0 && name !== "") {
|
|
878
|
+
return true;
|
|
879
|
+
}
|
|
880
|
+
const where = typeof param.in === "string" ? `${param.in} parameter` : "parameter";
|
|
881
|
+
const shown = param.name === void 0 ? "no name" : `name ${JSON.stringify(param.name)}`;
|
|
882
|
+
onWarning?.(`A ${where} of ${location} has ${shown} and was skipped. Give it a name in the spec.`);
|
|
883
|
+
return false;
|
|
884
|
+
});
|
|
885
|
+
return named.map((param) => ({
|
|
886
|
+
name: asName(param.name),
|
|
226
887
|
in: param.in,
|
|
227
888
|
required: param.required || param.in === "path",
|
|
228
889
|
schema: param.schema,
|
|
@@ -233,23 +894,38 @@ function parseParameters(operationParams, pathParams) {
|
|
|
233
894
|
}
|
|
234
895
|
__name(parseParameters, "parseParameters");
|
|
235
896
|
|
|
236
|
-
// ../shared/src/
|
|
237
|
-
function
|
|
238
|
-
return
|
|
897
|
+
// ../shared/src/emit/literal.emit.ts
|
|
898
|
+
function quoteLiteral(value) {
|
|
899
|
+
return `'${escapeSingleQuoted(value)}'`;
|
|
239
900
|
}
|
|
240
|
-
__name(
|
|
241
|
-
function
|
|
242
|
-
return
|
|
901
|
+
__name(quoteLiteral, "quoteLiteral");
|
|
902
|
+
function escapeSingleQuoted(value) {
|
|
903
|
+
return value.replace(/[\\']/g, (char) => `\\${char}`).replace(/\r/g, "\\r").replace(/\n/g, "\\n");
|
|
243
904
|
}
|
|
244
|
-
__name(
|
|
245
|
-
function
|
|
246
|
-
return
|
|
905
|
+
__name(escapeSingleQuoted, "escapeSingleQuoted");
|
|
906
|
+
function escapeTemplateLiteral(value) {
|
|
907
|
+
return value.replace(/[\\`]/g, (char) => `\\${char}`).replace(/\$\{/g, "\\${").replace(/\r/g, "\\r");
|
|
247
908
|
}
|
|
248
|
-
__name(
|
|
249
|
-
function
|
|
250
|
-
return
|
|
909
|
+
__name(escapeTemplateLiteral, "escapeTemplateLiteral");
|
|
910
|
+
function escapeDoubleQuoted(value) {
|
|
911
|
+
return value.replace(/[\\"]/g, (char) => `\\${char}`).replace(/\r/g, "\\r").replace(/\n/g, "\\n");
|
|
251
912
|
}
|
|
252
|
-
__name(
|
|
913
|
+
__name(escapeDoubleQuoted, "escapeDoubleQuoted");
|
|
914
|
+
function emitPropertyName(name) {
|
|
915
|
+
return IDENTIFIER_NAME.test(name) ? name : `"${escapeDoubleQuoted(name)}"`;
|
|
916
|
+
}
|
|
917
|
+
__name(emitPropertyName, "emitPropertyName");
|
|
918
|
+
var IDENTIFIER_NAME = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
919
|
+
function escapeJsDoc(text) {
|
|
920
|
+
return text.replace(/\*\//g, "*\\/");
|
|
921
|
+
}
|
|
922
|
+
__name(escapeJsDoc, "escapeJsDoc");
|
|
923
|
+
function emitDocs(description) {
|
|
924
|
+
return typeof description === "string" && description ? [
|
|
925
|
+
escapeJsDoc(description)
|
|
926
|
+
] : void 0;
|
|
927
|
+
}
|
|
928
|
+
__name(emitDocs, "emitDocs");
|
|
253
929
|
|
|
254
930
|
// ../shared/src/utils/functions/class-names.ts
|
|
255
931
|
function decorate(base, defaultSuffix, naming) {
|
|
@@ -293,7 +969,7 @@ function getTypeScriptType(schemaOrType, config, formatOrNullable, isNullable, c
|
|
|
293
969
|
switch (schema.type) {
|
|
294
970
|
case "string":
|
|
295
971
|
if (schema.enum) {
|
|
296
|
-
return schema.enum.map((value) => typeof value === "string" ? `'${
|
|
972
|
+
return schema.enum.map((value) => typeof value === "string" ? `'${escapeSingleQuoted(value)}'` : String(value)).join(" | ");
|
|
297
973
|
}
|
|
298
974
|
if (schema.format === "date" || schema.format === "date-time") {
|
|
299
975
|
const dateType = config.options.dateType === "Date" ? "Date" : "string";
|
|
@@ -329,10 +1005,6 @@ function nullableType(type, isNullable) {
|
|
|
329
1005
|
return type + (isNullable ? " | null" : "");
|
|
330
1006
|
}
|
|
331
1007
|
__name(nullableType, "nullableType");
|
|
332
|
-
function escapeString(str) {
|
|
333
|
-
return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
334
|
-
}
|
|
335
|
-
__name(escapeString, "escapeString");
|
|
336
1008
|
|
|
337
1009
|
// ../shared/src/utils/functions/extract-swagger-response-type.ts
|
|
338
1010
|
function getResponseTypeFromResponse(response, responseTypeMapping) {
|
|
@@ -480,7 +1152,7 @@ function getResponseType(response, config) {
|
|
|
480
1152
|
__name(getResponseType, "getResponseType");
|
|
481
1153
|
|
|
482
1154
|
// ../shared/src/core/normalize.ts
|
|
483
|
-
function normalizeSpec(spec) {
|
|
1155
|
+
function normalizeSpec(spec, onWarning) {
|
|
484
1156
|
const rawDefinitions = spec.definitions || spec.components?.schemas || {};
|
|
485
1157
|
const definitions = Object.fromEntries(Object.entries(rawDefinitions).map(([name, definition]) => [
|
|
486
1158
|
name,
|
|
@@ -490,6 +1162,7 @@ function normalizeSpec(spec) {
|
|
|
490
1162
|
const parts = ref.split("/");
|
|
491
1163
|
return definitions[parts[parts.length - 1]];
|
|
492
1164
|
}, "resolveReference");
|
|
1165
|
+
const resolveParameter = createParameterResolver(spec);
|
|
493
1166
|
return {
|
|
494
1167
|
version: spec.swagger ? {
|
|
495
1168
|
type: "swagger",
|
|
@@ -499,7 +1172,7 @@ function normalizeSpec(spec) {
|
|
|
499
1172
|
version: spec.openapi
|
|
500
1173
|
} : null,
|
|
501
1174
|
definitions,
|
|
502
|
-
operations: extractPaths(spec.paths).map((operation) => normalizeOperation(normalizeOperationSchemas(operation), resolveReference)),
|
|
1175
|
+
operations: extractPaths(spec.paths, void 0, onWarning, resolveParameter).map((operation) => normalizeOperation(normalizeOperationSchemas(operation), resolveReference, onWarning)),
|
|
503
1176
|
resolveReference
|
|
504
1177
|
};
|
|
505
1178
|
}
|
|
@@ -589,24 +1262,29 @@ function normalizeContentSchemas(content) {
|
|
|
589
1262
|
]));
|
|
590
1263
|
}
|
|
591
1264
|
__name(normalizeContentSchemas, "normalizeContentSchemas");
|
|
592
|
-
function normalizeOperation(operation, resolveRef) {
|
|
1265
|
+
function normalizeOperation(operation, resolveRef, onWarning) {
|
|
1266
|
+
warnAboutUnboundParameters(operation, onWarning);
|
|
593
1267
|
const content = operation.requestBody?.content;
|
|
594
1268
|
const isMultipart = !!content?.[CONTENT_TYPES.MULTIPART];
|
|
595
1269
|
const isUrlEncoded = !!content?.[CONTENT_TYPES.FORM_URLENCODED] && !content?.[CONTENT_TYPES.JSON];
|
|
596
1270
|
const formDataSchema = isMultipart ? resolveBodySchema(operation.requestBody, CONTENT_TYPES.MULTIPART, resolveRef) : void 0;
|
|
597
1271
|
const urlEncodedSchema = isUrlEncoded ? resolveBodySchema(operation.requestBody, CONTENT_TYPES.FORM_URLENCODED, resolveRef) : void 0;
|
|
598
1272
|
const responseInfo = determineResponseInfo(operation);
|
|
1273
|
+
const pathParams = operation.parameters?.filter((p) => p.in === "path") || [];
|
|
1274
|
+
const queryParams = operation.parameters?.filter((p) => p.in === "query") || [];
|
|
1275
|
+
const formDataFields = Object.keys(formDataSchema?.properties || {});
|
|
1276
|
+
const urlEncodedFields = Object.keys(urlEncodedSchema?.properties || {});
|
|
599
1277
|
return {
|
|
600
1278
|
...operation,
|
|
601
|
-
pathParams
|
|
602
|
-
queryParams
|
|
1279
|
+
pathParams,
|
|
1280
|
+
queryParams,
|
|
603
1281
|
hasBody: !!operation.requestBody,
|
|
604
1282
|
isMultipart,
|
|
605
1283
|
isUrlEncoded,
|
|
606
1284
|
formDataSchema,
|
|
607
|
-
formDataFields
|
|
1285
|
+
formDataFields,
|
|
608
1286
|
urlEncodedSchema,
|
|
609
|
-
urlEncodedFields
|
|
1287
|
+
urlEncodedFields,
|
|
610
1288
|
responseType: responseInfo.responseType,
|
|
611
1289
|
acceptHeader: responseInfo.acceptHeader
|
|
612
1290
|
};
|
|
@@ -635,6 +1313,68 @@ function determineResponseInfo(operation) {
|
|
|
635
1313
|
};
|
|
636
1314
|
}
|
|
637
1315
|
__name(determineResponseInfo, "determineResponseInfo");
|
|
1316
|
+
function createParameterResolver(spec) {
|
|
1317
|
+
const components = (spec.swagger ? spec.parameters : spec.components?.parameters) ?? {};
|
|
1318
|
+
const prefix = spec.swagger ? "#/parameters/" : "#/components/parameters/";
|
|
1319
|
+
return (ref) => {
|
|
1320
|
+
const chain = [];
|
|
1321
|
+
let current = ref;
|
|
1322
|
+
for (; ; ) {
|
|
1323
|
+
if (chain.includes(current)) {
|
|
1324
|
+
return {
|
|
1325
|
+
problem: `is part of a reference cycle (${[
|
|
1326
|
+
...chain,
|
|
1327
|
+
current
|
|
1328
|
+
].map((r) => `"${r}"`).join(" -> ")})`
|
|
1329
|
+
};
|
|
1330
|
+
}
|
|
1331
|
+
chain.push(current);
|
|
1332
|
+
const hash = current.indexOf("#");
|
|
1333
|
+
if (hash > 0) {
|
|
1334
|
+
return {
|
|
1335
|
+
problem: `points into another document ("${current.slice(0, hash)}"); references into other documents are not supported, so bundle the spec into one document first`
|
|
1336
|
+
};
|
|
1337
|
+
}
|
|
1338
|
+
if (!current.startsWith(prefix)) {
|
|
1339
|
+
return {
|
|
1340
|
+
problem: `is not a parameter component pointer (expected "${prefix}<name>")`
|
|
1341
|
+
};
|
|
1342
|
+
}
|
|
1343
|
+
const name = current.slice(prefix.length).replace(/~1/g, "/").replace(/~0/g, "~");
|
|
1344
|
+
const candidate = Object.prototype.hasOwnProperty.call(components, name) ? components[name] : void 0;
|
|
1345
|
+
if (!candidate || typeof candidate !== "object") {
|
|
1346
|
+
return {
|
|
1347
|
+
problem: `does not resolve: there is no parameter component named "${name}"`
|
|
1348
|
+
};
|
|
1349
|
+
}
|
|
1350
|
+
const next = candidate.$ref;
|
|
1351
|
+
if (typeof next === "string") {
|
|
1352
|
+
current = next;
|
|
1353
|
+
continue;
|
|
1354
|
+
}
|
|
1355
|
+
return {
|
|
1356
|
+
parameter: candidate
|
|
1357
|
+
};
|
|
1358
|
+
}
|
|
1359
|
+
};
|
|
1360
|
+
}
|
|
1361
|
+
__name(createParameterResolver, "createParameterResolver");
|
|
1362
|
+
function warnAboutUnboundParameters(operation, onWarning) {
|
|
1363
|
+
for (const param of operation.parameters ?? []) {
|
|
1364
|
+
if (param.in === "path" || param.in === "query") {
|
|
1365
|
+
continue;
|
|
1366
|
+
}
|
|
1367
|
+
if (param.in === "header" || param.in === "cookie") {
|
|
1368
|
+
if (param.required) {
|
|
1369
|
+
onWarning?.(`Required ${param.in} parameter "${param.name}" of ${describeOperation(operation)} is not bound by the generated clients \u2014 callers must pass it through the trailing options parameter.`);
|
|
1370
|
+
}
|
|
1371
|
+
continue;
|
|
1372
|
+
}
|
|
1373
|
+
const kind = param.in === "formData" || param.in === "body" ? `Swagger 2.0 \`in: ${param.in}\`` : `\`in: ${String(param.in)}\``;
|
|
1374
|
+
onWarning?.(`${kind} parameter "${param.name}" of ${describeOperation(operation)} is not supported and was dropped` + (param.required ? " (it is marked required)" : "") + ". Describe it as a requestBody, or as a path or query parameter, to have it generated.");
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
__name(warnAboutUnboundParameters, "warnAboutUnboundParameters");
|
|
638
1378
|
|
|
639
1379
|
// ../shared/src/core/swagger-parser.ts
|
|
640
1380
|
var SwaggerParser = class _SwaggerParser {
|
|
@@ -643,24 +1383,36 @@ var SwaggerParser = class _SwaggerParser {
|
|
|
643
1383
|
}
|
|
644
1384
|
spec;
|
|
645
1385
|
normalized;
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
throw new SpecParseError("Swagger spec is not valid. Check your `validateInput` condition.");
|
|
650
|
-
}
|
|
1386
|
+
/** Non-fatal problems found while normalizing — a parameter with no usable name, for one. */
|
|
1387
|
+
onWarning;
|
|
1388
|
+
constructor(spec, onWarning) {
|
|
651
1389
|
this.spec = spec;
|
|
1390
|
+
this.onWarning = onWarning;
|
|
652
1391
|
}
|
|
653
1392
|
/**
|
|
654
1393
|
* Loads, parses and wraps a spec.
|
|
655
1394
|
*
|
|
1395
|
+
* @param onWarning receives non-fatal spec problems found while parsing or
|
|
1396
|
+
* normalizing — a deep-pointer `$ref` that cannot be inlined, a parameter
|
|
1397
|
+
* with no usable name.
|
|
656
1398
|
* @throws SpecLoadError when the file/URL cannot be read.
|
|
657
|
-
* @throws SpecParseError when the content cannot be parsed
|
|
658
|
-
* config's `validateInput` hook rejects the spec.
|
|
1399
|
+
* @throws SpecParseError when the content cannot be parsed, `$ref` inlining
|
|
1400
|
+
* fails, or the config's `validateInput` hook rejects the spec.
|
|
659
1401
|
*/
|
|
660
|
-
static async create(swaggerPathOrUrl, config) {
|
|
1402
|
+
static async create(swaggerPathOrUrl, config, onWarning) {
|
|
661
1403
|
const swaggerContent = await loadSpecContent(swaggerPathOrUrl);
|
|
662
1404
|
const spec = parseSpecContent(swaggerContent, swaggerPathOrUrl);
|
|
663
|
-
|
|
1405
|
+
const isInputValid = config.validateInput?.(spec) ?? true;
|
|
1406
|
+
if (!isInputValid) {
|
|
1407
|
+
throw new SpecParseError("Swagger spec is not valid. Check your `validateInput` condition.");
|
|
1408
|
+
}
|
|
1409
|
+
let inlinedSpec;
|
|
1410
|
+
try {
|
|
1411
|
+
inlinedSpec = inlineNestedRefs(spec, onWarning);
|
|
1412
|
+
} catch (error) {
|
|
1413
|
+
throw new SpecParseError(`Failed to inline nested $refs in the spec: ${error instanceof Error ? error.message : String(error)}`, swaggerPathOrUrl, error);
|
|
1414
|
+
}
|
|
1415
|
+
return new _SwaggerParser(inlinedSpec, onWarning);
|
|
664
1416
|
}
|
|
665
1417
|
/**
|
|
666
1418
|
* The version-free model generators consume. Computed once and cached —
|
|
@@ -668,7 +1420,7 @@ var SwaggerParser = class _SwaggerParser {
|
|
|
668
1420
|
* can be used as Map keys across generators.
|
|
669
1421
|
*/
|
|
670
1422
|
getNormalizedSpec() {
|
|
671
|
-
this.normalized ??= normalizeSpec(this.spec);
|
|
1423
|
+
this.normalized ??= normalizeSpec(this.spec, this.onWarning);
|
|
672
1424
|
return this.normalized;
|
|
673
1425
|
}
|
|
674
1426
|
/** Definition map regardless of version: 2.0 `definitions` or 3.x `components.schemas`. */
|
|
@@ -698,25 +1450,29 @@ var SwaggerParser = class _SwaggerParser {
|
|
|
698
1450
|
}
|
|
699
1451
|
/** Whether the spec declares a supported version (Swagger 2.x or OpenAPI 3.x). */
|
|
700
1452
|
isValidSpec() {
|
|
701
|
-
return
|
|
1453
|
+
return specVersionOf(this.spec.swagger, "2.") || specVersionOf(this.spec.openapi, "3.");
|
|
702
1454
|
}
|
|
703
1455
|
/** Detected flavor + literal version string, or null when neither field is present. */
|
|
704
1456
|
getSpecVersion() {
|
|
705
|
-
if (this.spec.swagger) {
|
|
1457
|
+
if (this.spec.swagger !== void 0 && this.spec.swagger !== null) {
|
|
706
1458
|
return {
|
|
707
1459
|
type: "swagger",
|
|
708
|
-
version: this.spec.swagger
|
|
1460
|
+
version: String(this.spec.swagger)
|
|
709
1461
|
};
|
|
710
1462
|
}
|
|
711
|
-
if (this.spec.openapi) {
|
|
1463
|
+
if (this.spec.openapi !== void 0 && this.spec.openapi !== null) {
|
|
712
1464
|
return {
|
|
713
1465
|
type: "openapi",
|
|
714
|
-
version: this.spec.openapi
|
|
1466
|
+
version: String(this.spec.openapi)
|
|
715
1467
|
};
|
|
716
1468
|
}
|
|
717
1469
|
return null;
|
|
718
1470
|
}
|
|
719
1471
|
};
|
|
1472
|
+
function specVersionOf(value, majorPrefix) {
|
|
1473
|
+
return typeof value === "string" || typeof value === "number" ? String(value).startsWith(majorPrefix) : false;
|
|
1474
|
+
}
|
|
1475
|
+
__name(specVersionOf, "specVersionOf");
|
|
720
1476
|
|
|
721
1477
|
// ../shared/src/emit/headers.emit.ts
|
|
722
1478
|
function emitHeaders(options) {
|
|
@@ -737,7 +1493,7 @@ ${emitDefaultHeaderGuards(customHeaders)}`;
|
|
|
737
1493
|
headerCode += `
|
|
738
1494
|
// Advertise the response content type declared in the spec
|
|
739
1495
|
if (!headers.has('Accept')) {
|
|
740
|
-
headers = headers.set('Accept',
|
|
1496
|
+
headers = headers.set('Accept', ${quoteLiteral(accept)});
|
|
741
1497
|
}`;
|
|
742
1498
|
}
|
|
743
1499
|
if (contentType?.isMultipart) {
|
|
@@ -761,38 +1517,273 @@ if (!headers.has('Content-Type')) {
|
|
|
761
1517
|
}
|
|
762
1518
|
__name(emitHeaders, "emitHeaders");
|
|
763
1519
|
function emitDefaultHeaderGuards(customHeaders) {
|
|
764
|
-
return Object.entries(customHeaders).map(([key, value]) =>
|
|
765
|
-
|
|
766
|
-
|
|
1520
|
+
return Object.entries(customHeaders).map(([key, value]) => {
|
|
1521
|
+
const name = quoteLiteral(key);
|
|
1522
|
+
return `if (!headers.has(${name})) {
|
|
1523
|
+
headers = headers.set(${name}, ${quoteLiteral(value)});
|
|
1524
|
+
}`;
|
|
1525
|
+
}).join("\n");
|
|
767
1526
|
}
|
|
768
1527
|
__name(emitDefaultHeaderGuards, "emitDefaultHeaderGuards");
|
|
769
1528
|
|
|
1529
|
+
// ../shared/src/utils/functions/get-request-body-type.ts
|
|
1530
|
+
function getRequestBodyType(requestBody, config) {
|
|
1531
|
+
const content = requestBody.content || {};
|
|
1532
|
+
const jsonContent = content[CONTENT_TYPES.JSON];
|
|
1533
|
+
if (jsonContent?.schema) {
|
|
1534
|
+
return getTypeScriptType(jsonContent.schema, config, jsonContent.schema.nullable);
|
|
1535
|
+
}
|
|
1536
|
+
return "any";
|
|
1537
|
+
}
|
|
1538
|
+
__name(getRequestBodyType, "getRequestBodyType");
|
|
1539
|
+
|
|
1540
|
+
// ../shared/src/utils/functions/is-data-type-interface.ts
|
|
1541
|
+
function isDataTypeInterface(type) {
|
|
1542
|
+
const invalidTypes = [
|
|
1543
|
+
"any",
|
|
1544
|
+
"File",
|
|
1545
|
+
"string",
|
|
1546
|
+
"number",
|
|
1547
|
+
"boolean",
|
|
1548
|
+
"object",
|
|
1549
|
+
"unknown",
|
|
1550
|
+
"[]",
|
|
1551
|
+
"Array"
|
|
1552
|
+
];
|
|
1553
|
+
return !invalidTypes.some((invalidType) => type.includes(invalidType));
|
|
1554
|
+
}
|
|
1555
|
+
__name(isDataTypeInterface, "isDataTypeInterface");
|
|
1556
|
+
|
|
1557
|
+
// ../shared/src/utils/functions/argument-names.ts
|
|
1558
|
+
var RESERVED_WORDS = [
|
|
1559
|
+
"arguments",
|
|
1560
|
+
"await",
|
|
1561
|
+
"break",
|
|
1562
|
+
"case",
|
|
1563
|
+
"catch",
|
|
1564
|
+
"class",
|
|
1565
|
+
"const",
|
|
1566
|
+
"continue",
|
|
1567
|
+
"debugger",
|
|
1568
|
+
"default",
|
|
1569
|
+
"delete",
|
|
1570
|
+
"do",
|
|
1571
|
+
"else",
|
|
1572
|
+
"enum",
|
|
1573
|
+
"eval",
|
|
1574
|
+
"export",
|
|
1575
|
+
"extends",
|
|
1576
|
+
"false",
|
|
1577
|
+
"finally",
|
|
1578
|
+
"for",
|
|
1579
|
+
"function",
|
|
1580
|
+
"if",
|
|
1581
|
+
"implements",
|
|
1582
|
+
"import",
|
|
1583
|
+
"in",
|
|
1584
|
+
"instanceof",
|
|
1585
|
+
"interface",
|
|
1586
|
+
"let",
|
|
1587
|
+
"new",
|
|
1588
|
+
"null",
|
|
1589
|
+
"package",
|
|
1590
|
+
"private",
|
|
1591
|
+
"protected",
|
|
1592
|
+
"public",
|
|
1593
|
+
"return",
|
|
1594
|
+
"static",
|
|
1595
|
+
"super",
|
|
1596
|
+
"switch",
|
|
1597
|
+
"this",
|
|
1598
|
+
"throw",
|
|
1599
|
+
"true",
|
|
1600
|
+
"try",
|
|
1601
|
+
"typeof",
|
|
1602
|
+
"var",
|
|
1603
|
+
"void",
|
|
1604
|
+
"while",
|
|
1605
|
+
"with",
|
|
1606
|
+
"yield"
|
|
1607
|
+
];
|
|
1608
|
+
var SERVICE_ARGUMENT_PROFILE = Object.freeze({
|
|
1609
|
+
reserved: Object.freeze([
|
|
1610
|
+
"observe",
|
|
1611
|
+
"options",
|
|
1612
|
+
"url",
|
|
1613
|
+
"params",
|
|
1614
|
+
"headers",
|
|
1615
|
+
"formData",
|
|
1616
|
+
"formBody"
|
|
1617
|
+
]),
|
|
1618
|
+
bindsRequestBody: true
|
|
1619
|
+
});
|
|
1620
|
+
var RESOURCE_ARGUMENT_PROFILE = Object.freeze({
|
|
1621
|
+
reserved: Object.freeze([
|
|
1622
|
+
"resourceOptions",
|
|
1623
|
+
"requestOptions",
|
|
1624
|
+
"params",
|
|
1625
|
+
"headers"
|
|
1626
|
+
]),
|
|
1627
|
+
bindsRequestBody: false
|
|
1628
|
+
});
|
|
1629
|
+
var REQUEST_BODY_KEY = /* @__PURE__ */ Symbol("requestBody");
|
|
1630
|
+
function resolveArgumentNames(operation, config, profile) {
|
|
1631
|
+
let byConfig = RESOLVED.get(operation);
|
|
1632
|
+
if (!byConfig) {
|
|
1633
|
+
byConfig = /* @__PURE__ */ new WeakMap();
|
|
1634
|
+
RESOLVED.set(operation, byConfig);
|
|
1635
|
+
}
|
|
1636
|
+
let byProfile = byConfig.get(config);
|
|
1637
|
+
if (!byProfile) {
|
|
1638
|
+
byProfile = /* @__PURE__ */ new WeakMap();
|
|
1639
|
+
byConfig.set(config, byProfile);
|
|
1640
|
+
}
|
|
1641
|
+
const cached = byProfile.get(profile);
|
|
1642
|
+
if (cached) {
|
|
1643
|
+
return cached;
|
|
1644
|
+
}
|
|
1645
|
+
const resolved = computeArgumentNames(operation, config, profile);
|
|
1646
|
+
byProfile.set(profile, resolved);
|
|
1647
|
+
return resolved;
|
|
1648
|
+
}
|
|
1649
|
+
__name(resolveArgumentNames, "resolveArgumentNames");
|
|
1650
|
+
var RESOLVED = /* @__PURE__ */ new WeakMap();
|
|
1651
|
+
function computeArgumentNames(operation, config, profile) {
|
|
1652
|
+
const entries = [
|
|
1653
|
+
...operation.pathParams.map((param) => ({
|
|
1654
|
+
key: param.name,
|
|
1655
|
+
base: camelCase(param.name)
|
|
1656
|
+
})),
|
|
1657
|
+
...operation.formDataFields.map((field) => ({
|
|
1658
|
+
key: field,
|
|
1659
|
+
base: camelCase(field)
|
|
1660
|
+
})),
|
|
1661
|
+
...operation.urlEncodedFields.map((field) => ({
|
|
1662
|
+
key: field,
|
|
1663
|
+
base: camelCase(field)
|
|
1664
|
+
}))
|
|
1665
|
+
];
|
|
1666
|
+
const bodyBase = profile.bindsRequestBody ? jsonBodyIdentifier(operation, config) : void 0;
|
|
1667
|
+
if (bodyBase !== void 0) {
|
|
1668
|
+
entries.push({
|
|
1669
|
+
key: REQUEST_BODY_KEY,
|
|
1670
|
+
base: bodyBase
|
|
1671
|
+
});
|
|
1672
|
+
}
|
|
1673
|
+
entries.push(...operation.queryParams.map((param) => ({
|
|
1674
|
+
key: param.name,
|
|
1675
|
+
base: camelCase(param.name)
|
|
1676
|
+
})));
|
|
1677
|
+
const identifiers = /* @__PURE__ */ new Map();
|
|
1678
|
+
const used = /* @__PURE__ */ new Set([
|
|
1679
|
+
...profile.reserved,
|
|
1680
|
+
...RESERVED_WORDS
|
|
1681
|
+
]);
|
|
1682
|
+
const renamed = [];
|
|
1683
|
+
const merged = /* @__PURE__ */ new Set();
|
|
1684
|
+
for (const { key, base } of entries) {
|
|
1685
|
+
if (identifiers.has(key)) {
|
|
1686
|
+
if (typeof key === "string") {
|
|
1687
|
+
merged.add(key);
|
|
1688
|
+
}
|
|
1689
|
+
continue;
|
|
1690
|
+
}
|
|
1691
|
+
let identifier = base;
|
|
1692
|
+
let suffix = 2;
|
|
1693
|
+
while (used.has(identifier)) {
|
|
1694
|
+
identifier = `${base}${suffix}`;
|
|
1695
|
+
suffix++;
|
|
1696
|
+
}
|
|
1697
|
+
if (identifier !== base) {
|
|
1698
|
+
renamed.push(Object.freeze({
|
|
1699
|
+
source: typeof key === "string" ? key : base,
|
|
1700
|
+
identifier
|
|
1701
|
+
}));
|
|
1702
|
+
}
|
|
1703
|
+
used.add(identifier);
|
|
1704
|
+
identifiers.set(key, identifier);
|
|
1705
|
+
}
|
|
1706
|
+
return {
|
|
1707
|
+
// The fallback runs the *same* rules the resolver would have, rather
|
|
1708
|
+
// than a bare camelCase: an unregistered wire name otherwise came back
|
|
1709
|
+
// as `class` or `options`, names the resolver never assigns.
|
|
1710
|
+
// Uniquified against everything already assigned, not just the reserved
|
|
1711
|
+
// words: an unregistered wire name that camelCases onto an identifier
|
|
1712
|
+
// this operation already bound would otherwise alias it — compiling
|
|
1713
|
+
// fine and sending the wrong value. Only reachable for operations built
|
|
1714
|
+
// outside the normalizer, but the resolver is public API.
|
|
1715
|
+
of: /* @__PURE__ */ __name((wireName) => identifiers.get(wireName) ?? deriveLocalName(camelCase(wireName), [
|
|
1716
|
+
...used
|
|
1717
|
+
]), "of"),
|
|
1718
|
+
body: identifiers.get(REQUEST_BODY_KEY),
|
|
1719
|
+
all: Object.freeze([
|
|
1720
|
+
...identifiers.values()
|
|
1721
|
+
]),
|
|
1722
|
+
renamed: Object.freeze(renamed),
|
|
1723
|
+
merged: Object.freeze([
|
|
1724
|
+
...merged
|
|
1725
|
+
])
|
|
1726
|
+
};
|
|
1727
|
+
}
|
|
1728
|
+
__name(computeArgumentNames, "computeArgumentNames");
|
|
1729
|
+
function jsonBodyIdentifier(operation, config) {
|
|
1730
|
+
if (!operation.requestBody || operation.isMultipart) {
|
|
1731
|
+
return void 0;
|
|
1732
|
+
}
|
|
1733
|
+
if (!operation.requestBody.content?.[CONTENT_TYPES.JSON]) {
|
|
1734
|
+
return void 0;
|
|
1735
|
+
}
|
|
1736
|
+
const bodyType = getRequestBodyType(operation.requestBody, config);
|
|
1737
|
+
return isDataTypeInterface(bodyType) ? camelCase(bodyType) : "requestBody";
|
|
1738
|
+
}
|
|
1739
|
+
__name(jsonBodyIdentifier, "jsonBodyIdentifier");
|
|
1740
|
+
function deriveLocalName(base, taken) {
|
|
1741
|
+
const used = new Set(taken);
|
|
1742
|
+
let name = base;
|
|
1743
|
+
let suffix = 2;
|
|
1744
|
+
while (used.has(name)) {
|
|
1745
|
+
name = `${base}${suffix}`;
|
|
1746
|
+
suffix++;
|
|
1747
|
+
}
|
|
1748
|
+
return name;
|
|
1749
|
+
}
|
|
1750
|
+
__name(deriveLocalName, "deriveLocalName");
|
|
1751
|
+
|
|
770
1752
|
// ../shared/src/emit/url.emit.ts
|
|
771
1753
|
function plainParamValue(identifier) {
|
|
772
1754
|
return identifier;
|
|
773
1755
|
}
|
|
774
1756
|
__name(plainParamValue, "plainParamValue");
|
|
775
|
-
function emitUrlExpression(path15, pathParams, paramValue = plainParamValue) {
|
|
776
|
-
|
|
1757
|
+
function emitUrlExpression(path15, pathParams, argumentNames, paramValue = plainParamValue) {
|
|
1758
|
+
const declared = new Set(pathParams.map((param) => param.name));
|
|
1759
|
+
const unresolved = (path15.match(/{([^{}]*)}/g) ?? []).map((placeholder) => placeholder.slice(1, -1)).filter((name) => !declared.has(name));
|
|
1760
|
+
if (unresolved.length > 0) {
|
|
1761
|
+
throw new UnresolvedPathTemplateError(`Path "${path15}" declares no parameter for ${unresolved.map((name) => `{${name}}`).join(", ")}. Declare each placeholder as a path parameter, or remove it from the path.`, path15, unresolved);
|
|
1762
|
+
}
|
|
1763
|
+
let urlExpression = "`${this.basePath}" + escapeTemplateLiteral(path15) + "`";
|
|
777
1764
|
pathParams.forEach((param) => {
|
|
778
|
-
|
|
1765
|
+
const replacement = "${" + paramValue(argumentNames.of(param.name)) + "}";
|
|
1766
|
+
urlExpression = urlExpression.split(`{${escapeTemplateLiteral(param.name)}}`).join(replacement);
|
|
779
1767
|
});
|
|
780
1768
|
return urlExpression;
|
|
781
1769
|
}
|
|
782
1770
|
__name(emitUrlExpression, "emitUrlExpression");
|
|
783
|
-
function emitUrlConstruction(path15, pathParams) {
|
|
784
|
-
return `const url = ${emitUrlExpression(path15, pathParams)};`;
|
|
1771
|
+
function emitUrlConstruction(path15, pathParams, argumentNames) {
|
|
1772
|
+
return `const url = ${emitUrlExpression(path15, pathParams, argumentNames)};`;
|
|
785
1773
|
}
|
|
786
1774
|
__name(emitUrlConstruction, "emitUrlConstruction");
|
|
787
1775
|
|
|
788
1776
|
// ../shared/src/emit/query-params.emit.ts
|
|
789
|
-
function emitQueryParams(queryParams) {
|
|
1777
|
+
function emitQueryParams(queryParams, argumentNames) {
|
|
790
1778
|
if (queryParams.length === 0) {
|
|
791
1779
|
return "";
|
|
792
1780
|
}
|
|
793
|
-
const paramMappings = queryParams.map((param) =>
|
|
794
|
-
|
|
795
|
-
|
|
1781
|
+
const paramMappings = queryParams.map((param) => {
|
|
1782
|
+
const identifier = argumentNames.of(param.name);
|
|
1783
|
+
return `if (${identifier} != null) {
|
|
1784
|
+
params = HttpParamsBuilder.addToHttpParams(params, ${identifier}, ${quoteLiteral(param.name)});
|
|
1785
|
+
}`;
|
|
1786
|
+
}).join("\n");
|
|
796
1787
|
return `
|
|
797
1788
|
let params = new HttpParams();
|
|
798
1789
|
${paramMappings}`;
|
|
@@ -854,55 +1845,158 @@ function listGeneratedBarrelDirs(project, rootPath) {
|
|
|
854
1845
|
__name(listGeneratedBarrelDirs, "listGeneratedBarrelDirs");
|
|
855
1846
|
|
|
856
1847
|
// ../shared/src/utils/functions/token-names.ts
|
|
857
|
-
function
|
|
858
|
-
|
|
1848
|
+
function effectiveClientName(clientName) {
|
|
1849
|
+
return clientName ? clientName : "default";
|
|
1850
|
+
}
|
|
1851
|
+
__name(effectiveClientName, "effectiveClientName");
|
|
1852
|
+
function clientNameIdentifier(clientName) {
|
|
1853
|
+
return isValidIdentifier(clientName) ? capitalizeFirst(clientName) : pascalCase(clientName);
|
|
1854
|
+
}
|
|
1855
|
+
__name(clientNameIdentifier, "clientNameIdentifier");
|
|
1856
|
+
function tokenSuffix(clientName) {
|
|
1857
|
+
return effectiveClientName(clientName).toUpperCase().replace(/[^A-Z0-9]/g, "_");
|
|
1858
|
+
}
|
|
1859
|
+
__name(tokenSuffix, "tokenSuffix");
|
|
1860
|
+
function getClientContextTokenName(clientName) {
|
|
1861
|
+
const clientSuffix = tokenSuffix(clientName);
|
|
859
1862
|
return `CLIENT_CONTEXT_TOKEN_${clientSuffix}`;
|
|
860
1863
|
}
|
|
861
1864
|
__name(getClientContextTokenName, "getClientContextTokenName");
|
|
862
|
-
function getBasePathTokenName(clientName
|
|
863
|
-
const clientSuffix = clientName
|
|
1865
|
+
function getBasePathTokenName(clientName) {
|
|
1866
|
+
const clientSuffix = tokenSuffix(clientName);
|
|
864
1867
|
return `BASE_PATH_${clientSuffix}`;
|
|
865
1868
|
}
|
|
866
1869
|
__name(getBasePathTokenName, "getBasePathTokenName");
|
|
867
|
-
function getInterceptorsTokenName(clientName
|
|
868
|
-
const clientSuffix = clientName
|
|
1870
|
+
function getInterceptorsTokenName(clientName) {
|
|
1871
|
+
const clientSuffix = tokenSuffix(clientName);
|
|
869
1872
|
return `HTTP_INTERCEPTORS_${clientSuffix}`;
|
|
870
1873
|
}
|
|
871
1874
|
__name(getInterceptorsTokenName, "getInterceptorsTokenName");
|
|
872
1875
|
|
|
873
|
-
// ../shared/src/utils/functions/
|
|
874
|
-
function
|
|
875
|
-
|
|
1876
|
+
// ../shared/src/utils/functions/controller-groups.ts
|
|
1877
|
+
function groupOperationsByController(operations, onWarning) {
|
|
1878
|
+
const groups = {};
|
|
1879
|
+
const controllerByFold = /* @__PURE__ */ new Map();
|
|
1880
|
+
const tagSpellings = /* @__PURE__ */ new Map();
|
|
1881
|
+
const namelessTags = /* @__PURE__ */ new Set();
|
|
1882
|
+
operations.forEach((operation) => {
|
|
1883
|
+
const tag = operation.tags?.[0];
|
|
1884
|
+
let rawName = "Default";
|
|
1885
|
+
if (tag !== void 0) {
|
|
1886
|
+
rawName = tag;
|
|
1887
|
+
} else {
|
|
1888
|
+
const pathParts = operation.path.split("/").filter((part) => part && !part.startsWith("{"));
|
|
1889
|
+
if (pathParts.length > 1) {
|
|
1890
|
+
rawName = pathParts[1];
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
const sanitized = pascalCase(rawName);
|
|
1894
|
+
const isNameless = !new RegExp("\\p{L}", "u").test(sanitized);
|
|
1895
|
+
const candidate = isNameless ? "Default" : sanitized;
|
|
1896
|
+
const fold = candidate.toLowerCase();
|
|
1897
|
+
const controllerName = controllerByFold.get(fold) ?? candidate;
|
|
1898
|
+
controllerByFold.set(fold, controllerName);
|
|
1899
|
+
if (tag !== void 0 && isNameless) {
|
|
1900
|
+
namelessTags.add(tag);
|
|
1901
|
+
} else if (tag !== void 0) {
|
|
1902
|
+
const spellings = tagSpellings.get(controllerName) ?? /* @__PURE__ */ new Set();
|
|
1903
|
+
spellings.add(tag);
|
|
1904
|
+
tagSpellings.set(controllerName, spellings);
|
|
1905
|
+
}
|
|
1906
|
+
if (!groups[controllerName]) {
|
|
1907
|
+
groups[controllerName] = [];
|
|
1908
|
+
}
|
|
1909
|
+
groups[controllerName].push(operation);
|
|
1910
|
+
});
|
|
1911
|
+
for (const tag of namelessTags) {
|
|
1912
|
+
onWarning?.(`Tag "${tag}" contains no characters usable in a name \u2014 its operations are generated into the "Default" controller. Rename the tag to give them a file of their own.`);
|
|
1913
|
+
}
|
|
1914
|
+
for (const [controllerName, spellings] of tagSpellings) {
|
|
1915
|
+
if (spellings.size > 1) {
|
|
1916
|
+
onWarning?.(`Tags ${[
|
|
1917
|
+
...spellings
|
|
1918
|
+
].map((name) => `"${name}"`).join(" and ")} all map to the controller "${controllerName}" \u2014 their operations are generated into one file. Rename one tag to keep them apart.`);
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
return groups;
|
|
876
1922
|
}
|
|
877
|
-
__name(
|
|
1923
|
+
__name(groupOperationsByController, "groupOperationsByController");
|
|
878
1924
|
|
|
879
|
-
// ../shared/src/utils/functions/
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
1925
|
+
// ../shared/src/utils/functions/method-names.ts
|
|
1926
|
+
var RESERVED_MEMBER_NAMES = Object.freeze([
|
|
1927
|
+
"constructor",
|
|
1928
|
+
"httpClient",
|
|
1929
|
+
"basePath",
|
|
1930
|
+
"clientContextToken",
|
|
1931
|
+
"createContextWithClientId"
|
|
1932
|
+
]);
|
|
1933
|
+
var RESERVED_MEMBER_SET = new Set(RESERVED_MEMBER_NAMES);
|
|
1934
|
+
function reservedMemberCollision(operation, config) {
|
|
1935
|
+
if (config.options.customizeMethodName || !operation.operationId) {
|
|
1936
|
+
return void 0;
|
|
885
1937
|
}
|
|
886
|
-
|
|
1938
|
+
const natural = camelCase(operation.operationId);
|
|
1939
|
+
return RESERVED_MEMBER_SET.has(natural) ? {
|
|
1940
|
+
from: natural,
|
|
1941
|
+
to: `_${natural}`
|
|
1942
|
+
} : void 0;
|
|
887
1943
|
}
|
|
888
|
-
__name(
|
|
1944
|
+
__name(reservedMemberCollision, "reservedMemberCollision");
|
|
1945
|
+
function getOperationMethodName(operation, config) {
|
|
1946
|
+
const customize = config.options.customizeMethodName;
|
|
1947
|
+
if (!customize) {
|
|
1948
|
+
return defaultOperationMethodName(operation);
|
|
1949
|
+
}
|
|
1950
|
+
if (operation.operationId == null) {
|
|
1951
|
+
throw new InvalidIdentifierError(`customizeMethodName needs an operationId, and ${describeOperation(operation)} has none. Add one to the spec, or drop customizeMethodName to use the derived name.`, operation);
|
|
1952
|
+
}
|
|
1953
|
+
const customName = customize(operation.operationId);
|
|
1954
|
+
if (!isValidIdentifier(customName) || RESERVED_MEMBER_SET.has(customName)) {
|
|
1955
|
+
throw new InvalidIdentifierError(`customizeMethodName returned "${customName}" for ${describeOperation(operation)}, which is not a usable TypeScript method name. Return an identifier \u2014 letters, digits, "_" and "$", not starting with a digit \u2014 and not one of ${RESERVED_MEMBER_NAMES.map((name) => `"${name}"`).join(", ")}.`, operation, customName);
|
|
1956
|
+
}
|
|
1957
|
+
return customName;
|
|
1958
|
+
}
|
|
1959
|
+
__name(getOperationMethodName, "getOperationMethodName");
|
|
1960
|
+
function defaultOperationMethodName(operation) {
|
|
1961
|
+
if (operation.operationId) {
|
|
1962
|
+
const name = camelCase(operation.operationId);
|
|
1963
|
+
return RESERVED_MEMBER_SET.has(name) ? `_${name}` : name;
|
|
1964
|
+
}
|
|
1965
|
+
const method = pascalCase(operation.method.toLowerCase());
|
|
1966
|
+
const pathParts = operation.path.split("/").filter((segment) => segment !== "").map((segment) => pascalCase(segment));
|
|
1967
|
+
const resource = pathParts.join("") || "resource";
|
|
1968
|
+
return `${camelCase(resource)}${method}`;
|
|
1969
|
+
}
|
|
1970
|
+
__name(defaultOperationMethodName, "defaultOperationMethodName");
|
|
889
1971
|
|
|
890
|
-
// ../shared/src/utils/functions/
|
|
891
|
-
function
|
|
892
|
-
const
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
"number",
|
|
897
|
-
"boolean",
|
|
898
|
-
"object",
|
|
899
|
-
"unknown",
|
|
900
|
-
"[]",
|
|
901
|
-
"Array"
|
|
1972
|
+
// ../shared/src/utils/functions/distinct-member-names.ts
|
|
1973
|
+
function assertDistinctMemberNames(serviceClass, className, operations, methodNameOf) {
|
|
1974
|
+
const methodNames = serviceClass.getMethods().map((method) => method.getName());
|
|
1975
|
+
const propertyNames = new Set(serviceClass.getProperties().map((property) => property.getName()));
|
|
1976
|
+
const duplicates = [
|
|
1977
|
+
...new Set(methodNames.filter((name, index) => methodNames.indexOf(name) !== index || propertyNames.has(name)))
|
|
902
1978
|
];
|
|
903
|
-
|
|
1979
|
+
if (duplicates.length === 0) {
|
|
1980
|
+
return;
|
|
1981
|
+
}
|
|
1982
|
+
const byName = new Map(duplicates.map((name) => [
|
|
1983
|
+
name,
|
|
1984
|
+
[]
|
|
1985
|
+
]));
|
|
1986
|
+
for (const operation of operations) {
|
|
1987
|
+
byName.get(methodNameOf(operation))?.push(operation);
|
|
1988
|
+
}
|
|
1989
|
+
const detail = [
|
|
1990
|
+
...byName
|
|
1991
|
+
].map(([name, ops]) => {
|
|
1992
|
+
const from = ops.map(describeOperation).join(" and ");
|
|
1993
|
+
return propertyNames.has(name) ? `"${name}" from ${from} (a property of ${className})` : `"${name}" from ${from}`;
|
|
1994
|
+
}).join("; ");
|
|
1995
|
+
throw new DuplicateGeneratedNameError(`Operations map to the same member name in ${className}: ${detail}. Ensure each operationId maps to a unique name.`, duplicates, [
|
|
1996
|
+
...byName.values()
|
|
1997
|
+
].flat());
|
|
904
1998
|
}
|
|
905
|
-
__name(
|
|
1999
|
+
__name(assertDistinctMemberNames, "assertDistinctMemberNames");
|
|
906
2000
|
|
|
907
2001
|
// ../shared/src/config/constants.ts
|
|
908
2002
|
var disableLinting = `// @ts-nocheck
|
|
@@ -938,7 +2032,7 @@ var PROVIDER_GENERATOR_HEADER_COMMENT = defaultHeaderComment + `* Generated prov
|
|
|
938
2032
|
* Do not edit this file manually
|
|
939
2033
|
*/
|
|
940
2034
|
`;
|
|
941
|
-
var BASE_INTERCEPTOR_HEADER_COMMENT = /* @__PURE__ */ __name((clientName) => defaultHeaderComment + `* Generated Base Interceptor for client ${clientName}
|
|
2035
|
+
var BASE_INTERCEPTOR_HEADER_COMMENT = /* @__PURE__ */ __name((clientName) => defaultHeaderComment + `* Generated Base Interceptor for client ${escapeJsDoc(clientName)}
|
|
942
2036
|
* Do not edit this file manually
|
|
943
2037
|
*/
|
|
944
2038
|
`, "BASE_INTERCEPTOR_HEADER_COMMENT");
|
|
@@ -949,11 +2043,9 @@ var ZOD_PLUGIN_INDEX_GENERATOR_HEADER_COMMENT = defaultHeaderComment + `* Genera
|
|
|
949
2043
|
|
|
950
2044
|
// src/lib/cli.ts
|
|
951
2045
|
var import_commander = require("commander");
|
|
952
|
-
var fs3 = __toESM(require("fs"));
|
|
953
|
-
var path14 = __toESM(require("path"));
|
|
954
2046
|
|
|
955
2047
|
// package.json
|
|
956
|
-
var version = "0.
|
|
2048
|
+
var version = "0.4.0";
|
|
957
2049
|
|
|
958
2050
|
// src/lib/core/generator.ts
|
|
959
2051
|
var import_ts_morph10 = require("ts-morph");
|
|
@@ -962,17 +2054,8 @@ var import_ts_morph10 = require("ts-morph");
|
|
|
962
2054
|
var import_ts_morph = require("ts-morph");
|
|
963
2055
|
|
|
964
2056
|
// src/lib/generators/type/type-resolver.ts
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
}
|
|
968
|
-
__name(escapeString2, "escapeString");
|
|
969
|
-
function sanitizePropertyName(name) {
|
|
970
|
-
if (!/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name)) {
|
|
971
|
-
return `"${name}"`;
|
|
972
|
-
}
|
|
973
|
-
return name;
|
|
974
|
-
}
|
|
975
|
-
__name(sanitizePropertyName, "sanitizePropertyName");
|
|
2057
|
+
var escapeString = escapeSingleQuoted;
|
|
2058
|
+
var sanitizePropertyName = emitPropertyName;
|
|
976
2059
|
var TypeResolver = class {
|
|
977
2060
|
static {
|
|
978
2061
|
__name(this, "TypeResolver");
|
|
@@ -1043,7 +2126,7 @@ var TypeResolver = class {
|
|
|
1043
2126
|
return this.resolveReference(schema.$ref);
|
|
1044
2127
|
}
|
|
1045
2128
|
if (schema.enum) {
|
|
1046
|
-
return schema.enum.map((value) => typeof value === "string" ? `'${
|
|
2129
|
+
return schema.enum.map((value) => typeof value === "string" ? `'${escapeString(value)}'` : String(value)).join(" | ");
|
|
1047
2130
|
}
|
|
1048
2131
|
if (schema.allOf) {
|
|
1049
2132
|
return schema.allOf.map((def) => this.resolve(def)).filter((type) => type !== "any" && type !== "unknown").join(" & ") || "Record<string, unknown>";
|
|
@@ -1147,9 +2230,7 @@ var EnumBuilder = class {
|
|
|
1147
2230
|
}
|
|
1148
2231
|
build(name, definition) {
|
|
1149
2232
|
if (!definition.enum?.length) return [];
|
|
1150
|
-
const docs =
|
|
1151
|
-
definition.description
|
|
1152
|
-
] : void 0;
|
|
2233
|
+
const docs = this.config.options.generateEnumBasedOnDescription ? void 0 : emitDocs(definition.description);
|
|
1153
2234
|
if (this.config.options.enumStyle === "enum") {
|
|
1154
2235
|
return this.buildEnumAsEnum(name, definition, docs);
|
|
1155
2236
|
} else {
|
|
@@ -1190,7 +2271,7 @@ var EnumBuilder = class {
|
|
|
1190
2271
|
const objectProperties = [];
|
|
1191
2272
|
const unionType = definition.enum.map((value) => {
|
|
1192
2273
|
const key = toEnumKey(value);
|
|
1193
|
-
const val = typeof value === "string" ? `'${
|
|
2274
|
+
const val = typeof value === "string" ? `'${escapeString(value)}'` : isNaN(value) ? `'${value}'` : `${value}`;
|
|
1194
2275
|
objectProperties.push(`${key}: ${val} as ${name}`);
|
|
1195
2276
|
return val;
|
|
1196
2277
|
}).join(" | ");
|
|
@@ -1250,9 +2331,7 @@ var InterfaceBuilder = class {
|
|
|
1250
2331
|
kind: import_ts_morph2.StructureKind.Interface,
|
|
1251
2332
|
name,
|
|
1252
2333
|
isExported: true,
|
|
1253
|
-
docs: definition.description
|
|
1254
|
-
definition.description
|
|
1255
|
-
] : void 0,
|
|
2334
|
+
docs: emitDocs(definition.description),
|
|
1256
2335
|
properties: this.buildProperties(definition),
|
|
1257
2336
|
indexSignatures: this.buildIndexSignatures(definition)
|
|
1258
2337
|
};
|
|
@@ -1271,9 +2350,7 @@ var InterfaceBuilder = class {
|
|
|
1271
2350
|
type: propertyType,
|
|
1272
2351
|
isReadonly: isReadOnly,
|
|
1273
2352
|
hasQuestionToken: !isRequired,
|
|
1274
|
-
docs: property.description
|
|
1275
|
-
property.description
|
|
1276
|
-
] : void 0
|
|
2353
|
+
docs: emitDocs(property.description)
|
|
1277
2354
|
};
|
|
1278
2355
|
});
|
|
1279
2356
|
}
|
|
@@ -1443,24 +2520,46 @@ var TypeGenerator = class {
|
|
|
1443
2520
|
this.interfaceBuilder = new InterfaceBuilder(this.resolver);
|
|
1444
2521
|
}
|
|
1445
2522
|
async generate() {
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
}
|
|
1455
|
-
Object.entries(definitions).forEach(([name, definition]) => {
|
|
1456
|
-
this.statements.push(...this.collectTypeStructure(name, definition));
|
|
1457
|
-
});
|
|
1458
|
-
this.statements.push(...buildSdkTypes(this.config));
|
|
1459
|
-
this.applyBatchUpdates();
|
|
1460
|
-
await this.finalize();
|
|
1461
|
-
} catch (error) {
|
|
1462
|
-
throw new Error(`Failed to generate types: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
2523
|
+
const definitions = this.parser.getNormalizedSpec().definitions;
|
|
2524
|
+
if (!definitions || Object.keys(definitions).length === 0) {
|
|
2525
|
+
this.onWarning?.("No definitions found in swagger file");
|
|
2526
|
+
}
|
|
2527
|
+
this.assertDistinctTypeNames(definitions);
|
|
2528
|
+
if (this.config.options.modelFileStructure === "per-type") {
|
|
2529
|
+
this.generatePerType(definitions);
|
|
2530
|
+
return;
|
|
1463
2531
|
}
|
|
2532
|
+
Object.entries(definitions).forEach(([name, definition]) => {
|
|
2533
|
+
this.statements.push(...this.collectTypeStructure(name, definition));
|
|
2534
|
+
});
|
|
2535
|
+
this.statements.push(...buildSdkTypes(this.config));
|
|
2536
|
+
this.applyBatchUpdates();
|
|
2537
|
+
await this.finalize();
|
|
2538
|
+
}
|
|
2539
|
+
/**
|
|
2540
|
+
* Two schemas whose names sanitize onto one type name (`Pet-Store` and
|
|
2541
|
+
* `Pet.Store` both become `Pet_Store`) would emit two declarations of one
|
|
2542
|
+
* interface — and TypeScript declaration-merges those, so the compile
|
|
2543
|
+
* check cannot see it and one model silently acquires the other's
|
|
2544
|
+
* properties. Worse than a hard error, hence a hard error.
|
|
2545
|
+
*/
|
|
2546
|
+
assertDistinctTypeNames(definitions) {
|
|
2547
|
+
const rawByType = /* @__PURE__ */ new Map();
|
|
2548
|
+
for (const rawName of Object.keys(definitions)) {
|
|
2549
|
+
const typeName = this.resolver.pascalName(rawName);
|
|
2550
|
+
rawByType.set(typeName, [
|
|
2551
|
+
...rawByType.get(typeName) ?? [],
|
|
2552
|
+
rawName
|
|
2553
|
+
]);
|
|
2554
|
+
}
|
|
2555
|
+
const collisions = [
|
|
2556
|
+
...rawByType
|
|
2557
|
+
].filter(([, raws]) => raws.length > 1);
|
|
2558
|
+
if (collisions.length === 0) {
|
|
2559
|
+
return;
|
|
2560
|
+
}
|
|
2561
|
+
const detail = collisions.map(([typeName, raws]) => `"${typeName}" from schemas ${raws.map((raw) => `"${raw}"`).join(" and ")}`).join("; ");
|
|
2562
|
+
throw new DuplicateGeneratedNameError(`Schemas map to the same type name: ${detail}. Rename one schema to keep them apart.`, collisions.map(([typeName]) => typeName));
|
|
1464
2563
|
}
|
|
1465
2564
|
collectTypeStructure(name, definition) {
|
|
1466
2565
|
const typeName = this.resolver.pascalName(name);
|
|
@@ -1484,9 +2583,7 @@ var TypeGenerator = class {
|
|
|
1484
2583
|
kind: import_ts_morph4.StructureKind.TypeAlias,
|
|
1485
2584
|
name: typeName,
|
|
1486
2585
|
isExported: true,
|
|
1487
|
-
docs: definition.description
|
|
1488
|
-
definition.description
|
|
1489
|
-
] : void 0,
|
|
2586
|
+
docs: emitDocs(definition.description),
|
|
1490
2587
|
type: this.resolver.resolve(definition)
|
|
1491
2588
|
}
|
|
1492
2589
|
];
|
|
@@ -1503,9 +2600,7 @@ var TypeGenerator = class {
|
|
|
1503
2600
|
name,
|
|
1504
2601
|
type: typeExpression,
|
|
1505
2602
|
isExported: true,
|
|
1506
|
-
docs: definition.description
|
|
1507
|
-
definition.description
|
|
1508
|
-
] : void 0
|
|
2603
|
+
docs: emitDocs(definition.description)
|
|
1509
2604
|
};
|
|
1510
2605
|
}
|
|
1511
2606
|
buildArrayTypeAlias(name, definition) {
|
|
@@ -1514,9 +2609,7 @@ var TypeGenerator = class {
|
|
|
1514
2609
|
kind: import_ts_morph4.StructureKind.TypeAlias,
|
|
1515
2610
|
name,
|
|
1516
2611
|
isExported: true,
|
|
1517
|
-
docs: definition.description
|
|
1518
|
-
definition.description
|
|
1519
|
-
] : void 0,
|
|
2612
|
+
docs: emitDocs(definition.description),
|
|
1520
2613
|
type: `Array<${itemType}>`
|
|
1521
2614
|
};
|
|
1522
2615
|
}
|
|
@@ -1524,10 +2617,9 @@ var TypeGenerator = class {
|
|
|
1524
2617
|
this.addSdkImports(this.sourceFile);
|
|
1525
2618
|
this.sourceFile.addStatements(this.statements);
|
|
1526
2619
|
}
|
|
1527
|
-
|
|
2620
|
+
finalize() {
|
|
1528
2621
|
this.sourceFile.formatText();
|
|
1529
2622
|
this.sourceFile.insertText(0, TYPE_GENERATOR_HEADER_COMMENT);
|
|
1530
|
-
await this.sourceFile.save();
|
|
1531
2623
|
}
|
|
1532
2624
|
generatePerType(definitions) {
|
|
1533
2625
|
const registry = new ModelFileRegistry(this.onWarning);
|
|
@@ -1616,7 +2708,6 @@ var TypeGenerator = class {
|
|
|
1616
2708
|
finalizeModelSourceFile(sourceFile) {
|
|
1617
2709
|
sourceFile.formatText();
|
|
1618
2710
|
sourceFile.insertText(0, TYPE_GENERATOR_HEADER_COMMENT);
|
|
1619
|
-
sourceFile.saveSync();
|
|
1620
2711
|
}
|
|
1621
2712
|
};
|
|
1622
2713
|
|
|
@@ -1629,9 +2720,9 @@ var TokenGenerator = class {
|
|
|
1629
2720
|
}
|
|
1630
2721
|
project;
|
|
1631
2722
|
clientName;
|
|
1632
|
-
constructor(project, clientName
|
|
2723
|
+
constructor(project, clientName) {
|
|
1633
2724
|
this.project = project;
|
|
1634
|
-
this.clientName = clientName;
|
|
2725
|
+
this.clientName = effectiveClientName(clientName);
|
|
1635
2726
|
}
|
|
1636
2727
|
generate(outputDir) {
|
|
1637
2728
|
const tokensDir = path2.join(outputDir, "tokens");
|
|
@@ -1654,9 +2745,9 @@ var TokenGenerator = class {
|
|
|
1654
2745
|
moduleSpecifier: "@angular/common/http"
|
|
1655
2746
|
}
|
|
1656
2747
|
]);
|
|
1657
|
-
const basePathTokenName = this.
|
|
1658
|
-
const interceptorsTokenName = this.
|
|
1659
|
-
const clientContextTokenName = this.
|
|
2748
|
+
const basePathTokenName = getBasePathTokenName(this.clientName);
|
|
2749
|
+
const interceptorsTokenName = getInterceptorsTokenName(this.clientName);
|
|
2750
|
+
const clientContextTokenName = getClientContextTokenName(this.clientName);
|
|
1660
2751
|
sourceFile.addVariableStatement({
|
|
1661
2752
|
isExported: true,
|
|
1662
2753
|
declarationKind: import_ts_morph5.VariableDeclarationKind.Const,
|
|
@@ -1670,7 +2761,7 @@ var TokenGenerator = class {
|
|
|
1670
2761
|
}
|
|
1671
2762
|
],
|
|
1672
2763
|
leadingTrivia: `/**
|
|
1673
|
-
* Injection token for the ${this.clientName} client base API path
|
|
2764
|
+
* Injection token for the ${escapeJsDoc(this.clientName)} client base API path
|
|
1674
2765
|
*/
|
|
1675
2766
|
`
|
|
1676
2767
|
});
|
|
@@ -1687,7 +2778,7 @@ var TokenGenerator = class {
|
|
|
1687
2778
|
}
|
|
1688
2779
|
],
|
|
1689
2780
|
leadingTrivia: `/**
|
|
1690
|
-
* Injection token for the ${this.clientName} client HTTP interceptor instances
|
|
2781
|
+
* Injection token for the ${escapeJsDoc(this.clientName)} client HTTP interceptor instances
|
|
1691
2782
|
*/
|
|
1692
2783
|
`
|
|
1693
2784
|
});
|
|
@@ -1697,11 +2788,11 @@ var TokenGenerator = class {
|
|
|
1697
2788
|
declarations: [
|
|
1698
2789
|
{
|
|
1699
2790
|
name: clientContextTokenName,
|
|
1700
|
-
initializer: `new HttpContextToken<string>(() =>
|
|
2791
|
+
initializer: `new HttpContextToken<string>(() => ${quoteLiteral(this.clientName)})`
|
|
1701
2792
|
}
|
|
1702
2793
|
],
|
|
1703
2794
|
leadingTrivia: `/**
|
|
1704
|
-
* HttpContext token to identify requests belonging to the ${this.clientName} client
|
|
2795
|
+
* HttpContext token to identify requests belonging to the ${escapeJsDoc(this.clientName)} client
|
|
1705
2796
|
*/
|
|
1706
2797
|
`
|
|
1707
2798
|
});
|
|
@@ -1736,19 +2827,6 @@ var TokenGenerator = class {
|
|
|
1736
2827
|
});
|
|
1737
2828
|
}
|
|
1738
2829
|
sourceFile.formatText();
|
|
1739
|
-
sourceFile.saveSync();
|
|
1740
|
-
}
|
|
1741
|
-
getBasePathTokenName() {
|
|
1742
|
-
const clientSuffix = this.clientName.toUpperCase().replace(/[^A-Z0-9]/g, "_");
|
|
1743
|
-
return `BASE_PATH_${clientSuffix}`;
|
|
1744
|
-
}
|
|
1745
|
-
getInterceptorsTokenName() {
|
|
1746
|
-
const clientSuffix = this.clientName.toUpperCase().replace(/[^A-Z0-9]/g, "_");
|
|
1747
|
-
return `HTTP_INTERCEPTORS_${clientSuffix}`;
|
|
1748
|
-
}
|
|
1749
|
-
getClientContextTokenName() {
|
|
1750
|
-
const clientSuffix = this.clientName.toUpperCase().replace(/[^A-Z0-9]/g, "_");
|
|
1751
|
-
return `CLIENT_CONTEXT_TOKEN_${clientSuffix}`;
|
|
1752
2830
|
}
|
|
1753
2831
|
};
|
|
1754
2832
|
|
|
@@ -1888,7 +2966,6 @@ var FileDownloadGenerator = class {
|
|
|
1888
2966
|
return fallbackFilename;`
|
|
1889
2967
|
});
|
|
1890
2968
|
sourceFile.formatText();
|
|
1891
|
-
sourceFile.saveSync();
|
|
1892
2969
|
}
|
|
1893
2970
|
};
|
|
1894
2971
|
|
|
@@ -2042,7 +3119,6 @@ var DateTransformerGenerator = class {
|
|
|
2042
3119
|
]
|
|
2043
3120
|
});
|
|
2044
3121
|
sourceFile.formatText();
|
|
2045
|
-
sourceFile.saveSync();
|
|
2046
3122
|
}
|
|
2047
3123
|
};
|
|
2048
3124
|
|
|
@@ -2105,7 +3181,6 @@ var MainIndexGenerator = class {
|
|
|
2105
3181
|
});
|
|
2106
3182
|
});
|
|
2107
3183
|
sourceFile.formatText();
|
|
2108
|
-
sourceFile.saveSync();
|
|
2109
3184
|
}
|
|
2110
3185
|
};
|
|
2111
3186
|
|
|
@@ -2121,7 +3196,7 @@ var ProviderGenerator = class {
|
|
|
2121
3196
|
constructor(project, config) {
|
|
2122
3197
|
this.project = project;
|
|
2123
3198
|
this.config = config;
|
|
2124
|
-
this.clientName = config.clientName
|
|
3199
|
+
this.clientName = effectiveClientName(config.clientName);
|
|
2125
3200
|
}
|
|
2126
3201
|
generate(outputDir) {
|
|
2127
3202
|
const filePath = path6.join(outputDir, "providers.ts");
|
|
@@ -2130,7 +3205,7 @@ var ProviderGenerator = class {
|
|
|
2130
3205
|
});
|
|
2131
3206
|
const basePathTokenName = getBasePathTokenName(this.clientName);
|
|
2132
3207
|
const interceptorsTokenName = getInterceptorsTokenName(this.clientName);
|
|
2133
|
-
const baseInterceptorClassName = `${
|
|
3208
|
+
const baseInterceptorClassName = `${clientNameIdentifier(this.clientName)}BaseInterceptor`;
|
|
2134
3209
|
sourceFile.addImportDeclarations([
|
|
2135
3210
|
{
|
|
2136
3211
|
namedImports: [
|
|
@@ -2206,22 +3281,19 @@ var ProviderGenerator = class {
|
|
|
2206
3281
|
});
|
|
2207
3282
|
}
|
|
2208
3283
|
sourceFile.addInterface({
|
|
2209
|
-
name: `${
|
|
3284
|
+
name: `${clientNameIdentifier(this.clientName)}Config`,
|
|
2210
3285
|
isExported: true,
|
|
2211
|
-
docs:
|
|
2212
|
-
`Configuration options for ${this.clientName} client`
|
|
2213
|
-
],
|
|
3286
|
+
docs: emitDocs(`Configuration options for ${this.clientName} client`),
|
|
2214
3287
|
properties: configProperties
|
|
2215
3288
|
});
|
|
2216
3289
|
this.addMainProviderFunction(sourceFile, basePathTokenName, interceptorsTokenName, baseInterceptorClassName);
|
|
2217
3290
|
sourceFile.insertText(0, PROVIDER_GENERATOR_HEADER_COMMENT);
|
|
2218
3291
|
sourceFile.formatText();
|
|
2219
|
-
sourceFile.saveSync();
|
|
2220
3292
|
}
|
|
2221
3293
|
addMainProviderFunction(sourceFile, basePathTokenName, interceptorsTokenName, baseInterceptorClassName) {
|
|
2222
3294
|
const hasDateInterceptor = this.config.options.dateType === "Date";
|
|
2223
|
-
const functionName = `provide${
|
|
2224
|
-
const configTypeName = `${
|
|
3295
|
+
const functionName = `provide${clientNameIdentifier(this.clientName)}Client`;
|
|
3296
|
+
const configTypeName = `${clientNameIdentifier(this.clientName)}Config`;
|
|
2225
3297
|
const functionBody = `
|
|
2226
3298
|
const providers: Provider[] = [
|
|
2227
3299
|
// Base path token for this client
|
|
@@ -2269,7 +3341,7 @@ return makeEnvironmentProviders(providers);`;
|
|
|
2269
3341
|
name: functionName,
|
|
2270
3342
|
isExported: true,
|
|
2271
3343
|
docs: [
|
|
2272
|
-
`Provides configuration for ${this.clientName} client`,
|
|
3344
|
+
`Provides configuration for ${escapeJsDoc(this.clientName)} client`,
|
|
2273
3345
|
"",
|
|
2274
3346
|
"@example",
|
|
2275
3347
|
"```typescript",
|
|
@@ -2315,9 +3387,6 @@ return makeEnvironmentProviders(providers);`;
|
|
|
2315
3387
|
});
|
|
2316
3388
|
}
|
|
2317
3389
|
}
|
|
2318
|
-
capitalizeFirst(str) {
|
|
2319
|
-
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
2320
|
-
}
|
|
2321
3390
|
};
|
|
2322
3391
|
|
|
2323
3392
|
// src/lib/generators/utility/base-interceptor.generator.ts
|
|
@@ -2329,9 +3398,9 @@ var BaseInterceptorGenerator = class {
|
|
|
2329
3398
|
}
|
|
2330
3399
|
#project;
|
|
2331
3400
|
#clientName;
|
|
2332
|
-
constructor(project, clientName
|
|
3401
|
+
constructor(project, clientName) {
|
|
2333
3402
|
this.#project = project;
|
|
2334
|
-
this.#clientName = clientName;
|
|
3403
|
+
this.#clientName = effectiveClientName(clientName);
|
|
2335
3404
|
}
|
|
2336
3405
|
generate(outputDir) {
|
|
2337
3406
|
const utilsDir = path7.join(outputDir, "utils");
|
|
@@ -2374,7 +3443,7 @@ var BaseInterceptorGenerator = class {
|
|
|
2374
3443
|
}
|
|
2375
3444
|
]);
|
|
2376
3445
|
sourceFile.addClass({
|
|
2377
|
-
name: `${
|
|
3446
|
+
name: `${clientNameIdentifier(this.#clientName)}BaseInterceptor`,
|
|
2378
3447
|
isExported: true,
|
|
2379
3448
|
decorators: [
|
|
2380
3449
|
{
|
|
@@ -2438,10 +3507,6 @@ var BaseInterceptorGenerator = class {
|
|
|
2438
3507
|
});
|
|
2439
3508
|
sourceFile.insertText(0, BASE_INTERCEPTOR_HEADER_COMMENT(this.#clientName));
|
|
2440
3509
|
sourceFile.formatText();
|
|
2441
|
-
sourceFile.saveSync();
|
|
2442
|
-
}
|
|
2443
|
-
capitalizeFirst(str) {
|
|
2444
|
-
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
2445
3510
|
}
|
|
2446
3511
|
};
|
|
2447
3512
|
|
|
@@ -2474,7 +3539,6 @@ var HttpParamsBuilderGenerator = class {
|
|
|
2474
3539
|
});
|
|
2475
3540
|
this.addMethods(classDeclaration);
|
|
2476
3541
|
sourceFile.formatText();
|
|
2477
|
-
sourceFile.saveSync();
|
|
2478
3542
|
}
|
|
2479
3543
|
addMethods(classDeclaration) {
|
|
2480
3544
|
const methods = [
|
|
@@ -2670,22 +3734,23 @@ var ServiceMethodBodyGenerator = class {
|
|
|
2670
3734
|
this.config = config;
|
|
2671
3735
|
}
|
|
2672
3736
|
generateMethodBody(operation) {
|
|
3737
|
+
const argumentNames = resolveArgumentNames(operation, this.config, SERVICE_ARGUMENT_PROFILE);
|
|
2673
3738
|
const bodyParts = [
|
|
2674
|
-
emitUrlConstruction(operation.path, operation.pathParams),
|
|
2675
|
-
emitQueryParams(operation.queryParams),
|
|
3739
|
+
emitUrlConstruction(operation.path, operation.pathParams, argumentNames),
|
|
3740
|
+
emitQueryParams(operation.queryParams, argumentNames),
|
|
2676
3741
|
emitHeaders({
|
|
2677
3742
|
optionsExpression: "options",
|
|
2678
3743
|
customHeaders: this.config.options.customHeaders,
|
|
2679
3744
|
accept: this.config.options.emitAcceptHeader ?? true ? operation.acceptHeader : void 0,
|
|
2680
3745
|
contentType: operation
|
|
2681
3746
|
}),
|
|
2682
|
-
this.generateMultipartFormData(operation),
|
|
2683
|
-
this.generateUrlEncodedFormData(operation),
|
|
2684
|
-
this.generateHttpRequest(operation)
|
|
3747
|
+
this.generateMultipartFormData(operation, argumentNames),
|
|
3748
|
+
this.generateUrlEncodedFormData(operation, argumentNames),
|
|
3749
|
+
this.generateHttpRequest(operation, argumentNames)
|
|
2685
3750
|
];
|
|
2686
3751
|
return bodyParts.filter(Boolean).join("\n");
|
|
2687
3752
|
}
|
|
2688
|
-
generateMultipartFormData(operation) {
|
|
3753
|
+
generateMultipartFormData(operation, argumentNames) {
|
|
2689
3754
|
if (!operation.isMultipart || operation.formDataFields.length === 0) {
|
|
2690
3755
|
return "";
|
|
2691
3756
|
}
|
|
@@ -2694,21 +3759,22 @@ var ServiceMethodBodyGenerator = class {
|
|
|
2694
3759
|
const fieldSchema = properties[field];
|
|
2695
3760
|
const isFile = fieldSchema?.type === "string" && fieldSchema?.format === "binary";
|
|
2696
3761
|
const isArray = fieldSchema?.type === "array";
|
|
3762
|
+
const arg = argumentNames.of(field);
|
|
2697
3763
|
if (isArray) {
|
|
2698
3764
|
const itemSchema = Array.isArray(fieldSchema.items) ? fieldSchema.items[0] : fieldSchema.items;
|
|
2699
3765
|
const isFileArray = itemSchema?.type === "string" && itemSchema?.format === "binary";
|
|
2700
3766
|
const valueExpression = isFileArray ? "item" : "String(item)";
|
|
2701
|
-
return `if (${
|
|
2702
|
-
${
|
|
3767
|
+
return `if (${arg} !== undefined && Array.isArray(${arg})) {
|
|
3768
|
+
${arg}.forEach((item) => {
|
|
2703
3769
|
if (item !== undefined && item !== null) {
|
|
2704
|
-
formData.append(
|
|
3770
|
+
formData.append(${quoteLiteral(field)}, ${valueExpression});
|
|
2705
3771
|
}
|
|
2706
3772
|
});
|
|
2707
3773
|
}`;
|
|
2708
3774
|
} else {
|
|
2709
|
-
const valueExpression = isFile ?
|
|
2710
|
-
return `if (${
|
|
2711
|
-
formData.append(
|
|
3775
|
+
const valueExpression = isFile ? arg : `String(${arg})`;
|
|
3776
|
+
return `if (${arg} !== undefined) {
|
|
3777
|
+
formData.append(${quoteLiteral(field)}, ${valueExpression});
|
|
2712
3778
|
}`;
|
|
2713
3779
|
}
|
|
2714
3780
|
}).join("\n");
|
|
@@ -2716,7 +3782,7 @@ var ServiceMethodBodyGenerator = class {
|
|
|
2716
3782
|
const formData = new FormData();
|
|
2717
3783
|
${formDataAppends}`;
|
|
2718
3784
|
}
|
|
2719
|
-
generateUrlEncodedFormData(operation) {
|
|
3785
|
+
generateUrlEncodedFormData(operation, argumentNames) {
|
|
2720
3786
|
if (!operation.isUrlEncoded || operation.urlEncodedFields.length === 0) {
|
|
2721
3787
|
return "";
|
|
2722
3788
|
}
|
|
@@ -2724,17 +3790,18 @@ ${formDataAppends}`;
|
|
|
2724
3790
|
const formBodyAppends = operation.urlEncodedFields.map((field) => {
|
|
2725
3791
|
const fieldSchema = properties[field];
|
|
2726
3792
|
const isArray = fieldSchema?.type === "array";
|
|
3793
|
+
const arg = argumentNames.of(field);
|
|
2727
3794
|
if (isArray) {
|
|
2728
|
-
return `if (${
|
|
2729
|
-
${
|
|
3795
|
+
return `if (${arg} !== undefined && Array.isArray(${arg})) {
|
|
3796
|
+
${arg}.forEach((item) => {
|
|
2730
3797
|
if (item !== undefined && item !== null) {
|
|
2731
|
-
formBody.append(
|
|
3798
|
+
formBody.append(${quoteLiteral(field)}, String(item));
|
|
2732
3799
|
}
|
|
2733
3800
|
});
|
|
2734
3801
|
}`;
|
|
2735
3802
|
} else {
|
|
2736
|
-
return `if (${
|
|
2737
|
-
formBody.append(
|
|
3803
|
+
return `if (${arg} !== undefined && ${arg} !== null) {
|
|
3804
|
+
formBody.append(${quoteLiteral(field)}, String(${arg}));
|
|
2738
3805
|
}`;
|
|
2739
3806
|
}
|
|
2740
3807
|
}).join("\n");
|
|
@@ -2742,7 +3809,7 @@ ${formDataAppends}`;
|
|
|
2742
3809
|
const formBody = new URLSearchParams();
|
|
2743
3810
|
${formBodyAppends}`;
|
|
2744
3811
|
}
|
|
2745
|
-
generateHttpRequest(operation) {
|
|
3812
|
+
generateHttpRequest(operation, argumentNames) {
|
|
2746
3813
|
const httpMethod = operation.method.toLowerCase();
|
|
2747
3814
|
let bodyParam = "";
|
|
2748
3815
|
if (operation.hasBody) {
|
|
@@ -2751,9 +3818,7 @@ ${formBodyAppends}`;
|
|
|
2751
3818
|
} else if (operation.isUrlEncoded) {
|
|
2752
3819
|
bodyParam = "formBody.toString()";
|
|
2753
3820
|
} else if (operation.requestBody?.content?.[CONTENT_TYPES.JSON]) {
|
|
2754
|
-
|
|
2755
|
-
const isInterface = isDataTypeInterface(bodyType);
|
|
2756
|
-
bodyParam = isInterface ? camelCase(bodyType) : "requestBody";
|
|
3821
|
+
bodyParam = argumentNames.body ?? "requestBody";
|
|
2757
3822
|
}
|
|
2758
3823
|
}
|
|
2759
3824
|
const methodsWithBody = [
|
|
@@ -2868,9 +3933,10 @@ var ServiceMethodParamsGenerator = class {
|
|
|
2868
3933
|
}
|
|
2869
3934
|
generateApiParameters(operation) {
|
|
2870
3935
|
const params = [];
|
|
3936
|
+
const argumentNames = resolveArgumentNames(operation, this.config, SERVICE_ARGUMENT_PROFILE);
|
|
2871
3937
|
operation.pathParams.forEach((param) => {
|
|
2872
3938
|
params.push({
|
|
2873
|
-
name:
|
|
3939
|
+
name: argumentNames.of(param.name),
|
|
2874
3940
|
// Swagger 2.0 puts type/format/enum on the parameter itself; the
|
|
2875
3941
|
// spread (vs passing param directly) is needed because Parameter
|
|
2876
3942
|
// lacks TypeSchema's index signature — a fresh literal satisfies it.
|
|
@@ -2884,24 +3950,22 @@ var ServiceMethodParamsGenerator = class {
|
|
|
2884
3950
|
if (requestBody) {
|
|
2885
3951
|
const jsonContent = requestBody.content?.[CONTENT_TYPES.JSON];
|
|
2886
3952
|
if (operation.isMultipart) {
|
|
2887
|
-
params.push(...this.convertObjectToSingleParams(operation.formDataSchema));
|
|
3953
|
+
params.push(...this.convertObjectToSingleParams(operation.formDataSchema, argumentNames));
|
|
2888
3954
|
}
|
|
2889
3955
|
if (operation.isUrlEncoded) {
|
|
2890
|
-
params.push(...this.convertObjectToSingleParams(operation.urlEncodedSchema));
|
|
3956
|
+
params.push(...this.convertObjectToSingleParams(operation.urlEncodedSchema, argumentNames));
|
|
2891
3957
|
}
|
|
2892
|
-
if (jsonContent && !operation.isMultipart) {
|
|
2893
|
-
const bodyType = this.getRequestBodyType(requestBody);
|
|
2894
|
-
const isInterface = isDataTypeInterface(bodyType);
|
|
3958
|
+
if (jsonContent && !operation.isMultipart && argumentNames.body) {
|
|
2895
3959
|
params.push({
|
|
2896
|
-
name:
|
|
2897
|
-
type:
|
|
3960
|
+
name: argumentNames.body,
|
|
3961
|
+
type: this.getRequestBodyType(requestBody),
|
|
2898
3962
|
hasQuestionToken: !requestBody.required
|
|
2899
3963
|
});
|
|
2900
3964
|
}
|
|
2901
3965
|
}
|
|
2902
3966
|
operation.queryParams.forEach((param) => {
|
|
2903
3967
|
params.push({
|
|
2904
|
-
name:
|
|
3968
|
+
name: argumentNames.of(param.name),
|
|
2905
3969
|
type: getTypeScriptType(param.schema || {
|
|
2906
3970
|
...param
|
|
2907
3971
|
}, this.config),
|
|
@@ -2944,11 +4008,11 @@ var ServiceMethodParamsGenerator = class {
|
|
|
2944
4008
|
return "any";
|
|
2945
4009
|
}
|
|
2946
4010
|
/** `schema` arrives ref-resolved from the normalizer (formData/urlEncoded schema). */
|
|
2947
|
-
convertObjectToSingleParams(schema) {
|
|
4011
|
+
convertObjectToSingleParams(schema, argumentNames) {
|
|
2948
4012
|
const params = [];
|
|
2949
4013
|
Object.entries(schema?.properties ?? {}).forEach(([key, value]) => {
|
|
2950
4014
|
params.push({
|
|
2951
|
-
name: key,
|
|
4015
|
+
name: argumentNames.of(key),
|
|
2952
4016
|
type: getTypeScriptType(value, this.config, value.nullable),
|
|
2953
4017
|
hasQuestionToken: !schema?.required?.includes(key)
|
|
2954
4018
|
});
|
|
@@ -3085,9 +4149,7 @@ ${methodBody}`;
|
|
|
3085
4149
|
returnType,
|
|
3086
4150
|
statements: methodBody,
|
|
3087
4151
|
overloads: methodOverLoads,
|
|
3088
|
-
docs: operation.description
|
|
3089
|
-
operation.description
|
|
3090
|
-
] : void 0
|
|
4152
|
+
docs: emitDocs(operation.description)
|
|
3091
4153
|
});
|
|
3092
4154
|
}
|
|
3093
4155
|
generateSingleRequestParameters(requestObject) {
|
|
@@ -3097,29 +4159,11 @@ ${methodBody}`;
|
|
|
3097
4159
|
];
|
|
3098
4160
|
}
|
|
3099
4161
|
generateMethodName(operation) {
|
|
3100
|
-
|
|
3101
|
-
if (operation.operationId == null) {
|
|
3102
|
-
throw new Error(`Operation ID is required for method name customization of operation: (${operation.method}) ${operation.path}`);
|
|
3103
|
-
}
|
|
3104
|
-
return this.config.options.customizeMethodName(operation.operationId);
|
|
3105
|
-
} else {
|
|
3106
|
-
return this.defaultNameGenerator(operation);
|
|
3107
|
-
}
|
|
4162
|
+
return getOperationMethodName(operation, this.config);
|
|
3108
4163
|
}
|
|
3109
4164
|
generateReturnType() {
|
|
3110
4165
|
return "Observable<any>";
|
|
3111
4166
|
}
|
|
3112
|
-
defaultNameGenerator(operation) {
|
|
3113
|
-
if (operation.operationId) {
|
|
3114
|
-
return camelCase(operation.operationId);
|
|
3115
|
-
}
|
|
3116
|
-
const method = pascalCase(operation.method.toLowerCase());
|
|
3117
|
-
const pathParts = operation.path.split("/").map((str) => {
|
|
3118
|
-
return pascalCase(pascalCase(str).replace(/[^a-zA-Z0-9]/g, ""));
|
|
3119
|
-
});
|
|
3120
|
-
const resource = pathParts.join("") || "resource";
|
|
3121
|
-
return `${camelCase(resource)}${method}`;
|
|
3122
|
-
}
|
|
3123
4167
|
};
|
|
3124
4168
|
|
|
3125
4169
|
// src/lib/generators/service/request-params.generator.ts
|
|
@@ -3132,8 +4176,10 @@ var RequestParamsGenerator = class {
|
|
|
3132
4176
|
paramsGenerator;
|
|
3133
4177
|
registry = /* @__PURE__ */ new Map();
|
|
3134
4178
|
usedInterfaceNames = /* @__PURE__ */ new Set();
|
|
3135
|
-
|
|
4179
|
+
onWarning;
|
|
4180
|
+
constructor(project, config, onWarning) {
|
|
3136
4181
|
this.project = project;
|
|
4182
|
+
this.onWarning = onWarning;
|
|
3137
4183
|
this.paramsGenerator = new ServiceMethodParamsGenerator(config);
|
|
3138
4184
|
}
|
|
3139
4185
|
buildRegistry(controllerGroups, getMethodName) {
|
|
@@ -3143,10 +4189,6 @@ var RequestParamsGenerator = class {
|
|
|
3143
4189
|
if (parameters.length === 0) {
|
|
3144
4190
|
return;
|
|
3145
4191
|
}
|
|
3146
|
-
const reserved = parameters.find((param) => param.name === "observe" || param.name === "options");
|
|
3147
|
-
if (reserved) {
|
|
3148
|
-
throw new Error(`Parameter name '${reserved.name}' conflicts with the reserved '${reserved.name}' method parameter when useSingleRequestParameter is enabled: (${operation.method}) ${operation.path}`);
|
|
3149
|
-
}
|
|
3150
4192
|
const interfaceName = this.reserveInterfaceName(controllerName, getMethodName(operation));
|
|
3151
4193
|
this.registry.set(operation, ServiceMethodRequestObjectGenerator.createEntry(interfaceName, parameters));
|
|
3152
4194
|
});
|
|
@@ -3166,15 +4208,12 @@ var RequestParamsGenerator = class {
|
|
|
3166
4208
|
name: entry.interfaceName,
|
|
3167
4209
|
isExported: true,
|
|
3168
4210
|
properties: ServiceMethodRequestObjectGenerator.toInterfaceProperties(entry),
|
|
3169
|
-
docs: operation.description
|
|
3170
|
-
operation.description
|
|
3171
|
-
] : void 0
|
|
4211
|
+
docs: emitDocs(operation.description)
|
|
3172
4212
|
});
|
|
3173
4213
|
});
|
|
3174
4214
|
this.addMissingImports(sourceFile);
|
|
3175
4215
|
sourceFile.formatText();
|
|
3176
4216
|
sourceFile.insertText(0, REQUEST_PARAMS_GENERATOR_HEADER_COMMENT);
|
|
3177
|
-
sourceFile.saveSync();
|
|
3178
4217
|
this.addModelsBarrelExport(outputRoot);
|
|
3179
4218
|
}
|
|
3180
4219
|
/**
|
|
@@ -3204,19 +4243,23 @@ var RequestParamsGenerator = class {
|
|
|
3204
4243
|
base,
|
|
3205
4244
|
`${pascalCase(controllerName)}${base}`
|
|
3206
4245
|
];
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
4246
|
+
if (!this.usedInterfaceNames.has(base)) {
|
|
4247
|
+
this.usedInterfaceNames.add(base);
|
|
4248
|
+
return base;
|
|
4249
|
+
}
|
|
4250
|
+
const warnRenamed = /* @__PURE__ */ __name((name) => {
|
|
4251
|
+
this.usedInterfaceNames.add(name);
|
|
4252
|
+
this.onWarning?.(`Request-parameter interface "${base}" is already taken; the parameters of "${methodName}" in ${controllerName} are exposed as "${name}". Renaming the operationId keeps the type name stable.`);
|
|
4253
|
+
return name;
|
|
4254
|
+
}, "warnRenamed");
|
|
4255
|
+
if (!this.usedInterfaceNames.has(candidates[1])) {
|
|
4256
|
+
return warnRenamed(candidates[1]);
|
|
3212
4257
|
}
|
|
3213
4258
|
let suffix = 2;
|
|
3214
4259
|
while (this.usedInterfaceNames.has(`${candidates[1]}${suffix}`)) {
|
|
3215
4260
|
suffix++;
|
|
3216
4261
|
}
|
|
3217
|
-
|
|
3218
|
-
this.usedInterfaceNames.add(name);
|
|
3219
|
-
return name;
|
|
4262
|
+
return warnRenamed(`${candidates[1]}${suffix}`);
|
|
3220
4263
|
}
|
|
3221
4264
|
addModelsBarrelExport(outputRoot) {
|
|
3222
4265
|
const modelsIndex = this.project.getSourceFile(path9.join(outputRoot, "models", "index.ts"));
|
|
@@ -3227,7 +4270,6 @@ var RequestParamsGenerator = class {
|
|
|
3227
4270
|
moduleSpecifier: "./request-params"
|
|
3228
4271
|
});
|
|
3229
4272
|
modelsIndex.formatText();
|
|
3230
|
-
modelsIndex.saveSync();
|
|
3231
4273
|
}
|
|
3232
4274
|
};
|
|
3233
4275
|
|
|
@@ -3256,34 +4298,14 @@ var ServiceGenerator = class {
|
|
|
3256
4298
|
this.onWarning?.("No API paths found in the specification");
|
|
3257
4299
|
return;
|
|
3258
4300
|
}
|
|
3259
|
-
const controllerGroups = this.
|
|
4301
|
+
const controllerGroups = groupOperationsByController(paths, this.onWarning);
|
|
3260
4302
|
if (this.config.options.useSingleRequestParameter) {
|
|
3261
|
-
const requestParamsGenerator = new RequestParamsGenerator(this.project, this.config);
|
|
4303
|
+
const requestParamsGenerator = new RequestParamsGenerator(this.project, this.config, this.onWarning);
|
|
3262
4304
|
this.requestObjects = requestParamsGenerator.buildRegistry(controllerGroups, (operation) => this.methodGenerator.generateMethodName(operation));
|
|
3263
4305
|
requestParamsGenerator.generate(outputRoot);
|
|
3264
4306
|
}
|
|
3265
4307
|
await Promise.all(Object.entries(controllerGroups).map(([controllerName, operations]) => this.generateServiceFile(controllerName, operations, outputDir)));
|
|
3266
4308
|
}
|
|
3267
|
-
groupPathsByController(paths) {
|
|
3268
|
-
const groups = {};
|
|
3269
|
-
paths.forEach((path15) => {
|
|
3270
|
-
let controllerName = "Default";
|
|
3271
|
-
if (path15.tags && path15.tags.length > 0) {
|
|
3272
|
-
controllerName = path15.tags[0];
|
|
3273
|
-
} else {
|
|
3274
|
-
const pathParts = path15.path.split("/").filter((p) => p && !p.startsWith("{"));
|
|
3275
|
-
if (pathParts.length > 1) {
|
|
3276
|
-
controllerName = pascalCase(pathParts[1]);
|
|
3277
|
-
}
|
|
3278
|
-
}
|
|
3279
|
-
controllerName = pascalCase(controllerName);
|
|
3280
|
-
if (!groups[controllerName]) {
|
|
3281
|
-
groups[controllerName] = [];
|
|
3282
|
-
}
|
|
3283
|
-
groups[controllerName].push(path15);
|
|
3284
|
-
});
|
|
3285
|
-
return groups;
|
|
3286
|
-
}
|
|
3287
4309
|
async generateServiceFile(controllerName, operations, outputDir) {
|
|
3288
4310
|
const fileName = `${camelCase(controllerName)}.service.ts`;
|
|
3289
4311
|
const filePath = path10.join(outputDir, fileName);
|
|
@@ -3293,7 +4315,21 @@ var ServiceGenerator = class {
|
|
|
3293
4315
|
this.addServiceClass(sourceFile, controllerName, operations);
|
|
3294
4316
|
sourceFile.fixMissingImports().formatText();
|
|
3295
4317
|
sourceFile.insertText(0, SERVICE_GENERATOR_HEADER_COMMENT(controllerName));
|
|
3296
|
-
|
|
4318
|
+
}
|
|
4319
|
+
/**
|
|
4320
|
+
* A renamed argument is part of the method's public signature, and the
|
|
4321
|
+
* suffix depends on which other arguments the operation has — so adding or
|
|
4322
|
+
* removing one renumbers the survivor and breaks call sites. Silent is the
|
|
4323
|
+
* one thing that must not happen.
|
|
4324
|
+
*/
|
|
4325
|
+
warnAboutRenamedArguments(operation) {
|
|
4326
|
+
const { renamed, merged } = resolveArgumentNames(operation, this.config, SERVICE_ARGUMENT_PROFILE);
|
|
4327
|
+
for (const { source, identifier } of renamed) {
|
|
4328
|
+
this.onWarning?.(`Parameter "${source}" of ${describeOperation(operation)} is exposed as "${identifier}" \u2014 its natural name is already taken by another parameter or by the method itself. Renaming it in the spec keeps the generated signature stable.`);
|
|
4329
|
+
}
|
|
4330
|
+
for (const wireName of merged) {
|
|
4331
|
+
this.onWarning?.(`Parameter "${wireName}" of ${describeOperation(operation)} is declared in more than one location; they collapse into one argument, so the first declaration's type wins and the same value is sent for both.`);
|
|
4332
|
+
}
|
|
3297
4333
|
}
|
|
3298
4334
|
addServiceClass(sourceFile, controllerName, operations) {
|
|
3299
4335
|
const className = getServiceClassName(controllerName, this.config.options.naming?.services);
|
|
@@ -3380,13 +4416,24 @@ var ServiceGenerator = class {
|
|
|
3380
4416
|
],
|
|
3381
4417
|
returnType: "HttpContext",
|
|
3382
4418
|
statements: `const context = existingContext || new HttpContext();
|
|
3383
|
-
return context.set(this.clientContextToken,
|
|
4419
|
+
return context.set(this.clientContextToken, ${quoteLiteral(effectiveClientName(this.config.clientName))});`
|
|
3384
4420
|
});
|
|
3385
4421
|
operations.forEach((operation) => {
|
|
4422
|
+
this.warnAboutRenamedArguments(operation);
|
|
4423
|
+
this.warnAboutReservedMethodName(operation);
|
|
3386
4424
|
this.methodGenerator.addServiceMethod(serviceClass, operation, this.requestObjects?.get(operation));
|
|
3387
4425
|
});
|
|
3388
|
-
|
|
3389
|
-
|
|
4426
|
+
assertDistinctMemberNames(serviceClass, className, operations, (op) => this.methodGenerator.generateMethodName(op));
|
|
4427
|
+
}
|
|
4428
|
+
/**
|
|
4429
|
+
* A derived method name that landed on a member the class binds itself is
|
|
4430
|
+
* prefixed rather than rejected — the spec is valid — but the rename is
|
|
4431
|
+
* public signature and must be said out loud.
|
|
4432
|
+
*/
|
|
4433
|
+
warnAboutReservedMethodName(operation) {
|
|
4434
|
+
const collision = reservedMemberCollision(operation, this.config);
|
|
4435
|
+
if (collision) {
|
|
4436
|
+
this.onWarning?.(`Operation ${describeOperation(operation)} would be named "${collision.from}", which the generated class already binds \u2014 it is emitted as "${collision.to}". Rename the operationId to choose the name.`);
|
|
3390
4437
|
}
|
|
3391
4438
|
}
|
|
3392
4439
|
};
|
|
@@ -3423,7 +4470,6 @@ var ServiceIndexGenerator = class {
|
|
|
3423
4470
|
moduleSpecifier: `./${serviceName}.service`
|
|
3424
4471
|
});
|
|
3425
4472
|
});
|
|
3426
|
-
sourceFile.saveSync();
|
|
3427
4473
|
}
|
|
3428
4474
|
};
|
|
3429
4475
|
|
|
@@ -3441,18 +4487,6 @@ var NAMING_KEYS = [
|
|
|
3441
4487
|
];
|
|
3442
4488
|
var NAME_PREFIX_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
3443
4489
|
var NAME_SUFFIX_PATTERN = /^[A-Za-z0-9_]*$/;
|
|
3444
|
-
var ConfigValidationError = class extends Error {
|
|
3445
|
-
static {
|
|
3446
|
-
__name(this, "ConfigValidationError");
|
|
3447
|
-
}
|
|
3448
|
-
issues;
|
|
3449
|
-
constructor(issues) {
|
|
3450
|
-
super(`Invalid ng-openapi configuration:
|
|
3451
|
-
${issues.map((issue) => ` - ${issue}`).join("\n")}`);
|
|
3452
|
-
this.name = "ConfigValidationError";
|
|
3453
|
-
this.issues = issues;
|
|
3454
|
-
}
|
|
3455
|
-
};
|
|
3456
4490
|
function validateGeneratorConfig(config) {
|
|
3457
4491
|
if (!config || typeof config !== "object") {
|
|
3458
4492
|
throw new ConfigValidationError([
|
|
@@ -3611,7 +4645,12 @@ async function generateFromConfig(config, reporter = {}) {
|
|
|
3611
4645
|
const outputPath = config.output;
|
|
3612
4646
|
const generateServices = config.options.generateServices ?? true;
|
|
3613
4647
|
const warnings = [];
|
|
4648
|
+
const seenWarnings = /* @__PURE__ */ new Set();
|
|
3614
4649
|
const onWarning = /* @__PURE__ */ __name((message) => {
|
|
4650
|
+
if (seenWarnings.has(message)) {
|
|
4651
|
+
return;
|
|
4652
|
+
}
|
|
4653
|
+
seenWarnings.add(message);
|
|
3615
4654
|
warnings.push(message);
|
|
3616
4655
|
reporter.onWarning?.(message);
|
|
3617
4656
|
}, "onWarning");
|
|
@@ -3636,7 +4675,7 @@ async function generateFromConfig(config, reporter = {}) {
|
|
|
3636
4675
|
}
|
|
3637
4676
|
});
|
|
3638
4677
|
reporter.onPhase?.("processing-spec");
|
|
3639
|
-
const swaggerParser = await SwaggerParser.create(config.input, config);
|
|
4678
|
+
const swaggerParser = await SwaggerParser.create(config.input, config, onWarning);
|
|
3640
4679
|
if (!swaggerParser.isValidSpec()) {
|
|
3641
4680
|
const versionInfo = swaggerParser.getSpecVersion();
|
|
3642
4681
|
throw new SpecParseError(`Invalid or unsupported specification format. Expected OpenAPI 3.x or Swagger 2.x. ${versionInfo ? `Found: ${versionInfo.type} ${versionInfo.version}` : "No version info found"}`, config.input);
|
|
@@ -3645,7 +4684,7 @@ async function generateFromConfig(config, reporter = {}) {
|
|
|
3645
4684
|
const typeGenerator = new TypeGenerator(swaggerParser, project, config, outputPath, onWarning);
|
|
3646
4685
|
await typeGenerator.generate();
|
|
3647
4686
|
reporter.onPhase?.("types-generated");
|
|
3648
|
-
if (generateServices) {
|
|
4687
|
+
if (generateServices || config.plugins?.length) {
|
|
3649
4688
|
const tokenGenerator = new TokenGenerator(project, config.clientName);
|
|
3650
4689
|
tokenGenerator.generate(outputPath);
|
|
3651
4690
|
if (config.options.dateType === "Date") {
|
|
@@ -3660,6 +4699,8 @@ async function generateFromConfig(config, reporter = {}) {
|
|
|
3660
4699
|
providerGenerator.generate(outputPath);
|
|
3661
4700
|
const baseInterceptorGenerator = new BaseInterceptorGenerator(project, config.clientName);
|
|
3662
4701
|
baseInterceptorGenerator.generate(outputPath);
|
|
4702
|
+
}
|
|
4703
|
+
if (generateServices) {
|
|
3663
4704
|
const serviceGenerator = new ServiceGenerator(swaggerParser, project, config, onWarning);
|
|
3664
4705
|
await serviceGenerator.generate(outputPath);
|
|
3665
4706
|
const indexGenerator = new ServiceIndexGenerator(project, config.options.naming?.services);
|
|
@@ -3680,6 +4721,7 @@ async function generateFromConfig(config, reporter = {}) {
|
|
|
3680
4721
|
}
|
|
3681
4722
|
const mainIndexGenerator = new MainIndexGenerator(project, config);
|
|
3682
4723
|
mainIndexGenerator.generateMainIndex(outputPath);
|
|
4724
|
+
await project.save();
|
|
3683
4725
|
return {
|
|
3684
4726
|
client: config.clientName,
|
|
3685
4727
|
filesWritten: project.getSourceFiles().map((sourceFile) => sourceFile.getFilePath()),
|
|
@@ -3689,6 +4731,43 @@ async function generateFromConfig(config, reporter = {}) {
|
|
|
3689
4731
|
}
|
|
3690
4732
|
__name(generateFromConfig, "generateFromConfig");
|
|
3691
4733
|
|
|
4734
|
+
// src/lib/core/config-loader.ts
|
|
4735
|
+
var fs3 = __toESM(require("fs"));
|
|
4736
|
+
var path14 = __toESM(require("path"));
|
|
4737
|
+
async function loadConfigFile(configPath) {
|
|
4738
|
+
const resolvedPath = path14.resolve(configPath);
|
|
4739
|
+
if (!fs3.existsSync(resolvedPath)) {
|
|
4740
|
+
throw new ConfigLoadError(`Configuration file not found: ${resolvedPath}`, resolvedPath);
|
|
4741
|
+
}
|
|
4742
|
+
try {
|
|
4743
|
+
delete require.cache[require.resolve(resolvedPath)];
|
|
4744
|
+
if (resolvedPath.endsWith(".ts")) {
|
|
4745
|
+
require("ts-node/register");
|
|
4746
|
+
}
|
|
4747
|
+
const configModule = require(resolvedPath);
|
|
4748
|
+
const config = configModule.default || configModule.config || configModule;
|
|
4749
|
+
if (!config.input || !config.output) {
|
|
4750
|
+
throw new ConfigValidationError([
|
|
4751
|
+
'Configuration must include "input" and "output" properties'
|
|
4752
|
+
]);
|
|
4753
|
+
}
|
|
4754
|
+
const configDir = path14.dirname(resolvedPath);
|
|
4755
|
+
if (!isUrl(config.input) && !path14.isAbsolute(config.input)) {
|
|
4756
|
+
config.input = path14.resolve(configDir, config.input);
|
|
4757
|
+
}
|
|
4758
|
+
if (!path14.isAbsolute(config.output)) {
|
|
4759
|
+
config.output = path14.resolve(configDir, config.output);
|
|
4760
|
+
}
|
|
4761
|
+
return config;
|
|
4762
|
+
} catch (error) {
|
|
4763
|
+
if (error instanceof NgOpenApiError) {
|
|
4764
|
+
throw error;
|
|
4765
|
+
}
|
|
4766
|
+
throw new ConfigLoadError(`Failed to load configuration file: ${resolvedPath}`, resolvedPath, error);
|
|
4767
|
+
}
|
|
4768
|
+
}
|
|
4769
|
+
__name(loadConfigFile, "loadConfigFile");
|
|
4770
|
+
|
|
3692
4771
|
// src/lib/cli.ts
|
|
3693
4772
|
var program = new import_commander.Command();
|
|
3694
4773
|
function createConsoleReporter(config) {
|
|
@@ -3721,43 +4800,22 @@ async function runGeneration(config) {
|
|
|
3721
4800
|
const inputType = isUrl(config.input) ? "URL" : "file";
|
|
3722
4801
|
const sourceInfo = `from ${inputType}: ${config.input}`;
|
|
3723
4802
|
const clientPrefix = result.client ? `${result.client} ` : "";
|
|
3724
|
-
|
|
4803
|
+
const outcome = result.warnings.length === 0 ? "completed successfully" : `completed with ${countWarnings(result.warnings.length)}`;
|
|
4804
|
+
console.log(`\u{1F389} ${clientPrefix}Generation ${outcome} ${sourceInfo} -> ${config.output}`);
|
|
4805
|
+
return result.warnings.length;
|
|
3725
4806
|
}
|
|
3726
4807
|
__name(runGeneration, "runGeneration");
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
if (!fs3.existsSync(resolvedPath)) {
|
|
3730
|
-
throw new Error(`Configuration file not found: ${resolvedPath}`);
|
|
3731
|
-
}
|
|
3732
|
-
delete require.cache[require.resolve(resolvedPath)];
|
|
3733
|
-
try {
|
|
3734
|
-
if (resolvedPath.endsWith(".ts")) {
|
|
3735
|
-
require("ts-node/register");
|
|
3736
|
-
}
|
|
3737
|
-
const configModule = require(resolvedPath);
|
|
3738
|
-
const config = configModule.default || configModule.config || configModule;
|
|
3739
|
-
if (!config.input || !config.output) {
|
|
3740
|
-
throw new Error('Configuration must include "input" and "output" properties');
|
|
3741
|
-
}
|
|
3742
|
-
const configDir = path14.dirname(resolvedPath);
|
|
3743
|
-
if (!isUrl(config.input) && !path14.isAbsolute(config.input)) {
|
|
3744
|
-
config.input = path14.resolve(configDir, config.input);
|
|
3745
|
-
}
|
|
3746
|
-
if (!path14.isAbsolute(config.output)) {
|
|
3747
|
-
config.output = path14.resolve(configDir, config.output);
|
|
3748
|
-
}
|
|
3749
|
-
return config;
|
|
3750
|
-
} catch (error) {
|
|
3751
|
-
throw new Error(`Failed to load configuration file: ${error instanceof Error ? error.message : error}`);
|
|
3752
|
-
}
|
|
4808
|
+
function countWarnings(count) {
|
|
4809
|
+
return `${count} warning${count === 1 ? "" : "s"}`;
|
|
3753
4810
|
}
|
|
3754
|
-
__name(
|
|
4811
|
+
__name(countWarnings, "countWarnings");
|
|
3755
4812
|
async function generateFromOptions(options) {
|
|
3756
4813
|
const timestamp = (/* @__PURE__ */ new Date()).getTime();
|
|
4814
|
+
let warningCount = 0;
|
|
3757
4815
|
try {
|
|
3758
4816
|
if (options.config) {
|
|
3759
4817
|
const config = await loadConfigFile(options.config);
|
|
3760
|
-
await runGeneration(config);
|
|
4818
|
+
warningCount = await runGeneration(config);
|
|
3761
4819
|
} else if (options.input) {
|
|
3762
4820
|
const config = {
|
|
3763
4821
|
input: options.input,
|
|
@@ -3771,16 +4829,30 @@ async function generateFromOptions(options) {
|
|
|
3771
4829
|
generateServices: !options.typesOnly
|
|
3772
4830
|
}
|
|
3773
4831
|
};
|
|
3774
|
-
await runGeneration(config);
|
|
4832
|
+
warningCount = await runGeneration(config);
|
|
3775
4833
|
} else {
|
|
3776
4834
|
console.error("Error: Either --config or --input option is required");
|
|
3777
4835
|
program.help({
|
|
3778
4836
|
error: true
|
|
3779
4837
|
});
|
|
3780
4838
|
}
|
|
3781
|
-
|
|
4839
|
+
if (warningCount === 0) {
|
|
4840
|
+
console.log("\u2728 Generation completed successfully!");
|
|
4841
|
+
} else {
|
|
4842
|
+
console.log(`\u2728 Generation completed with ${countWarnings(warningCount)} \u2014 see above; each describes spec content that was not generated as written.`);
|
|
4843
|
+
}
|
|
3782
4844
|
} catch (error) {
|
|
3783
4845
|
console.error("\u274C Generation failed:", error instanceof Error ? error.message : error);
|
|
4846
|
+
const MAX_CAUSES = 3;
|
|
4847
|
+
let cause = error?.cause;
|
|
4848
|
+
for (let depth = 0; cause !== void 0 && cause !== null; depth++) {
|
|
4849
|
+
if (depth === MAX_CAUSES) {
|
|
4850
|
+
console.error(" \u2026 further causes omitted");
|
|
4851
|
+
break;
|
|
4852
|
+
}
|
|
4853
|
+
console.error(" caused by:", cause instanceof Error ? cause.message : cause);
|
|
4854
|
+
cause = cause.cause;
|
|
4855
|
+
}
|
|
3784
4856
|
if (error instanceof SpecLoadError && isUrl(error.source)) {
|
|
3785
4857
|
console.error("\u{1F4A1} Tip: Make sure the URL is accessible and returns a valid OpenAPI/Swagger specification");
|
|
3786
4858
|
console.error("\u{1F4A1} Alternative: Download the specification file locally and use the file path instead");
|