ng-openapi 0.2.22 → 0.3.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/README.md +41 -17
- package/cli.cjs +1199 -857
- package/index.d.ts +529 -83
- package/index.js +1265 -906
- package/package.json +1 -1
package/cli.cjs
CHANGED
|
@@ -24,6 +24,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
mod
|
|
25
25
|
));
|
|
26
26
|
|
|
27
|
+
// ../shared/src/core/spec-loader.ts
|
|
28
|
+
var fs = __toESM(require("fs"));
|
|
29
|
+
|
|
27
30
|
// ../shared/src/utils/functions/is-url.ts
|
|
28
31
|
function isUrl(input) {
|
|
29
32
|
try {
|
|
@@ -38,16 +41,197 @@ function isUrl(input) {
|
|
|
38
41
|
}
|
|
39
42
|
__name(isUrl, "isUrl");
|
|
40
43
|
|
|
41
|
-
// src/
|
|
42
|
-
var
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
// ../shared/src/errors.ts
|
|
45
|
+
var NgOpenApiError = class extends Error {
|
|
46
|
+
static {
|
|
47
|
+
__name(this, "NgOpenApiError");
|
|
48
|
+
}
|
|
49
|
+
/** The underlying error that caused this one, when there is one. */
|
|
50
|
+
cause;
|
|
51
|
+
constructor(message, cause) {
|
|
52
|
+
super(message);
|
|
53
|
+
this.name = new.target.name;
|
|
54
|
+
this.cause = cause;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
var SpecLoadError = class extends NgOpenApiError {
|
|
58
|
+
static {
|
|
59
|
+
__name(this, "SpecLoadError");
|
|
60
|
+
}
|
|
61
|
+
/** The file path or URL that failed to load. */
|
|
62
|
+
source;
|
|
63
|
+
constructor(message, source, cause) {
|
|
64
|
+
super(message, cause);
|
|
65
|
+
this.source = source;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
var SpecParseError = class extends NgOpenApiError {
|
|
69
|
+
static {
|
|
70
|
+
__name(this, "SpecParseError");
|
|
71
|
+
}
|
|
72
|
+
/** The file path or URL the content came from, when known. */
|
|
73
|
+
source;
|
|
74
|
+
constructor(message, source, cause) {
|
|
75
|
+
super(message, cause);
|
|
76
|
+
this.source = source;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
45
79
|
|
|
46
|
-
//
|
|
47
|
-
|
|
80
|
+
// ../shared/src/core/spec-loader.ts
|
|
81
|
+
async function loadSpecContent(pathOrUrl) {
|
|
82
|
+
if (isUrl(pathOrUrl)) {
|
|
83
|
+
return await fetchUrlContent(pathOrUrl);
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
return fs.readFileSync(pathOrUrl, "utf8");
|
|
87
|
+
} catch (error) {
|
|
88
|
+
throw new SpecLoadError(`Failed to read spec file: ${pathOrUrl}${error instanceof Error ? ` - ${error.message}` : ""}`, pathOrUrl, error);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
__name(loadSpecContent, "loadSpecContent");
|
|
92
|
+
async function fetchUrlContent(url) {
|
|
93
|
+
try {
|
|
94
|
+
const response = await fetch(url, {
|
|
95
|
+
method: "GET",
|
|
96
|
+
headers: {
|
|
97
|
+
Accept: "application/json, application/yaml, text/yaml, text/plain, */*",
|
|
98
|
+
"User-Agent": "ng-openapi"
|
|
99
|
+
},
|
|
100
|
+
// 30 second timeout
|
|
101
|
+
signal: AbortSignal.timeout(3e4)
|
|
102
|
+
});
|
|
103
|
+
if (!response.ok) {
|
|
104
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
105
|
+
}
|
|
106
|
+
const content = await response.text();
|
|
107
|
+
if (!content || content.trim() === "") {
|
|
108
|
+
throw new Error(`Empty response from URL: ${url}`);
|
|
109
|
+
}
|
|
110
|
+
return content;
|
|
111
|
+
} catch (error) {
|
|
112
|
+
let errorMessage = `Failed to fetch content from URL: ${url}`;
|
|
113
|
+
if (error instanceof Error && (error.name === "TimeoutError" || error.name === "AbortError")) {
|
|
114
|
+
errorMessage += " - Request timeout (30s)";
|
|
115
|
+
} else if (error instanceof Error && error.message) {
|
|
116
|
+
errorMessage += ` - ${error.message}`;
|
|
117
|
+
}
|
|
118
|
+
throw new SpecLoadError(errorMessage, url, error);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
__name(fetchUrlContent, "fetchUrlContent");
|
|
48
122
|
|
|
49
|
-
// src/
|
|
50
|
-
var
|
|
123
|
+
// ../shared/src/core/spec-format.ts
|
|
124
|
+
var path = __toESM(require("path"));
|
|
125
|
+
var yaml = __toESM(require("js-yaml"));
|
|
126
|
+
function parseSpecContent(content, pathOrUrl) {
|
|
127
|
+
let format;
|
|
128
|
+
if (isUrl(pathOrUrl)) {
|
|
129
|
+
const urlPath = new URL(pathOrUrl).pathname.toLowerCase();
|
|
130
|
+
if (urlPath.endsWith(".json")) {
|
|
131
|
+
format = "json";
|
|
132
|
+
} else if (urlPath.endsWith(".yaml") || urlPath.endsWith(".yml")) {
|
|
133
|
+
format = "yaml";
|
|
134
|
+
} else {
|
|
135
|
+
format = detectFormat(content);
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
const extension = path.extname(pathOrUrl).toLowerCase();
|
|
139
|
+
switch (extension) {
|
|
140
|
+
case ".json":
|
|
141
|
+
format = "json";
|
|
142
|
+
break;
|
|
143
|
+
case ".yaml":
|
|
144
|
+
format = "yaml";
|
|
145
|
+
break;
|
|
146
|
+
case ".yml":
|
|
147
|
+
format = "yml";
|
|
148
|
+
break;
|
|
149
|
+
default:
|
|
150
|
+
format = detectFormat(content);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
switch (format) {
|
|
155
|
+
case "json":
|
|
156
|
+
return JSON.parse(content);
|
|
157
|
+
case "yaml":
|
|
158
|
+
case "yml":
|
|
159
|
+
return yaml.load(content);
|
|
160
|
+
default:
|
|
161
|
+
throw new Error(`Unable to determine format for: ${pathOrUrl}`);
|
|
162
|
+
}
|
|
163
|
+
} catch (error) {
|
|
164
|
+
throw new SpecParseError(`Failed to parse ${format.toUpperCase()} content from: ${pathOrUrl}. Error: ${error instanceof Error ? error.message : error}`, pathOrUrl, error);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
__name(parseSpecContent, "parseSpecContent");
|
|
168
|
+
function detectFormat(content) {
|
|
169
|
+
const trimmed = content.trim();
|
|
170
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
171
|
+
return "json";
|
|
172
|
+
}
|
|
173
|
+
if (trimmed.includes("openapi:") || trimmed.includes("swagger:") || trimmed.includes("---") || /^[a-zA-Z][a-zA-Z0-9_]*\s*:/.test(trimmed)) {
|
|
174
|
+
return "yaml";
|
|
175
|
+
}
|
|
176
|
+
return "json";
|
|
177
|
+
}
|
|
178
|
+
__name(detectFormat, "detectFormat");
|
|
179
|
+
|
|
180
|
+
// ../shared/src/utils/content-types.constants.ts
|
|
181
|
+
var CONTENT_TYPES = {
|
|
182
|
+
MULTIPART: "multipart/form-data",
|
|
183
|
+
FORM_URLENCODED: "application/x-www-form-urlencoded",
|
|
184
|
+
JSON: "application/json"
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// ../shared/src/utils/functions/extract-paths.ts
|
|
188
|
+
function extractPaths(swaggerPaths = {}, methods = [
|
|
189
|
+
"get",
|
|
190
|
+
"post",
|
|
191
|
+
"put",
|
|
192
|
+
"patch",
|
|
193
|
+
"delete",
|
|
194
|
+
"options",
|
|
195
|
+
"head"
|
|
196
|
+
]) {
|
|
197
|
+
const paths = [];
|
|
198
|
+
Object.entries(swaggerPaths).forEach(([path14, pathItem]) => {
|
|
199
|
+
methods.forEach((method) => {
|
|
200
|
+
const operation = pathItem[method];
|
|
201
|
+
if (operation) {
|
|
202
|
+
paths.push({
|
|
203
|
+
path: path14,
|
|
204
|
+
method: method.toUpperCase(),
|
|
205
|
+
operationId: operation.operationId,
|
|
206
|
+
summary: operation.summary,
|
|
207
|
+
description: operation.description,
|
|
208
|
+
tags: operation.tags || [],
|
|
209
|
+
parameters: parseParameters(operation.parameters || [], pathItem.parameters || []),
|
|
210
|
+
requestBody: operation.requestBody,
|
|
211
|
+
responses: operation.responses || {}
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
return paths;
|
|
217
|
+
}
|
|
218
|
+
__name(extractPaths, "extractPaths");
|
|
219
|
+
function parseParameters(operationParams, pathParams) {
|
|
220
|
+
const allParams = [
|
|
221
|
+
...pathParams,
|
|
222
|
+
...operationParams
|
|
223
|
+
];
|
|
224
|
+
return allParams.map((param) => ({
|
|
225
|
+
name: param.name,
|
|
226
|
+
in: param.in,
|
|
227
|
+
required: param.required || param.in === "path",
|
|
228
|
+
schema: param.schema,
|
|
229
|
+
type: param.type,
|
|
230
|
+
format: param.format,
|
|
231
|
+
description: param.description
|
|
232
|
+
}));
|
|
233
|
+
}
|
|
234
|
+
__name(parseParameters, "parseParameters");
|
|
51
235
|
|
|
52
236
|
// ../shared/src/utils/string.utils.ts
|
|
53
237
|
function camelCase(str) {
|
|
@@ -85,7 +269,7 @@ function getTypeScriptType(schemaOrType, config, formatOrNullable, isNullable, c
|
|
|
85
269
|
return nullableType(pascalCaseForEnums(refName), nullable);
|
|
86
270
|
}
|
|
87
271
|
if (schema.type === "array") {
|
|
88
|
-
const itemType = schema.items ? getTypeScriptType(schema.items, config, void 0, void 0, context) : "unknown";
|
|
272
|
+
const itemType = schema.items ? Array.isArray(schema.items) ? "any" : getTypeScriptType(schema.items, config, void 0, void 0, context) : "unknown";
|
|
89
273
|
return nullable ? `(Array<${itemType}> | null)` : `Array<${itemType}>`;
|
|
90
274
|
}
|
|
91
275
|
switch (schema.type) {
|
|
@@ -132,78 +316,6 @@ function escapeString(str) {
|
|
|
132
316
|
}
|
|
133
317
|
__name(escapeString, "escapeString");
|
|
134
318
|
|
|
135
|
-
// ../shared/src/utils/functions/token-names.ts
|
|
136
|
-
function getClientContextTokenName(clientName = "default") {
|
|
137
|
-
const clientSuffix = clientName.toUpperCase().replace(/[^A-Z0-9]/g, "_");
|
|
138
|
-
return `CLIENT_CONTEXT_TOKEN_${clientSuffix}`;
|
|
139
|
-
}
|
|
140
|
-
__name(getClientContextTokenName, "getClientContextTokenName");
|
|
141
|
-
function getBasePathTokenName(clientName = "default") {
|
|
142
|
-
const clientSuffix = clientName.toUpperCase().replace(/[^A-Z0-9]/g, "_");
|
|
143
|
-
return `BASE_PATH_${clientSuffix}`;
|
|
144
|
-
}
|
|
145
|
-
__name(getBasePathTokenName, "getBasePathTokenName");
|
|
146
|
-
function getInterceptorsTokenName(clientName = "default") {
|
|
147
|
-
const clientSuffix = clientName.toUpperCase().replace(/[^A-Z0-9]/g, "_");
|
|
148
|
-
return `HTTP_INTERCEPTORS_${clientSuffix}`;
|
|
149
|
-
}
|
|
150
|
-
__name(getInterceptorsTokenName, "getInterceptorsTokenName");
|
|
151
|
-
|
|
152
|
-
// ../shared/src/utils/functions/duplicate-function-name.ts
|
|
153
|
-
function hasDuplicateFunctionNames(arr) {
|
|
154
|
-
return new Set(arr.map((fn) => fn.getName())).size !== arr.length;
|
|
155
|
-
}
|
|
156
|
-
__name(hasDuplicateFunctionNames, "hasDuplicateFunctionNames");
|
|
157
|
-
|
|
158
|
-
// ../shared/src/utils/functions/extract-paths.ts
|
|
159
|
-
function extractPaths(swaggerPaths = {}, methods = [
|
|
160
|
-
"get",
|
|
161
|
-
"post",
|
|
162
|
-
"put",
|
|
163
|
-
"patch",
|
|
164
|
-
"delete",
|
|
165
|
-
"options",
|
|
166
|
-
"head"
|
|
167
|
-
]) {
|
|
168
|
-
const paths = [];
|
|
169
|
-
Object.entries(swaggerPaths).forEach(([path14, pathItem]) => {
|
|
170
|
-
methods.forEach((method) => {
|
|
171
|
-
if (pathItem[method]) {
|
|
172
|
-
const operation = pathItem[method];
|
|
173
|
-
paths.push({
|
|
174
|
-
path: path14,
|
|
175
|
-
method: method.toUpperCase(),
|
|
176
|
-
operationId: operation.operationId,
|
|
177
|
-
summary: operation.summary,
|
|
178
|
-
description: operation.description,
|
|
179
|
-
tags: operation.tags || [],
|
|
180
|
-
parameters: parseParameters(operation.parameters || [], pathItem.parameters || []),
|
|
181
|
-
requestBody: operation.requestBody,
|
|
182
|
-
responses: operation.responses || {}
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
});
|
|
186
|
-
});
|
|
187
|
-
return paths;
|
|
188
|
-
}
|
|
189
|
-
__name(extractPaths, "extractPaths");
|
|
190
|
-
function parseParameters(operationParams, pathParams) {
|
|
191
|
-
const allParams = [
|
|
192
|
-
...pathParams,
|
|
193
|
-
...operationParams
|
|
194
|
-
];
|
|
195
|
-
return allParams.map((param) => ({
|
|
196
|
-
name: param.name,
|
|
197
|
-
in: param.in,
|
|
198
|
-
required: param.required || param.in === "path",
|
|
199
|
-
schema: param.schema,
|
|
200
|
-
type: param.type,
|
|
201
|
-
format: param.format,
|
|
202
|
-
description: param.description
|
|
203
|
-
}));
|
|
204
|
-
}
|
|
205
|
-
__name(parseParameters, "parseParameters");
|
|
206
|
-
|
|
207
319
|
// ../shared/src/utils/functions/extract-swagger-response-type.ts
|
|
208
320
|
function getResponseTypeFromResponse(response, responseTypeMapping) {
|
|
209
321
|
const content = response.content || {};
|
|
@@ -267,7 +379,7 @@ function isPrimitiveType(schema) {
|
|
|
267
379
|
"integer",
|
|
268
380
|
"boolean"
|
|
269
381
|
];
|
|
270
|
-
if (primitiveTypes.includes(schema.type)) {
|
|
382
|
+
if (schema.type && primitiveTypes.includes(schema.type)) {
|
|
271
383
|
return true;
|
|
272
384
|
}
|
|
273
385
|
if (schema.type === "array") {
|
|
@@ -338,199 +450,204 @@ function getResponseType(response, config) {
|
|
|
338
450
|
}
|
|
339
451
|
__name(getResponseType, "getResponseType");
|
|
340
452
|
|
|
341
|
-
// ../shared/src/
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
453
|
+
// ../shared/src/core/normalize.ts
|
|
454
|
+
function normalizeSpec(spec) {
|
|
455
|
+
const rawDefinitions = spec.definitions || spec.components?.schemas || {};
|
|
456
|
+
const definitions = Object.fromEntries(Object.entries(rawDefinitions).map(([name, definition]) => [
|
|
457
|
+
name,
|
|
458
|
+
normalizeSchema(definition)
|
|
459
|
+
]));
|
|
460
|
+
const resolveReference = /* @__PURE__ */ __name((ref) => {
|
|
461
|
+
const parts = ref.split("/");
|
|
462
|
+
return definitions[parts[parts.length - 1]];
|
|
463
|
+
}, "resolveReference");
|
|
464
|
+
return {
|
|
465
|
+
version: spec.swagger ? {
|
|
466
|
+
type: "swagger",
|
|
467
|
+
version: spec.swagger
|
|
468
|
+
} : spec.openapi ? {
|
|
469
|
+
type: "openapi",
|
|
470
|
+
version: spec.openapi
|
|
471
|
+
} : null,
|
|
472
|
+
definitions,
|
|
473
|
+
operations: extractPaths(spec.paths).map((operation) => normalizeOperation(normalizeOperationSchemas(operation), resolveReference)),
|
|
474
|
+
resolveReference
|
|
475
|
+
};
|
|
476
|
+
}
|
|
477
|
+
__name(normalizeSpec, "normalizeSpec");
|
|
478
|
+
function normalizeSchema(schema) {
|
|
479
|
+
const normalized = {
|
|
480
|
+
...schema
|
|
481
|
+
};
|
|
482
|
+
const rawType = normalized.type;
|
|
483
|
+
if (Array.isArray(rawType)) {
|
|
484
|
+
const types = rawType.filter((t) => t !== "null");
|
|
485
|
+
if (types.length < rawType.length) {
|
|
486
|
+
normalized.nullable = true;
|
|
487
|
+
}
|
|
488
|
+
normalized.type = types.length === 1 ? types[0] : types.length === 0 ? "null" : types;
|
|
489
|
+
}
|
|
490
|
+
if (normalized.const !== void 0 && !normalized.enum) {
|
|
491
|
+
const constType = typeof normalized.const;
|
|
492
|
+
if (constType === "string" || constType === "number") {
|
|
493
|
+
normalized.enum = [
|
|
494
|
+
normalized.const
|
|
495
|
+
];
|
|
496
|
+
if (normalized.type === void 0) {
|
|
497
|
+
normalized.type = constType;
|
|
498
|
+
}
|
|
499
|
+
delete normalized.const;
|
|
500
|
+
} else if (constType === "boolean") {
|
|
501
|
+
if (normalized.type === void 0) {
|
|
502
|
+
normalized.type = "boolean";
|
|
503
|
+
}
|
|
504
|
+
delete normalized.const;
|
|
505
|
+
}
|
|
354
506
|
}
|
|
355
|
-
|
|
507
|
+
if (normalized.properties) {
|
|
508
|
+
normalized.properties = Object.fromEntries(Object.entries(normalized.properties).map(([name, property]) => [
|
|
509
|
+
name,
|
|
510
|
+
normalizeSchema(property)
|
|
511
|
+
]));
|
|
512
|
+
}
|
|
513
|
+
if (normalized.items) {
|
|
514
|
+
normalized.items = Array.isArray(normalized.items) ? normalized.items.map(normalizeSchema) : normalizeSchema(normalized.items);
|
|
515
|
+
}
|
|
516
|
+
if (typeof normalized.additionalProperties === "object") {
|
|
517
|
+
normalized.additionalProperties = normalizeSchema(normalized.additionalProperties);
|
|
518
|
+
}
|
|
519
|
+
if (normalized.allOf) {
|
|
520
|
+
normalized.allOf = normalized.allOf.map(normalizeSchema);
|
|
521
|
+
}
|
|
522
|
+
if (normalized.oneOf) {
|
|
523
|
+
normalized.oneOf = normalized.oneOf.map(normalizeSchema);
|
|
524
|
+
}
|
|
525
|
+
if (normalized.anyOf) {
|
|
526
|
+
normalized.anyOf = normalized.anyOf.map(normalizeSchema);
|
|
527
|
+
}
|
|
528
|
+
return normalized;
|
|
356
529
|
}
|
|
357
|
-
__name(
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
530
|
+
__name(normalizeSchema, "normalizeSchema");
|
|
531
|
+
function normalizeOperationSchemas(operation) {
|
|
532
|
+
return {
|
|
533
|
+
...operation,
|
|
534
|
+
parameters: operation.parameters?.map((parameter) => parameter.schema ? {
|
|
535
|
+
...parameter,
|
|
536
|
+
schema: normalizeSchema(parameter.schema)
|
|
537
|
+
} : parameter),
|
|
538
|
+
requestBody: operation.requestBody ? {
|
|
539
|
+
...operation.requestBody,
|
|
540
|
+
content: normalizeContentSchemas(operation.requestBody.content)
|
|
541
|
+
} : operation.requestBody,
|
|
542
|
+
responses: operation.responses ? Object.fromEntries(Object.entries(operation.responses).map(([status, response]) => [
|
|
543
|
+
status,
|
|
544
|
+
{
|
|
545
|
+
...response,
|
|
546
|
+
content: normalizeContentSchemas(response.content)
|
|
547
|
+
}
|
|
548
|
+
])) : operation.responses
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
__name(normalizeOperationSchemas, "normalizeOperationSchemas");
|
|
552
|
+
function normalizeContentSchemas(content) {
|
|
553
|
+
if (!content) return content;
|
|
554
|
+
return Object.fromEntries(Object.entries(content).map(([contentType, mediaType]) => [
|
|
555
|
+
contentType,
|
|
556
|
+
mediaType?.schema ? {
|
|
557
|
+
...mediaType,
|
|
558
|
+
schema: normalizeSchema(mediaType.schema)
|
|
559
|
+
} : mediaType
|
|
560
|
+
]));
|
|
561
|
+
}
|
|
562
|
+
__name(normalizeContentSchemas, "normalizeContentSchemas");
|
|
563
|
+
function normalizeOperation(operation, resolveRef) {
|
|
564
|
+
const content = operation.requestBody?.content;
|
|
565
|
+
const isMultipart = !!content?.[CONTENT_TYPES.MULTIPART];
|
|
566
|
+
const isUrlEncoded = !!content?.[CONTENT_TYPES.FORM_URLENCODED] && !content?.[CONTENT_TYPES.JSON];
|
|
567
|
+
const formDataSchema = isMultipart ? resolveBodySchema(operation.requestBody, CONTENT_TYPES.MULTIPART, resolveRef) : void 0;
|
|
568
|
+
const urlEncodedSchema = isUrlEncoded ? resolveBodySchema(operation.requestBody, CONTENT_TYPES.FORM_URLENCODED, resolveRef) : void 0;
|
|
569
|
+
return {
|
|
570
|
+
...operation,
|
|
571
|
+
pathParams: operation.parameters?.filter((p) => p.in === "path") || [],
|
|
572
|
+
queryParams: operation.parameters?.filter((p) => p.in === "query") || [],
|
|
573
|
+
hasBody: !!operation.requestBody,
|
|
574
|
+
isMultipart,
|
|
575
|
+
isUrlEncoded,
|
|
576
|
+
formDataSchema,
|
|
577
|
+
formDataFields: Object.keys(formDataSchema?.properties || {}),
|
|
578
|
+
urlEncodedSchema,
|
|
579
|
+
urlEncodedFields: Object.keys(urlEncodedSchema?.properties || {}),
|
|
580
|
+
responseType: determineResponseType(operation)
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
__name(normalizeOperation, "normalizeOperation");
|
|
584
|
+
function resolveBodySchema(requestBody, contentType, resolveRef) {
|
|
585
|
+
const schema = requestBody?.content?.[contentType]?.schema;
|
|
586
|
+
return schema?.$ref ? resolveRef(schema.$ref) : schema;
|
|
587
|
+
}
|
|
588
|
+
__name(resolveBodySchema, "resolveBodySchema");
|
|
589
|
+
function determineResponseType(operation) {
|
|
590
|
+
const successResponses = [
|
|
591
|
+
"200",
|
|
592
|
+
"201",
|
|
593
|
+
"202",
|
|
594
|
+
"204",
|
|
595
|
+
"206"
|
|
371
596
|
];
|
|
372
|
-
|
|
597
|
+
for (const statusCode of successResponses) {
|
|
598
|
+
const response = operation.responses?.[statusCode];
|
|
599
|
+
if (!response) continue;
|
|
600
|
+
return getResponseTypeFromResponse(response);
|
|
601
|
+
}
|
|
602
|
+
return "json";
|
|
373
603
|
}
|
|
374
|
-
__name(
|
|
375
|
-
|
|
376
|
-
// ../shared/src/config/constants.ts
|
|
377
|
-
var disableLinting = `/* @ts-nocheck */
|
|
378
|
-
/* eslint-disable */
|
|
379
|
-
/* @noformat */
|
|
380
|
-
/* @formatter:off */
|
|
381
|
-
`;
|
|
382
|
-
var authorComment = `/**
|
|
383
|
-
* Generated by ng-openapi
|
|
384
|
-
`;
|
|
385
|
-
var defaultHeaderComment = disableLinting + authorComment;
|
|
386
|
-
var TYPE_GENERATOR_HEADER_COMMENT = defaultHeaderComment + `* Generated TypeScript interfaces from Swagger specification
|
|
387
|
-
* Do not edit this file manually
|
|
388
|
-
*/
|
|
389
|
-
`;
|
|
390
|
-
var SERVICE_INDEX_GENERATOR_HEADER_COMMENT = defaultHeaderComment + `* Generated service exports
|
|
391
|
-
* Do not edit this file manually
|
|
392
|
-
*/
|
|
393
|
-
`;
|
|
394
|
-
var SERVICE_GENERATOR_HEADER_COMMENT = /* @__PURE__ */ __name((controllerName) => defaultHeaderComment + `* Generated Angular service for ${controllerName} controller
|
|
395
|
-
* Do not edit this file manually
|
|
396
|
-
*/
|
|
397
|
-
`, "SERVICE_GENERATOR_HEADER_COMMENT");
|
|
398
|
-
var REQUEST_PARAMS_GENERATOR_HEADER_COMMENT = defaultHeaderComment + `* Generated request parameter interfaces
|
|
399
|
-
* Do not edit this file manually
|
|
400
|
-
*/
|
|
401
|
-
`;
|
|
402
|
-
var MAIN_INDEX_GENERATOR_HEADER_COMMENT = defaultHeaderComment + `* Entrypoint for the client
|
|
403
|
-
* Do not edit this file manually
|
|
404
|
-
*/
|
|
405
|
-
`;
|
|
406
|
-
var PROVIDER_GENERATOR_HEADER_COMMENT = defaultHeaderComment + `* Generated provider functions for easy setup
|
|
407
|
-
* Do not edit this file manually
|
|
408
|
-
*/
|
|
409
|
-
`;
|
|
410
|
-
var BASE_INTERCEPTOR_HEADER_COMMENT = /* @__PURE__ */ __name((clientName) => defaultHeaderComment + `* Generated Base Interceptor for client ${clientName}
|
|
411
|
-
* Do not edit this file manually
|
|
412
|
-
*/
|
|
413
|
-
`, "BASE_INTERCEPTOR_HEADER_COMMENT");
|
|
414
|
-
var ZOD_PLUGIN_INDEX_GENERATOR_HEADER_COMMENT = defaultHeaderComment + `* Generated Zod Schemas exports
|
|
415
|
-
* Do not edit this file manually
|
|
416
|
-
*/
|
|
417
|
-
`;
|
|
604
|
+
__name(determineResponseType, "determineResponseType");
|
|
418
605
|
|
|
419
606
|
// ../shared/src/core/swagger-parser.ts
|
|
420
|
-
var fs = __toESM(require("fs"));
|
|
421
|
-
var path = __toESM(require("path"));
|
|
422
|
-
var yaml = __toESM(require("js-yaml"));
|
|
423
607
|
var SwaggerParser = class _SwaggerParser {
|
|
424
608
|
static {
|
|
425
609
|
__name(this, "SwaggerParser");
|
|
426
610
|
}
|
|
427
611
|
spec;
|
|
612
|
+
normalized;
|
|
428
613
|
constructor(spec, config) {
|
|
429
614
|
const isInputValid = config.validateInput?.(spec) ?? true;
|
|
430
615
|
if (!isInputValid) {
|
|
431
|
-
throw new
|
|
616
|
+
throw new SpecParseError("Swagger spec is not valid. Check your `validateInput` condition.");
|
|
432
617
|
}
|
|
433
618
|
this.spec = spec;
|
|
434
619
|
}
|
|
620
|
+
/**
|
|
621
|
+
* Loads, parses and wraps a spec.
|
|
622
|
+
*
|
|
623
|
+
* @throws SpecLoadError when the file/URL cannot be read.
|
|
624
|
+
* @throws SpecParseError when the content cannot be parsed or the
|
|
625
|
+
* config's `validateInput` hook rejects the spec.
|
|
626
|
+
*/
|
|
435
627
|
static async create(swaggerPathOrUrl, config) {
|
|
436
|
-
const swaggerContent = await
|
|
437
|
-
const spec =
|
|
628
|
+
const swaggerContent = await loadSpecContent(swaggerPathOrUrl);
|
|
629
|
+
const spec = parseSpecContent(swaggerContent, swaggerPathOrUrl);
|
|
438
630
|
return new _SwaggerParser(spec, config);
|
|
439
631
|
}
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
try {
|
|
449
|
-
const response = await fetch(url, {
|
|
450
|
-
method: "GET",
|
|
451
|
-
headers: {
|
|
452
|
-
Accept: "application/json, application/yaml, text/yaml, text/plain, */*",
|
|
453
|
-
"User-Agent": "ng-openapi"
|
|
454
|
-
},
|
|
455
|
-
// 30 second timeout
|
|
456
|
-
signal: AbortSignal.timeout(3e4)
|
|
457
|
-
});
|
|
458
|
-
if (!response.ok) {
|
|
459
|
-
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
460
|
-
}
|
|
461
|
-
const content = await response.text();
|
|
462
|
-
if (!content || content.trim() === "") {
|
|
463
|
-
throw new Error(`Empty response from URL: ${url}`);
|
|
464
|
-
}
|
|
465
|
-
return content;
|
|
466
|
-
} catch (error) {
|
|
467
|
-
let errorMessage = `Failed to fetch content from URL: ${url}`;
|
|
468
|
-
if (error.name === "AbortError") {
|
|
469
|
-
errorMessage += " - Request timeout (30s)";
|
|
470
|
-
} else if (error.message) {
|
|
471
|
-
errorMessage += ` - ${error.message}`;
|
|
472
|
-
}
|
|
473
|
-
throw new Error(errorMessage);
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
static parseSpecContent(content, pathOrUrl) {
|
|
477
|
-
let format;
|
|
478
|
-
if (isUrl(pathOrUrl)) {
|
|
479
|
-
const urlPath = new URL(pathOrUrl).pathname.toLowerCase();
|
|
480
|
-
if (urlPath.endsWith(".json")) {
|
|
481
|
-
format = "json";
|
|
482
|
-
} else if (urlPath.endsWith(".yaml") || urlPath.endsWith(".yml")) {
|
|
483
|
-
format = "yaml";
|
|
484
|
-
} else {
|
|
485
|
-
format = _SwaggerParser.detectFormat(content);
|
|
486
|
-
}
|
|
487
|
-
} else {
|
|
488
|
-
const extension = path.extname(pathOrUrl).toLowerCase();
|
|
489
|
-
switch (extension) {
|
|
490
|
-
case ".json":
|
|
491
|
-
format = "json";
|
|
492
|
-
break;
|
|
493
|
-
case ".yaml":
|
|
494
|
-
format = "yaml";
|
|
495
|
-
break;
|
|
496
|
-
case ".yml":
|
|
497
|
-
format = "yml";
|
|
498
|
-
break;
|
|
499
|
-
default:
|
|
500
|
-
format = _SwaggerParser.detectFormat(content);
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
try {
|
|
504
|
-
switch (format) {
|
|
505
|
-
case "json":
|
|
506
|
-
return JSON.parse(content);
|
|
507
|
-
case "yaml":
|
|
508
|
-
case "yml":
|
|
509
|
-
return yaml.load(content);
|
|
510
|
-
default:
|
|
511
|
-
throw new Error(`Unable to determine format for: ${pathOrUrl}`);
|
|
512
|
-
}
|
|
513
|
-
} catch (error) {
|
|
514
|
-
throw new Error(`Failed to parse ${format.toUpperCase()} content from: ${pathOrUrl}. Error: ${error instanceof Error ? error.message : error}`);
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
static detectFormat(content) {
|
|
518
|
-
const trimmed = content.trim();
|
|
519
|
-
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
520
|
-
return "json";
|
|
521
|
-
}
|
|
522
|
-
if (trimmed.includes("openapi:") || trimmed.includes("swagger:") || trimmed.includes("---") || /^[a-zA-Z][a-zA-Z0-9_]*\s*:/.test(trimmed)) {
|
|
523
|
-
return "yaml";
|
|
524
|
-
}
|
|
525
|
-
return "json";
|
|
632
|
+
/**
|
|
633
|
+
* The version-free model generators consume. Computed once and cached —
|
|
634
|
+
* all generators share the same NormalizedOperation instances, so they
|
|
635
|
+
* can be used as Map keys across generators.
|
|
636
|
+
*/
|
|
637
|
+
getNormalizedSpec() {
|
|
638
|
+
this.normalized ??= normalizeSpec(this.spec);
|
|
639
|
+
return this.normalized;
|
|
526
640
|
}
|
|
641
|
+
/** Definition map regardless of version: 2.0 `definitions` or 3.x `components.schemas`. */
|
|
527
642
|
getDefinitions() {
|
|
528
643
|
return this.spec.definitions || this.spec.components?.schemas || {};
|
|
529
644
|
}
|
|
645
|
+
/** One definition by bare name, or undefined when the spec has none by that name. */
|
|
530
646
|
getDefinition(name) {
|
|
531
647
|
const definitions = this.getDefinitions();
|
|
532
648
|
return definitions[name];
|
|
533
649
|
}
|
|
650
|
+
/** Resolves "#/definitions/X" / "#/components/schemas/X" style refs by their last segment. */
|
|
534
651
|
resolveReference(ref) {
|
|
535
652
|
const parts = ref.split("/");
|
|
536
653
|
const definitionName = parts[parts.length - 1];
|
|
@@ -539,15 +656,18 @@ var SwaggerParser = class _SwaggerParser {
|
|
|
539
656
|
getAllDefinitionNames() {
|
|
540
657
|
return Object.keys(this.getDefinitions());
|
|
541
658
|
}
|
|
659
|
+
/** The raw parsed spec — prefer getNormalizedSpec() unless raw access is the point. */
|
|
542
660
|
getSpec() {
|
|
543
661
|
return this.spec;
|
|
544
662
|
}
|
|
545
663
|
getPaths() {
|
|
546
664
|
return this.spec.paths || {};
|
|
547
665
|
}
|
|
666
|
+
/** Whether the spec declares a supported version (Swagger 2.x or OpenAPI 3.x). */
|
|
548
667
|
isValidSpec() {
|
|
549
668
|
return !!(this.spec.swagger && this.spec.swagger.startsWith("2.") || this.spec.openapi && this.spec.openapi.startsWith("3."));
|
|
550
669
|
}
|
|
670
|
+
/** Detected flavor + literal version string, or null when neither field is present. */
|
|
551
671
|
getSpecVersion() {
|
|
552
672
|
if (this.spec.swagger) {
|
|
553
673
|
return {
|
|
@@ -565,94 +685,374 @@ var SwaggerParser = class _SwaggerParser {
|
|
|
565
685
|
}
|
|
566
686
|
};
|
|
567
687
|
|
|
568
|
-
// src/
|
|
688
|
+
// ../shared/src/emit/headers.emit.ts
|
|
689
|
+
function emitHeaders(options) {
|
|
690
|
+
const { optionsExpression, customHeaders, contentType } = options;
|
|
691
|
+
let headerCode = `
|
|
692
|
+
let headers: HttpHeaders;
|
|
693
|
+
if (${optionsExpression}?.headers instanceof HttpHeaders) {
|
|
694
|
+
headers = ${optionsExpression}.headers;
|
|
695
|
+
} else {
|
|
696
|
+
headers = new HttpHeaders(${optionsExpression}?.headers);
|
|
697
|
+
}`;
|
|
698
|
+
if (customHeaders) {
|
|
699
|
+
headerCode += `
|
|
700
|
+
// Add default headers if not already present
|
|
701
|
+
${emitDefaultHeaderGuards(customHeaders)}`;
|
|
702
|
+
}
|
|
703
|
+
if (contentType?.isMultipart) {
|
|
704
|
+
headerCode += `
|
|
705
|
+
// Remove Content-Type for multipart (browser will set it with boundary)
|
|
706
|
+
headers = headers.delete('Content-Type');`;
|
|
707
|
+
} else if (contentType?.isUrlEncoded) {
|
|
708
|
+
headerCode += `
|
|
709
|
+
// Set Content-Type for URL-encoded form data
|
|
710
|
+
if (!headers.has('Content-Type')) {
|
|
711
|
+
headers = headers.set('Content-Type', 'application/x-www-form-urlencoded');
|
|
712
|
+
}`;
|
|
713
|
+
} else if (contentType?.hasBody) {
|
|
714
|
+
headerCode += `
|
|
715
|
+
// Set Content-Type for JSON requests if not already set
|
|
716
|
+
if (!headers.has('Content-Type')) {
|
|
717
|
+
headers = headers.set('Content-Type', 'application/json');
|
|
718
|
+
}`;
|
|
719
|
+
}
|
|
720
|
+
return headerCode;
|
|
721
|
+
}
|
|
722
|
+
__name(emitHeaders, "emitHeaders");
|
|
723
|
+
function emitDefaultHeaderGuards(customHeaders) {
|
|
724
|
+
return Object.entries(customHeaders).map(([key, value]) => `if (!headers.has('${key}')) {
|
|
725
|
+
headers = headers.set('${key}', '${value}');
|
|
726
|
+
}`).join("\n");
|
|
727
|
+
}
|
|
728
|
+
__name(emitDefaultHeaderGuards, "emitDefaultHeaderGuards");
|
|
729
|
+
|
|
730
|
+
// ../shared/src/emit/url.emit.ts
|
|
731
|
+
function plainParamValue(identifier) {
|
|
732
|
+
return identifier;
|
|
733
|
+
}
|
|
734
|
+
__name(plainParamValue, "plainParamValue");
|
|
735
|
+
function emitUrlExpression(path14, pathParams, paramValue = plainParamValue) {
|
|
736
|
+
let urlExpression = `\`\${this.basePath}${path14}\``;
|
|
737
|
+
pathParams.forEach((param) => {
|
|
738
|
+
urlExpression = urlExpression.replace(`{${param.name}}`, `\${${paramValue(camelCase(param.name))}}`);
|
|
739
|
+
});
|
|
740
|
+
return urlExpression;
|
|
741
|
+
}
|
|
742
|
+
__name(emitUrlExpression, "emitUrlExpression");
|
|
743
|
+
function emitUrlConstruction(path14, pathParams) {
|
|
744
|
+
return `const url = ${emitUrlExpression(path14, pathParams)};`;
|
|
745
|
+
}
|
|
746
|
+
__name(emitUrlConstruction, "emitUrlConstruction");
|
|
747
|
+
|
|
748
|
+
// ../shared/src/emit/query-params.emit.ts
|
|
749
|
+
function emitQueryParams(queryParams) {
|
|
750
|
+
if (queryParams.length === 0) {
|
|
751
|
+
return "";
|
|
752
|
+
}
|
|
753
|
+
const paramMappings = queryParams.map((param) => `if (${camelCase(param.name)} != null) {
|
|
754
|
+
params = HttpParamsBuilder.addToHttpParams(params, ${camelCase(param.name)}, '${param.name}');
|
|
755
|
+
}`).join("\n");
|
|
756
|
+
return `
|
|
757
|
+
let params = new HttpParams();
|
|
758
|
+
${paramMappings}`;
|
|
759
|
+
}
|
|
760
|
+
__name(emitQueryParams, "emitQueryParams");
|
|
761
|
+
|
|
762
|
+
// ../shared/src/emit/response-type.emit.ts
|
|
763
|
+
function emitResponseTypeOption(responseType) {
|
|
764
|
+
if (responseType === "json") {
|
|
765
|
+
return "";
|
|
766
|
+
}
|
|
767
|
+
return `responseType: '${responseType}'`;
|
|
768
|
+
}
|
|
769
|
+
__name(emitResponseTypeOption, "emitResponseTypeOption");
|
|
770
|
+
function joinRequestOptionEntries(entries) {
|
|
771
|
+
return entries.filter((entry) => entry && !entry.includes("undefined")).join(",\n ");
|
|
772
|
+
}
|
|
773
|
+
__name(joinRequestOptionEntries, "joinRequestOptionEntries");
|
|
774
|
+
|
|
775
|
+
// ../shared/src/utils/functions/token-names.ts
|
|
776
|
+
function getClientContextTokenName(clientName = "default") {
|
|
777
|
+
const clientSuffix = clientName.toUpperCase().replace(/[^A-Z0-9]/g, "_");
|
|
778
|
+
return `CLIENT_CONTEXT_TOKEN_${clientSuffix}`;
|
|
779
|
+
}
|
|
780
|
+
__name(getClientContextTokenName, "getClientContextTokenName");
|
|
781
|
+
function getBasePathTokenName(clientName = "default") {
|
|
782
|
+
const clientSuffix = clientName.toUpperCase().replace(/[^A-Z0-9]/g, "_");
|
|
783
|
+
return `BASE_PATH_${clientSuffix}`;
|
|
784
|
+
}
|
|
785
|
+
__name(getBasePathTokenName, "getBasePathTokenName");
|
|
786
|
+
function getInterceptorsTokenName(clientName = "default") {
|
|
787
|
+
const clientSuffix = clientName.toUpperCase().replace(/[^A-Z0-9]/g, "_");
|
|
788
|
+
return `HTTP_INTERCEPTORS_${clientSuffix}`;
|
|
789
|
+
}
|
|
790
|
+
__name(getInterceptorsTokenName, "getInterceptorsTokenName");
|
|
791
|
+
|
|
792
|
+
// ../shared/src/utils/functions/duplicate-function-name.ts
|
|
793
|
+
function hasDuplicateFunctionNames(arr) {
|
|
794
|
+
return new Set(arr.map((fn) => fn.getName())).size !== arr.length;
|
|
795
|
+
}
|
|
796
|
+
__name(hasDuplicateFunctionNames, "hasDuplicateFunctionNames");
|
|
797
|
+
|
|
798
|
+
// ../shared/src/utils/functions/get-request-body-type.ts
|
|
799
|
+
function getRequestBodyType(requestBody, config) {
|
|
800
|
+
const content = requestBody.content || {};
|
|
801
|
+
const jsonContent = content[CONTENT_TYPES.JSON];
|
|
802
|
+
if (jsonContent?.schema) {
|
|
803
|
+
return getTypeScriptType(jsonContent.schema, config, jsonContent.schema.nullable);
|
|
804
|
+
}
|
|
805
|
+
return "any";
|
|
806
|
+
}
|
|
807
|
+
__name(getRequestBodyType, "getRequestBodyType");
|
|
808
|
+
|
|
809
|
+
// ../shared/src/utils/functions/is-data-type-interface.ts
|
|
810
|
+
function isDataTypeInterface(type) {
|
|
811
|
+
const invalidTypes = [
|
|
812
|
+
"any",
|
|
813
|
+
"File",
|
|
814
|
+
"string",
|
|
815
|
+
"number",
|
|
816
|
+
"boolean",
|
|
817
|
+
"object",
|
|
818
|
+
"unknown",
|
|
819
|
+
"[]",
|
|
820
|
+
"Array"
|
|
821
|
+
];
|
|
822
|
+
return !invalidTypes.some((invalidType) => type.includes(invalidType));
|
|
823
|
+
}
|
|
824
|
+
__name(isDataTypeInterface, "isDataTypeInterface");
|
|
825
|
+
|
|
826
|
+
// ../shared/src/config/constants.ts
|
|
827
|
+
var disableLinting = `/* @ts-nocheck */
|
|
828
|
+
/* eslint-disable */
|
|
829
|
+
/* @noformat */
|
|
830
|
+
/* @formatter:off */
|
|
831
|
+
`;
|
|
832
|
+
var authorComment = `/**
|
|
833
|
+
* Generated by ng-openapi
|
|
834
|
+
`;
|
|
835
|
+
var defaultHeaderComment = disableLinting + authorComment;
|
|
836
|
+
var TYPE_GENERATOR_HEADER_COMMENT = defaultHeaderComment + `* Generated TypeScript interfaces from Swagger specification
|
|
837
|
+
* Do not edit this file manually
|
|
838
|
+
*/
|
|
839
|
+
`;
|
|
840
|
+
var SERVICE_INDEX_GENERATOR_HEADER_COMMENT = defaultHeaderComment + `* Generated service exports
|
|
841
|
+
* Do not edit this file manually
|
|
842
|
+
*/
|
|
843
|
+
`;
|
|
844
|
+
var SERVICE_GENERATOR_HEADER_COMMENT = /* @__PURE__ */ __name((controllerName) => defaultHeaderComment + `* Generated Angular service for ${controllerName} controller
|
|
845
|
+
* Do not edit this file manually
|
|
846
|
+
*/
|
|
847
|
+
`, "SERVICE_GENERATOR_HEADER_COMMENT");
|
|
848
|
+
var REQUEST_PARAMS_GENERATOR_HEADER_COMMENT = defaultHeaderComment + `* Generated request parameter interfaces
|
|
849
|
+
* Do not edit this file manually
|
|
850
|
+
*/
|
|
851
|
+
`;
|
|
852
|
+
var MAIN_INDEX_GENERATOR_HEADER_COMMENT = defaultHeaderComment + `* Entrypoint for the client
|
|
853
|
+
* Do not edit this file manually
|
|
854
|
+
*/
|
|
855
|
+
`;
|
|
856
|
+
var PROVIDER_GENERATOR_HEADER_COMMENT = defaultHeaderComment + `* Generated provider functions for easy setup
|
|
857
|
+
* Do not edit this file manually
|
|
858
|
+
*/
|
|
859
|
+
`;
|
|
860
|
+
var BASE_INTERCEPTOR_HEADER_COMMENT = /* @__PURE__ */ __name((clientName) => defaultHeaderComment + `* Generated Base Interceptor for client ${clientName}
|
|
861
|
+
* Do not edit this file manually
|
|
862
|
+
*/
|
|
863
|
+
`, "BASE_INTERCEPTOR_HEADER_COMMENT");
|
|
864
|
+
var ZOD_PLUGIN_INDEX_GENERATOR_HEADER_COMMENT = defaultHeaderComment + `* Generated Zod Schemas exports
|
|
865
|
+
* Do not edit this file manually
|
|
866
|
+
*/
|
|
867
|
+
`;
|
|
868
|
+
|
|
869
|
+
// src/lib/cli.ts
|
|
870
|
+
var import_commander = require("commander");
|
|
871
|
+
var fs4 = __toESM(require("fs"));
|
|
872
|
+
var path13 = __toESM(require("path"));
|
|
873
|
+
|
|
874
|
+
// package.json
|
|
875
|
+
var version = "0.3.0";
|
|
876
|
+
|
|
877
|
+
// src/lib/core/generator.ts
|
|
878
|
+
var import_ts_morph10 = require("ts-morph");
|
|
879
|
+
|
|
880
|
+
// src/lib/generators/type/enum-builder.ts
|
|
569
881
|
var import_ts_morph = require("ts-morph");
|
|
570
|
-
|
|
882
|
+
|
|
883
|
+
// src/lib/generators/type/type-resolver.ts
|
|
884
|
+
function escapeString2(str) {
|
|
885
|
+
return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
886
|
+
}
|
|
887
|
+
__name(escapeString2, "escapeString");
|
|
888
|
+
function sanitizePropertyName(name) {
|
|
889
|
+
if (!/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name)) {
|
|
890
|
+
return `"${name}"`;
|
|
891
|
+
}
|
|
892
|
+
return name;
|
|
893
|
+
}
|
|
894
|
+
__name(sanitizePropertyName, "sanitizePropertyName");
|
|
895
|
+
var TypeResolver = class {
|
|
571
896
|
static {
|
|
572
|
-
__name(this, "
|
|
897
|
+
__name(this, "TypeResolver");
|
|
573
898
|
}
|
|
574
|
-
project;
|
|
575
|
-
parser;
|
|
576
|
-
sourceFile;
|
|
577
|
-
generatedTypes = /* @__PURE__ */ new Set();
|
|
578
899
|
config;
|
|
579
|
-
|
|
900
|
+
onWarning;
|
|
901
|
+
resolutionCache = /* @__PURE__ */ new WeakMap();
|
|
580
902
|
pascalCaseCache = /* @__PURE__ */ new Map();
|
|
581
903
|
sanitizedNameCache = /* @__PURE__ */ new Map();
|
|
582
|
-
|
|
583
|
-
// Batch collection for AST operations
|
|
584
|
-
statements = [];
|
|
585
|
-
deferredTypes = /* @__PURE__ */ new Map();
|
|
586
|
-
constructor(parser, project, config, outputRoot) {
|
|
904
|
+
constructor(config, onWarning) {
|
|
587
905
|
this.config = config;
|
|
588
|
-
this.
|
|
589
|
-
this.parser = parser;
|
|
590
|
-
const outputPath = outputRoot + "/models/index.ts";
|
|
591
|
-
this.sourceFile = this.project.createSourceFile(outputPath, "", {
|
|
592
|
-
overwrite: true
|
|
593
|
-
});
|
|
906
|
+
this.onWarning = onWarning;
|
|
594
907
|
}
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
console.warn("No definitions found in swagger file");
|
|
600
|
-
}
|
|
601
|
-
this.collectAllTypeStructures(definitions);
|
|
602
|
-
this.collectSdkTypes();
|
|
603
|
-
this.applyBatchUpdates();
|
|
604
|
-
await this.finalize();
|
|
605
|
-
} catch (error) {
|
|
606
|
-
console.error("Error in generate():", error);
|
|
607
|
-
throw new Error(`Failed to generate types: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
908
|
+
resolve(schema) {
|
|
909
|
+
const cached = this.resolutionCache.get(schema);
|
|
910
|
+
if (cached !== void 0) {
|
|
911
|
+
return cached;
|
|
608
912
|
}
|
|
913
|
+
const result = this.resolveUncached(schema);
|
|
914
|
+
this.resolutionCache.set(schema, result);
|
|
915
|
+
return result;
|
|
609
916
|
}
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
Object.entries(definitions).forEach(([name, definition]) => {
|
|
616
|
-
this.collectTypeStructure(name, definition);
|
|
617
|
-
});
|
|
618
|
-
this.deferredTypes.forEach((definition, name) => {
|
|
619
|
-
this.collectTypeStructure(name, definition);
|
|
620
|
-
});
|
|
917
|
+
pascalName(str) {
|
|
918
|
+
if (!this.pascalCaseCache.has(str)) {
|
|
919
|
+
this.pascalCaseCache.set(str, pascalCaseForEnums(str));
|
|
920
|
+
}
|
|
921
|
+
return this.pascalCaseCache.get(str);
|
|
621
922
|
}
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
923
|
+
sanitizeName(name) {
|
|
924
|
+
if (!this.sanitizedNameCache.has(name)) {
|
|
925
|
+
this.sanitizedNameCache.set(name, sanitizePropertyName(name));
|
|
926
|
+
}
|
|
927
|
+
return this.sanitizedNameCache.get(name);
|
|
928
|
+
}
|
|
929
|
+
getArrayItemType(items) {
|
|
930
|
+
if (Array.isArray(items)) {
|
|
931
|
+
const types = items.map((item) => this.resolve(item));
|
|
932
|
+
return `[${types.join(", ")}]`;
|
|
632
933
|
} else {
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
934
|
+
return this.resolve(items);
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
resolveUncached(schema) {
|
|
938
|
+
if (schema.$ref) {
|
|
939
|
+
return this.resolveReference(schema.$ref);
|
|
940
|
+
}
|
|
941
|
+
if (schema.enum) {
|
|
942
|
+
return schema.enum.map((value) => typeof value === "string" ? `'${escapeString2(value)}'` : String(value)).join(" | ");
|
|
943
|
+
}
|
|
944
|
+
if (schema.allOf) {
|
|
945
|
+
return schema.allOf.map((def) => this.resolve(def)).filter((type) => type !== "any" && type !== "unknown").join(" & ") || "Record<string, unknown>";
|
|
946
|
+
}
|
|
947
|
+
if (schema.oneOf) {
|
|
948
|
+
return schema.oneOf.map((def) => this.resolve(def)).filter((type, index, array) => type !== "any" && type !== "unknown" && array.indexOf(type) === index).join(" | ") || "unknown";
|
|
949
|
+
}
|
|
950
|
+
if (schema.anyOf) {
|
|
951
|
+
return schema.anyOf.map((def) => this.resolve(def)).filter((type) => type !== "any" && type !== "unknown").join(" | ") || "unknown";
|
|
952
|
+
}
|
|
953
|
+
if (schema.type === "array") {
|
|
954
|
+
const itemType = schema.items ? this.getArrayItemType(schema.items) : "unknown";
|
|
955
|
+
return `Array<${itemType}>`;
|
|
956
|
+
}
|
|
957
|
+
if (schema.type === "object") {
|
|
958
|
+
if (schema.properties) {
|
|
959
|
+
return this.generateInlineObjectType(schema);
|
|
960
|
+
}
|
|
961
|
+
if (schema.additionalProperties) {
|
|
962
|
+
const valueType = typeof schema.additionalProperties === "object" ? this.resolve(schema.additionalProperties) : "unknown";
|
|
963
|
+
return `Record<string, ${valueType}>`;
|
|
964
|
+
}
|
|
965
|
+
return "Record<string, unknown>";
|
|
966
|
+
}
|
|
967
|
+
return this.mapSwaggerTypeToTypeScript(schema.type, schema.format, schema.nullable);
|
|
968
|
+
}
|
|
969
|
+
generateInlineObjectType(definition) {
|
|
970
|
+
if (!definition.properties) {
|
|
971
|
+
if (definition.additionalProperties) {
|
|
972
|
+
const additionalType = typeof definition.additionalProperties === "object" ? this.resolve(definition.additionalProperties) : "unknown";
|
|
973
|
+
return `Record<string, ${additionalType}>`;
|
|
974
|
+
}
|
|
975
|
+
return "Record<string, unknown>";
|
|
976
|
+
}
|
|
977
|
+
const properties = Object.entries(definition.properties).map(([key, prop]) => {
|
|
978
|
+
const isRequired = definition.required?.includes(key) ?? false;
|
|
979
|
+
const questionMark = isRequired ? "" : "?";
|
|
980
|
+
const sanitizedKey = this.sanitizeName(key);
|
|
981
|
+
return `${sanitizedKey}${questionMark}: ${this.resolve(prop)}`;
|
|
982
|
+
}).join("; ");
|
|
983
|
+
return `{ ${properties} }`;
|
|
984
|
+
}
|
|
985
|
+
resolveReference(ref) {
|
|
986
|
+
const refName = ref.split("/").pop();
|
|
987
|
+
if (!refName) {
|
|
988
|
+
this.onWarning?.(`Invalid reference format: ${ref}`);
|
|
989
|
+
return "unknown";
|
|
990
|
+
}
|
|
991
|
+
return this.pascalName(refName);
|
|
992
|
+
}
|
|
993
|
+
mapSwaggerTypeToTypeScript(type, format, isNullable) {
|
|
994
|
+
switch (type) {
|
|
995
|
+
case "string":
|
|
996
|
+
if (format === "date" || format === "date-time") {
|
|
997
|
+
const dateType = this.config.options.dateType === "Date" ? "Date" : "string";
|
|
998
|
+
return this.nullableType(dateType, isNullable);
|
|
999
|
+
}
|
|
1000
|
+
if (format === "binary") return "Blob";
|
|
1001
|
+
if (format === "uuid") return "string";
|
|
1002
|
+
if (format === "email") return "string";
|
|
1003
|
+
if (format === "uri") return "string";
|
|
1004
|
+
return this.nullableType("string", isNullable);
|
|
1005
|
+
case "number":
|
|
1006
|
+
case "integer":
|
|
1007
|
+
return this.nullableType("number", isNullable);
|
|
1008
|
+
case "boolean":
|
|
1009
|
+
return this.nullableType("boolean", isNullable);
|
|
1010
|
+
case "array":
|
|
1011
|
+
return this.nullableType("any[]", isNullable);
|
|
1012
|
+
case "object":
|
|
1013
|
+
return this.nullableType("Record<string, unknown>", isNullable);
|
|
1014
|
+
case "null":
|
|
1015
|
+
return this.nullableType("null", isNullable);
|
|
1016
|
+
default:
|
|
1017
|
+
if (Array.isArray(type)) {
|
|
1018
|
+
const types = type.map((t) => this.mapSwaggerTypeToTypeScript(t, void 0, isNullable));
|
|
1019
|
+
return this.nullableType(types.join(" | "), isNullable);
|
|
1020
|
+
}
|
|
1021
|
+
return this.nullableType("any", isNullable);
|
|
643
1022
|
}
|
|
644
1023
|
}
|
|
645
|
-
|
|
646
|
-
|
|
1024
|
+
nullableType(type, isNullable) {
|
|
1025
|
+
return type + (isNullable ? " | null" : "");
|
|
1026
|
+
}
|
|
1027
|
+
};
|
|
1028
|
+
|
|
1029
|
+
// src/lib/generators/type/enum-builder.ts
|
|
1030
|
+
function toEnumKey(value) {
|
|
1031
|
+
const str = value.toString();
|
|
1032
|
+
const hasLeadingMinus = str.startsWith("-");
|
|
1033
|
+
const pascalCased = pascalCase(str);
|
|
1034
|
+
return hasLeadingMinus ? pascalCased.replace(/^([0-9])/, "_n$1") : pascalCased.replace(/^([0-9])/, "_$1");
|
|
1035
|
+
}
|
|
1036
|
+
__name(toEnumKey, "toEnumKey");
|
|
1037
|
+
var EnumBuilder = class {
|
|
1038
|
+
static {
|
|
1039
|
+
__name(this, "EnumBuilder");
|
|
1040
|
+
}
|
|
1041
|
+
config;
|
|
1042
|
+
onWarning;
|
|
1043
|
+
constructor(config, onWarning) {
|
|
1044
|
+
this.config = config;
|
|
1045
|
+
this.onWarning = onWarning;
|
|
1046
|
+
}
|
|
1047
|
+
build(name, definition) {
|
|
1048
|
+
if (!definition.enum?.length) return [];
|
|
647
1049
|
const docs = !this.config.options.generateEnumBasedOnDescription && definition.description ? [
|
|
648
1050
|
definition.description
|
|
649
1051
|
] : void 0;
|
|
650
1052
|
if (this.config.options.enumStyle === "enum") {
|
|
651
|
-
|
|
652
|
-
this.statements.push(...statement);
|
|
1053
|
+
return this.buildEnumAsEnum(name, definition, docs);
|
|
653
1054
|
} else {
|
|
654
|
-
|
|
655
|
-
this.statements.push(...statement);
|
|
1055
|
+
return this.buildEnumAsUnion(name, definition, docs);
|
|
656
1056
|
}
|
|
657
1057
|
}
|
|
658
1058
|
buildEnumAsEnum(name, definition, docs) {
|
|
@@ -661,7 +1061,7 @@ var TypeGenerator = class {
|
|
|
661
1061
|
const isStringEnum = definition.enum.some((value) => typeof value === "string");
|
|
662
1062
|
if (isStringEnum) {
|
|
663
1063
|
const members = definition.enum.map((value) => ({
|
|
664
|
-
name:
|
|
1064
|
+
name: toEnumKey(value),
|
|
665
1065
|
value: `${String(value)}`
|
|
666
1066
|
}));
|
|
667
1067
|
statements.push({
|
|
@@ -672,7 +1072,7 @@ var TypeGenerator = class {
|
|
|
672
1072
|
members
|
|
673
1073
|
});
|
|
674
1074
|
} else {
|
|
675
|
-
const members = this.buildEnumMembers(definition);
|
|
1075
|
+
const members = this.buildEnumMembers(name, definition);
|
|
676
1076
|
statements.push({
|
|
677
1077
|
kind: import_ts_morph.StructureKind.Enum,
|
|
678
1078
|
name,
|
|
@@ -688,8 +1088,8 @@ var TypeGenerator = class {
|
|
|
688
1088
|
const statements = [];
|
|
689
1089
|
const objectProperties = [];
|
|
690
1090
|
const unionType = definition.enum.map((value) => {
|
|
691
|
-
const key =
|
|
692
|
-
const val = typeof value === "string" ? `'${
|
|
1091
|
+
const key = toEnumKey(value);
|
|
1092
|
+
const val = typeof value === "string" ? `'${escapeString2(value)}'` : isNaN(value) ? `'${value}'` : `${value}`;
|
|
693
1093
|
objectProperties.push(`${key}: ${val} as ${name}`);
|
|
694
1094
|
return val;
|
|
695
1095
|
}).join(" | ");
|
|
@@ -713,7 +1113,7 @@ var TypeGenerator = class {
|
|
|
713
1113
|
});
|
|
714
1114
|
return statements;
|
|
715
1115
|
}
|
|
716
|
-
buildEnumMembers(definition) {
|
|
1116
|
+
buildEnumMembers(name, definition) {
|
|
717
1117
|
if (definition.description && this.config.options.generateEnumBasedOnDescription) {
|
|
718
1118
|
try {
|
|
719
1119
|
const enumValueObjects = JSON.parse(definition.description);
|
|
@@ -722,63 +1122,49 @@ var TypeGenerator = class {
|
|
|
722
1122
|
value: obj.Value
|
|
723
1123
|
}));
|
|
724
1124
|
} catch {
|
|
1125
|
+
if (/^\s*[[{]/.test(definition.description)) {
|
|
1126
|
+
this.onWarning?.(`Enum "${name}": description looks like JSON (generateEnumBasedOnDescription) but could not be used \u2014 falling back to raw enum values`);
|
|
1127
|
+
}
|
|
725
1128
|
}
|
|
726
1129
|
}
|
|
727
1130
|
return definition.enum?.map((value) => ({
|
|
728
|
-
name:
|
|
1131
|
+
name: toEnumKey(value),
|
|
729
1132
|
value
|
|
730
1133
|
}));
|
|
731
1134
|
}
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
this
|
|
739
|
-
kind: import_ts_morph.StructureKind.TypeAlias,
|
|
740
|
-
name,
|
|
741
|
-
type: typeExpression,
|
|
742
|
-
isExported: true,
|
|
743
|
-
docs: definition.description ? [
|
|
744
|
-
definition.description
|
|
745
|
-
] : void 0
|
|
746
|
-
});
|
|
1135
|
+
};
|
|
1136
|
+
|
|
1137
|
+
// src/lib/generators/type/interface-builder.ts
|
|
1138
|
+
var import_ts_morph2 = require("ts-morph");
|
|
1139
|
+
var InterfaceBuilder = class {
|
|
1140
|
+
static {
|
|
1141
|
+
__name(this, "InterfaceBuilder");
|
|
747
1142
|
}
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
this.
|
|
751
|
-
kind: import_ts_morph.StructureKind.TypeAlias,
|
|
752
|
-
name,
|
|
753
|
-
isExported: true,
|
|
754
|
-
docs: definition.description ? [
|
|
755
|
-
definition.description
|
|
756
|
-
] : void 0,
|
|
757
|
-
type: `Array<${itemType}>`
|
|
758
|
-
});
|
|
1143
|
+
resolver;
|
|
1144
|
+
constructor(resolver) {
|
|
1145
|
+
this.resolver = resolver;
|
|
759
1146
|
}
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
kind: import_ts_morph.StructureKind.Interface,
|
|
1147
|
+
build(name, definition) {
|
|
1148
|
+
return {
|
|
1149
|
+
kind: import_ts_morph2.StructureKind.Interface,
|
|
764
1150
|
name,
|
|
765
1151
|
isExported: true,
|
|
766
1152
|
docs: definition.description ? [
|
|
767
1153
|
definition.description
|
|
768
1154
|
] : void 0,
|
|
769
|
-
properties,
|
|
1155
|
+
properties: this.buildProperties(definition),
|
|
770
1156
|
indexSignatures: this.buildIndexSignatures(definition)
|
|
771
|
-
}
|
|
1157
|
+
};
|
|
772
1158
|
}
|
|
773
|
-
|
|
1159
|
+
buildProperties(definition) {
|
|
774
1160
|
if (!definition.properties) {
|
|
775
1161
|
return [];
|
|
776
1162
|
}
|
|
777
1163
|
return Object.entries(definition.properties).map(([propertyName, property]) => {
|
|
778
1164
|
const isRequired = definition.required?.includes(propertyName) ?? false;
|
|
779
1165
|
const isReadOnly = property.readOnly;
|
|
780
|
-
const propertyType = this.
|
|
781
|
-
const sanitizedName = this.
|
|
1166
|
+
const propertyType = this.resolver.resolve(property);
|
|
1167
|
+
const sanitizedName = this.resolver.sanitizeName(propertyName);
|
|
782
1168
|
return {
|
|
783
1169
|
name: sanitizedName,
|
|
784
1170
|
type: propertyType,
|
|
@@ -820,121 +1206,156 @@ var TypeGenerator = class {
|
|
|
820
1206
|
}
|
|
821
1207
|
return [];
|
|
822
1208
|
}
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
1209
|
+
};
|
|
1210
|
+
|
|
1211
|
+
// src/lib/generators/type/sdk-types.ts
|
|
1212
|
+
var import_ts_morph3 = require("ts-morph");
|
|
1213
|
+
function buildSdkTypes(config) {
|
|
1214
|
+
const { response } = config.options.validation ?? {};
|
|
1215
|
+
const typeParameters = [
|
|
1216
|
+
"TResponseType extends 'arraybuffer' | 'blob' | 'json' | 'text'"
|
|
1217
|
+
];
|
|
1218
|
+
const properties = [
|
|
1219
|
+
{
|
|
1220
|
+
name: "headers",
|
|
1221
|
+
type: "HttpHeaders",
|
|
1222
|
+
hasQuestionToken: true
|
|
1223
|
+
},
|
|
1224
|
+
{
|
|
1225
|
+
name: "reportProgress",
|
|
1226
|
+
type: "boolean",
|
|
1227
|
+
hasQuestionToken: true
|
|
1228
|
+
},
|
|
1229
|
+
{
|
|
1230
|
+
name: "responseType",
|
|
1231
|
+
type: "TResponseType",
|
|
1232
|
+
hasQuestionToken: true
|
|
1233
|
+
},
|
|
1234
|
+
{
|
|
1235
|
+
name: "withCredentials",
|
|
1236
|
+
type: "boolean",
|
|
1237
|
+
hasQuestionToken: true
|
|
1238
|
+
},
|
|
1239
|
+
{
|
|
1240
|
+
name: "context",
|
|
1241
|
+
type: "HttpContext",
|
|
1242
|
+
hasQuestionToken: true
|
|
827
1243
|
}
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
1244
|
+
];
|
|
1245
|
+
if (response) {
|
|
1246
|
+
properties.push({
|
|
1247
|
+
name: "parse",
|
|
1248
|
+
type: "(response: unknown) => TReturnType",
|
|
1249
|
+
hasQuestionToken: true
|
|
1250
|
+
});
|
|
1251
|
+
typeParameters.push("TReturnType");
|
|
831
1252
|
}
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
if (schema.oneOf) {
|
|
843
|
-
return schema.oneOf.map((def) => this.resolveSwaggerTypeCached(def)).filter((type, index, array) => type !== "any" && type !== "unknown" && array.indexOf(type) === index).join(" | ") || "unknown";
|
|
844
|
-
}
|
|
845
|
-
if (schema.anyOf) {
|
|
846
|
-
return schema.anyOf.map((def) => this.resolveSwaggerTypeCached(def)).filter((type) => type !== "any" && type !== "unknown").join(" | ") || "unknown";
|
|
847
|
-
}
|
|
848
|
-
if (schema.type === "array") {
|
|
849
|
-
const itemType = schema.items ? this.getArrayItemType(schema.items) : "unknown";
|
|
850
|
-
return `Array<${itemType}>`;
|
|
851
|
-
}
|
|
852
|
-
if (schema.type === "object") {
|
|
853
|
-
if (schema.properties) {
|
|
854
|
-
return this.generateInlineObjectType(schema);
|
|
855
|
-
}
|
|
856
|
-
if (schema.additionalProperties) {
|
|
857
|
-
const valueType = typeof schema.additionalProperties === "object" ? this.resolveSwaggerTypeCached(schema.additionalProperties) : "unknown";
|
|
858
|
-
return `Record<string, ${valueType}>`;
|
|
859
|
-
}
|
|
860
|
-
return "Record<string, unknown>";
|
|
1253
|
+
return [
|
|
1254
|
+
{
|
|
1255
|
+
kind: import_ts_morph3.StructureKind.Interface,
|
|
1256
|
+
name: "RequestOptions",
|
|
1257
|
+
isExported: true,
|
|
1258
|
+
typeParameters,
|
|
1259
|
+
properties,
|
|
1260
|
+
docs: [
|
|
1261
|
+
"Request Options for Angular HttpClient requests"
|
|
1262
|
+
]
|
|
861
1263
|
}
|
|
862
|
-
|
|
1264
|
+
];
|
|
1265
|
+
}
|
|
1266
|
+
__name(buildSdkTypes, "buildSdkTypes");
|
|
1267
|
+
|
|
1268
|
+
// src/lib/generators/type/type.generator.ts
|
|
1269
|
+
var import_ts_morph4 = require("ts-morph");
|
|
1270
|
+
var TypeGenerator = class {
|
|
1271
|
+
static {
|
|
1272
|
+
__name(this, "TypeGenerator");
|
|
863
1273
|
}
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
1274
|
+
parser;
|
|
1275
|
+
config;
|
|
1276
|
+
sourceFile;
|
|
1277
|
+
resolver;
|
|
1278
|
+
enumBuilder;
|
|
1279
|
+
interfaceBuilder;
|
|
1280
|
+
statements = [];
|
|
1281
|
+
onWarning;
|
|
1282
|
+
constructor(parser, project, config, outputRoot, onWarning) {
|
|
1283
|
+
this.config = config;
|
|
1284
|
+
this.parser = parser;
|
|
1285
|
+
this.onWarning = onWarning;
|
|
1286
|
+
const outputPath = outputRoot + "/models/index.ts";
|
|
1287
|
+
this.sourceFile = project.createSourceFile(outputPath, "", {
|
|
1288
|
+
overwrite: true
|
|
1289
|
+
});
|
|
1290
|
+
this.resolver = new TypeResolver(config, onWarning);
|
|
1291
|
+
this.enumBuilder = new EnumBuilder(config, onWarning);
|
|
1292
|
+
this.interfaceBuilder = new InterfaceBuilder(this.resolver);
|
|
879
1293
|
}
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
1294
|
+
async generate() {
|
|
1295
|
+
try {
|
|
1296
|
+
const definitions = this.parser.getNormalizedSpec().definitions;
|
|
1297
|
+
if (!definitions || Object.keys(definitions).length === 0) {
|
|
1298
|
+
this.onWarning?.("No definitions found in swagger file");
|
|
1299
|
+
}
|
|
1300
|
+
Object.entries(definitions).forEach(([name, definition]) => {
|
|
1301
|
+
this.collectTypeStructure(name, definition);
|
|
1302
|
+
});
|
|
1303
|
+
this.statements.push(...buildSdkTypes(this.config));
|
|
1304
|
+
this.applyBatchUpdates();
|
|
1305
|
+
await this.finalize();
|
|
1306
|
+
} catch (error) {
|
|
1307
|
+
throw new Error(`Failed to generate types: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
885
1308
|
}
|
|
886
|
-
return this.getCachedPascalCase(refName);
|
|
887
1309
|
}
|
|
888
|
-
|
|
889
|
-
const
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
{
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
type:
|
|
907
|
-
hasQuestionToken: true
|
|
908
|
-
},
|
|
909
|
-
{
|
|
910
|
-
name: "withCredentials",
|
|
911
|
-
type: "boolean",
|
|
912
|
-
hasQuestionToken: true
|
|
913
|
-
},
|
|
914
|
-
{
|
|
915
|
-
name: "context",
|
|
916
|
-
type: "HttpContext",
|
|
917
|
-
hasQuestionToken: true
|
|
918
|
-
}
|
|
919
|
-
];
|
|
920
|
-
if (response) {
|
|
921
|
-
properties.push({
|
|
922
|
-
name: "parse",
|
|
923
|
-
type: "(response: unknown) => TReturnType",
|
|
924
|
-
hasQuestionToken: true
|
|
1310
|
+
collectTypeStructure(name, definition) {
|
|
1311
|
+
const typeName = this.resolver.pascalName(name);
|
|
1312
|
+
if (definition.enum) {
|
|
1313
|
+
this.statements.push(...this.enumBuilder.build(typeName, definition));
|
|
1314
|
+
} else if (definition.allOf) {
|
|
1315
|
+
this.statements.push(this.buildCompositeTypeAlias(typeName, definition));
|
|
1316
|
+
} else if (definition.items) {
|
|
1317
|
+
this.statements.push(this.buildArrayTypeAlias(typeName, definition));
|
|
1318
|
+
} else if (definition.properties) {
|
|
1319
|
+
this.statements.push(this.interfaceBuilder.build(typeName, definition));
|
|
1320
|
+
} else {
|
|
1321
|
+
this.statements.push({
|
|
1322
|
+
kind: import_ts_morph4.StructureKind.TypeAlias,
|
|
1323
|
+
name: typeName,
|
|
1324
|
+
isExported: true,
|
|
1325
|
+
docs: definition.description ? [
|
|
1326
|
+
definition.description
|
|
1327
|
+
] : void 0,
|
|
1328
|
+
type: this.resolver.resolve(definition)
|
|
925
1329
|
});
|
|
926
|
-
typeParameters.push("TReturnType");
|
|
927
1330
|
}
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
1331
|
+
}
|
|
1332
|
+
buildCompositeTypeAlias(name, definition) {
|
|
1333
|
+
let typeExpression = "";
|
|
1334
|
+
if (definition.allOf) {
|
|
1335
|
+
const types = definition.allOf.map((def) => this.resolver.resolve(def)).filter((type) => type !== "any" && type !== "unknown");
|
|
1336
|
+
typeExpression = types.length > 0 ? types.join(" & ") : "Record<string, unknown>";
|
|
1337
|
+
}
|
|
1338
|
+
return {
|
|
1339
|
+
kind: import_ts_morph4.StructureKind.TypeAlias,
|
|
1340
|
+
name,
|
|
1341
|
+
type: typeExpression,
|
|
931
1342
|
isExported: true,
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
1343
|
+
docs: definition.description ? [
|
|
1344
|
+
definition.description
|
|
1345
|
+
] : void 0
|
|
1346
|
+
};
|
|
1347
|
+
}
|
|
1348
|
+
buildArrayTypeAlias(name, definition) {
|
|
1349
|
+
const itemType = definition.items ? this.resolver.getArrayItemType(definition.items) : "unknown";
|
|
1350
|
+
return {
|
|
1351
|
+
kind: import_ts_morph4.StructureKind.TypeAlias,
|
|
1352
|
+
name,
|
|
1353
|
+
isExported: true,
|
|
1354
|
+
docs: definition.description ? [
|
|
1355
|
+
definition.description
|
|
1356
|
+
] : void 0,
|
|
1357
|
+
type: `Array<${itemType}>`
|
|
1358
|
+
};
|
|
938
1359
|
}
|
|
939
1360
|
applyBatchUpdates() {
|
|
940
1361
|
this.sourceFile.insertText(0, TYPE_GENERATOR_HEADER_COMMENT);
|
|
@@ -953,81 +1374,10 @@ var TypeGenerator = class {
|
|
|
953
1374
|
this.sourceFile.formatText();
|
|
954
1375
|
await this.sourceFile.save();
|
|
955
1376
|
}
|
|
956
|
-
// Cached helper methods
|
|
957
|
-
getCachedPascalCase(str) {
|
|
958
|
-
if (!this.pascalCaseCache.has(str)) {
|
|
959
|
-
this.pascalCaseCache.set(str, pascalCaseForEnums(str));
|
|
960
|
-
}
|
|
961
|
-
return this.pascalCaseCache.get(str);
|
|
962
|
-
}
|
|
963
|
-
getCachedSanitizedName(name) {
|
|
964
|
-
if (!this.sanitizedNameCache.has(name)) {
|
|
965
|
-
this.sanitizedNameCache.set(name, this.sanitizePropertyName(name));
|
|
966
|
-
}
|
|
967
|
-
return this.sanitizedNameCache.get(name);
|
|
968
|
-
}
|
|
969
|
-
// Original helper methods
|
|
970
|
-
mapSwaggerTypeToTypeScript(type, format, isNullable) {
|
|
971
|
-
switch (type) {
|
|
972
|
-
case "string":
|
|
973
|
-
if (format === "date" || format === "date-time") {
|
|
974
|
-
const dateType = this.config.options.dateType === "Date" ? "Date" : "string";
|
|
975
|
-
return this.nullableType(dateType, isNullable);
|
|
976
|
-
}
|
|
977
|
-
if (format === "binary") return "Blob";
|
|
978
|
-
if (format === "uuid") return "string";
|
|
979
|
-
if (format === "email") return "string";
|
|
980
|
-
if (format === "uri") return "string";
|
|
981
|
-
return this.nullableType("string", isNullable);
|
|
982
|
-
case "number":
|
|
983
|
-
case "integer":
|
|
984
|
-
return this.nullableType("number", isNullable);
|
|
985
|
-
case "boolean":
|
|
986
|
-
return this.nullableType("boolean", isNullable);
|
|
987
|
-
case "array":
|
|
988
|
-
return this.nullableType("any[]", isNullable);
|
|
989
|
-
case "object":
|
|
990
|
-
return this.nullableType("Record<string, unknown>", isNullable);
|
|
991
|
-
case "null":
|
|
992
|
-
return this.nullableType("null", isNullable);
|
|
993
|
-
default:
|
|
994
|
-
if (Array.isArray(type)) {
|
|
995
|
-
const types = type.map((t) => this.mapSwaggerTypeToTypeScript(t, void 0, isNullable));
|
|
996
|
-
return this.nullableType(types.join(" | "), isNullable);
|
|
997
|
-
}
|
|
998
|
-
return this.nullableType("any", isNullable);
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
nullableType(type, isNullable) {
|
|
1002
|
-
return type + (isNullable ? " | null" : "");
|
|
1003
|
-
}
|
|
1004
|
-
sanitizePropertyName(name) {
|
|
1005
|
-
if (!/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name)) {
|
|
1006
|
-
return `"${name}"`;
|
|
1007
|
-
}
|
|
1008
|
-
return name;
|
|
1009
|
-
}
|
|
1010
|
-
toEnumKey(value) {
|
|
1011
|
-
const str = value.toString();
|
|
1012
|
-
const hasLeadingMinus = str.startsWith("-");
|
|
1013
|
-
const pascalCased = pascalCase(str);
|
|
1014
|
-
return hasLeadingMinus ? pascalCased.replace(/^([0-9])/, "_n$1") : pascalCased.replace(/^([0-9])/, "_$1");
|
|
1015
|
-
}
|
|
1016
|
-
getArrayItemType(items) {
|
|
1017
|
-
if (Array.isArray(items)) {
|
|
1018
|
-
const types = items.map((item) => this.resolveSwaggerTypeCached(item));
|
|
1019
|
-
return `[${types.join(", ")}]`;
|
|
1020
|
-
} else {
|
|
1021
|
-
return this.resolveSwaggerTypeCached(items);
|
|
1022
|
-
}
|
|
1023
|
-
}
|
|
1024
|
-
escapeString(str) {
|
|
1025
|
-
return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
1026
|
-
}
|
|
1027
1377
|
};
|
|
1028
1378
|
|
|
1029
1379
|
// src/lib/generators/utility/token.generator.ts
|
|
1030
|
-
var
|
|
1380
|
+
var import_ts_morph5 = require("ts-morph");
|
|
1031
1381
|
var path2 = __toESM(require("path"));
|
|
1032
1382
|
var TokenGenerator = class {
|
|
1033
1383
|
static {
|
|
@@ -1065,7 +1415,7 @@ var TokenGenerator = class {
|
|
|
1065
1415
|
const clientContextTokenName = this.getClientContextTokenName();
|
|
1066
1416
|
sourceFile.addVariableStatement({
|
|
1067
1417
|
isExported: true,
|
|
1068
|
-
declarationKind:
|
|
1418
|
+
declarationKind: import_ts_morph5.VariableDeclarationKind.Const,
|
|
1069
1419
|
declarations: [
|
|
1070
1420
|
{
|
|
1071
1421
|
name: basePathTokenName,
|
|
@@ -1082,7 +1432,7 @@ var TokenGenerator = class {
|
|
|
1082
1432
|
});
|
|
1083
1433
|
sourceFile.addVariableStatement({
|
|
1084
1434
|
isExported: true,
|
|
1085
|
-
declarationKind:
|
|
1435
|
+
declarationKind: import_ts_morph5.VariableDeclarationKind.Const,
|
|
1086
1436
|
declarations: [
|
|
1087
1437
|
{
|
|
1088
1438
|
name: interceptorsTokenName,
|
|
@@ -1099,7 +1449,7 @@ var TokenGenerator = class {
|
|
|
1099
1449
|
});
|
|
1100
1450
|
sourceFile.addVariableStatement({
|
|
1101
1451
|
isExported: true,
|
|
1102
|
-
declarationKind:
|
|
1452
|
+
declarationKind: import_ts_morph5.VariableDeclarationKind.Const,
|
|
1103
1453
|
declarations: [
|
|
1104
1454
|
{
|
|
1105
1455
|
name: clientContextTokenName,
|
|
@@ -1114,7 +1464,7 @@ var TokenGenerator = class {
|
|
|
1114
1464
|
if (this.clientName === "default") {
|
|
1115
1465
|
sourceFile.addVariableStatement({
|
|
1116
1466
|
isExported: true,
|
|
1117
|
-
declarationKind:
|
|
1467
|
+
declarationKind: import_ts_morph5.VariableDeclarationKind.Const,
|
|
1118
1468
|
declarations: [
|
|
1119
1469
|
{
|
|
1120
1470
|
name: "BASE_PATH",
|
|
@@ -1128,7 +1478,7 @@ var TokenGenerator = class {
|
|
|
1128
1478
|
});
|
|
1129
1479
|
sourceFile.addVariableStatement({
|
|
1130
1480
|
isExported: true,
|
|
1131
|
-
declarationKind:
|
|
1481
|
+
declarationKind: import_ts_morph5.VariableDeclarationKind.Const,
|
|
1132
1482
|
declarations: [
|
|
1133
1483
|
{
|
|
1134
1484
|
name: "CLIENT_CONTEXT_TOKEN",
|
|
@@ -1299,7 +1649,7 @@ var FileDownloadGenerator = class {
|
|
|
1299
1649
|
};
|
|
1300
1650
|
|
|
1301
1651
|
// src/lib/generators/utility/date-transformer.generator.ts
|
|
1302
|
-
var
|
|
1652
|
+
var import_ts_morph6 = require("ts-morph");
|
|
1303
1653
|
var path4 = __toESM(require("path"));
|
|
1304
1654
|
var DateTransformerGenerator = class {
|
|
1305
1655
|
static {
|
|
@@ -1342,7 +1692,7 @@ var DateTransformerGenerator = class {
|
|
|
1342
1692
|
]);
|
|
1343
1693
|
sourceFile.addVariableStatement({
|
|
1344
1694
|
isExported: true,
|
|
1345
|
-
declarationKind:
|
|
1695
|
+
declarationKind: import_ts_morph6.VariableDeclarationKind.Const,
|
|
1346
1696
|
declarations: [
|
|
1347
1697
|
{
|
|
1348
1698
|
name: "ISO_DATE_REGEX",
|
|
@@ -1414,7 +1764,7 @@ var DateTransformerGenerator = class {
|
|
|
1414
1764
|
{
|
|
1415
1765
|
name: "dateRegex",
|
|
1416
1766
|
type: "RegExp",
|
|
1417
|
-
scope:
|
|
1767
|
+
scope: import_ts_morph6.Scope.Private,
|
|
1418
1768
|
isReadonly: true,
|
|
1419
1769
|
initializer: "ISO_DATE_REGEX"
|
|
1420
1770
|
}
|
|
@@ -1712,7 +2062,7 @@ return makeEnvironmentProviders(providers);`;
|
|
|
1712
2062
|
};
|
|
1713
2063
|
|
|
1714
2064
|
// src/lib/generators/utility/base-interceptor.generator.ts
|
|
1715
|
-
var
|
|
2065
|
+
var import_ts_morph7 = require("ts-morph");
|
|
1716
2066
|
var path7 = __toESM(require("path"));
|
|
1717
2067
|
var BaseInterceptorGenerator = class {
|
|
1718
2068
|
static {
|
|
@@ -1781,14 +2131,14 @@ var BaseInterceptorGenerator = class {
|
|
|
1781
2131
|
{
|
|
1782
2132
|
name: "httpInterceptors",
|
|
1783
2133
|
type: "HttpInterceptor[]",
|
|
1784
|
-
scope:
|
|
2134
|
+
scope: import_ts_morph7.Scope.Private,
|
|
1785
2135
|
isReadonly: true,
|
|
1786
2136
|
initializer: `inject(${interceptorsTokenName})`
|
|
1787
2137
|
},
|
|
1788
2138
|
{
|
|
1789
2139
|
name: "clientContextToken",
|
|
1790
2140
|
type: "HttpContextToken<string>",
|
|
1791
|
-
scope:
|
|
2141
|
+
scope: import_ts_morph7.Scope.Private,
|
|
1792
2142
|
isReadonly: true,
|
|
1793
2143
|
initializer: clientContextTokenName
|
|
1794
2144
|
}
|
|
@@ -1838,7 +2188,7 @@ var BaseInterceptorGenerator = class {
|
|
|
1838
2188
|
|
|
1839
2189
|
// src/lib/generators/utility/http-params-builder.generator.ts
|
|
1840
2190
|
var path8 = __toESM(require("path"));
|
|
1841
|
-
var
|
|
2191
|
+
var import_ts_morph8 = require("ts-morph");
|
|
1842
2192
|
var HttpParamsBuilderGenerator = class {
|
|
1843
2193
|
static {
|
|
1844
2194
|
__name(this, "HttpParamsBuilderGenerator");
|
|
@@ -1872,7 +2222,7 @@ var HttpParamsBuilderGenerator = class {
|
|
|
1872
2222
|
{
|
|
1873
2223
|
name: "addToHttpParams",
|
|
1874
2224
|
isStatic: true,
|
|
1875
|
-
scope:
|
|
2225
|
+
scope: import_ts_morph8.Scope.Public,
|
|
1876
2226
|
parameters: [
|
|
1877
2227
|
{
|
|
1878
2228
|
name: "httpParams",
|
|
@@ -1905,7 +2255,7 @@ return this.addToHttpParamsRecursive(httpParams, value, key);`
|
|
|
1905
2255
|
{
|
|
1906
2256
|
name: "addToHttpParamsRecursive",
|
|
1907
2257
|
isStatic: true,
|
|
1908
|
-
scope:
|
|
2258
|
+
scope: import_ts_morph8.Scope.Private,
|
|
1909
2259
|
parameters: [
|
|
1910
2260
|
{
|
|
1911
2261
|
name: "httpParams",
|
|
@@ -1944,7 +2294,7 @@ return this.handlePrimitive(httpParams, value, key);`
|
|
|
1944
2294
|
{
|
|
1945
2295
|
name: "handleArray",
|
|
1946
2296
|
isStatic: true,
|
|
1947
|
-
scope:
|
|
2297
|
+
scope: import_ts_morph8.Scope.Private,
|
|
1948
2298
|
parameters: [
|
|
1949
2299
|
{
|
|
1950
2300
|
name: "httpParams",
|
|
@@ -1969,7 +2319,7 @@ return httpParams;`
|
|
|
1969
2319
|
{
|
|
1970
2320
|
name: "handleDate",
|
|
1971
2321
|
isStatic: true,
|
|
1972
|
-
scope:
|
|
2322
|
+
scope: import_ts_morph8.Scope.Private,
|
|
1973
2323
|
parameters: [
|
|
1974
2324
|
{
|
|
1975
2325
|
name: "httpParams",
|
|
@@ -1994,7 +2344,7 @@ return httpParams.append(key, date.toISOString());`
|
|
|
1994
2344
|
{
|
|
1995
2345
|
name: "handleObject",
|
|
1996
2346
|
isStatic: true,
|
|
1997
|
-
scope:
|
|
2347
|
+
scope: import_ts_morph8.Scope.Private,
|
|
1998
2348
|
parameters: [
|
|
1999
2349
|
{
|
|
2000
2350
|
name: "httpParams",
|
|
@@ -2020,7 +2370,7 @@ return httpParams;`
|
|
|
2020
2370
|
{
|
|
2021
2371
|
name: "handlePrimitive",
|
|
2022
2372
|
isStatic: true,
|
|
2023
|
-
scope:
|
|
2373
|
+
scope: import_ts_morph8.Scope.Private,
|
|
2024
2374
|
parameters: [
|
|
2025
2375
|
{
|
|
2026
2376
|
name: "httpParams",
|
|
@@ -2048,7 +2398,7 @@ return httpParams.append(key, value);`
|
|
|
2048
2398
|
};
|
|
2049
2399
|
|
|
2050
2400
|
// src/lib/generators/service/service.generator.ts
|
|
2051
|
-
var
|
|
2401
|
+
var import_ts_morph9 = require("ts-morph");
|
|
2052
2402
|
var path10 = __toESM(require("path"));
|
|
2053
2403
|
|
|
2054
2404
|
// src/lib/generators/service/service-method/service-method-body.generator.ts
|
|
@@ -2057,132 +2407,30 @@ var ServiceMethodBodyGenerator = class {
|
|
|
2057
2407
|
__name(this, "ServiceMethodBodyGenerator");
|
|
2058
2408
|
}
|
|
2059
2409
|
config;
|
|
2060
|
-
|
|
2061
|
-
constructor(config, parser) {
|
|
2410
|
+
constructor(config) {
|
|
2062
2411
|
this.config = config;
|
|
2063
|
-
this.parser = parser;
|
|
2064
2412
|
}
|
|
2065
2413
|
generateMethodBody(operation) {
|
|
2066
|
-
const context = this.createGenerationContext(operation);
|
|
2067
2414
|
const bodyParts = [
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2415
|
+
emitUrlConstruction(operation.path, operation.pathParams),
|
|
2416
|
+
emitQueryParams(operation.queryParams),
|
|
2417
|
+
emitHeaders({
|
|
2418
|
+
optionsExpression: "options",
|
|
2419
|
+
customHeaders: this.config.options.customHeaders,
|
|
2420
|
+
contentType: operation
|
|
2421
|
+
}),
|
|
2422
|
+
this.generateMultipartFormData(operation),
|
|
2423
|
+
this.generateUrlEncodedFormData(operation),
|
|
2424
|
+
this.generateHttpRequest(operation)
|
|
2075
2425
|
];
|
|
2076
2426
|
return bodyParts.filter(Boolean).join("\n");
|
|
2077
2427
|
}
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
}
|
|
2081
|
-
isUrlEncodedFormData(operation) {
|
|
2082
|
-
return !!operation.requestBody?.content?.[CONTENT_TYPES.FORM_URLENCODED] && !operation.requestBody?.content?.[CONTENT_TYPES.JSON];
|
|
2083
|
-
}
|
|
2084
|
-
getFormDataFields(operation) {
|
|
2085
|
-
if (!this.isMultipartFormData(operation)) {
|
|
2086
|
-
return [];
|
|
2087
|
-
}
|
|
2088
|
-
const schema = operation.requestBody?.content?.[CONTENT_TYPES.MULTIPART].schema;
|
|
2089
|
-
let resolvedSchema = schema;
|
|
2090
|
-
if (schema?.$ref) {
|
|
2091
|
-
resolvedSchema = this.parser.resolveReference(schema.$ref);
|
|
2092
|
-
}
|
|
2093
|
-
const properties = resolvedSchema?.properties || {};
|
|
2094
|
-
return Object.keys(properties);
|
|
2095
|
-
}
|
|
2096
|
-
getUrlEncodedFields(operation) {
|
|
2097
|
-
if (!this.isUrlEncodedFormData(operation)) {
|
|
2098
|
-
return [];
|
|
2099
|
-
}
|
|
2100
|
-
const schema = operation.requestBody?.content?.[CONTENT_TYPES.FORM_URLENCODED].schema;
|
|
2101
|
-
let resolvedSchema = schema;
|
|
2102
|
-
if (schema?.$ref) {
|
|
2103
|
-
resolvedSchema = this.parser.resolveReference(schema.$ref);
|
|
2104
|
-
}
|
|
2105
|
-
const properties = resolvedSchema?.properties || {};
|
|
2106
|
-
return Object.keys(properties);
|
|
2107
|
-
}
|
|
2108
|
-
createGenerationContext(operation) {
|
|
2109
|
-
return {
|
|
2110
|
-
pathParams: operation.parameters?.filter((p) => p.in === "path") || [],
|
|
2111
|
-
queryParams: operation.parameters?.filter((p) => p.in === "query") || [],
|
|
2112
|
-
hasBody: !!operation.requestBody,
|
|
2113
|
-
isMultipart: this.isMultipartFormData(operation),
|
|
2114
|
-
isUrlEncoded: this.isUrlEncodedFormData(operation),
|
|
2115
|
-
formDataFields: this.getFormDataFields(operation),
|
|
2116
|
-
urlEncodedFields: this.getUrlEncodedFields(operation),
|
|
2117
|
-
responseType: this.determineResponseType(operation)
|
|
2118
|
-
};
|
|
2119
|
-
}
|
|
2120
|
-
generateUrlConstruction(operation, context) {
|
|
2121
|
-
let urlExpression = `\`\${this.basePath}${operation.path}\``;
|
|
2122
|
-
if (context.pathParams.length > 0) {
|
|
2123
|
-
context.pathParams.forEach((param) => {
|
|
2124
|
-
urlExpression = urlExpression.replace(`{${param.name}}`, `\${${camelCase(param.name)}}`);
|
|
2125
|
-
});
|
|
2126
|
-
}
|
|
2127
|
-
return `const url = ${urlExpression};`;
|
|
2128
|
-
}
|
|
2129
|
-
generateQueryParams(context) {
|
|
2130
|
-
if (context.queryParams.length === 0) {
|
|
2131
|
-
return "";
|
|
2132
|
-
}
|
|
2133
|
-
const paramMappings = context.queryParams.map((param) => `if (${camelCase(param.name)} != null) {
|
|
2134
|
-
params = HttpParamsBuilder.addToHttpParams(params, ${camelCase(param.name)}, '${param.name}');
|
|
2135
|
-
}`).join("\n");
|
|
2136
|
-
return `
|
|
2137
|
-
let params = new HttpParams();
|
|
2138
|
-
${paramMappings}`;
|
|
2139
|
-
}
|
|
2140
|
-
generateHeaders(context) {
|
|
2141
|
-
const hasCustomHeaders = this.config.options.customHeaders;
|
|
2142
|
-
let headerCode = `
|
|
2143
|
-
let headers: HttpHeaders;
|
|
2144
|
-
if (options?.headers instanceof HttpHeaders) {
|
|
2145
|
-
headers = options.headers;
|
|
2146
|
-
} else {
|
|
2147
|
-
headers = new HttpHeaders(options?.headers);
|
|
2148
|
-
}`;
|
|
2149
|
-
if (hasCustomHeaders) {
|
|
2150
|
-
headerCode += `
|
|
2151
|
-
// Add default headers if not already present
|
|
2152
|
-
${Object.entries(this.config.options.customHeaders || {}).map(([key, value]) => `if (!headers.has('${key}')) {
|
|
2153
|
-
headers = headers.set('${key}', '${value}');
|
|
2154
|
-
}`).join("\n")}`;
|
|
2155
|
-
}
|
|
2156
|
-
if (context.isMultipart) {
|
|
2157
|
-
headerCode += `
|
|
2158
|
-
// Remove Content-Type for multipart (browser will set it with boundary)
|
|
2159
|
-
headers = headers.delete('Content-Type');`;
|
|
2160
|
-
} else if (context.isUrlEncoded) {
|
|
2161
|
-
headerCode += `
|
|
2162
|
-
// Set Content-Type for URL-encoded form data
|
|
2163
|
-
if (!headers.has('Content-Type')) {
|
|
2164
|
-
headers = headers.set('Content-Type', 'application/x-www-form-urlencoded');
|
|
2165
|
-
}`;
|
|
2166
|
-
} else if (context.hasBody) {
|
|
2167
|
-
headerCode += `
|
|
2168
|
-
// Set Content-Type for JSON requests if not already set
|
|
2169
|
-
if (!headers.has('Content-Type')) {
|
|
2170
|
-
headers = headers.set('Content-Type', 'application/json');
|
|
2171
|
-
}`;
|
|
2172
|
-
}
|
|
2173
|
-
return headerCode;
|
|
2174
|
-
}
|
|
2175
|
-
generateMultipartFormData(operation, context) {
|
|
2176
|
-
if (!context.isMultipart || context.formDataFields.length === 0) {
|
|
2428
|
+
generateMultipartFormData(operation) {
|
|
2429
|
+
if (!operation.isMultipart || operation.formDataFields.length === 0) {
|
|
2177
2430
|
return "";
|
|
2178
2431
|
}
|
|
2179
|
-
const
|
|
2180
|
-
|
|
2181
|
-
if (schema?.$ref) {
|
|
2182
|
-
resolvedSchema = this.parser.resolveReference(schema.$ref);
|
|
2183
|
-
}
|
|
2184
|
-
const properties = resolvedSchema?.properties || {};
|
|
2185
|
-
const formDataAppends = context.formDataFields.map((field) => {
|
|
2432
|
+
const properties = operation.formDataSchema?.properties || {};
|
|
2433
|
+
const formDataAppends = operation.formDataFields.map((field) => {
|
|
2186
2434
|
const fieldSchema = properties[field];
|
|
2187
2435
|
const isFile = fieldSchema?.type === "string" && fieldSchema?.format === "binary";
|
|
2188
2436
|
const isArray = fieldSchema?.type === "array";
|
|
@@ -2208,17 +2456,12 @@ if (!headers.has('Content-Type')) {
|
|
|
2208
2456
|
const formData = new FormData();
|
|
2209
2457
|
${formDataAppends}`;
|
|
2210
2458
|
}
|
|
2211
|
-
generateUrlEncodedFormData(operation
|
|
2212
|
-
if (!
|
|
2459
|
+
generateUrlEncodedFormData(operation) {
|
|
2460
|
+
if (!operation.isUrlEncoded || operation.urlEncodedFields.length === 0) {
|
|
2213
2461
|
return "";
|
|
2214
2462
|
}
|
|
2215
|
-
const
|
|
2216
|
-
|
|
2217
|
-
if (schema?.$ref) {
|
|
2218
|
-
resolvedSchema = this.parser.resolveReference(schema.$ref);
|
|
2219
|
-
}
|
|
2220
|
-
const properties = resolvedSchema?.properties || {};
|
|
2221
|
-
const formBodyAppends = context.urlEncodedFields.map((field) => {
|
|
2463
|
+
const properties = operation.urlEncodedSchema?.properties || {};
|
|
2464
|
+
const formBodyAppends = operation.urlEncodedFields.map((field) => {
|
|
2222
2465
|
const fieldSchema = properties[field];
|
|
2223
2466
|
const isArray = fieldSchema?.type === "array";
|
|
2224
2467
|
if (isArray) {
|
|
@@ -2239,32 +2482,13 @@ ${formDataAppends}`;
|
|
|
2239
2482
|
const formBody = new URLSearchParams();
|
|
2240
2483
|
${formBodyAppends}`;
|
|
2241
2484
|
}
|
|
2242
|
-
|
|
2243
|
-
const options = [];
|
|
2244
|
-
options.push("observe: observe as any");
|
|
2245
|
-
options.push("headers");
|
|
2246
|
-
if (context.queryParams.length > 0) {
|
|
2247
|
-
options.push("params");
|
|
2248
|
-
}
|
|
2249
|
-
if (context.responseType !== "json") {
|
|
2250
|
-
options.push(`responseType: '${context.responseType}' as '${context.responseType}'`);
|
|
2251
|
-
}
|
|
2252
|
-
options.push("reportProgress: options?.reportProgress");
|
|
2253
|
-
options.push("withCredentials: options?.withCredentials");
|
|
2254
|
-
options.push("context: this.createContextWithClientId(options?.context)");
|
|
2255
|
-
const formattedOptions = options.filter((opt) => opt && !opt.includes("undefined")).join(",\n ");
|
|
2256
|
-
return `
|
|
2257
|
-
const requestOptions: any = {
|
|
2258
|
-
${formattedOptions}
|
|
2259
|
-
};`;
|
|
2260
|
-
}
|
|
2261
|
-
generateHttpRequest(operation, context) {
|
|
2485
|
+
generateHttpRequest(operation) {
|
|
2262
2486
|
const httpMethod = operation.method.toLowerCase();
|
|
2263
2487
|
let bodyParam = "";
|
|
2264
|
-
if (
|
|
2265
|
-
if (
|
|
2488
|
+
if (operation.hasBody) {
|
|
2489
|
+
if (operation.isMultipart) {
|
|
2266
2490
|
bodyParam = "formData";
|
|
2267
|
-
} else if (
|
|
2491
|
+
} else if (operation.isUrlEncoded) {
|
|
2268
2492
|
bodyParam = "formBody.toString()";
|
|
2269
2493
|
} else if (operation.requestBody?.content?.[CONTENT_TYPES.JSON]) {
|
|
2270
2494
|
const bodyType = getRequestBodyType(operation.requestBody, this.config);
|
|
@@ -2278,28 +2502,23 @@ const requestOptions: any = {
|
|
|
2278
2502
|
"patch"
|
|
2279
2503
|
];
|
|
2280
2504
|
const parseResponse = this.config.options.validation?.response ? `.pipe(map(response => options?.parse?.(response) ?? response))` : "";
|
|
2505
|
+
const entries = [];
|
|
2281
2506
|
if (methodsWithBody.includes(httpMethod)) {
|
|
2282
|
-
|
|
2283
|
-
return this.httpClient.${httpMethod}(url, ${bodyParam || "null"}, requestOptions)${parseResponse};`;
|
|
2284
|
-
} else {
|
|
2285
|
-
return `
|
|
2286
|
-
return this.httpClient.${httpMethod}(url, requestOptions)${parseResponse};`;
|
|
2507
|
+
entries.push(`body: ${bodyParam || "null"}`);
|
|
2287
2508
|
}
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
"
|
|
2292
|
-
"201",
|
|
2293
|
-
"202",
|
|
2294
|
-
"204",
|
|
2295
|
-
"206"
|
|
2296
|
-
];
|
|
2297
|
-
for (const statusCode of successResponses) {
|
|
2298
|
-
const response = operation.responses?.[statusCode];
|
|
2299
|
-
if (!response) continue;
|
|
2300
|
-
return getResponseTypeFromResponse(response);
|
|
2509
|
+
entries.push("observe");
|
|
2510
|
+
entries.push("headers");
|
|
2511
|
+
if (operation.queryParams.length > 0) {
|
|
2512
|
+
entries.push("params");
|
|
2301
2513
|
}
|
|
2302
|
-
|
|
2514
|
+
entries.push(emitResponseTypeOption(operation.responseType));
|
|
2515
|
+
entries.push("reportProgress: options?.reportProgress");
|
|
2516
|
+
entries.push("withCredentials: options?.withCredentials");
|
|
2517
|
+
entries.push("context: this.createContextWithClientId(options?.context)");
|
|
2518
|
+
return `
|
|
2519
|
+
return this.httpClient.request('${httpMethod}', url, {
|
|
2520
|
+
${joinRequestOptionEntries(entries)}
|
|
2521
|
+
})${parseResponse};`;
|
|
2303
2522
|
}
|
|
2304
2523
|
};
|
|
2305
2524
|
|
|
@@ -2376,10 +2595,8 @@ var ServiceMethodParamsGenerator = class {
|
|
|
2376
2595
|
__name(this, "ServiceMethodParamsGenerator");
|
|
2377
2596
|
}
|
|
2378
2597
|
config;
|
|
2379
|
-
|
|
2380
|
-
constructor(config, parser) {
|
|
2598
|
+
constructor(config) {
|
|
2381
2599
|
this.config = config;
|
|
2382
|
-
this.parser = parser;
|
|
2383
2600
|
}
|
|
2384
2601
|
generateMethodParameters(operation) {
|
|
2385
2602
|
const params = this.generateApiParameters(operation);
|
|
@@ -2391,28 +2608,28 @@ var ServiceMethodParamsGenerator = class {
|
|
|
2391
2608
|
}
|
|
2392
2609
|
generateApiParameters(operation) {
|
|
2393
2610
|
const params = [];
|
|
2394
|
-
|
|
2395
|
-
pathParams.forEach((param) => {
|
|
2611
|
+
operation.pathParams.forEach((param) => {
|
|
2396
2612
|
params.push({
|
|
2397
2613
|
name: camelCase(param.name),
|
|
2398
|
-
|
|
2614
|
+
// Swagger 2.0 puts type/format/enum on the parameter itself; the
|
|
2615
|
+
// spread (vs passing param directly) is needed because Parameter
|
|
2616
|
+
// lacks TypeSchema's index signature — a fresh literal satisfies it.
|
|
2617
|
+
type: getTypeScriptType(param.schema || {
|
|
2618
|
+
...param
|
|
2619
|
+
}, this.config),
|
|
2399
2620
|
hasQuestionToken: !param.required
|
|
2400
2621
|
});
|
|
2401
2622
|
});
|
|
2402
2623
|
const requestBody = operation.requestBody;
|
|
2403
2624
|
if (requestBody) {
|
|
2404
|
-
const formDataContent = requestBody.content?.[CONTENT_TYPES.MULTIPART];
|
|
2405
|
-
const urlEncodedContent = requestBody.content?.[CONTENT_TYPES.FORM_URLENCODED];
|
|
2406
2625
|
const jsonContent = requestBody.content?.[CONTENT_TYPES.JSON];
|
|
2407
|
-
if (
|
|
2408
|
-
|
|
2409
|
-
params.push(...formParams);
|
|
2626
|
+
if (operation.isMultipart) {
|
|
2627
|
+
params.push(...this.convertObjectToSingleParams(operation.formDataSchema));
|
|
2410
2628
|
}
|
|
2411
|
-
if (
|
|
2412
|
-
|
|
2413
|
-
params.push(...formParams);
|
|
2629
|
+
if (operation.isUrlEncoded) {
|
|
2630
|
+
params.push(...this.convertObjectToSingleParams(operation.urlEncodedSchema));
|
|
2414
2631
|
}
|
|
2415
|
-
if (jsonContent && !
|
|
2632
|
+
if (jsonContent && !operation.isMultipart) {
|
|
2416
2633
|
const bodyType = this.getRequestBodyType(requestBody);
|
|
2417
2634
|
const isInterface = isDataTypeInterface(bodyType);
|
|
2418
2635
|
params.push({
|
|
@@ -2422,11 +2639,12 @@ var ServiceMethodParamsGenerator = class {
|
|
|
2422
2639
|
});
|
|
2423
2640
|
}
|
|
2424
2641
|
}
|
|
2425
|
-
|
|
2426
|
-
queryParams.forEach((param) => {
|
|
2642
|
+
operation.queryParams.forEach((param) => {
|
|
2427
2643
|
params.push({
|
|
2428
2644
|
name: camelCase(param.name),
|
|
2429
|
-
type: getTypeScriptType(param.schema ||
|
|
2645
|
+
type: getTypeScriptType(param.schema || {
|
|
2646
|
+
...param
|
|
2647
|
+
}, this.config),
|
|
2430
2648
|
hasQuestionToken: !param.required
|
|
2431
2649
|
});
|
|
2432
2650
|
});
|
|
@@ -2465,17 +2683,14 @@ var ServiceMethodParamsGenerator = class {
|
|
|
2465
2683
|
}
|
|
2466
2684
|
return "any";
|
|
2467
2685
|
}
|
|
2686
|
+
/** `schema` arrives ref-resolved from the normalizer (formData/urlEncoded schema). */
|
|
2468
2687
|
convertObjectToSingleParams(schema) {
|
|
2469
2688
|
const params = [];
|
|
2470
|
-
|
|
2471
|
-
if (schema?.$ref) {
|
|
2472
|
-
resolvedSchema = this.parser.resolveReference(schema.$ref);
|
|
2473
|
-
}
|
|
2474
|
-
Object.entries(resolvedSchema?.properties ?? {}).forEach(([key, value]) => {
|
|
2689
|
+
Object.entries(schema?.properties ?? {}).forEach(([key, value]) => {
|
|
2475
2690
|
params.push({
|
|
2476
2691
|
name: key,
|
|
2477
2692
|
type: getTypeScriptType(value, this.config, value.nullable),
|
|
2478
|
-
hasQuestionToken: !
|
|
2693
|
+
hasQuestionToken: !schema?.required?.includes(key)
|
|
2479
2694
|
});
|
|
2480
2695
|
});
|
|
2481
2696
|
return params;
|
|
@@ -2490,9 +2705,9 @@ var ServiceMethodOverloadsGenerator = class {
|
|
|
2490
2705
|
config;
|
|
2491
2706
|
paramsGenerator;
|
|
2492
2707
|
responseDataType = "any";
|
|
2493
|
-
constructor(config
|
|
2708
|
+
constructor(config) {
|
|
2494
2709
|
this.config = config;
|
|
2495
|
-
this.paramsGenerator = new ServiceMethodParamsGenerator(config
|
|
2710
|
+
this.paramsGenerator = new ServiceMethodParamsGenerator(config);
|
|
2496
2711
|
}
|
|
2497
2712
|
generateMethodOverloads(operation, requestObject) {
|
|
2498
2713
|
const observeTypes = [
|
|
@@ -2501,7 +2716,7 @@ var ServiceMethodOverloadsGenerator = class {
|
|
|
2501
2716
|
"events"
|
|
2502
2717
|
];
|
|
2503
2718
|
const overloads = [];
|
|
2504
|
-
const responseType =
|
|
2719
|
+
const responseType = operation.responseType;
|
|
2505
2720
|
observeTypes.forEach((observe) => {
|
|
2506
2721
|
const overload = this.generateMethodOverload(operation, observe, responseType, requestObject);
|
|
2507
2722
|
if (overload) {
|
|
@@ -2577,21 +2792,6 @@ var ServiceMethodOverloadsGenerator = class {
|
|
|
2577
2792
|
}
|
|
2578
2793
|
return `RequestOptions<'${responseType}', ${additionalTypeParameters.join(", ")}>`;
|
|
2579
2794
|
}
|
|
2580
|
-
determineResponseTypeForOperation(operation) {
|
|
2581
|
-
const successResponses = [
|
|
2582
|
-
"200",
|
|
2583
|
-
"201",
|
|
2584
|
-
"202",
|
|
2585
|
-
"204",
|
|
2586
|
-
"206"
|
|
2587
|
-
];
|
|
2588
|
-
for (const statusCode of successResponses) {
|
|
2589
|
-
const response = operation.responses?.[statusCode];
|
|
2590
|
-
if (!response) continue;
|
|
2591
|
-
return getResponseTypeFromResponse(response);
|
|
2592
|
-
}
|
|
2593
|
-
return "json";
|
|
2594
|
-
}
|
|
2595
2795
|
};
|
|
2596
2796
|
|
|
2597
2797
|
// src/lib/generators/service/service-method.generator.ts
|
|
@@ -2603,11 +2803,11 @@ var ServiceMethodGenerator = class {
|
|
|
2603
2803
|
bodyGenerator;
|
|
2604
2804
|
overloadsGenerator;
|
|
2605
2805
|
paramsGenerator;
|
|
2606
|
-
constructor(config
|
|
2806
|
+
constructor(config) {
|
|
2607
2807
|
this.config = config;
|
|
2608
|
-
this.bodyGenerator = new ServiceMethodBodyGenerator(config
|
|
2609
|
-
this.overloadsGenerator = new ServiceMethodOverloadsGenerator(config
|
|
2610
|
-
this.paramsGenerator = new ServiceMethodParamsGenerator(config
|
|
2808
|
+
this.bodyGenerator = new ServiceMethodBodyGenerator(config);
|
|
2809
|
+
this.overloadsGenerator = new ServiceMethodOverloadsGenerator(config);
|
|
2810
|
+
this.paramsGenerator = new ServiceMethodParamsGenerator(config);
|
|
2611
2811
|
}
|
|
2612
2812
|
addServiceMethod(serviceClass, operation, requestObject) {
|
|
2613
2813
|
const methodName = this.generateMethodName(operation);
|
|
@@ -2672,9 +2872,9 @@ var RequestParamsGenerator = class {
|
|
|
2672
2872
|
paramsGenerator;
|
|
2673
2873
|
registry = /* @__PURE__ */ new Map();
|
|
2674
2874
|
usedInterfaceNames = /* @__PURE__ */ new Set();
|
|
2675
|
-
constructor(
|
|
2875
|
+
constructor(project, config) {
|
|
2676
2876
|
this.project = project;
|
|
2677
|
-
this.paramsGenerator = new ServiceMethodParamsGenerator(config
|
|
2877
|
+
this.paramsGenerator = new ServiceMethodParamsGenerator(config);
|
|
2678
2878
|
}
|
|
2679
2879
|
buildRegistry(controllerGroups, getMethodName) {
|
|
2680
2880
|
Object.entries(controllerGroups).forEach(([controllerName, operations]) => {
|
|
@@ -2778,31 +2978,27 @@ var ServiceGenerator = class {
|
|
|
2778
2978
|
}
|
|
2779
2979
|
project;
|
|
2780
2980
|
parser;
|
|
2781
|
-
spec;
|
|
2782
2981
|
config;
|
|
2783
2982
|
methodGenerator;
|
|
2784
2983
|
requestObjects;
|
|
2785
|
-
|
|
2984
|
+
onWarning;
|
|
2985
|
+
constructor(parser, project, config, onWarning) {
|
|
2786
2986
|
this.config = config;
|
|
2787
2987
|
this.project = project;
|
|
2788
2988
|
this.parser = parser;
|
|
2789
|
-
this.
|
|
2790
|
-
|
|
2791
|
-
const versionInfo = this.parser.getSpecVersion();
|
|
2792
|
-
throw new Error(`Invalid or unsupported specification format. Expected OpenAPI 3.x or Swagger 2.x. ${versionInfo ? `Found: ${versionInfo.type} ${versionInfo.version}` : "No version info found"}`);
|
|
2793
|
-
}
|
|
2794
|
-
this.methodGenerator = new ServiceMethodGenerator(config, parser);
|
|
2989
|
+
this.onWarning = onWarning;
|
|
2990
|
+
this.methodGenerator = new ServiceMethodGenerator(config);
|
|
2795
2991
|
}
|
|
2796
2992
|
async generate(outputRoot) {
|
|
2797
2993
|
const outputDir = path10.join(outputRoot, "services");
|
|
2798
|
-
const paths =
|
|
2994
|
+
const paths = this.parser.getNormalizedSpec().operations;
|
|
2799
2995
|
if (paths.length === 0) {
|
|
2800
|
-
|
|
2996
|
+
this.onWarning?.("No API paths found in the specification");
|
|
2801
2997
|
return;
|
|
2802
2998
|
}
|
|
2803
2999
|
const controllerGroups = this.groupPathsByController(paths);
|
|
2804
3000
|
if (this.config.options.useSingleRequestParameter) {
|
|
2805
|
-
const requestParamsGenerator = new RequestParamsGenerator(this.
|
|
3001
|
+
const requestParamsGenerator = new RequestParamsGenerator(this.project, this.config);
|
|
2806
3002
|
this.requestObjects = requestParamsGenerator.buildRegistry(controllerGroups, (operation) => this.methodGenerator.generateMethodName(operation));
|
|
2807
3003
|
requestParamsGenerator.generate(outputRoot);
|
|
2808
3004
|
}
|
|
@@ -2898,27 +3094,27 @@ var ServiceGenerator = class {
|
|
|
2898
3094
|
serviceClass.addProperty({
|
|
2899
3095
|
name: "httpClient",
|
|
2900
3096
|
type: "HttpClient",
|
|
2901
|
-
scope:
|
|
3097
|
+
scope: import_ts_morph9.Scope.Private,
|
|
2902
3098
|
isReadonly: true,
|
|
2903
3099
|
initializer: "inject(HttpClient)"
|
|
2904
3100
|
});
|
|
2905
3101
|
serviceClass.addProperty({
|
|
2906
3102
|
name: "basePath",
|
|
2907
3103
|
type: "string",
|
|
2908
|
-
scope:
|
|
3104
|
+
scope: import_ts_morph9.Scope.Private,
|
|
2909
3105
|
isReadonly: true,
|
|
2910
3106
|
initializer: `inject(${basePathTokenName})`
|
|
2911
3107
|
});
|
|
2912
3108
|
serviceClass.addProperty({
|
|
2913
3109
|
name: "clientContextToken",
|
|
2914
3110
|
type: "HttpContextToken<string>",
|
|
2915
|
-
scope:
|
|
3111
|
+
scope: import_ts_morph9.Scope.Private,
|
|
2916
3112
|
isReadonly: true,
|
|
2917
3113
|
initializer: clientContextTokenName
|
|
2918
3114
|
});
|
|
2919
3115
|
serviceClass.addMethod({
|
|
2920
3116
|
name: "createContextWithClientId",
|
|
2921
|
-
scope:
|
|
3117
|
+
scope: import_ts_morph9.Scope.Private,
|
|
2922
3118
|
parameters: [
|
|
2923
3119
|
{
|
|
2924
3120
|
name: "existingContext",
|
|
@@ -2971,6 +3167,111 @@ var ServiceIndexGenerator = class {
|
|
|
2971
3167
|
}
|
|
2972
3168
|
};
|
|
2973
3169
|
|
|
3170
|
+
// src/lib/core/config-validation.ts
|
|
3171
|
+
var RESPONSE_TYPES = [
|
|
3172
|
+
"json",
|
|
3173
|
+
"blob",
|
|
3174
|
+
"arraybuffer",
|
|
3175
|
+
"text"
|
|
3176
|
+
];
|
|
3177
|
+
var ConfigValidationError = class extends Error {
|
|
3178
|
+
static {
|
|
3179
|
+
__name(this, "ConfigValidationError");
|
|
3180
|
+
}
|
|
3181
|
+
issues;
|
|
3182
|
+
constructor(issues) {
|
|
3183
|
+
super(`Invalid ng-openapi configuration:
|
|
3184
|
+
${issues.map((issue) => ` - ${issue}`).join("\n")}`);
|
|
3185
|
+
this.name = "ConfigValidationError";
|
|
3186
|
+
this.issues = issues;
|
|
3187
|
+
}
|
|
3188
|
+
};
|
|
3189
|
+
function validateGeneratorConfig(config) {
|
|
3190
|
+
if (!config || typeof config !== "object") {
|
|
3191
|
+
throw new ConfigValidationError([
|
|
3192
|
+
"config must be an object \u2014 see https://ng-openapi.dev for the shape"
|
|
3193
|
+
]);
|
|
3194
|
+
}
|
|
3195
|
+
const issues = [];
|
|
3196
|
+
const c = config;
|
|
3197
|
+
if (typeof c.input !== "string" || c.input.trim() === "") {
|
|
3198
|
+
issues.push("`input` must be a non-empty string (path or URL of the OpenAPI/Swagger spec)");
|
|
3199
|
+
}
|
|
3200
|
+
if (typeof c.output !== "string" || c.output.trim() === "") {
|
|
3201
|
+
issues.push("`output` must be a non-empty string (output directory)");
|
|
3202
|
+
}
|
|
3203
|
+
if (c.clientName !== void 0 && typeof c.clientName !== "string") {
|
|
3204
|
+
issues.push("`clientName` must be a string");
|
|
3205
|
+
}
|
|
3206
|
+
if (c.validateInput !== void 0 && typeof c.validateInput !== "function") {
|
|
3207
|
+
issues.push("`validateInput` must be a function (spec) => boolean");
|
|
3208
|
+
}
|
|
3209
|
+
if (!c.options || typeof c.options !== "object") {
|
|
3210
|
+
issues.push("`options` must be an object with at least `dateType` and `enumStyle`");
|
|
3211
|
+
} else {
|
|
3212
|
+
const options = c.options;
|
|
3213
|
+
if (options.dateType !== "string" && options.dateType !== "Date") {
|
|
3214
|
+
issues.push(`\`options.dateType\` must be "string" or "Date", got ${JSON.stringify(options.dateType)}`);
|
|
3215
|
+
}
|
|
3216
|
+
if (options.enumStyle !== "enum" && options.enumStyle !== "union") {
|
|
3217
|
+
issues.push(`\`options.enumStyle\` must be "enum" or "union", got ${JSON.stringify(options.enumStyle)}`);
|
|
3218
|
+
}
|
|
3219
|
+
const booleanKeys = [
|
|
3220
|
+
"generateServices",
|
|
3221
|
+
"generateEnumBasedOnDescription",
|
|
3222
|
+
"useSingleRequestParameter"
|
|
3223
|
+
];
|
|
3224
|
+
for (const key of booleanKeys) {
|
|
3225
|
+
if (options[key] !== void 0 && typeof options[key] !== "boolean") {
|
|
3226
|
+
issues.push(`\`options.${key}\` must be a boolean`);
|
|
3227
|
+
}
|
|
3228
|
+
}
|
|
3229
|
+
if (options.customizeMethodName !== void 0 && typeof options.customizeMethodName !== "function") {
|
|
3230
|
+
issues.push("`options.customizeMethodName` must be a function (operationId) => string");
|
|
3231
|
+
}
|
|
3232
|
+
if (options.validation !== void 0 && (typeof options.validation !== "object" || options.validation === null)) {
|
|
3233
|
+
issues.push("`options.validation` must be an object like { response?: boolean }");
|
|
3234
|
+
}
|
|
3235
|
+
if (options.customHeaders !== void 0) {
|
|
3236
|
+
if (typeof options.customHeaders !== "object" || options.customHeaders === null) {
|
|
3237
|
+
issues.push("`options.customHeaders` must be an object of header name \u2192 value strings");
|
|
3238
|
+
} else {
|
|
3239
|
+
for (const [header, value] of Object.entries(options.customHeaders)) {
|
|
3240
|
+
if (typeof value !== "string") {
|
|
3241
|
+
issues.push(`\`options.customHeaders["${header}"]\` must be a string`);
|
|
3242
|
+
}
|
|
3243
|
+
}
|
|
3244
|
+
}
|
|
3245
|
+
}
|
|
3246
|
+
if (options.responseTypeMapping !== void 0) {
|
|
3247
|
+
if (typeof options.responseTypeMapping !== "object" || options.responseTypeMapping === null) {
|
|
3248
|
+
issues.push("`options.responseTypeMapping` must be an object of content type \u2192 response type");
|
|
3249
|
+
} else {
|
|
3250
|
+
for (const [contentType, value] of Object.entries(options.responseTypeMapping)) {
|
|
3251
|
+
if (!RESPONSE_TYPES.includes(value)) {
|
|
3252
|
+
issues.push(`\`options.responseTypeMapping["${contentType}"]\` must be one of ${RESPONSE_TYPES.join(", ")}, got ${JSON.stringify(value)}`);
|
|
3253
|
+
}
|
|
3254
|
+
}
|
|
3255
|
+
}
|
|
3256
|
+
}
|
|
3257
|
+
}
|
|
3258
|
+
if (c.plugins !== void 0) {
|
|
3259
|
+
if (!Array.isArray(c.plugins)) {
|
|
3260
|
+
issues.push("`plugins` must be an array of plugin classes (e.g. HttpResourcePlugin, ZodPlugin)");
|
|
3261
|
+
} else {
|
|
3262
|
+
c.plugins.forEach((plugin, index) => {
|
|
3263
|
+
if (typeof plugin !== "function") {
|
|
3264
|
+
issues.push(`\`plugins[${index}]\` must be a plugin class, got ${typeof plugin}`);
|
|
3265
|
+
}
|
|
3266
|
+
});
|
|
3267
|
+
}
|
|
3268
|
+
}
|
|
3269
|
+
if (issues.length > 0) {
|
|
3270
|
+
throw new ConfigValidationError(issues);
|
|
3271
|
+
}
|
|
3272
|
+
}
|
|
3273
|
+
__name(validateGeneratorConfig, "validateGeneratorConfig");
|
|
3274
|
+
|
|
2974
3275
|
// src/lib/core/generator.ts
|
|
2975
3276
|
var fs3 = __toESM(require("fs"));
|
|
2976
3277
|
var path12 = __toESM(require("path"));
|
|
@@ -2979,7 +3280,7 @@ function validateInput(inputPath) {
|
|
|
2979
3280
|
return;
|
|
2980
3281
|
}
|
|
2981
3282
|
if (!fs3.existsSync(inputPath)) {
|
|
2982
|
-
throw new
|
|
3283
|
+
throw new SpecLoadError(`Input file not found: ${inputPath}`, inputPath);
|
|
2983
3284
|
}
|
|
2984
3285
|
const extension = path12.extname(inputPath).toLowerCase();
|
|
2985
3286
|
const supportedExtensions = [
|
|
@@ -2988,89 +3289,124 @@ function validateInput(inputPath) {
|
|
|
2988
3289
|
".yml"
|
|
2989
3290
|
];
|
|
2990
3291
|
if (!supportedExtensions.includes(extension)) {
|
|
2991
|
-
throw new
|
|
3292
|
+
throw new SpecLoadError(`Failed to parse ${extension || "specification"}. Supported formats are .json, .yaml, and .yml.`, inputPath);
|
|
2992
3293
|
}
|
|
2993
3294
|
}
|
|
2994
3295
|
__name(validateInput, "validateInput");
|
|
2995
|
-
async function generateFromConfig(config) {
|
|
3296
|
+
async function generateFromConfig(config, reporter = {}) {
|
|
3297
|
+
const startedAt = Date.now();
|
|
3298
|
+
validateGeneratorConfig(config);
|
|
2996
3299
|
validateInput(config.input);
|
|
2997
3300
|
const outputPath = config.output;
|
|
2998
3301
|
const generateServices = config.options.generateServices ?? true;
|
|
2999
|
-
const
|
|
3302
|
+
const warnings = [];
|
|
3303
|
+
const onWarning = /* @__PURE__ */ __name((message) => {
|
|
3304
|
+
warnings.push(message);
|
|
3305
|
+
reporter.onWarning?.(message);
|
|
3306
|
+
}, "onWarning");
|
|
3000
3307
|
if (!fs3.existsSync(outputPath)) {
|
|
3001
3308
|
fs3.mkdirSync(outputPath, {
|
|
3002
3309
|
recursive: true
|
|
3003
3310
|
});
|
|
3004
3311
|
}
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
...config.compilerOptions
|
|
3013
|
-
}
|
|
3014
|
-
});
|
|
3015
|
-
console.log(`\u{1F4E1} Processing OpenAPI specification from ${inputType}: ${config.input}`);
|
|
3016
|
-
const swaggerParser = await SwaggerParser.create(config.input, config);
|
|
3017
|
-
const typeGenerator = new TypeGenerator(swaggerParser, project, config, outputPath);
|
|
3018
|
-
await typeGenerator.generate();
|
|
3019
|
-
console.log(`\u2705 TypeScript interfaces generated`);
|
|
3020
|
-
if (generateServices) {
|
|
3021
|
-
const tokenGenerator = new TokenGenerator(project, config.clientName);
|
|
3022
|
-
tokenGenerator.generate(outputPath);
|
|
3023
|
-
if (config.options.dateType === "Date") {
|
|
3024
|
-
const dateTransformer = new DateTransformerGenerator(project);
|
|
3025
|
-
dateTransformer.generate(outputPath);
|
|
3026
|
-
}
|
|
3027
|
-
const fileDownloadHelper = new FileDownloadGenerator(project);
|
|
3028
|
-
fileDownloadHelper.generate(outputPath);
|
|
3029
|
-
const httpParamsBuilderGenerator = new HttpParamsBuilderGenerator(project);
|
|
3030
|
-
httpParamsBuilderGenerator.generate(outputPath);
|
|
3031
|
-
const providerGenerator = new ProviderGenerator(project, config);
|
|
3032
|
-
providerGenerator.generate(outputPath);
|
|
3033
|
-
const baseInterceptorGenerator = new BaseInterceptorGenerator(project, config.clientName);
|
|
3034
|
-
baseInterceptorGenerator.generate(outputPath);
|
|
3035
|
-
const serviceGenerator = new ServiceGenerator(swaggerParser, project, config);
|
|
3036
|
-
await serviceGenerator.generate(outputPath);
|
|
3037
|
-
const indexGenerator = new ServiceIndexGenerator(project);
|
|
3038
|
-
indexGenerator.generateIndex(outputPath);
|
|
3039
|
-
console.log(`\u2705 Angular services generated`);
|
|
3040
|
-
}
|
|
3041
|
-
if (config.plugins?.length) {
|
|
3042
|
-
for (const plugin of config.plugins) {
|
|
3043
|
-
const generatorClass = plugin;
|
|
3044
|
-
const pluginGenerator = new generatorClass(swaggerParser, project, config);
|
|
3045
|
-
await pluginGenerator.generate(outputPath);
|
|
3046
|
-
}
|
|
3047
|
-
console.log(`\u2705 Plugins are generated`);
|
|
3048
|
-
}
|
|
3049
|
-
const mainIndexGenerator = new MainIndexGenerator(project, config);
|
|
3050
|
-
mainIndexGenerator.generateMainIndex(outputPath);
|
|
3051
|
-
const sourceInfo = `from ${inputType}: ${config.input}`;
|
|
3052
|
-
if (config.clientName) {
|
|
3053
|
-
console.log(`\u{1F389} ${config.clientName} Generation completed successfully ${sourceInfo} -> ${outputPath}`);
|
|
3054
|
-
} else {
|
|
3055
|
-
console.log(`\u{1F389} Generation completed successfully ${sourceInfo} -> ${outputPath}`);
|
|
3056
|
-
}
|
|
3057
|
-
} catch (error) {
|
|
3058
|
-
if (error instanceof Error) {
|
|
3059
|
-
console.error("\u274C Error during generation:", error.message);
|
|
3060
|
-
if (error.message.includes("fetch") || error.message.includes("Failed to fetch")) {
|
|
3061
|
-
console.error("\u{1F4A1} Tip: Make sure the URL is accessible and returns a valid OpenAPI/Swagger specification");
|
|
3062
|
-
console.error("\u{1F4A1} Alternative: Download the specification file locally and use the file path instead");
|
|
3063
|
-
}
|
|
3064
|
-
} else {
|
|
3065
|
-
console.error("\u274C Unknown error during generation:", error);
|
|
3312
|
+
const project = new import_ts_morph10.Project({
|
|
3313
|
+
compilerOptions: {
|
|
3314
|
+
declaration: true,
|
|
3315
|
+
target: import_ts_morph10.ScriptTarget.ES2022,
|
|
3316
|
+
module: import_ts_morph10.ModuleKind.Preserve,
|
|
3317
|
+
strict: true,
|
|
3318
|
+
...config.compilerOptions
|
|
3066
3319
|
}
|
|
3067
|
-
|
|
3068
|
-
|
|
3320
|
+
});
|
|
3321
|
+
reporter.onPhase?.("processing-spec");
|
|
3322
|
+
const swaggerParser = await SwaggerParser.create(config.input, config);
|
|
3323
|
+
if (!swaggerParser.isValidSpec()) {
|
|
3324
|
+
const versionInfo = swaggerParser.getSpecVersion();
|
|
3325
|
+
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);
|
|
3326
|
+
}
|
|
3327
|
+
const normalizedSpec = swaggerParser.getNormalizedSpec();
|
|
3328
|
+
const typeGenerator = new TypeGenerator(swaggerParser, project, config, outputPath, onWarning);
|
|
3329
|
+
await typeGenerator.generate();
|
|
3330
|
+
reporter.onPhase?.("types-generated");
|
|
3331
|
+
if (generateServices) {
|
|
3332
|
+
const tokenGenerator = new TokenGenerator(project, config.clientName);
|
|
3333
|
+
tokenGenerator.generate(outputPath);
|
|
3334
|
+
if (config.options.dateType === "Date") {
|
|
3335
|
+
const dateTransformer = new DateTransformerGenerator(project);
|
|
3336
|
+
dateTransformer.generate(outputPath);
|
|
3337
|
+
}
|
|
3338
|
+
const fileDownloadHelper = new FileDownloadGenerator(project);
|
|
3339
|
+
fileDownloadHelper.generate(outputPath);
|
|
3340
|
+
const httpParamsBuilderGenerator = new HttpParamsBuilderGenerator(project);
|
|
3341
|
+
httpParamsBuilderGenerator.generate(outputPath);
|
|
3342
|
+
const providerGenerator = new ProviderGenerator(project, config);
|
|
3343
|
+
providerGenerator.generate(outputPath);
|
|
3344
|
+
const baseInterceptorGenerator = new BaseInterceptorGenerator(project, config.clientName);
|
|
3345
|
+
baseInterceptorGenerator.generate(outputPath);
|
|
3346
|
+
const serviceGenerator = new ServiceGenerator(swaggerParser, project, config, onWarning);
|
|
3347
|
+
await serviceGenerator.generate(outputPath);
|
|
3348
|
+
const indexGenerator = new ServiceIndexGenerator(project);
|
|
3349
|
+
indexGenerator.generateIndex(outputPath);
|
|
3350
|
+
reporter.onPhase?.("services-generated");
|
|
3351
|
+
}
|
|
3352
|
+
if (config.plugins?.length) {
|
|
3353
|
+
for (const plugin of config.plugins) {
|
|
3354
|
+
const pluginGenerator = new plugin({
|
|
3355
|
+
spec: normalizedSpec,
|
|
3356
|
+
project,
|
|
3357
|
+
config,
|
|
3358
|
+
onWarning
|
|
3359
|
+
});
|
|
3360
|
+
await pluginGenerator.generate(outputPath);
|
|
3361
|
+
}
|
|
3362
|
+
reporter.onPhase?.("plugins-generated");
|
|
3363
|
+
}
|
|
3364
|
+
const mainIndexGenerator = new MainIndexGenerator(project, config);
|
|
3365
|
+
mainIndexGenerator.generateMainIndex(outputPath);
|
|
3366
|
+
return {
|
|
3367
|
+
client: config.clientName,
|
|
3368
|
+
filesWritten: project.getSourceFiles().map((sourceFile) => sourceFile.getFilePath()),
|
|
3369
|
+
warnings,
|
|
3370
|
+
durationMs: Date.now() - startedAt
|
|
3371
|
+
};
|
|
3069
3372
|
}
|
|
3070
3373
|
__name(generateFromConfig, "generateFromConfig");
|
|
3071
3374
|
|
|
3072
3375
|
// src/lib/cli.ts
|
|
3073
3376
|
var program = new import_commander.Command();
|
|
3377
|
+
function createConsoleReporter(config) {
|
|
3378
|
+
const inputType = isUrl(config.input) ? "URL" : "file";
|
|
3379
|
+
return {
|
|
3380
|
+
onPhase(phase) {
|
|
3381
|
+
switch (phase) {
|
|
3382
|
+
case "processing-spec":
|
|
3383
|
+
console.log(`\u{1F4E1} Processing OpenAPI specification from ${inputType}: ${config.input}`);
|
|
3384
|
+
break;
|
|
3385
|
+
case "types-generated":
|
|
3386
|
+
console.log("\u2705 TypeScript interfaces generated");
|
|
3387
|
+
break;
|
|
3388
|
+
case "services-generated":
|
|
3389
|
+
console.log("\u2705 Angular services generated");
|
|
3390
|
+
break;
|
|
3391
|
+
case "plugins-generated":
|
|
3392
|
+
console.log("\u2705 Plugins are generated");
|
|
3393
|
+
break;
|
|
3394
|
+
}
|
|
3395
|
+
},
|
|
3396
|
+
onWarning(message) {
|
|
3397
|
+
console.warn(`\u26A0\uFE0F ${message}`);
|
|
3398
|
+
}
|
|
3399
|
+
};
|
|
3400
|
+
}
|
|
3401
|
+
__name(createConsoleReporter, "createConsoleReporter");
|
|
3402
|
+
async function runGeneration(config) {
|
|
3403
|
+
const result = await generateFromConfig(config, createConsoleReporter(config));
|
|
3404
|
+
const inputType = isUrl(config.input) ? "URL" : "file";
|
|
3405
|
+
const sourceInfo = `from ${inputType}: ${config.input}`;
|
|
3406
|
+
const clientPrefix = result.client ? `${result.client} ` : "";
|
|
3407
|
+
console.log(`\u{1F389} ${clientPrefix}Generation completed successfully ${sourceInfo} -> ${config.output}`);
|
|
3408
|
+
}
|
|
3409
|
+
__name(runGeneration, "runGeneration");
|
|
3074
3410
|
async function loadConfigFile(configPath) {
|
|
3075
3411
|
const resolvedPath = path13.resolve(configPath);
|
|
3076
3412
|
if (!fs4.existsSync(resolvedPath)) {
|
|
@@ -3104,32 +3440,38 @@ async function generateFromOptions(options) {
|
|
|
3104
3440
|
try {
|
|
3105
3441
|
if (options.config) {
|
|
3106
3442
|
const config = await loadConfigFile(options.config);
|
|
3107
|
-
await
|
|
3443
|
+
await runGeneration(config);
|
|
3108
3444
|
} else if (options.input) {
|
|
3109
3445
|
const config = {
|
|
3110
3446
|
input: options.input,
|
|
3111
3447
|
output: options.output || "./src/generated",
|
|
3112
3448
|
options: {
|
|
3449
|
+
// Passed through unchecked on purpose: validateGeneratorConfig
|
|
3450
|
+
// rejects anything but "string" | "Date" with an actionable error
|
|
3113
3451
|
dateType: options.dateType || "Date",
|
|
3114
3452
|
enumStyle: "enum",
|
|
3115
3453
|
generateEnumBasedOnDescription: true,
|
|
3116
3454
|
generateServices: !options.typesOnly
|
|
3117
3455
|
}
|
|
3118
3456
|
};
|
|
3119
|
-
await
|
|
3457
|
+
await runGeneration(config);
|
|
3120
3458
|
} else {
|
|
3121
3459
|
console.error("Error: Either --config or --input option is required");
|
|
3122
|
-
program.help(
|
|
3123
|
-
|
|
3460
|
+
program.help({
|
|
3461
|
+
error: true
|
|
3462
|
+
});
|
|
3124
3463
|
}
|
|
3125
3464
|
console.log("\u2728 Generation completed successfully!");
|
|
3126
3465
|
} catch (error) {
|
|
3127
3466
|
console.error("\u274C Generation failed:", error instanceof Error ? error.message : error);
|
|
3128
|
-
|
|
3467
|
+
if (error instanceof SpecLoadError && isUrl(error.source)) {
|
|
3468
|
+
console.error("\u{1F4A1} Tip: Make sure the URL is accessible and returns a valid OpenAPI/Swagger specification");
|
|
3469
|
+
console.error("\u{1F4A1} Alternative: Download the specification file locally and use the file path instead");
|
|
3470
|
+
}
|
|
3471
|
+
process.exitCode = 1;
|
|
3129
3472
|
} finally {
|
|
3130
3473
|
const duration = ((/* @__PURE__ */ new Date()).getTime() - timestamp) / 1e3;
|
|
3131
3474
|
console.log(`\u23F1\uFE0F Duration: ${duration.toFixed(2)} seconds`);
|
|
3132
|
-
process.exit(0);
|
|
3133
3475
|
}
|
|
3134
3476
|
}
|
|
3135
3477
|
__name(generateFromOptions, "generateFromOptions");
|