@ps-aux/api-client-gen 0.7.0-rc.2 → 0.7.0-rc.3
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 +7 -1
- package/dist/bin.cjs +7 -4
- package/dist/bin.mjs +7 -4
- package/dist/generateApiClient.cjs +1476 -1307
- package/dist/generateApiClient.mjs +1476 -1307
- package/dist/index.cjs +3 -3
- package/dist/index.d.cts +2101 -18
- package/dist/index.d.mts +2101 -18
- package/dist/index.d.ts +2101 -18
- package/dist/index.mjs +3 -3
- package/package.json +4 -3
- package/templates/http-client.eta +10 -1
- package/templates/procedure-call.ejs +2 -2
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var Path
|
|
3
|
+
var Path = require('path');
|
|
4
4
|
var fs$2 = require('fs');
|
|
5
5
|
var fs = require('fs/promises');
|
|
6
|
-
var Path = require('node:path');
|
|
7
|
-
var promises = require('node:fs/promises');
|
|
8
|
-
var fs$1 = require('node:fs');
|
|
9
6
|
var swaggerTypescriptApi = require('swagger-typescript-api');
|
|
10
7
|
var node_url = require('node:url');
|
|
8
|
+
var promises = require('node:fs/promises');
|
|
9
|
+
var Path$1 = require('node:path');
|
|
10
|
+
var fs$1 = require('node:fs');
|
|
11
11
|
var tsToZod = require('ts-to-zod');
|
|
12
12
|
|
|
13
13
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
@@ -572,69 +572,6 @@ function formatError(error, mapper = (issue) => issue.message) {
|
|
|
572
572
|
processError(error);
|
|
573
573
|
return fieldErrors;
|
|
574
574
|
}
|
|
575
|
-
/** Format a ZodError as a human-readable string in the following form.
|
|
576
|
-
*
|
|
577
|
-
* From
|
|
578
|
-
*
|
|
579
|
-
* ```ts
|
|
580
|
-
* ZodError {
|
|
581
|
-
* issues: [
|
|
582
|
-
* {
|
|
583
|
-
* expected: 'string',
|
|
584
|
-
* code: 'invalid_type',
|
|
585
|
-
* path: [ 'username' ],
|
|
586
|
-
* message: 'Invalid input: expected string'
|
|
587
|
-
* },
|
|
588
|
-
* {
|
|
589
|
-
* expected: 'number',
|
|
590
|
-
* code: 'invalid_type',
|
|
591
|
-
* path: [ 'favoriteNumbers', 1 ],
|
|
592
|
-
* message: 'Invalid input: expected number'
|
|
593
|
-
* }
|
|
594
|
-
* ];
|
|
595
|
-
* }
|
|
596
|
-
* ```
|
|
597
|
-
*
|
|
598
|
-
* to
|
|
599
|
-
*
|
|
600
|
-
* ```
|
|
601
|
-
* username
|
|
602
|
-
* ✖ Expected number, received string at "username
|
|
603
|
-
* favoriteNumbers[0]
|
|
604
|
-
* ✖ Invalid input: expected number
|
|
605
|
-
* ```
|
|
606
|
-
*/
|
|
607
|
-
function toDotPath(_path) {
|
|
608
|
-
const segs = [];
|
|
609
|
-
const path = _path.map((seg) => (typeof seg === "object" ? seg.key : seg));
|
|
610
|
-
for (const seg of path) {
|
|
611
|
-
if (typeof seg === "number")
|
|
612
|
-
segs.push(`[${seg}]`);
|
|
613
|
-
else if (typeof seg === "symbol")
|
|
614
|
-
segs.push(`[${JSON.stringify(String(seg))}]`);
|
|
615
|
-
else if (/[^\w$]/.test(seg))
|
|
616
|
-
segs.push(`[${JSON.stringify(seg)}]`);
|
|
617
|
-
else {
|
|
618
|
-
if (segs.length)
|
|
619
|
-
segs.push(".");
|
|
620
|
-
segs.push(seg);
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
return segs.join("");
|
|
624
|
-
}
|
|
625
|
-
function prettifyError(error) {
|
|
626
|
-
const lines = [];
|
|
627
|
-
// sort by path length
|
|
628
|
-
const issues = [...error.issues].sort((a, b) => (a.path ?? []).length - (b.path ?? []).length);
|
|
629
|
-
// Process each issue
|
|
630
|
-
for (const issue of issues) {
|
|
631
|
-
lines.push(`✖ ${issue.message}`);
|
|
632
|
-
if (issue.path?.length)
|
|
633
|
-
lines.push(` → at ${toDotPath(issue.path)}`);
|
|
634
|
-
}
|
|
635
|
-
// Convert Map to formatted string
|
|
636
|
-
return lines.join("\n");
|
|
637
|
-
}
|
|
638
575
|
|
|
639
576
|
const _parse = (_Err) => (schema, value, _ctx, _params) => {
|
|
640
577
|
const ctx = _ctx ? Object.assign(_ctx, { async: false }) : { async: false };
|
|
@@ -4103,6 +4040,53 @@ function superRefine(fn) {
|
|
|
4103
4040
|
return _superRefine(fn);
|
|
4104
4041
|
}
|
|
4105
4042
|
|
|
4043
|
+
const vNextOasGeneratorSchema = strictObject({
|
|
4044
|
+
selection: strictObject({
|
|
4045
|
+
ignoreOperationsWithTags: array(string()).default([]),
|
|
4046
|
+
allSchemas: boolean().default(true)
|
|
4047
|
+
}).optional(),
|
|
4048
|
+
codegen: strictObject({
|
|
4049
|
+
unwrap: boolean().default(true),
|
|
4050
|
+
withRequestParams: boolean().default(false),
|
|
4051
|
+
pathParamsStyle: _enum(["object", "positional"]).default("positional"),
|
|
4052
|
+
enumStyle: _enum(["enum", "union"]).default("enum")
|
|
4053
|
+
}),
|
|
4054
|
+
compat: strictObject({
|
|
4055
|
+
uppercaseEnumKeys: boolean(),
|
|
4056
|
+
swaggerTsApiRequiredBooleans: boolean()
|
|
4057
|
+
}).optional()
|
|
4058
|
+
});
|
|
4059
|
+
const legacyOasGeneratorSchema = strictObject({
|
|
4060
|
+
legacy: strictObject({
|
|
4061
|
+
ignoreOperationsWithTags: array(string()).optional()
|
|
4062
|
+
})
|
|
4063
|
+
});
|
|
4064
|
+
const configSchema = strictObject({
|
|
4065
|
+
srcSpec: string().trim().min(1),
|
|
4066
|
+
dstDir: string().trim().min(1),
|
|
4067
|
+
apiName: string().trim().min(1),
|
|
4068
|
+
openApiGenerator: union([
|
|
4069
|
+
vNextOasGeneratorSchema,
|
|
4070
|
+
legacyOasGeneratorSchema
|
|
4071
|
+
]),
|
|
4072
|
+
responseWrapper: strictObject({
|
|
4073
|
+
symbol: string().trim().min(1),
|
|
4074
|
+
import: string().trim().min(1).optional(),
|
|
4075
|
+
unwrapExpr: string().optional()
|
|
4076
|
+
}).optional(),
|
|
4077
|
+
zodSchemas: strictObject({
|
|
4078
|
+
enabled: boolean().optional(),
|
|
4079
|
+
localDateTimes: boolean().optional()
|
|
4080
|
+
}).optional()
|
|
4081
|
+
});
|
|
4082
|
+
const parseConfig = (userConfig, filePath) => {
|
|
4083
|
+
const parsed = configSchema.safeParse(userConfig);
|
|
4084
|
+
if (parsed.success) return parsed.data;
|
|
4085
|
+
throw new Error(`Invalid config at ${filePath}: ${parsed.error.message}`, {
|
|
4086
|
+
cause: parsed.error
|
|
4087
|
+
});
|
|
4088
|
+
};
|
|
4089
|
+
|
|
4106
4090
|
const downloadSpec = async (path, url) => {
|
|
4107
4091
|
let response;
|
|
4108
4092
|
try {
|
|
@@ -4122,6 +4106,86 @@ const downloadSpec = async (path, url) => {
|
|
|
4122
4106
|
await fs.writeFile(path, content);
|
|
4123
4107
|
};
|
|
4124
4108
|
|
|
4109
|
+
const toPascalCaseIdentifier = (str) => {
|
|
4110
|
+
const words = str.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[^a-zA-Z0-9]+/).filter(Boolean);
|
|
4111
|
+
const pascalCase = words.map((word) => word[0].toUpperCase() + word.substring(1)).join("");
|
|
4112
|
+
if (!pascalCase.length) return "Api";
|
|
4113
|
+
if (/^[0-9]/.test(pascalCase)) return `Api${pascalCase}`;
|
|
4114
|
+
return pascalCase;
|
|
4115
|
+
};
|
|
4116
|
+
const groupBy = (values, getKey) => {
|
|
4117
|
+
const groups = /* @__PURE__ */ new Map();
|
|
4118
|
+
for (const value of values) {
|
|
4119
|
+
const key = getKey(value);
|
|
4120
|
+
const existing = groups.get(key);
|
|
4121
|
+
if (existing) {
|
|
4122
|
+
existing.push(value);
|
|
4123
|
+
} else {
|
|
4124
|
+
groups.set(key, [value]);
|
|
4125
|
+
}
|
|
4126
|
+
}
|
|
4127
|
+
return groups;
|
|
4128
|
+
};
|
|
4129
|
+
|
|
4130
|
+
const generateOpenApiClient = async (inputFile, {
|
|
4131
|
+
name,
|
|
4132
|
+
outputDir,
|
|
4133
|
+
ignoreOperationsWithTags
|
|
4134
|
+
}, log) => {
|
|
4135
|
+
log(`Will generate API client name=${name} to ${outputDir}`);
|
|
4136
|
+
const fileName = "index";
|
|
4137
|
+
const dstFile = Path.join(outputDir, `${fileName}.ts`);
|
|
4138
|
+
const prettier = await import('prettier');
|
|
4139
|
+
const prettierConfig = await prettier.resolveConfig(dstFile) ?? {};
|
|
4140
|
+
const prettierConfigForGenerator = {
|
|
4141
|
+
...prettierConfig,
|
|
4142
|
+
// swagger-typescript-api defaults to printWidth=120; keep Prettier default 80 unless explicitly configured.
|
|
4143
|
+
printWidth: prettierConfig.printWidth ?? 80
|
|
4144
|
+
};
|
|
4145
|
+
const ignoreTags = ignoreOperationsWithTags && new Set(ignoreOperationsWithTags);
|
|
4146
|
+
await swaggerTypescriptApi.generateApi({
|
|
4147
|
+
name: "index",
|
|
4148
|
+
output: outputDir,
|
|
4149
|
+
input: inputFile,
|
|
4150
|
+
// modular: true,
|
|
4151
|
+
httpClientType: "axios",
|
|
4152
|
+
templates: getTemplatesDir(),
|
|
4153
|
+
defaultResponseAsSuccess: true,
|
|
4154
|
+
sortTypes: true,
|
|
4155
|
+
// generateRouteTypes: true,
|
|
4156
|
+
singleHttpClient: true,
|
|
4157
|
+
// extractRequestBody: true,
|
|
4158
|
+
prettier: prettierConfigForGenerator,
|
|
4159
|
+
moduleNameFirstTag: true,
|
|
4160
|
+
apiClassName: toPascalCaseIdentifier(name),
|
|
4161
|
+
hooks: {
|
|
4162
|
+
onCreateRoute: (routeData) => {
|
|
4163
|
+
if (ignoreTags) {
|
|
4164
|
+
const ignore = routeData.raw.tags?.find(
|
|
4165
|
+
(t) => ignoreTags.has(t)
|
|
4166
|
+
);
|
|
4167
|
+
if (ignore) return false;
|
|
4168
|
+
}
|
|
4169
|
+
return routeData;
|
|
4170
|
+
}
|
|
4171
|
+
},
|
|
4172
|
+
// @ts-ignore
|
|
4173
|
+
codeGenConstructs: () => ({
|
|
4174
|
+
Keyword: {}
|
|
4175
|
+
})
|
|
4176
|
+
// extractRequestParams: true,
|
|
4177
|
+
});
|
|
4178
|
+
return { types: dstFile, promiseWrapper: [dstFile] };
|
|
4179
|
+
};
|
|
4180
|
+
const getThisScriptDirname = () => {
|
|
4181
|
+
return Path.dirname(node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('generateApiClient.cjs', document.baseURI).href))));
|
|
4182
|
+
};
|
|
4183
|
+
const getTemplatesDir = () => {
|
|
4184
|
+
const currentDir = getThisScriptDirname();
|
|
4185
|
+
const templatesRelativePath = Path.basename(currentDir) === "dist" ? "../templates" : "../../../templates";
|
|
4186
|
+
return Path.resolve(currentDir, templatesRelativePath);
|
|
4187
|
+
};
|
|
4188
|
+
|
|
4125
4189
|
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4126
4190
|
const asRecord = (value) => {
|
|
4127
4191
|
return isRecord(value) ? value : void 0;
|
|
@@ -4547,31 +4611,6 @@ const syntheticOperationId = (method, path) => {
|
|
|
4547
4611
|
return `_${compact}`;
|
|
4548
4612
|
};
|
|
4549
4613
|
|
|
4550
|
-
const toPascalCaseIdentifier = (str) => {
|
|
4551
|
-
const words = str.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[^a-zA-Z0-9]+/).filter(Boolean);
|
|
4552
|
-
const pascalCase = words.map((word) => word[0].toUpperCase() + word.substring(1)).join("");
|
|
4553
|
-
if (!pascalCase.length) return "Api";
|
|
4554
|
-
if (/^[0-9]/.test(pascalCase)) return `Api${pascalCase}`;
|
|
4555
|
-
return pascalCase;
|
|
4556
|
-
};
|
|
4557
|
-
const groupBy = (values, getKey) => {
|
|
4558
|
-
const groups = /* @__PURE__ */ new Map();
|
|
4559
|
-
for (const value of values) {
|
|
4560
|
-
const key = getKey(value);
|
|
4561
|
-
const existing = groups.get(key);
|
|
4562
|
-
if (existing) {
|
|
4563
|
-
existing.push(value);
|
|
4564
|
-
} else {
|
|
4565
|
-
groups.set(key, [value]);
|
|
4566
|
-
}
|
|
4567
|
-
}
|
|
4568
|
-
return groups;
|
|
4569
|
-
};
|
|
4570
|
-
|
|
4571
|
-
var httpClientCommonTypesSource = "export type KnownRequestContentType =\n | 'application/json'\n | 'multipart/form-data'\n | 'application/x-www-form-urlencoded'\n\nexport type RequestContentType = KnownRequestContentType | string\n\nexport type QueryValue =\n | string\n | number\n | boolean\n | null\n | undefined\n | QueryValue[]\n | Record<string, any>\n // Empty schema support\n | unknown\n\nexport type QueryParams = Record<string, QueryValue>\n\nexport type Request = {\n path: string\n method: 'GET' | 'POST' | 'PUT' | 'DELETE'\n format?: 'json' | 'document'\n headers?: Record<string, string>\n query?: QueryParams\n body?: any\n requestContentType?: RequestContentType\n}\n\nexport type QuerySerializer = (params: QueryParams) => string\n";
|
|
4572
|
-
|
|
4573
|
-
var httpClientPromiseTypesSource = "import type { Request } from './common-types'\n\nexport type HttpClient<RequestParams = never> = {\n request: <Data>(req: Request, params?: RequestParams) => Promise<Data>\n}\n";
|
|
4574
|
-
|
|
4575
4614
|
var __defProp$8 = Object.defineProperty;
|
|
4576
4615
|
var __defNormalProp$8 = (obj, key, value) => key in obj ? __defProp$8(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
4577
4616
|
var __publicField$8 = (obj, key, value) => __defNormalProp$8(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
@@ -4580,10 +4619,14 @@ class TsCodegen {
|
|
|
4580
4619
|
this.typeExprCodegen = typeExprCodegen;
|
|
4581
4620
|
__publicField$8(this, "usedTypeRefs", []);
|
|
4582
4621
|
__publicField$8(this, "usedTypeRefKeys", /* @__PURE__ */ new Set());
|
|
4583
|
-
__publicField$8(this, "
|
|
4622
|
+
__publicField$8(this, "typeExpr", (typeExpr) => {
|
|
4584
4623
|
this.collectTypeRefsFromExpr(typeExpr);
|
|
4585
4624
|
return this.typeExprCodegen.toCode(typeExpr).code;
|
|
4586
4625
|
});
|
|
4626
|
+
__publicField$8(this, "declaration", (name, declaration) => {
|
|
4627
|
+
this.collectTypeRefsFromDeclaration(declaration);
|
|
4628
|
+
return this.toDeclarationCode(name, declaration);
|
|
4629
|
+
});
|
|
4587
4630
|
__publicField$8(this, "toChunk", (name, code, exports$1) => {
|
|
4588
4631
|
const exportedSymbols = new Set(exports$1);
|
|
4589
4632
|
const refs = this.getTypeRefs().filter((ref) => {
|
|
@@ -4599,6 +4642,26 @@ class TsCodegen {
|
|
|
4599
4642
|
__publicField$8(this, "getTypeRefs", () => {
|
|
4600
4643
|
return this.usedTypeRefs.map((ref) => ({ ...ref }));
|
|
4601
4644
|
});
|
|
4645
|
+
__publicField$8(this, "toDeclarationCode", (name, declaration) => {
|
|
4646
|
+
switch (declaration.kind) {
|
|
4647
|
+
case "typeAlias":
|
|
4648
|
+
return `type ${name} = ${this.typeExprCodegen.toCode(declaration.typeExpr).code}`;
|
|
4649
|
+
case "enum": {
|
|
4650
|
+
const members = declaration.members.map(
|
|
4651
|
+
(member) => ` ${member.name} = ${JSON.stringify(member.value)}`
|
|
4652
|
+
).join(",\n");
|
|
4653
|
+
return `enum ${name} {
|
|
4654
|
+
${members}
|
|
4655
|
+
}`;
|
|
4656
|
+
}
|
|
4657
|
+
default: {
|
|
4658
|
+
const exhaustive = declaration;
|
|
4659
|
+
throw new Error(
|
|
4660
|
+
`Unsupported TypeScript declaration: ${String(exhaustive)}`
|
|
4661
|
+
);
|
|
4662
|
+
}
|
|
4663
|
+
}
|
|
4664
|
+
});
|
|
4602
4665
|
}
|
|
4603
4666
|
collectTypeRef(typeRef) {
|
|
4604
4667
|
if (typeRef.kind === "builtin") return;
|
|
@@ -4607,6 +4670,11 @@ class TsCodegen {
|
|
|
4607
4670
|
this.usedTypeRefKeys.add(key);
|
|
4608
4671
|
this.usedTypeRefs.push({ ...typeRef });
|
|
4609
4672
|
}
|
|
4673
|
+
collectTypeRefsFromDeclaration(declaration) {
|
|
4674
|
+
if (declaration.kind === "typeAlias") {
|
|
4675
|
+
this.collectTypeRefsFromExpr(declaration.typeExpr);
|
|
4676
|
+
}
|
|
4677
|
+
}
|
|
4610
4678
|
collectTypeRefsFromExpr(typeExpr) {
|
|
4611
4679
|
if (typeExpr.kind === "reference") {
|
|
4612
4680
|
this.collectTypeRef(typeExpr.ref);
|
|
@@ -4687,9 +4755,33 @@ const RESERVED_WORDS = /* @__PURE__ */ new Set([
|
|
|
4687
4755
|
"with",
|
|
4688
4756
|
"yield"
|
|
4689
4757
|
]);
|
|
4690
|
-
const TS_IDENTIFIER_PATTERN = /^[$A-Z_][0-9A-Z_$]*$/i;
|
|
4758
|
+
const TS_IDENTIFIER_PATTERN$1 = /^[$A-Z_][0-9A-Z_$]*$/i;
|
|
4691
4759
|
const isTsIdentifier = (value) => {
|
|
4692
|
-
return TS_IDENTIFIER_PATTERN.test(value);
|
|
4760
|
+
return TS_IDENTIFIER_PATTERN$1.test(value);
|
|
4761
|
+
};
|
|
4762
|
+
const splitIdentifierWords = (value) => {
|
|
4763
|
+
return value.replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[^a-zA-Z0-9]+/).filter(Boolean);
|
|
4764
|
+
};
|
|
4765
|
+
const toTsCamelIdentifier = (value, opts = {}) => {
|
|
4766
|
+
const words = splitIdentifierWords(value);
|
|
4767
|
+
if (!words.length) {
|
|
4768
|
+
throw new Error(
|
|
4769
|
+
`Could not derive a TypeScript identifier from "${value}".`
|
|
4770
|
+
);
|
|
4771
|
+
}
|
|
4772
|
+
const [firstWord, ...restWords] = words;
|
|
4773
|
+
let identifier = [
|
|
4774
|
+
firstWord[0].toLowerCase() + firstWord.substring(1),
|
|
4775
|
+
...restWords.map((word) => word[0].toUpperCase() + word.substring(1))
|
|
4776
|
+
].join("");
|
|
4777
|
+
if (/^[0-9]/.test(identifier)) {
|
|
4778
|
+
const prefix = opts.leadingDigitPrefix ?? "value";
|
|
4779
|
+
identifier = `${prefix}${identifier[0].toUpperCase()}${identifier.substring(1)}`;
|
|
4780
|
+
}
|
|
4781
|
+
if (RESERVED_WORDS.has(identifier)) {
|
|
4782
|
+
identifier = `${identifier}_`;
|
|
4783
|
+
}
|
|
4784
|
+
return identifier;
|
|
4693
4785
|
};
|
|
4694
4786
|
const toTsPropertyKey = (value) => {
|
|
4695
4787
|
return isTsIdentifier(value) ? value : JSON.stringify(value);
|
|
@@ -4806,1002 +4898,1268 @@ class TsCodegenFactory {
|
|
|
4806
4898
|
}
|
|
4807
4899
|
}
|
|
4808
4900
|
|
|
4901
|
+
const buildPathParamBindings = (paramNames) => {
|
|
4902
|
+
const usedBindings = /* @__PURE__ */ new Set();
|
|
4903
|
+
return paramNames.reduce((acc, paramName) => {
|
|
4904
|
+
acc[paramName] = toUniqueBindingName(paramName, usedBindings);
|
|
4905
|
+
return acc;
|
|
4906
|
+
}, {});
|
|
4907
|
+
};
|
|
4908
|
+
const toUniqueBindingName = (paramName, usedBindings) => {
|
|
4909
|
+
let base = toBindingBase(paramName);
|
|
4910
|
+
if (isReservedWord(base)) {
|
|
4911
|
+
base = `${base}_param`;
|
|
4912
|
+
}
|
|
4913
|
+
if (!usedBindings.has(base)) {
|
|
4914
|
+
usedBindings.add(base);
|
|
4915
|
+
return base;
|
|
4916
|
+
}
|
|
4917
|
+
let suffix = 2;
|
|
4918
|
+
let candidate = `${base}_${suffix}`;
|
|
4919
|
+
while (usedBindings.has(candidate)) {
|
|
4920
|
+
suffix += 1;
|
|
4921
|
+
candidate = `${base}_${suffix}`;
|
|
4922
|
+
}
|
|
4923
|
+
usedBindings.add(candidate);
|
|
4924
|
+
return candidate;
|
|
4925
|
+
};
|
|
4926
|
+
const toBindingBase = (paramName) => {
|
|
4927
|
+
if (isIdentifier(paramName)) {
|
|
4928
|
+
return paramName;
|
|
4929
|
+
}
|
|
4930
|
+
const sanitized = paramName.replace(/[^0-9A-Za-z_$]/g, "_");
|
|
4931
|
+
if (!sanitized.length) {
|
|
4932
|
+
return "pathParam";
|
|
4933
|
+
}
|
|
4934
|
+
if (/^[A-Za-z_$]/.test(sanitized)) {
|
|
4935
|
+
return sanitized;
|
|
4936
|
+
}
|
|
4937
|
+
return `_${sanitized}`;
|
|
4938
|
+
};
|
|
4939
|
+
const isIdentifier = (value) => {
|
|
4940
|
+
return /^[$A-Z_][0-9A-Z_$]*$/i.test(value);
|
|
4941
|
+
};
|
|
4942
|
+
const isReservedWord = (value) => {
|
|
4943
|
+
return RESERVED_WORDS.has(value);
|
|
4944
|
+
};
|
|
4945
|
+
|
|
4809
4946
|
var __defProp$5 = Object.defineProperty;
|
|
4810
4947
|
var __defNormalProp$5 = (obj, key, value) => key in obj ? __defProp$5(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
4811
4948
|
var __publicField$5 = (obj, key, value) => __defNormalProp$5(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4812
|
-
class
|
|
4813
|
-
constructor(
|
|
4814
|
-
this.schemas = schemas;
|
|
4949
|
+
class ParamsConstructor {
|
|
4950
|
+
constructor(options) {
|
|
4815
4951
|
this.options = options;
|
|
4816
|
-
__publicField$5(this, "
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
|
|
4952
|
+
__publicField$5(this, "process", (params, httpPath) => {
|
|
4953
|
+
const pathParamSpec = validatePathParams(params, httpPath);
|
|
4954
|
+
const pathParamBindings = pathParamSpec?.bindings;
|
|
4955
|
+
const funParams = params.flatMap(
|
|
4956
|
+
(param) => this.renderOperationParams(param, pathParamSpec)
|
|
4957
|
+
);
|
|
4958
|
+
const hasBody = params.some((p) => p.kind === "body");
|
|
4959
|
+
const hasQuery = params.some((p) => p.kind === "query");
|
|
4960
|
+
const path = this.renderPathTemplateLiteral(
|
|
4961
|
+
httpPath,
|
|
4962
|
+
this.options.paramNames.path,
|
|
4963
|
+
pathParamBindings
|
|
4964
|
+
);
|
|
4965
|
+
return {
|
|
4966
|
+
funParams,
|
|
4967
|
+
path,
|
|
4968
|
+
hasBody,
|
|
4969
|
+
hasQuery
|
|
4970
|
+
};
|
|
4825
4971
|
});
|
|
4826
|
-
__publicField$5(this, "
|
|
4827
|
-
if (!
|
|
4828
|
-
return
|
|
4972
|
+
__publicField$5(this, "renderOperationParams", (param, pathParamSpec) => {
|
|
4973
|
+
if (param.kind !== "path" || !pathParamSpec) {
|
|
4974
|
+
return [
|
|
4975
|
+
{
|
|
4976
|
+
identifier: this.paramName(param),
|
|
4977
|
+
typeExpr: param.typeExpr
|
|
4978
|
+
}
|
|
4979
|
+
];
|
|
4829
4980
|
}
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
|
|
4981
|
+
if (this.options.pathParamsStyle === "positional") {
|
|
4982
|
+
return pathParamSpec.placeholderNames.map((name) => ({
|
|
4983
|
+
identifier: pathParamSpec.bindings[name] ?? name,
|
|
4984
|
+
typeExpr: pathParamSpec.propertiesByName[name].typeExpr
|
|
4985
|
+
}));
|
|
4833
4986
|
}
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
|
|
4987
|
+
return [
|
|
4988
|
+
{
|
|
4989
|
+
identifier: this.renderPathParamDestructuring(
|
|
4990
|
+
pathParamSpec.bindings
|
|
4991
|
+
),
|
|
4992
|
+
typeExpr: param.typeExpr
|
|
4839
4993
|
}
|
|
4840
|
-
|
|
4841
|
-
const projected = this.fromSchemaCore(schema);
|
|
4842
|
-
const result = schema.nullable === true ? this.withNullable(projected) : projected;
|
|
4843
|
-
this.schemaCache.set(schema, result);
|
|
4844
|
-
if (typeof schema.$ref === "string") {
|
|
4845
|
-
this.refCache.set(this.refCacheKey(schema), result);
|
|
4846
|
-
}
|
|
4847
|
-
return result;
|
|
4994
|
+
];
|
|
4848
4995
|
});
|
|
4849
|
-
__publicField$5(this, "
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
if (member.kind === "inline" && member.expr.node === "union") {
|
|
4860
|
-
flatMembers.push(...member.expr.members);
|
|
4861
|
-
} else {
|
|
4862
|
-
flatMembers.push(member);
|
|
4863
|
-
}
|
|
4864
|
-
}
|
|
4865
|
-
if (flatMembers.length === 1) {
|
|
4866
|
-
return flatMembers[0];
|
|
4867
|
-
}
|
|
4868
|
-
return {
|
|
4869
|
-
kind: "inline",
|
|
4870
|
-
expr: {
|
|
4871
|
-
node: "union",
|
|
4872
|
-
members: flatMembers
|
|
4996
|
+
__publicField$5(this, "renderPathTemplateLiteral", (path, pathParamVarName, pathParamBindings) => {
|
|
4997
|
+
const escapedPath = path.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
|
|
4998
|
+
const pathWithParams = escapedPath.replace(
|
|
4999
|
+
/\{([^}]+)\}/g,
|
|
5000
|
+
(_, paramName) => {
|
|
5001
|
+
const binding = pathParamBindings?.[paramName];
|
|
5002
|
+
if (binding !== void 0) {
|
|
5003
|
+
return `\${${binding}}`;
|
|
5004
|
+
}
|
|
5005
|
+
return `\${${pathParamVarName}[${JSON.stringify(paramName)}]}`;
|
|
4873
5006
|
}
|
|
4874
|
-
|
|
5007
|
+
);
|
|
5008
|
+
return `\`${pathWithParams}\``;
|
|
4875
5009
|
});
|
|
4876
|
-
__publicField$5(this, "
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
}
|
|
4882
|
-
}));
|
|
4883
|
-
__publicField$5(this, "ref", (id) => {
|
|
4884
|
-
const name = this.refNameFromPath(id);
|
|
4885
|
-
return {
|
|
4886
|
-
kind: "reference",
|
|
4887
|
-
ref: {
|
|
4888
|
-
kind: "internal",
|
|
4889
|
-
name
|
|
5010
|
+
__publicField$5(this, "renderPathParamDestructuring", (pathParamBindings) => {
|
|
5011
|
+
const members = Object.entries(pathParamBindings).map(
|
|
5012
|
+
([paramName, binding]) => {
|
|
5013
|
+
const key = toTsPropertyKey(paramName);
|
|
5014
|
+
return binding === paramName ? key : `${key}: ${binding}`;
|
|
4890
5015
|
}
|
|
4891
|
-
};
|
|
4892
|
-
});
|
|
4893
|
-
__publicField$5(this, "builtinRef", (name) => ({
|
|
4894
|
-
kind: "reference",
|
|
4895
|
-
ref: {
|
|
4896
|
-
kind: "builtin",
|
|
4897
|
-
name
|
|
4898
|
-
}
|
|
4899
|
-
}));
|
|
4900
|
-
__publicField$5(this, "refNameFromPath", (ref) => {
|
|
4901
|
-
const parts = ref.split("/");
|
|
4902
|
-
const name = parts[parts.length - 1];
|
|
4903
|
-
if (!name) {
|
|
4904
|
-
throw new Error(`Unsupported schema reference: ${ref}`);
|
|
4905
|
-
}
|
|
4906
|
-
return name;
|
|
4907
|
-
});
|
|
4908
|
-
__publicField$5(this, "refCacheKey", (schema) => {
|
|
4909
|
-
return `${schema.$ref}|nullable:${schema.nullable === true}`;
|
|
4910
|
-
});
|
|
4911
|
-
__publicField$5(this, "isUnconstrainedSchema", (schema) => {
|
|
4912
|
-
const meaningfulKeys = Object.keys(schema).filter(
|
|
4913
|
-
(key) => key !== "nullable"
|
|
4914
5016
|
);
|
|
4915
|
-
return
|
|
5017
|
+
return `{ ${members.join(", ")} }`;
|
|
4916
5018
|
});
|
|
4917
|
-
__publicField$5(this, "
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
};
|
|
4932
|
-
this.schemaDefinitionsById.set(refId, definition);
|
|
4933
|
-
this.schemaDefinitions.push(definition);
|
|
4934
|
-
} finally {
|
|
4935
|
-
this.schemaDefinitionsInProgress.delete(refId);
|
|
5019
|
+
__publicField$5(this, "paramName", (p) => {
|
|
5020
|
+
switch (p.kind) {
|
|
5021
|
+
case "body":
|
|
5022
|
+
return this.options.paramNames.body;
|
|
5023
|
+
case "query":
|
|
5024
|
+
return this.options.paramNames.query;
|
|
5025
|
+
case "path":
|
|
5026
|
+
return this.options.paramNames.path;
|
|
5027
|
+
default: {
|
|
5028
|
+
const exhaustive = p;
|
|
5029
|
+
throw new Error(
|
|
5030
|
+
`Unsupported operation parameter kind: ${String(exhaustive)}`
|
|
5031
|
+
);
|
|
5032
|
+
}
|
|
4936
5033
|
}
|
|
4937
5034
|
});
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
5035
|
+
}
|
|
5036
|
+
}
|
|
5037
|
+
const extractPathPlaceholderNames = (pathTemplate) => {
|
|
5038
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5039
|
+
const names = [];
|
|
5040
|
+
const matcher = /\{([^}]+)\}/g;
|
|
5041
|
+
let match = matcher.exec(pathTemplate);
|
|
5042
|
+
while (match) {
|
|
5043
|
+
const name = match[1];
|
|
5044
|
+
if (!seen.has(name)) {
|
|
5045
|
+
seen.add(name);
|
|
5046
|
+
names.push(name);
|
|
5047
|
+
}
|
|
5048
|
+
match = matcher.exec(pathTemplate);
|
|
5049
|
+
}
|
|
5050
|
+
return names;
|
|
5051
|
+
};
|
|
5052
|
+
const isPathParam = (param) => {
|
|
5053
|
+
return param.kind === "path";
|
|
5054
|
+
};
|
|
5055
|
+
const isInlineObjectType = (typeExpr) => typeExpr.kind === "inline" && typeExpr.expr.node === "object";
|
|
5056
|
+
const renderNameList = (items) => {
|
|
5057
|
+
const values = [...items];
|
|
5058
|
+
if (!values.length) {
|
|
5059
|
+
return "(none)";
|
|
5060
|
+
}
|
|
5061
|
+
return values.map((v) => JSON.stringify(v)).join(", ");
|
|
5062
|
+
};
|
|
5063
|
+
const validatePathParams = (params, httpPath) => {
|
|
5064
|
+
const placeholders = extractPathPlaceholderNames(httpPath);
|
|
5065
|
+
const placeholderSet = new Set(placeholders);
|
|
5066
|
+
const pathParams = params.filter(isPathParam);
|
|
5067
|
+
if (pathParams.length > 1) {
|
|
5068
|
+
throw new Error(
|
|
5069
|
+
`Path "${httpPath}" has ${pathParams.length} path parameter objects; expected at most one.`
|
|
5070
|
+
);
|
|
5071
|
+
}
|
|
5072
|
+
const pathParam = pathParams[0];
|
|
5073
|
+
if (!pathParam) {
|
|
5074
|
+
if (placeholderSet.size > 0) {
|
|
5075
|
+
throw new Error(
|
|
5076
|
+
`Path "${httpPath}" is missing a path parameter object for placeholders: ${renderNameList(
|
|
5077
|
+
placeholderSet
|
|
5078
|
+
)}.`
|
|
4941
5079
|
);
|
|
4942
|
-
}
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
5080
|
+
}
|
|
5081
|
+
return void 0;
|
|
5082
|
+
}
|
|
5083
|
+
const pathParamExpr = pathParam.typeExpr.expr;
|
|
5084
|
+
if (pathParamExpr.additionalProperties !== void 0 && pathParamExpr.additionalProperties !== false) {
|
|
5085
|
+
throw new Error(
|
|
5086
|
+
`Path "${httpPath}" path parameter object must not declare additionalProperties.`
|
|
5087
|
+
);
|
|
5088
|
+
}
|
|
5089
|
+
const nestedObjectProps = pathParamExpr.properties.filter((prop) => isInlineObjectType(prop.typeExpr)).map((prop) => prop.name);
|
|
5090
|
+
if (nestedObjectProps.length > 0) {
|
|
5091
|
+
throw new Error(
|
|
5092
|
+
`Path "${httpPath}" path parameters must be depth-1. Nested object properties: ${renderNameList(
|
|
5093
|
+
nestedObjectProps
|
|
5094
|
+
)}.`
|
|
5095
|
+
);
|
|
5096
|
+
}
|
|
5097
|
+
const optionalPathProps = pathParamExpr.properties.filter((prop) => !prop.required).map((prop) => prop.name);
|
|
5098
|
+
if (optionalPathProps.length > 0) {
|
|
5099
|
+
throw new Error(
|
|
5100
|
+
`Path "${httpPath}" path parameters must all be required. Optional properties: ${renderNameList(
|
|
5101
|
+
optionalPathProps
|
|
5102
|
+
)}.`
|
|
5103
|
+
);
|
|
5104
|
+
}
|
|
5105
|
+
const declaredNames = pathParamExpr.properties.map((prop) => prop.name);
|
|
5106
|
+
const declaredNameSet = new Set(declaredNames);
|
|
5107
|
+
const missingInObject = [...placeholderSet].filter(
|
|
5108
|
+
(name) => !declaredNameSet.has(name)
|
|
5109
|
+
);
|
|
5110
|
+
const missingInPath = declaredNames.filter(
|
|
5111
|
+
(name) => !placeholderSet.has(name)
|
|
5112
|
+
);
|
|
5113
|
+
if (missingInObject.length > 0 || missingInPath.length > 0) {
|
|
5114
|
+
throw new Error(
|
|
5115
|
+
`Path "${httpPath}" placeholders and path parameter object keys must match exactly. Missing in object: ${renderNameList(
|
|
5116
|
+
missingInObject
|
|
5117
|
+
)}. Missing in path: ${renderNameList(missingInPath)}.`
|
|
5118
|
+
);
|
|
5119
|
+
}
|
|
5120
|
+
const bindings = buildPathParamBindings(placeholders);
|
|
5121
|
+
const propertiesByName = Object.fromEntries(
|
|
5122
|
+
pathParamExpr.properties.map((prop) => [prop.name, prop])
|
|
5123
|
+
);
|
|
5124
|
+
return {
|
|
5125
|
+
placeholderNames: placeholders,
|
|
5126
|
+
bindings,
|
|
5127
|
+
propertiesByName
|
|
5128
|
+
};
|
|
5129
|
+
};
|
|
5130
|
+
|
|
5131
|
+
const EMPTY_LINE_MARKER = "/*__EMPTY_LINE_MARKER__*/";
|
|
5132
|
+
|
|
5133
|
+
var __defProp$4 = Object.defineProperty;
|
|
5134
|
+
var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
5135
|
+
var __publicField$4 = (obj, key, value) => __defNormalProp$4(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5136
|
+
const REQUEST_PARAMS_TYPE = "RequestParams";
|
|
5137
|
+
const REQUEST_PARAMS_DEFAULT = "never";
|
|
5138
|
+
const REQUEST_PARAMS_ARG = "params";
|
|
5139
|
+
class ApiClientCodegen {
|
|
5140
|
+
constructor(opts, tsCodegenFactory) {
|
|
5141
|
+
this.opts = opts;
|
|
5142
|
+
this.tsCodegenFactory = tsCodegenFactory;
|
|
5143
|
+
__publicField$4(this, "generate", (apiName, operations) => {
|
|
5144
|
+
const tsCodegen = this.tsCodegenFactory.forNewChunk();
|
|
5145
|
+
const httpClientType = tsCodegen.typeExpr(this.opts.httpClient.typeName);
|
|
5146
|
+
let clientType = httpClientType;
|
|
5147
|
+
let classTypeParams = "";
|
|
5148
|
+
if (this.opts.withRequestParams) {
|
|
5149
|
+
clientType = `${httpClientType}<${REQUEST_PARAMS_TYPE}>`;
|
|
5150
|
+
classTypeParams = `<${REQUEST_PARAMS_TYPE} = ${REQUEST_PARAMS_DEFAULT}>`;
|
|
4946
5151
|
}
|
|
4947
|
-
|
|
5152
|
+
const methodsCode = Array.isArray(operations) ? this.opsCode(tsCodegen, operations) : this.groupsOpsCode(tsCodegen, operations);
|
|
5153
|
+
const code = `
|
|
5154
|
+
export class ${apiName}${classTypeParams} {
|
|
5155
|
+
constructor(private readonly client: ${clientType}) {}
|
|
5156
|
+
|
|
5157
|
+
${methodsCode}
|
|
5158
|
+
}
|
|
5159
|
+
`;
|
|
5160
|
+
return tsCodegen.toChunk("api", code, [apiName]);
|
|
4948
5161
|
});
|
|
4949
|
-
__publicField$
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
4958
|
-
|
|
4959
|
-
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
}
|
|
5162
|
+
__publicField$4(this, "groupsOpsCode", (codegen, groupedOps) => Object.entries(groupedOps).map(([groupName, ops]) => {
|
|
5163
|
+
const methodsCode = this.opsCode(codegen, ops, "object");
|
|
5164
|
+
return `
|
|
5165
|
+
${toTsPropertyKey(groupName)} = {
|
|
5166
|
+
${methodsCode}
|
|
5167
|
+
}
|
|
5168
|
+
`;
|
|
5169
|
+
}).join("\n\n"));
|
|
5170
|
+
__publicField$4(this, "opsCode", (codegen, operations, target = "class") => {
|
|
5171
|
+
const separator = target === "object" ? `,
|
|
5172
|
+
${EMPTY_LINE_MARKER}
|
|
5173
|
+
` : "\n\n";
|
|
5174
|
+
const methodsCode = operations.map((op) => this.renderOp(op, codegen, target)).join(separator);
|
|
5175
|
+
return methodsCode;
|
|
4964
5176
|
});
|
|
4965
|
-
__publicField$
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
}
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
});
|
|
4981
|
-
__publicField$5(this, "projectObjectType", (schema) => {
|
|
4982
|
-
const requiredRaw = schema.required;
|
|
4983
|
-
if (requiredRaw !== void 0 && !Array.isArray(requiredRaw)) {
|
|
4984
|
-
throw new Error(
|
|
4985
|
-
"Unsupported object schema: required must be an array"
|
|
4986
|
-
);
|
|
4987
|
-
}
|
|
4988
|
-
const required = new Set(
|
|
4989
|
-
(requiredRaw ?? []).map((item) => {
|
|
4990
|
-
if (typeof item !== "string") {
|
|
4991
|
-
throw new Error(
|
|
4992
|
-
"Unsupported object schema: required items must be strings"
|
|
4993
|
-
);
|
|
4994
|
-
}
|
|
4995
|
-
return item;
|
|
4996
|
-
})
|
|
5177
|
+
__publicField$4(this, "renderOp", (op, tsCodegen, target) => {
|
|
5178
|
+
const { signature: sig, http } = op;
|
|
5179
|
+
const invocationVars = this.opts.httpClient.invocation.vars;
|
|
5180
|
+
const paramNames = {
|
|
5181
|
+
path: "path",
|
|
5182
|
+
query: invocationVars.query,
|
|
5183
|
+
body: invocationVars.body
|
|
5184
|
+
};
|
|
5185
|
+
const paramsHelper = new ParamsConstructor({
|
|
5186
|
+
paramNames,
|
|
5187
|
+
pathParamsStyle: this.opts.pathParamsStyle
|
|
5188
|
+
});
|
|
5189
|
+
const { path, funParams, hasBody, hasQuery } = paramsHelper.process(
|
|
5190
|
+
sig.parameters,
|
|
5191
|
+
http.path
|
|
4997
5192
|
);
|
|
4998
|
-
const
|
|
4999
|
-
|
|
5000
|
-
throw new Error(
|
|
5001
|
-
"Unsupported object schema: properties must be an object"
|
|
5002
|
-
);
|
|
5003
|
-
}
|
|
5004
|
-
const properties = Object.entries(propertiesRaw ?? {}).map(
|
|
5005
|
-
([name, propertySchema]) => {
|
|
5006
|
-
if (!this.isSchemaNode(propertySchema)) {
|
|
5007
|
-
throw new Error(
|
|
5008
|
-
`Unsupported object property schema for "${name}"`
|
|
5009
|
-
);
|
|
5010
|
-
}
|
|
5011
|
-
return {
|
|
5012
|
-
name,
|
|
5013
|
-
required: this.isRequiredProperty(
|
|
5014
|
-
name,
|
|
5015
|
-
required,
|
|
5016
|
-
propertySchema
|
|
5017
|
-
),
|
|
5018
|
-
typeExpr: this.fromSchema(propertySchema)
|
|
5019
|
-
};
|
|
5020
|
-
}
|
|
5193
|
+
const params = funParams.map(
|
|
5194
|
+
(r) => `${r.identifier}: ${tsCodegen.typeExpr(r.typeExpr)}`
|
|
5021
5195
|
);
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
if (typeof schema.additionalProperties === "boolean") {
|
|
5025
|
-
additionalProperties = schema.additionalProperties;
|
|
5026
|
-
} else if (this.isSchemaNode(schema.additionalProperties)) {
|
|
5027
|
-
additionalProperties = this.fromSchema(
|
|
5028
|
-
schema.additionalProperties
|
|
5029
|
-
);
|
|
5030
|
-
} else {
|
|
5031
|
-
throw new Error(
|
|
5032
|
-
"Unsupported object schema: additionalProperties must be boolean or schema object"
|
|
5033
|
-
);
|
|
5034
|
-
}
|
|
5035
|
-
}
|
|
5036
|
-
return {
|
|
5037
|
-
kind: "inline",
|
|
5038
|
-
expr: {
|
|
5039
|
-
node: "object",
|
|
5040
|
-
properties,
|
|
5041
|
-
additionalProperties
|
|
5042
|
-
}
|
|
5043
|
-
};
|
|
5044
|
-
});
|
|
5045
|
-
__publicField$5(this, "isRequiredProperty", (name, required, propertySchema) => {
|
|
5046
|
-
if (required.has(name)) {
|
|
5047
|
-
return true;
|
|
5048
|
-
}
|
|
5049
|
-
if (!this.options.quirks?.swaggerTsApiRequiredBooleans) {
|
|
5050
|
-
return false;
|
|
5051
|
-
}
|
|
5052
|
-
return propertySchema.required === true;
|
|
5053
|
-
});
|
|
5054
|
-
__publicField$5(this, "mapSchemaArrayMembers", (value, kind) => {
|
|
5055
|
-
if (!Array.isArray(value) || !value.length) {
|
|
5056
|
-
throw new Error(
|
|
5057
|
-
`Unsupported schema: ${kind} must be a non-empty array`
|
|
5058
|
-
);
|
|
5196
|
+
if (this.opts.withRequestParams) {
|
|
5197
|
+
params.push(`${REQUEST_PARAMS_ARG}?: ${REQUEST_PARAMS_TYPE}`);
|
|
5059
5198
|
}
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5199
|
+
const paramsCode = params.join(", ");
|
|
5200
|
+
const responseType = tsCodegen.typeExpr(sig.returnType);
|
|
5201
|
+
const returnType = this.wrapInResponseWrapper(sig.returnType, tsCodegen);
|
|
5202
|
+
return this.renderFunctionCode({
|
|
5203
|
+
funName: sig.name,
|
|
5204
|
+
responseType,
|
|
5205
|
+
returnType,
|
|
5206
|
+
params: paramsCode,
|
|
5207
|
+
path,
|
|
5208
|
+
method: http.method,
|
|
5209
|
+
hasQuery,
|
|
5210
|
+
hasBody,
|
|
5211
|
+
requestContentType: http.requestContentType,
|
|
5212
|
+
responseFormat: http.responseFormat,
|
|
5213
|
+
requestParamsVar: this.opts.withRequestParams ? REQUEST_PARAMS_ARG : void 0,
|
|
5214
|
+
unwrap: this.opts.unwrap,
|
|
5215
|
+
target
|
|
5067
5216
|
});
|
|
5068
5217
|
});
|
|
5069
|
-
__publicField$
|
|
5070
|
-
return
|
|
5218
|
+
__publicField$4(this, "wrapInResponseWrapper", (typeExpr, tsCodegen) => {
|
|
5219
|
+
return tsCodegen.typeExpr({
|
|
5220
|
+
...this.opts.httpClient.responseWrapper,
|
|
5221
|
+
typeArgs: [typeExpr]
|
|
5222
|
+
});
|
|
5071
5223
|
});
|
|
5072
|
-
__publicField$
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
}
|
|
5108
|
-
if (!this.isSchemaNode(schema.items)) {
|
|
5109
|
-
throw new Error(
|
|
5110
|
-
"Unsupported array schema: items must be a schema"
|
|
5111
|
-
);
|
|
5112
|
-
}
|
|
5113
|
-
return {
|
|
5114
|
-
kind: "inline",
|
|
5115
|
-
expr: {
|
|
5116
|
-
node: "array",
|
|
5117
|
-
element: this.fromSchema(schema.items)
|
|
5118
|
-
}
|
|
5119
|
-
};
|
|
5120
|
-
}
|
|
5121
|
-
if (schema.type === "object") {
|
|
5122
|
-
return this.projectObjectType(schema);
|
|
5123
|
-
}
|
|
5124
|
-
if (schema.oneOf !== void 0) {
|
|
5125
|
-
return this.union(this.mapSchemaArrayMembers(schema.oneOf, "oneOf"));
|
|
5126
|
-
}
|
|
5127
|
-
if (schema.anyOf !== void 0) {
|
|
5128
|
-
return this.union(this.mapSchemaArrayMembers(schema.anyOf, "anyOf"));
|
|
5129
|
-
}
|
|
5130
|
-
if (schema.allOf !== void 0) {
|
|
5131
|
-
return {
|
|
5132
|
-
kind: "inline",
|
|
5133
|
-
expr: {
|
|
5134
|
-
node: "intersection",
|
|
5135
|
-
members: this.mapSchemaArrayMembers(schema.allOf, "allOf")
|
|
5136
|
-
}
|
|
5137
|
-
};
|
|
5138
|
-
}
|
|
5139
|
-
if (this.isBinaryFileSchema(schema)) {
|
|
5140
|
-
return this.builtinRef("File");
|
|
5141
|
-
}
|
|
5142
|
-
if (this.isScalarName(schema.type)) {
|
|
5143
|
-
return this.scalar(schema.type);
|
|
5144
|
-
}
|
|
5145
|
-
throw new Error(
|
|
5146
|
-
`Unsupported schema construct: ${JSON.stringify(schema, null, 2)}`
|
|
5147
|
-
);
|
|
5224
|
+
__publicField$4(this, "renderFunctionCode", ({
|
|
5225
|
+
funName,
|
|
5226
|
+
responseType,
|
|
5227
|
+
returnType,
|
|
5228
|
+
params,
|
|
5229
|
+
path,
|
|
5230
|
+
method,
|
|
5231
|
+
hasQuery,
|
|
5232
|
+
hasBody,
|
|
5233
|
+
requestContentType,
|
|
5234
|
+
responseFormat,
|
|
5235
|
+
requestParamsVar,
|
|
5236
|
+
unwrap,
|
|
5237
|
+
target
|
|
5238
|
+
}) => {
|
|
5239
|
+
let invocation = this.opts.httpClient.invocation.expr({
|
|
5240
|
+
funName,
|
|
5241
|
+
returnType,
|
|
5242
|
+
responseType,
|
|
5243
|
+
params,
|
|
5244
|
+
path,
|
|
5245
|
+
method,
|
|
5246
|
+
hasQuery,
|
|
5247
|
+
hasBody,
|
|
5248
|
+
requestContentType,
|
|
5249
|
+
responseFormat,
|
|
5250
|
+
requestParamsVar,
|
|
5251
|
+
unwrap
|
|
5252
|
+
});
|
|
5253
|
+
invocation = invocation.replaceAll("\n", "");
|
|
5254
|
+
const operator = target === "object" ? ":" : "=";
|
|
5255
|
+
const returnTypeCode = this.opts.httpClient.inferMethodReturnType ? "" : `:${returnType}`;
|
|
5256
|
+
return `${funName} ${operator} (${params})${returnTypeCode} =>
|
|
5257
|
+
this.client${invocation}
|
|
5258
|
+
`;
|
|
5148
5259
|
});
|
|
5149
5260
|
}
|
|
5150
5261
|
}
|
|
5151
5262
|
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
}
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5263
|
+
const promiseHttpClientCodegenSpec = () => ({
|
|
5264
|
+
typeName: {
|
|
5265
|
+
kind: "reference",
|
|
5266
|
+
ref: {
|
|
5267
|
+
kind: "internal",
|
|
5268
|
+
name: "HttpClient"
|
|
5269
|
+
}
|
|
5270
|
+
},
|
|
5271
|
+
responseWrapper: {
|
|
5272
|
+
kind: "reference",
|
|
5273
|
+
ref: {
|
|
5274
|
+
kind: "builtin",
|
|
5275
|
+
name: "Promise"
|
|
5276
|
+
}
|
|
5277
|
+
},
|
|
5278
|
+
inferMethodReturnType: true,
|
|
5279
|
+
invocation: {
|
|
5280
|
+
vars: {
|
|
5281
|
+
body: "body",
|
|
5282
|
+
query: "query"
|
|
5283
|
+
},
|
|
5284
|
+
expr: ({
|
|
5285
|
+
responseType,
|
|
5286
|
+
path,
|
|
5287
|
+
method,
|
|
5288
|
+
hasBody,
|
|
5289
|
+
hasQuery,
|
|
5290
|
+
requestContentType,
|
|
5291
|
+
responseFormat,
|
|
5292
|
+
requestParamsVar,
|
|
5293
|
+
unwrap
|
|
5294
|
+
}) => `.request<${responseType}>({
|
|
5295
|
+
method: '${method}',
|
|
5296
|
+
path: ${path}
|
|
5297
|
+
${hasQuery ? `,
|
|
5298
|
+
query` : ""}
|
|
5299
|
+
${hasBody ? `,
|
|
5300
|
+
body` : ""}
|
|
5301
|
+
${requestContentType ? `,
|
|
5302
|
+
requestContentType: '${requestContentType}'` : ""}
|
|
5303
|
+
${responseFormat ? `,
|
|
5304
|
+
format: '${responseFormat}'` : ""}
|
|
5305
|
+
}${requestParamsVar ? `,
|
|
5306
|
+
${requestParamsVar}` : ""})${unwrap ? `.then(res => res.body)` : ""}
|
|
5307
|
+
`
|
|
5189
5308
|
}
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
const
|
|
5193
|
-
|
|
5194
|
-
|
|
5309
|
+
});
|
|
5310
|
+
|
|
5311
|
+
const mergeChunks = (name, chunks, options = {}) => {
|
|
5312
|
+
if (chunks.length === 0) {
|
|
5313
|
+
if (options.allowEmpty) {
|
|
5314
|
+
return {
|
|
5315
|
+
name,
|
|
5316
|
+
code: "export {}",
|
|
5317
|
+
exports: [],
|
|
5318
|
+
refs: []
|
|
5319
|
+
};
|
|
5320
|
+
}
|
|
5321
|
+
throw new Error("Cannot merge empty chunk list.");
|
|
5322
|
+
}
|
|
5323
|
+
const orderedChunks = options.preserveChunkOrder ? chunks : orderByInternalDependencies(chunks);
|
|
5324
|
+
const mergedCode = orderedChunks.map((chunk) => chunk.code).join("\n\n");
|
|
5325
|
+
const exportedSymbols = /* @__PURE__ */ new Set();
|
|
5326
|
+
const mergedExports = [];
|
|
5327
|
+
for (const chunk of orderedChunks) {
|
|
5328
|
+
for (const symbol of chunk.exports) {
|
|
5329
|
+
if (exportedSymbols.has(symbol)) continue;
|
|
5330
|
+
exportedSymbols.add(symbol);
|
|
5331
|
+
mergedExports.push(symbol);
|
|
5332
|
+
}
|
|
5333
|
+
}
|
|
5334
|
+
const seenRefKeys = /* @__PURE__ */ new Set();
|
|
5335
|
+
const mergedRefs = orderedChunks.flatMap((chunk) => chunk.refs).filter((ref) => {
|
|
5336
|
+
if (ref.kind === "internal" && exportedSymbols.has(ref.name)) {
|
|
5337
|
+
return false;
|
|
5338
|
+
}
|
|
5339
|
+
const key = ref.kind === "internal" ? `internal:${ref.name}` : ref.kind === "external" ? `external:${ref.package}:${ref.name}` : `builtin:${ref.name}`;
|
|
5340
|
+
if (seenRefKeys.has(key)) return false;
|
|
5341
|
+
seenRefKeys.add(key);
|
|
5342
|
+
return true;
|
|
5343
|
+
});
|
|
5195
5344
|
return {
|
|
5196
|
-
|
|
5197
|
-
|
|
5345
|
+
name,
|
|
5346
|
+
code: mergedCode,
|
|
5347
|
+
exports: mergedExports,
|
|
5348
|
+
refs: mergedRefs
|
|
5198
5349
|
};
|
|
5199
5350
|
};
|
|
5200
|
-
const
|
|
5201
|
-
const
|
|
5202
|
-
|
|
5351
|
+
const orderByInternalDependencies = (chunks) => {
|
|
5352
|
+
const chunkIndex = /* @__PURE__ */ new Map();
|
|
5353
|
+
const exportOwnerBySymbol = /* @__PURE__ */ new Map();
|
|
5354
|
+
chunks.forEach((chunk, index) => {
|
|
5355
|
+
chunkIndex.set(chunk.name, index);
|
|
5356
|
+
for (const symbol of chunk.exports) {
|
|
5357
|
+
if (exportOwnerBySymbol.has(symbol)) {
|
|
5358
|
+
throw new Error(
|
|
5359
|
+
`Assertion error. Symbol "${symbol}" exported multiple times while ordering chunks.`
|
|
5360
|
+
);
|
|
5361
|
+
}
|
|
5362
|
+
exportOwnerBySymbol.set(symbol, chunk);
|
|
5363
|
+
}
|
|
5364
|
+
});
|
|
5365
|
+
const dependentsByProvider = /* @__PURE__ */ new Map();
|
|
5366
|
+
const inDegreeByChunk = /* @__PURE__ */ new Map();
|
|
5367
|
+
for (const chunk of chunks) {
|
|
5368
|
+
dependentsByProvider.set(chunk, /* @__PURE__ */ new Set());
|
|
5369
|
+
inDegreeByChunk.set(chunk, 0);
|
|
5370
|
+
}
|
|
5371
|
+
for (const chunk of chunks) {
|
|
5372
|
+
const providers = /* @__PURE__ */ new Set();
|
|
5373
|
+
for (const ref of chunk.refs) {
|
|
5374
|
+
if (ref.kind !== "internal") continue;
|
|
5375
|
+
const provider = exportOwnerBySymbol.get(ref.name);
|
|
5376
|
+
if (!provider || provider === chunk) continue;
|
|
5377
|
+
providers.add(provider);
|
|
5378
|
+
}
|
|
5379
|
+
for (const provider of providers) {
|
|
5380
|
+
dependentsByProvider.get(provider).add(chunk);
|
|
5381
|
+
inDegreeByChunk.set(chunk, inDegreeByChunk.get(chunk) + 1);
|
|
5382
|
+
}
|
|
5383
|
+
}
|
|
5384
|
+
const ready = chunks.filter((chunk) => inDegreeByChunk.get(chunk) === 0).sort((a, b) => chunkIndex.get(a.name) - chunkIndex.get(b.name));
|
|
5385
|
+
const ordered = [];
|
|
5386
|
+
while (ready.length > 0) {
|
|
5387
|
+
const provider = ready.shift();
|
|
5388
|
+
ordered.push(provider);
|
|
5389
|
+
for (const dependent of dependentsByProvider.get(provider)) {
|
|
5390
|
+
const nextInDegree = inDegreeByChunk.get(dependent) - 1;
|
|
5391
|
+
inDegreeByChunk.set(dependent, nextInDegree);
|
|
5392
|
+
if (nextInDegree === 0) {
|
|
5393
|
+
ready.push(dependent);
|
|
5394
|
+
}
|
|
5395
|
+
}
|
|
5396
|
+
ready.sort((a, b) => chunkIndex.get(a.name) - chunkIndex.get(b.name));
|
|
5397
|
+
}
|
|
5398
|
+
if (ordered.length !== chunks.length) {
|
|
5399
|
+
const cycleChunkNames = chunks.filter((chunk) => inDegreeByChunk.get(chunk) > 0).map((chunk) => chunk.name).join(", ");
|
|
5400
|
+
throw new Error(
|
|
5401
|
+
`Cannot order chunks with cyclic internal dependencies: ${cycleChunkNames}`
|
|
5402
|
+
);
|
|
5403
|
+
}
|
|
5404
|
+
return ordered;
|
|
5203
5405
|
};
|
|
5204
|
-
|
|
5205
|
-
|
|
5406
|
+
|
|
5407
|
+
var httpClientCommonTypesSource = "export type KnownRequestContentType =\n | 'application/json'\n | 'multipart/form-data'\n | 'application/x-www-form-urlencoded'\n\nexport type RequestContentType = KnownRequestContentType | string\n\nexport type QueryValue =\n | string\n | number\n | boolean\n | null\n | undefined\n | QueryValue[]\n | Record<string, any>\n // Empty schema support\n | unknown\n\nexport type QueryParams = Record<string, QueryValue>\n\nexport type Request = {\n path: string\n method: 'GET' | 'POST' | 'PUT' | 'DELETE'\n format?: 'json' | 'document'\n headers?: Record<string, string>\n query?: QueryParams\n body?: any\n requestContentType?: RequestContentType\n}\n\nexport type HttpResponse<Data> = {\n body: Data\n headers: Record<string, string | string[]>\n status: number\n}\n\nexport type QuerySerializer = (params: QueryParams) => string\n";
|
|
5408
|
+
|
|
5409
|
+
var httpClientPromiseTypesSource = "import type { HttpResponse, Request } from './common-types'\n\nexport type HttpClient<RequestParams = never> = {\n request: <Data>(\n req: Request,\n params?: RequestParams\n ) => Promise<HttpResponse<Data>>\n}\n";
|
|
5410
|
+
|
|
5411
|
+
const commonTypesCode = httpClientCommonTypesSource;
|
|
5412
|
+
const promiseHttpClientCode = httpClientPromiseTypesSource;
|
|
5413
|
+
const stripImports = (code) => code.replace(/^import[\s\S]*?from\s+['"][^'"]+['"]\s*;?\n?/gm, "").trim();
|
|
5414
|
+
const stripRequestParams = (code) => code.replace(/<RequestParams\s*=\s*never>/g, "").replace(/,\s*params\?:\s*RequestParams/g, "");
|
|
5415
|
+
const getHttpClientCode = (opts) => [
|
|
5416
|
+
commonTypesCode,
|
|
5417
|
+
opts.withRequestParams ? promiseHttpClientCode : stripRequestParams(promiseHttpClientCode)
|
|
5418
|
+
].map(stripImports).join("\n\n");
|
|
5419
|
+
const provideHttpClientCode = (gen, opts) => gen.toChunk(
|
|
5420
|
+
"HttpClient",
|
|
5421
|
+
getHttpClientCode({
|
|
5422
|
+
withRequestParams: opts.withRequestParams
|
|
5423
|
+
}),
|
|
5424
|
+
["HttpRequest", "HttpResponse", "HttpClient"]
|
|
5425
|
+
);
|
|
5426
|
+
|
|
5427
|
+
const generateTsCode = (clientName, projectResult, opts) => {
|
|
5428
|
+
const tsCodegenFac = new TsCodegenFactory();
|
|
5429
|
+
const { schemaDefinitions } = projectResult;
|
|
5430
|
+
const types = [...schemaDefinitions].sort((a, b) => a.name.localeCompare(b.name)).map((def) => {
|
|
5431
|
+
const cg = tsCodegenFac.forNewChunk();
|
|
5432
|
+
const code = `export ${cg.declaration(def.name, def.declaration)}`;
|
|
5433
|
+
return cg.toChunk(def.name, code, [def.name]);
|
|
5434
|
+
});
|
|
5435
|
+
const clientCodegen = new ApiClientCodegen(
|
|
5436
|
+
{ httpClient: promiseHttpClientCodegenSpec(), ...opts },
|
|
5437
|
+
tsCodegenFac
|
|
5438
|
+
);
|
|
5439
|
+
const generated = clientCodegen.generate(
|
|
5440
|
+
clientName,
|
|
5441
|
+
groupsOpsByTag(projectResult.operations)
|
|
5442
|
+
);
|
|
5443
|
+
const clientChunk = {
|
|
5444
|
+
...generated,
|
|
5445
|
+
name: `${clientName}.client`
|
|
5446
|
+
};
|
|
5447
|
+
const httpClientChunk = provideHttpClientCode(
|
|
5448
|
+
tsCodegenFac.forNewChunk(),
|
|
5449
|
+
opts
|
|
5450
|
+
);
|
|
5451
|
+
const typesChunk = mergeChunks(`${clientName}.types`, types, {
|
|
5452
|
+
preserveChunkOrder: true,
|
|
5453
|
+
allowEmpty: true
|
|
5454
|
+
});
|
|
5455
|
+
const indexChunk = createIndexChunk(clientName);
|
|
5456
|
+
const all = [typesChunk, httpClientChunk, clientChunk, indexChunk];
|
|
5457
|
+
return {
|
|
5458
|
+
all,
|
|
5459
|
+
typesName: typesChunk.name,
|
|
5460
|
+
promiseWrappersNames: [httpClientChunk.name, clientChunk.name]
|
|
5461
|
+
};
|
|
5206
5462
|
};
|
|
5207
|
-
const
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5463
|
+
const createIndexChunk = (clientName) => ({
|
|
5464
|
+
name: "index",
|
|
5465
|
+
code: [
|
|
5466
|
+
`export { ${clientName} } from './${clientName}.client'`,
|
|
5467
|
+
`export * from './${clientName}.types'`,
|
|
5468
|
+
"export * from './HttpClient'"
|
|
5469
|
+
].join("\n"),
|
|
5470
|
+
exports: [],
|
|
5471
|
+
refs: []
|
|
5472
|
+
});
|
|
5473
|
+
const groupsOpsByTag = (operations) => {
|
|
5474
|
+
const groupedOps = {};
|
|
5475
|
+
const rawTagsByNormalizedName = /* @__PURE__ */ new Map();
|
|
5476
|
+
operations.forEach((operation) => {
|
|
5477
|
+
const rawTag = operation.tags.find((tag) => tag.trim())?.trim() || "root";
|
|
5478
|
+
let groupName;
|
|
5479
|
+
try {
|
|
5480
|
+
groupName = toTsCamelIdentifier(rawTag, {
|
|
5481
|
+
leadingDigitPrefix: "tag"
|
|
5482
|
+
});
|
|
5483
|
+
} catch {
|
|
5484
|
+
throw new Error(
|
|
5485
|
+
`Invalid OpenAPI tag "${rawTag}": could not derive a client group name.`
|
|
5486
|
+
);
|
|
5228
5487
|
}
|
|
5229
|
-
|
|
5488
|
+
const existingRawTag = rawTagsByNormalizedName.get(groupName);
|
|
5489
|
+
if (existingRawTag && existingRawTag !== rawTag) {
|
|
5490
|
+
throw new Error(
|
|
5491
|
+
`Tag naming collision: "${existingRawTag}" and "${rawTag}" both normalize to "${groupName}". Rename one of these OpenAPI tags to continue.`
|
|
5492
|
+
);
|
|
5493
|
+
}
|
|
5494
|
+
rawTagsByNormalizedName.set(groupName, rawTag);
|
|
5495
|
+
const bucket = groupedOps[groupName] ?? [];
|
|
5496
|
+
bucket.push(operation.op);
|
|
5497
|
+
groupedOps[groupName] = bucket;
|
|
5498
|
+
});
|
|
5499
|
+
return groupedOps;
|
|
5230
5500
|
};
|
|
5231
|
-
|
|
5232
|
-
|
|
5501
|
+
|
|
5502
|
+
var __defProp$3 = Object.defineProperty;
|
|
5503
|
+
var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
5504
|
+
var __publicField$3 = (obj, key, value) => __defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5505
|
+
const TS_IDENTIFIER_PATTERN = /^[$A-Z_][0-9A-Z_$]*$/i;
|
|
5506
|
+
class ToTypeExprConverter {
|
|
5507
|
+
constructor(schemas = {}, options) {
|
|
5508
|
+
this.schemas = schemas;
|
|
5233
5509
|
this.options = options;
|
|
5234
|
-
__publicField$
|
|
5235
|
-
__publicField$
|
|
5236
|
-
__publicField$
|
|
5237
|
-
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
if (!operationName) {
|
|
5244
|
-
throw new Error(
|
|
5245
|
-
`Operation id is empty for ${operation.method.toUpperCase()} ${operation.path}`
|
|
5246
|
-
);
|
|
5247
|
-
}
|
|
5248
|
-
if (this.seenOperationIds.has(operationName)) {
|
|
5249
|
-
throw new Error(`Duplicate operation id: ${operationName}`);
|
|
5250
|
-
}
|
|
5251
|
-
return this.projectOne(operationName, operation);
|
|
5510
|
+
__publicField$3(this, "schemaCache", /* @__PURE__ */ new WeakMap());
|
|
5511
|
+
__publicField$3(this, "refCache", /* @__PURE__ */ new Map());
|
|
5512
|
+
__publicField$3(this, "schemaDefinitions", []);
|
|
5513
|
+
__publicField$3(this, "schemaDefinitionsById", /* @__PURE__ */ new Map());
|
|
5514
|
+
__publicField$3(this, "schemaDefinitionsInProgress", /* @__PURE__ */ new Set());
|
|
5515
|
+
__publicField$3(this, "getSchemaDefinitions", () => this.schemaDefinitions);
|
|
5516
|
+
__publicField$3(this, "registerAllSchemaDefinitions", () => {
|
|
5517
|
+
Object.keys(this.schemas).forEach((schemaName) => {
|
|
5518
|
+
this.ensureSchemaDefinition(`#/components/schemas/${schemaName}`);
|
|
5252
5519
|
});
|
|
5253
|
-
return {
|
|
5254
|
-
operations,
|
|
5255
|
-
schemaDefinitions: this.typeExprConverter.getSchemaDefinitions()
|
|
5256
|
-
};
|
|
5257
5520
|
});
|
|
5258
|
-
__publicField$
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
const pathParameter = this.projectPathParameter(operation);
|
|
5262
|
-
const queryParameter = this.projectQueryParameter(operation);
|
|
5263
|
-
const bodyParameter = this.projectBodyParameter(operation);
|
|
5264
|
-
if (pathParameter) parameters.push(pathParameter);
|
|
5265
|
-
if (queryParameter) parameters.push(queryParameter);
|
|
5266
|
-
if (bodyParameter) parameters.push(bodyParameter);
|
|
5267
|
-
return {
|
|
5268
|
-
op: {
|
|
5269
|
-
signature: {
|
|
5270
|
-
name: operationName,
|
|
5271
|
-
parameters,
|
|
5272
|
-
returnType: this.projectReturnType(operation)
|
|
5273
|
-
},
|
|
5274
|
-
http: {
|
|
5275
|
-
method: toHttpMethodUpper(operation.method),
|
|
5276
|
-
path: operation.path,
|
|
5277
|
-
requestContentType: this.projectRequestContentType(
|
|
5278
|
-
operation.requestBody
|
|
5279
|
-
),
|
|
5280
|
-
responseFormat: this.projectResponseFormat(operation)
|
|
5281
|
-
}
|
|
5282
|
-
},
|
|
5283
|
-
tags: [...operation.tags]
|
|
5284
|
-
};
|
|
5285
|
-
});
|
|
5286
|
-
__publicField$4(this, "projectRequestContentType", (requestBody) => {
|
|
5287
|
-
const { mediaType } = getPreferredMediaTypeEntry(
|
|
5288
|
-
requestBody?.content ?? {}
|
|
5289
|
-
);
|
|
5290
|
-
switch (mediaType) {
|
|
5291
|
-
case "application/json":
|
|
5292
|
-
case "multipart/form-data":
|
|
5293
|
-
case "application/x-www-form-urlencoded":
|
|
5294
|
-
return mediaType;
|
|
5295
|
-
default:
|
|
5296
|
-
return void 0;
|
|
5521
|
+
__publicField$3(this, "fromTypeExpr", (schema) => {
|
|
5522
|
+
if (!schema) {
|
|
5523
|
+
return this.scalar("unknown");
|
|
5297
5524
|
}
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
if (!selectedResponses.length) {
|
|
5302
|
-
return void 0;
|
|
5525
|
+
const bySchema = this.schemaCache.get(schema);
|
|
5526
|
+
if (bySchema) {
|
|
5527
|
+
return bySchema;
|
|
5303
5528
|
}
|
|
5304
|
-
if (
|
|
5305
|
-
|
|
5529
|
+
if (typeof schema.$ref === "string") {
|
|
5530
|
+
const byRef = this.refCache.get(this.refCacheKey(schema));
|
|
5531
|
+
if (byRef) {
|
|
5532
|
+
this.schemaCache.set(schema, byRef);
|
|
5533
|
+
return byRef;
|
|
5534
|
+
}
|
|
5306
5535
|
}
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
return "json";
|
|
5536
|
+
const projected = this.fromSchemaCore(schema);
|
|
5537
|
+
const result = schema.nullable === true ? this.withNullable(projected) : projected;
|
|
5538
|
+
this.schemaCache.set(schema, result);
|
|
5539
|
+
if (typeof schema.$ref === "string") {
|
|
5540
|
+
this.refCache.set(this.refCacheKey(schema), result);
|
|
5313
5541
|
}
|
|
5314
|
-
return
|
|
5542
|
+
return result;
|
|
5315
5543
|
});
|
|
5316
|
-
__publicField$
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5544
|
+
__publicField$3(this, "scalar", (name) => ({
|
|
5545
|
+
kind: "inline",
|
|
5546
|
+
expr: {
|
|
5547
|
+
node: "scalar",
|
|
5548
|
+
name
|
|
5549
|
+
}
|
|
5550
|
+
}));
|
|
5551
|
+
__publicField$3(this, "union", (members) => {
|
|
5552
|
+
const flatMembers = [];
|
|
5553
|
+
for (const member of members) {
|
|
5554
|
+
if (member.kind === "inline" && member.expr.node === "union") {
|
|
5555
|
+
flatMembers.push(...member.expr.members);
|
|
5556
|
+
} else {
|
|
5557
|
+
flatMembers.push(member);
|
|
5558
|
+
}
|
|
5559
|
+
}
|
|
5560
|
+
if (flatMembers.length === 1) {
|
|
5561
|
+
return flatMembers[0];
|
|
5562
|
+
}
|
|
5324
5563
|
return {
|
|
5325
5564
|
kind: "inline",
|
|
5326
5565
|
expr: {
|
|
5327
|
-
node: "
|
|
5328
|
-
|
|
5566
|
+
node: "union",
|
|
5567
|
+
members: flatMembers
|
|
5329
5568
|
}
|
|
5330
5569
|
};
|
|
5331
5570
|
});
|
|
5332
|
-
__publicField$
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
throw new Error("Unsupported path parameters: all must be required");
|
|
5571
|
+
__publicField$3(this, "literal", (value) => ({
|
|
5572
|
+
kind: "inline",
|
|
5573
|
+
expr: {
|
|
5574
|
+
node: "literal",
|
|
5575
|
+
value
|
|
5338
5576
|
}
|
|
5577
|
+
}));
|
|
5578
|
+
__publicField$3(this, "ref", (id) => {
|
|
5579
|
+
const name = this.refNameFromPath(id);
|
|
5339
5580
|
return {
|
|
5340
|
-
kind: "
|
|
5341
|
-
|
|
5342
|
-
|
|
5581
|
+
kind: "reference",
|
|
5582
|
+
ref: {
|
|
5583
|
+
kind: "internal",
|
|
5584
|
+
name
|
|
5585
|
+
}
|
|
5343
5586
|
};
|
|
5344
5587
|
});
|
|
5345
|
-
__publicField$
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5588
|
+
__publicField$3(this, "builtinRef", (name) => ({
|
|
5589
|
+
kind: "reference",
|
|
5590
|
+
ref: {
|
|
5591
|
+
kind: "builtin",
|
|
5592
|
+
name
|
|
5593
|
+
}
|
|
5594
|
+
}));
|
|
5595
|
+
__publicField$3(this, "refNameFromPath", (ref) => {
|
|
5596
|
+
const parts = ref.split("/");
|
|
5597
|
+
const name = parts[parts.length - 1];
|
|
5598
|
+
if (!name) {
|
|
5599
|
+
throw new Error(`Unsupported schema reference: ${ref}`);
|
|
5600
|
+
}
|
|
5601
|
+
return name;
|
|
5602
|
+
});
|
|
5603
|
+
__publicField$3(this, "refCacheKey", (schema) => {
|
|
5604
|
+
return `${schema.$ref}|nullable:${schema.nullable === true}`;
|
|
5605
|
+
});
|
|
5606
|
+
__publicField$3(this, "isUnconstrainedSchema", (schema) => {
|
|
5607
|
+
const meaningfulKeys = Object.keys(schema).filter(
|
|
5608
|
+
(key) => key !== "nullable"
|
|
5609
|
+
);
|
|
5610
|
+
return meaningfulKeys.length === 0;
|
|
5611
|
+
});
|
|
5612
|
+
__publicField$3(this, "ensureSchemaDefinition", (refId) => {
|
|
5613
|
+
if (this.schemaDefinitionsById.has(refId) || this.schemaDefinitionsInProgress.has(refId)) {
|
|
5614
|
+
return;
|
|
5615
|
+
}
|
|
5616
|
+
const schemaName = this.refNameFromPath(refId);
|
|
5617
|
+
const schema = this.schemas[schemaName];
|
|
5618
|
+
if (!schema) {
|
|
5619
|
+
return;
|
|
5620
|
+
}
|
|
5621
|
+
this.schemaDefinitionsInProgress.add(refId);
|
|
5622
|
+
try {
|
|
5623
|
+
const definition = {
|
|
5624
|
+
name: schemaName,
|
|
5625
|
+
declaration: this.toDeclaration(schema)
|
|
5626
|
+
};
|
|
5627
|
+
this.schemaDefinitionsById.set(refId, definition);
|
|
5628
|
+
this.schemaDefinitions.push(definition);
|
|
5629
|
+
} finally {
|
|
5630
|
+
this.schemaDefinitionsInProgress.delete(refId);
|
|
5631
|
+
}
|
|
5632
|
+
});
|
|
5633
|
+
__publicField$3(this, "isSchemaNode", (value) => {
|
|
5634
|
+
return Boolean(
|
|
5635
|
+
value && typeof value === "object" && !Array.isArray(value)
|
|
5636
|
+
);
|
|
5637
|
+
});
|
|
5638
|
+
__publicField$3(this, "toLiteralValue", (value) => {
|
|
5639
|
+
if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
5640
|
+
return value;
|
|
5641
|
+
}
|
|
5642
|
+
throw new Error(`Unsupported literal value: ${String(value)}`);
|
|
5643
|
+
});
|
|
5644
|
+
__publicField$3(this, "toDeclaration", (schema) => {
|
|
5645
|
+
const enumDeclaration = this.toEnumDeclaration(schema);
|
|
5646
|
+
if (enumDeclaration) {
|
|
5647
|
+
return enumDeclaration;
|
|
5648
|
+
}
|
|
5349
5649
|
return {
|
|
5350
|
-
kind: "
|
|
5351
|
-
|
|
5352
|
-
typeExpr: this.projectParameterGroupTypeExpr(params)
|
|
5650
|
+
kind: "typeAlias",
|
|
5651
|
+
typeExpr: this.fromTypeExpr(schema)
|
|
5353
5652
|
};
|
|
5354
5653
|
});
|
|
5355
|
-
__publicField$
|
|
5356
|
-
|
|
5357
|
-
|
|
5654
|
+
__publicField$3(this, "toEnumDeclaration", (schema) => {
|
|
5655
|
+
if (this.options.enumStyle !== "enum" || !Array.isArray(schema.enum)) {
|
|
5656
|
+
return void 0;
|
|
5657
|
+
}
|
|
5658
|
+
const valueType = this.getEnumValueType(schema.enum);
|
|
5659
|
+
if (!valueType) {
|
|
5660
|
+
return void 0;
|
|
5661
|
+
}
|
|
5358
5662
|
return {
|
|
5359
|
-
kind: "
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
this.selectRequestBodySchema(requestBody)
|
|
5363
|
-
)
|
|
5663
|
+
kind: "enum",
|
|
5664
|
+
valueType,
|
|
5665
|
+
members: this.toEnumMembers(schema.enum)
|
|
5364
5666
|
};
|
|
5365
5667
|
});
|
|
5366
|
-
__publicField$
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
return this.typeExprConverter.scalar("void");
|
|
5668
|
+
__publicField$3(this, "getEnumValueType", (values) => {
|
|
5669
|
+
if (!values.length) {
|
|
5670
|
+
return void 0;
|
|
5370
5671
|
}
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5672
|
+
if (values.every((value) => typeof value === "string")) {
|
|
5673
|
+
return "string";
|
|
5674
|
+
}
|
|
5675
|
+
if (values.every((value) => typeof value === "number")) {
|
|
5676
|
+
return "number";
|
|
5677
|
+
}
|
|
5678
|
+
return void 0;
|
|
5679
|
+
});
|
|
5680
|
+
__publicField$3(this, "toEnumMembers", (values) => {
|
|
5681
|
+
const usedNames = /* @__PURE__ */ new Map();
|
|
5682
|
+
return values.map((value, index) => {
|
|
5683
|
+
const enumValue = value;
|
|
5684
|
+
const baseName = this.toEnumMemberBaseName(enumValue, index);
|
|
5685
|
+
const nextCount = (usedNames.get(baseName) ?? 0) + 1;
|
|
5686
|
+
usedNames.set(baseName, nextCount);
|
|
5687
|
+
return {
|
|
5688
|
+
name: nextCount === 1 ? baseName : `${baseName}${nextCount}`,
|
|
5689
|
+
value: enumValue
|
|
5690
|
+
};
|
|
5377
5691
|
});
|
|
5378
|
-
return this.typeExprConverter.union(responseTypes);
|
|
5379
5692
|
});
|
|
5380
|
-
__publicField$
|
|
5381
|
-
if (
|
|
5382
|
-
|
|
5693
|
+
__publicField$3(this, "toEnumMemberBaseName", (value, index) => {
|
|
5694
|
+
if (typeof value === "string" && this.options.uppercaseEnumKeys) {
|
|
5695
|
+
const compatName = this.toCompatEnumMemberName(value);
|
|
5696
|
+
if (compatName) {
|
|
5697
|
+
return compatName;
|
|
5698
|
+
}
|
|
5699
|
+
}
|
|
5700
|
+
if (typeof value === "string" && TS_IDENTIFIER_PATTERN.test(value) && !RESERVED_WORDS.has(value)) {
|
|
5701
|
+
return value;
|
|
5702
|
+
}
|
|
5703
|
+
if (typeof value === "number" && Number.isInteger(value) && value >= 0) {
|
|
5704
|
+
return `Value${value}`;
|
|
5705
|
+
}
|
|
5706
|
+
const pascalName = toPascalCaseIdentifier(String(value));
|
|
5707
|
+
return pascalName === "Api" ? `Value${index + 1}` : pascalName;
|
|
5383
5708
|
});
|
|
5384
|
-
__publicField$
|
|
5385
|
-
|
|
5709
|
+
__publicField$3(this, "toCompatEnumMemberName", (value) => {
|
|
5710
|
+
if (/^([A-Z_]{1,})$/g.test(value)) {
|
|
5711
|
+
return value;
|
|
5712
|
+
}
|
|
5713
|
+
const pascalName = toPascalCaseIdentifier(value);
|
|
5714
|
+
return pascalName === "Api" ? void 0 : pascalName;
|
|
5386
5715
|
});
|
|
5387
|
-
__publicField$
|
|
5388
|
-
|
|
5716
|
+
__publicField$3(this, "isScalarName", (value) => {
|
|
5717
|
+
switch (value) {
|
|
5718
|
+
case "string":
|
|
5719
|
+
case "number":
|
|
5720
|
+
case "boolean":
|
|
5721
|
+
case "integer":
|
|
5722
|
+
case "null":
|
|
5723
|
+
case "unknown":
|
|
5724
|
+
case "any":
|
|
5725
|
+
case "never":
|
|
5726
|
+
case "void":
|
|
5727
|
+
return true;
|
|
5728
|
+
default:
|
|
5729
|
+
return false;
|
|
5730
|
+
}
|
|
5389
5731
|
});
|
|
5390
|
-
this
|
|
5391
|
-
|
|
5732
|
+
__publicField$3(this, "hasNullMember", (expr) => {
|
|
5733
|
+
if (expr.kind !== "inline") return false;
|
|
5734
|
+
if (expr.expr.node === "scalar") {
|
|
5735
|
+
return expr.expr.name === "null";
|
|
5736
|
+
}
|
|
5737
|
+
if (expr.expr.node === "union") {
|
|
5738
|
+
return expr.expr.members.some((member) => this.hasNullMember(member));
|
|
5739
|
+
}
|
|
5740
|
+
return false;
|
|
5392
5741
|
});
|
|
5393
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
suffix += 1;
|
|
5416
|
-
candidate = `${base}_${suffix}`;
|
|
5417
|
-
}
|
|
5418
|
-
usedBindings.add(candidate);
|
|
5419
|
-
return candidate;
|
|
5420
|
-
};
|
|
5421
|
-
const toBindingBase = (paramName) => {
|
|
5422
|
-
if (isIdentifier(paramName)) {
|
|
5423
|
-
return paramName;
|
|
5424
|
-
}
|
|
5425
|
-
const sanitized = paramName.replace(/[^0-9A-Za-z_$]/g, "_");
|
|
5426
|
-
if (!sanitized.length) {
|
|
5427
|
-
return "pathParam";
|
|
5428
|
-
}
|
|
5429
|
-
if (/^[A-Za-z_$]/.test(sanitized)) {
|
|
5430
|
-
return sanitized;
|
|
5431
|
-
}
|
|
5432
|
-
return `_${sanitized}`;
|
|
5433
|
-
};
|
|
5434
|
-
const isIdentifier = (value) => {
|
|
5435
|
-
return /^[$A-Z_][0-9A-Z_$]*$/i.test(value);
|
|
5436
|
-
};
|
|
5437
|
-
const isReservedWord = (value) => {
|
|
5438
|
-
return RESERVED_WORDS.has(value);
|
|
5439
|
-
};
|
|
5440
|
-
|
|
5441
|
-
var __defProp$3 = Object.defineProperty;
|
|
5442
|
-
var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
5443
|
-
var __publicField$3 = (obj, key, value) => __defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5444
|
-
class ParamsConstructor {
|
|
5445
|
-
constructor(options) {
|
|
5446
|
-
this.options = options;
|
|
5447
|
-
__publicField$3(this, "process", (params, httpPath) => {
|
|
5448
|
-
const pathParamSpec = validatePathParams(params, httpPath);
|
|
5449
|
-
const pathParamBindings = pathParamSpec?.bindings;
|
|
5450
|
-
const funParams = params.flatMap(
|
|
5451
|
-
(param) => this.renderOperationParams(param, pathParamSpec)
|
|
5742
|
+
__publicField$3(this, "withNullable", (expr) => {
|
|
5743
|
+
if (this.hasNullMember(expr)) {
|
|
5744
|
+
return expr;
|
|
5745
|
+
}
|
|
5746
|
+
return this.union([expr, this.scalar("null")]);
|
|
5747
|
+
});
|
|
5748
|
+
__publicField$3(this, "projectObjectType", (schema) => {
|
|
5749
|
+
const requiredRaw = schema.required;
|
|
5750
|
+
if (requiredRaw !== void 0 && !Array.isArray(requiredRaw)) {
|
|
5751
|
+
throw new Error(
|
|
5752
|
+
"Unsupported object schema: required must be an array"
|
|
5753
|
+
);
|
|
5754
|
+
}
|
|
5755
|
+
const required = new Set(
|
|
5756
|
+
(requiredRaw ?? []).map((item) => {
|
|
5757
|
+
if (typeof item !== "string") {
|
|
5758
|
+
throw new Error(
|
|
5759
|
+
"Unsupported object schema: required items must be strings"
|
|
5760
|
+
);
|
|
5761
|
+
}
|
|
5762
|
+
return item;
|
|
5763
|
+
})
|
|
5452
5764
|
);
|
|
5453
|
-
const
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5765
|
+
const propertiesRaw = schema.properties;
|
|
5766
|
+
if (propertiesRaw !== void 0 && (!propertiesRaw || typeof propertiesRaw !== "object" || Array.isArray(propertiesRaw))) {
|
|
5767
|
+
throw new Error(
|
|
5768
|
+
"Unsupported object schema: properties must be an object"
|
|
5769
|
+
);
|
|
5770
|
+
}
|
|
5771
|
+
const properties = Object.entries(propertiesRaw ?? {}).map(
|
|
5772
|
+
([name, propertySchema]) => {
|
|
5773
|
+
if (!this.isSchemaNode(propertySchema)) {
|
|
5774
|
+
throw new Error(
|
|
5775
|
+
`Unsupported object property schema for "${name}"`
|
|
5776
|
+
);
|
|
5777
|
+
}
|
|
5778
|
+
return {
|
|
5779
|
+
name,
|
|
5780
|
+
required: this.isRequiredProperty(
|
|
5781
|
+
name,
|
|
5782
|
+
required,
|
|
5783
|
+
propertySchema
|
|
5784
|
+
),
|
|
5785
|
+
typeExpr: this.fromTypeExpr(propertySchema)
|
|
5786
|
+
};
|
|
5787
|
+
}
|
|
5459
5788
|
);
|
|
5789
|
+
let additionalProperties;
|
|
5790
|
+
if (schema.additionalProperties !== void 0) {
|
|
5791
|
+
if (typeof schema.additionalProperties === "boolean") {
|
|
5792
|
+
additionalProperties = schema.additionalProperties;
|
|
5793
|
+
} else if (this.isSchemaNode(schema.additionalProperties)) {
|
|
5794
|
+
additionalProperties = this.fromTypeExpr(
|
|
5795
|
+
schema.additionalProperties
|
|
5796
|
+
);
|
|
5797
|
+
} else {
|
|
5798
|
+
throw new Error(
|
|
5799
|
+
"Unsupported object schema: additionalProperties must be boolean or schema object"
|
|
5800
|
+
);
|
|
5801
|
+
}
|
|
5802
|
+
}
|
|
5460
5803
|
return {
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5804
|
+
kind: "inline",
|
|
5805
|
+
expr: {
|
|
5806
|
+
node: "object",
|
|
5807
|
+
properties,
|
|
5808
|
+
additionalProperties
|
|
5809
|
+
}
|
|
5465
5810
|
};
|
|
5466
5811
|
});
|
|
5467
|
-
__publicField$3(this, "
|
|
5468
|
-
if (
|
|
5469
|
-
return
|
|
5470
|
-
{
|
|
5471
|
-
identifier: this.paramName(param),
|
|
5472
|
-
typeExpr: param.typeExpr
|
|
5473
|
-
}
|
|
5474
|
-
];
|
|
5812
|
+
__publicField$3(this, "isRequiredProperty", (name, required, propertySchema) => {
|
|
5813
|
+
if (required.has(name)) {
|
|
5814
|
+
return true;
|
|
5475
5815
|
}
|
|
5476
|
-
if (this.options.
|
|
5477
|
-
return
|
|
5478
|
-
identifier: pathParamSpec.bindings[name] ?? name,
|
|
5479
|
-
typeExpr: pathParamSpec.propertiesByName[name].typeExpr
|
|
5480
|
-
}));
|
|
5816
|
+
if (!this.options.swaggerTsApiRequiredBooleans) {
|
|
5817
|
+
return false;
|
|
5481
5818
|
}
|
|
5482
|
-
return
|
|
5483
|
-
{
|
|
5484
|
-
identifier: this.renderPathParamDestructuring(
|
|
5485
|
-
pathParamSpec.bindings
|
|
5486
|
-
),
|
|
5487
|
-
typeExpr: param.typeExpr
|
|
5488
|
-
}
|
|
5489
|
-
];
|
|
5819
|
+
return propertySchema.required === true;
|
|
5490
5820
|
});
|
|
5491
|
-
__publicField$3(this, "
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
|
|
5821
|
+
__publicField$3(this, "mapSchemaArrayMembers", (value, kind) => {
|
|
5822
|
+
if (!Array.isArray(value) || !value.length) {
|
|
5823
|
+
throw new Error(
|
|
5824
|
+
`Unsupported schema: ${kind} must be a non-empty array`
|
|
5825
|
+
);
|
|
5826
|
+
}
|
|
5827
|
+
return value.map((entry, index) => {
|
|
5828
|
+
if (!this.isSchemaNode(entry)) {
|
|
5829
|
+
throw new Error(
|
|
5830
|
+
`Unsupported schema: ${kind}[${index}] must be a schema object`
|
|
5831
|
+
);
|
|
5501
5832
|
}
|
|
5502
|
-
|
|
5503
|
-
|
|
5833
|
+
return this.fromTypeExpr(entry);
|
|
5834
|
+
});
|
|
5504
5835
|
});
|
|
5505
|
-
__publicField$3(this, "
|
|
5506
|
-
|
|
5507
|
-
([paramName, binding]) => {
|
|
5508
|
-
const key = toTsPropertyKey(paramName);
|
|
5509
|
-
return binding === paramName ? key : `${key}: ${binding}`;
|
|
5510
|
-
}
|
|
5511
|
-
);
|
|
5512
|
-
return `{ ${members.join(", ")} }`;
|
|
5836
|
+
__publicField$3(this, "isBinaryFileSchema", (schema) => {
|
|
5837
|
+
return schema.type === "string" && schema.format === "binary";
|
|
5513
5838
|
});
|
|
5514
|
-
__publicField$3(this, "
|
|
5515
|
-
|
|
5516
|
-
|
|
5517
|
-
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
|
|
5839
|
+
__publicField$3(this, "fromSchemaCore", (schema) => {
|
|
5840
|
+
if (this.isUnconstrainedSchema(schema)) {
|
|
5841
|
+
return this.scalar("unknown");
|
|
5842
|
+
}
|
|
5843
|
+
if (typeof schema.$ref === "string") {
|
|
5844
|
+
this.ensureSchemaDefinition(schema.$ref);
|
|
5845
|
+
return this.ref(schema.$ref);
|
|
5846
|
+
}
|
|
5847
|
+
if (schema.const !== void 0) {
|
|
5848
|
+
return this.literal(this.toLiteralValue(schema.const));
|
|
5849
|
+
}
|
|
5850
|
+
if (Array.isArray(schema.enum)) {
|
|
5851
|
+
if (!schema.enum.length) {
|
|
5524
5852
|
throw new Error(
|
|
5525
|
-
|
|
5853
|
+
"Unsupported enum schema: enum must be non-empty"
|
|
5854
|
+
);
|
|
5855
|
+
}
|
|
5856
|
+
return this.union(
|
|
5857
|
+
schema.enum.map(
|
|
5858
|
+
(value) => this.literal(this.toLiteralValue(value))
|
|
5859
|
+
)
|
|
5860
|
+
);
|
|
5861
|
+
}
|
|
5862
|
+
if (schema.type === "array") {
|
|
5863
|
+
if (schema.items === void 0) {
|
|
5864
|
+
return {
|
|
5865
|
+
kind: "inline",
|
|
5866
|
+
expr: {
|
|
5867
|
+
node: "array",
|
|
5868
|
+
element: this.scalar("unknown")
|
|
5869
|
+
}
|
|
5870
|
+
};
|
|
5871
|
+
}
|
|
5872
|
+
if (Array.isArray(schema.items)) {
|
|
5873
|
+
throw new Error("Unsupported array schema: tuple-style items");
|
|
5874
|
+
}
|
|
5875
|
+
if (!this.isSchemaNode(schema.items)) {
|
|
5876
|
+
throw new Error(
|
|
5877
|
+
"Unsupported array schema: items must be a schema"
|
|
5526
5878
|
);
|
|
5527
5879
|
}
|
|
5880
|
+
return {
|
|
5881
|
+
kind: "inline",
|
|
5882
|
+
expr: {
|
|
5883
|
+
node: "array",
|
|
5884
|
+
element: this.fromTypeExpr(schema.items)
|
|
5885
|
+
}
|
|
5886
|
+
};
|
|
5887
|
+
}
|
|
5888
|
+
if (schema.type === "object") {
|
|
5889
|
+
return this.projectObjectType(schema);
|
|
5890
|
+
}
|
|
5891
|
+
if (schema.oneOf !== void 0) {
|
|
5892
|
+
return this.union(this.mapSchemaArrayMembers(schema.oneOf, "oneOf"));
|
|
5893
|
+
}
|
|
5894
|
+
if (schema.anyOf !== void 0) {
|
|
5895
|
+
return this.union(this.mapSchemaArrayMembers(schema.anyOf, "anyOf"));
|
|
5896
|
+
}
|
|
5897
|
+
if (schema.allOf !== void 0) {
|
|
5898
|
+
return {
|
|
5899
|
+
kind: "inline",
|
|
5900
|
+
expr: {
|
|
5901
|
+
node: "intersection",
|
|
5902
|
+
members: this.mapSchemaArrayMembers(schema.allOf, "allOf")
|
|
5903
|
+
}
|
|
5904
|
+
};
|
|
5905
|
+
}
|
|
5906
|
+
if (this.isBinaryFileSchema(schema)) {
|
|
5907
|
+
return this.builtinRef("File");
|
|
5528
5908
|
}
|
|
5909
|
+
if (this.isScalarName(schema.type)) {
|
|
5910
|
+
return this.scalar(schema.type);
|
|
5911
|
+
}
|
|
5912
|
+
throw new Error(
|
|
5913
|
+
`Unsupported schema construct: ${JSON.stringify(schema, null, 2)}`
|
|
5914
|
+
);
|
|
5529
5915
|
});
|
|
5530
5916
|
}
|
|
5531
5917
|
}
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
|
|
5535
|
-
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
return
|
|
5918
|
+
|
|
5919
|
+
var __defProp$2 = Object.defineProperty;
|
|
5920
|
+
var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
5921
|
+
var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5922
|
+
const pickPreferredMediaType = (mediaTypes) => {
|
|
5923
|
+
if (!mediaTypes.length) return void 0;
|
|
5924
|
+
const exactJson = mediaTypes.find((media) => media === "application/json");
|
|
5925
|
+
if (exactJson) return exactJson;
|
|
5926
|
+
const jsonLike = mediaTypes.find((media) => /\+json$/i.test(media));
|
|
5927
|
+
if (jsonLike) return jsonLike;
|
|
5928
|
+
const wildcardJson = mediaTypes.find(
|
|
5929
|
+
(media) => /application\/\*\+json/i.test(media)
|
|
5930
|
+
);
|
|
5931
|
+
if (wildcardJson) return wildcardJson;
|
|
5932
|
+
return mediaTypes[0];
|
|
5546
5933
|
};
|
|
5547
|
-
const
|
|
5548
|
-
|
|
5934
|
+
const isSuccessStatusCode = (statusCode) => {
|
|
5935
|
+
const upper = statusCode.toUpperCase();
|
|
5936
|
+
if (/^2XX$/.test(upper)) {
|
|
5937
|
+
return true;
|
|
5938
|
+
}
|
|
5939
|
+
const numericCode = Number(statusCode);
|
|
5940
|
+
return Number.isFinite(numericCode) && numericCode >= 200 && numericCode < 300;
|
|
5549
5941
|
};
|
|
5550
|
-
const
|
|
5551
|
-
const
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
return "(none)";
|
|
5555
|
-
}
|
|
5556
|
-
return values.map((v) => JSON.stringify(v)).join(", ");
|
|
5557
|
-
};
|
|
5558
|
-
const validatePathParams = (params, httpPath) => {
|
|
5559
|
-
const placeholders = extractPathPlaceholderNames(httpPath);
|
|
5560
|
-
const placeholderSet = new Set(placeholders);
|
|
5561
|
-
const pathParams = params.filter(isPathParam);
|
|
5562
|
-
if (pathParams.length > 1) {
|
|
5563
|
-
throw new Error(
|
|
5564
|
-
`Path "${httpPath}" has ${pathParams.length} path parameter objects; expected at most one.`
|
|
5565
|
-
);
|
|
5566
|
-
}
|
|
5567
|
-
const pathParam = pathParams[0];
|
|
5568
|
-
if (!pathParam) {
|
|
5569
|
-
if (placeholderSet.size > 0) {
|
|
5570
|
-
throw new Error(
|
|
5571
|
-
`Path "${httpPath}" is missing a path parameter object for placeholders: ${renderNameList(
|
|
5572
|
-
placeholderSet
|
|
5573
|
-
)}.`
|
|
5574
|
-
);
|
|
5575
|
-
}
|
|
5576
|
-
return void 0;
|
|
5577
|
-
}
|
|
5578
|
-
const pathParamExpr = pathParam.typeExpr.expr;
|
|
5579
|
-
if (pathParamExpr.additionalProperties !== void 0 && pathParamExpr.additionalProperties !== false) {
|
|
5580
|
-
throw new Error(
|
|
5581
|
-
`Path "${httpPath}" path parameter object must not declare additionalProperties.`
|
|
5582
|
-
);
|
|
5583
|
-
}
|
|
5584
|
-
const nestedObjectProps = pathParamExpr.properties.filter((prop) => isInlineObjectType(prop.typeExpr)).map((prop) => prop.name);
|
|
5585
|
-
if (nestedObjectProps.length > 0) {
|
|
5586
|
-
throw new Error(
|
|
5587
|
-
`Path "${httpPath}" path parameters must be depth-1. Nested object properties: ${renderNameList(
|
|
5588
|
-
nestedObjectProps
|
|
5589
|
-
)}.`
|
|
5590
|
-
);
|
|
5942
|
+
const selectReturnResponses = (responses) => {
|
|
5943
|
+
const entries = Object.entries(responses);
|
|
5944
|
+
if (!entries.length) {
|
|
5945
|
+
return [];
|
|
5591
5946
|
}
|
|
5592
|
-
const
|
|
5593
|
-
if (
|
|
5594
|
-
|
|
5595
|
-
`Path "${httpPath}" path parameters must all be required. Optional properties: ${renderNameList(
|
|
5596
|
-
optionalPathProps
|
|
5597
|
-
)}.`
|
|
5598
|
-
);
|
|
5947
|
+
const success = entries.filter(([statusCode]) => isSuccessStatusCode(statusCode)).map(([, response]) => response);
|
|
5948
|
+
if (success.length) {
|
|
5949
|
+
return success;
|
|
5599
5950
|
}
|
|
5600
|
-
const
|
|
5601
|
-
|
|
5602
|
-
const missingInObject = [...placeholderSet].filter(
|
|
5603
|
-
(name) => !declaredNameSet.has(name)
|
|
5604
|
-
);
|
|
5605
|
-
const missingInPath = declaredNames.filter(
|
|
5606
|
-
(name) => !placeholderSet.has(name)
|
|
5951
|
+
const defaultResponse = entries.find(
|
|
5952
|
+
([statusCode]) => statusCode.toLowerCase() === "default"
|
|
5607
5953
|
);
|
|
5608
|
-
if (
|
|
5609
|
-
|
|
5610
|
-
`Path "${httpPath}" placeholders and path parameter object keys must match exactly. Missing in object: ${renderNameList(
|
|
5611
|
-
missingInObject
|
|
5612
|
-
)}. Missing in path: ${renderNameList(missingInPath)}.`
|
|
5613
|
-
);
|
|
5954
|
+
if (defaultResponse) {
|
|
5955
|
+
return [defaultResponse[1]];
|
|
5614
5956
|
}
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
|
|
5618
|
-
);
|
|
5957
|
+
return entries.map(([, response]) => response);
|
|
5958
|
+
};
|
|
5959
|
+
const getPreferredMediaTypeEntry = (content) => {
|
|
5960
|
+
const mediaType = pickPreferredMediaType(Object.keys(content));
|
|
5961
|
+
if (!mediaType) return {};
|
|
5619
5962
|
return {
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
propertiesByName
|
|
5963
|
+
mediaType,
|
|
5964
|
+
media: content[mediaType]
|
|
5623
5965
|
};
|
|
5624
5966
|
};
|
|
5625
|
-
|
|
5626
|
-
const
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
const
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
|
|
5967
|
+
const isBinaryFileResponse = (response) => {
|
|
5968
|
+
const { mediaType, media } = getPreferredMediaTypeEntry(response.content);
|
|
5969
|
+
return media?.schema?.type === "string" && media.schema?.format === "binary" && mediaType !== "multipart/form-data";
|
|
5970
|
+
};
|
|
5971
|
+
const isJsonLikeMediaType = (mediaType) => {
|
|
5972
|
+
return mediaType === "application/json" || typeof mediaType === "string" && /\+json$/i.test(mediaType);
|
|
5973
|
+
};
|
|
5974
|
+
const toHttpMethodUpper = (method) => {
|
|
5975
|
+
switch (method) {
|
|
5976
|
+
case "get":
|
|
5977
|
+
return "GET";
|
|
5978
|
+
case "post":
|
|
5979
|
+
return "POST";
|
|
5980
|
+
case "put":
|
|
5981
|
+
return "PUT";
|
|
5982
|
+
case "delete":
|
|
5983
|
+
return "DELETE";
|
|
5984
|
+
case "patch":
|
|
5985
|
+
return "PATCH";
|
|
5986
|
+
case "options":
|
|
5987
|
+
return "OPTIONS";
|
|
5988
|
+
case "head":
|
|
5989
|
+
return "HEAD";
|
|
5990
|
+
case "trace":
|
|
5991
|
+
return "TRACE";
|
|
5992
|
+
default: {
|
|
5993
|
+
const exhaustive = method;
|
|
5994
|
+
throw new Error(`Unsupported HTTP method: ${String(exhaustive)}`);
|
|
5995
|
+
}
|
|
5996
|
+
}
|
|
5997
|
+
};
|
|
5998
|
+
class OperationProjector {
|
|
5999
|
+
constructor(options) {
|
|
6000
|
+
this.options = options;
|
|
6001
|
+
__publicField$2(this, "seenOperationIds", /* @__PURE__ */ new Set());
|
|
6002
|
+
__publicField$2(this, "typeExprConverter");
|
|
6003
|
+
__publicField$2(this, "project", (openApi) => {
|
|
6004
|
+
this.seenOperationIds.clear();
|
|
6005
|
+
this.typeExprConverter = new ToTypeExprConverter(
|
|
6006
|
+
openApi.schemas,
|
|
6007
|
+
this.options.typeExtraction
|
|
6008
|
+
);
|
|
6009
|
+
if (this.options.includeAllSchema) {
|
|
6010
|
+
this.typeExprConverter.registerAllSchemaDefinitions();
|
|
5646
6011
|
}
|
|
5647
|
-
const
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
return
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
`;
|
|
5664
|
-
}).join("\n\n"));
|
|
5665
|
-
__publicField$2(this, "opsCode", (codegen, operations, target = "class") => {
|
|
5666
|
-
const separator = target === "object" ? `,
|
|
5667
|
-
${EMPTY_LINE_MARKER}
|
|
5668
|
-
` : "\n\n";
|
|
5669
|
-
const methodsCode = operations.map((op) => this.renderOp(op, codegen, target)).join(separator);
|
|
5670
|
-
return methodsCode;
|
|
6012
|
+
const operations = openApi.operations.map((operation) => {
|
|
6013
|
+
const operationName = (operation.operationId ?? syntheticOperationId(operation.method, operation.path)).trim();
|
|
6014
|
+
if (!operationName) {
|
|
6015
|
+
throw new Error(
|
|
6016
|
+
`Operation id is empty for ${operation.method.toUpperCase()} ${operation.path}`
|
|
6017
|
+
);
|
|
6018
|
+
}
|
|
6019
|
+
if (this.seenOperationIds.has(operationName)) {
|
|
6020
|
+
throw new Error(`Duplicate operation id: ${operationName}`);
|
|
6021
|
+
}
|
|
6022
|
+
return this.projectOne(operationName, operation);
|
|
6023
|
+
});
|
|
6024
|
+
return {
|
|
6025
|
+
operations,
|
|
6026
|
+
schemaDefinitions: this.typeExprConverter.getSchemaDefinitions()
|
|
6027
|
+
};
|
|
5671
6028
|
});
|
|
5672
|
-
__publicField$2(this, "
|
|
5673
|
-
|
|
5674
|
-
const
|
|
5675
|
-
const
|
|
5676
|
-
|
|
5677
|
-
|
|
5678
|
-
|
|
6029
|
+
__publicField$2(this, "projectOne", (operationName, operation) => {
|
|
6030
|
+
this.seenOperationIds.add(operationName);
|
|
6031
|
+
const parameters = [];
|
|
6032
|
+
const pathParameter = this.projectPathParameter(operation);
|
|
6033
|
+
const queryParameter = this.projectQueryParameter(operation);
|
|
6034
|
+
const bodyParameter = this.projectBodyParameter(operation);
|
|
6035
|
+
if (pathParameter) parameters.push(pathParameter);
|
|
6036
|
+
if (queryParameter) parameters.push(queryParameter);
|
|
6037
|
+
if (bodyParameter) parameters.push(bodyParameter);
|
|
6038
|
+
return {
|
|
6039
|
+
op: {
|
|
6040
|
+
signature: {
|
|
6041
|
+
name: operationName,
|
|
6042
|
+
parameters,
|
|
6043
|
+
returnType: this.projectReturnType(operation)
|
|
6044
|
+
},
|
|
6045
|
+
http: {
|
|
6046
|
+
method: toHttpMethodUpper(operation.method),
|
|
6047
|
+
path: operation.path,
|
|
6048
|
+
requestContentType: this.projectRequestContentType(
|
|
6049
|
+
operation.requestBody
|
|
6050
|
+
),
|
|
6051
|
+
responseFormat: this.projectResponseFormat(operation)
|
|
6052
|
+
}
|
|
6053
|
+
},
|
|
6054
|
+
tags: [...operation.tags]
|
|
5679
6055
|
};
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
const { path, funParams, hasBody, hasQuery } = paramsHelper.process(
|
|
5685
|
-
sig.parameters,
|
|
5686
|
-
http.path
|
|
5687
|
-
);
|
|
5688
|
-
const params = funParams.map(
|
|
5689
|
-
(r) => `${r.identifier}: ${tsCodegen.toCode(r.typeExpr)}`
|
|
6056
|
+
});
|
|
6057
|
+
__publicField$2(this, "projectRequestContentType", (requestBody) => {
|
|
6058
|
+
const { mediaType } = getPreferredMediaTypeEntry(
|
|
6059
|
+
requestBody?.content ?? {}
|
|
5690
6060
|
);
|
|
5691
|
-
|
|
5692
|
-
|
|
6061
|
+
switch (mediaType) {
|
|
6062
|
+
case "application/json":
|
|
6063
|
+
case "multipart/form-data":
|
|
6064
|
+
case "application/x-www-form-urlencoded":
|
|
6065
|
+
return mediaType;
|
|
6066
|
+
default:
|
|
6067
|
+
return void 0;
|
|
5693
6068
|
}
|
|
5694
|
-
const paramsCode = params.join(", ");
|
|
5695
|
-
const responseType = tsCodegen.toCode(sig.returnType);
|
|
5696
|
-
const returnType = this.wrapInResponseWrapper(sig.returnType, tsCodegen);
|
|
5697
|
-
return this.renderFunctionCode({
|
|
5698
|
-
funName: sig.name,
|
|
5699
|
-
responseType,
|
|
5700
|
-
returnType,
|
|
5701
|
-
params: paramsCode,
|
|
5702
|
-
path,
|
|
5703
|
-
method: http.method,
|
|
5704
|
-
hasQuery,
|
|
5705
|
-
hasBody,
|
|
5706
|
-
requestContentType: http.requestContentType,
|
|
5707
|
-
responseFormat: http.responseFormat,
|
|
5708
|
-
requestParamsVar: this.opts.requestParams ? REQUEST_PARAMS_ARG : void 0,
|
|
5709
|
-
unwrap: this.opts.unwrap ?? false,
|
|
5710
|
-
target
|
|
5711
|
-
});
|
|
5712
6069
|
});
|
|
5713
|
-
__publicField$2(this, "
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
}
|
|
6070
|
+
__publicField$2(this, "projectResponseFormat", (operation) => {
|
|
6071
|
+
const selectedResponses = selectReturnResponses(operation.responses);
|
|
6072
|
+
if (!selectedResponses.length) {
|
|
6073
|
+
return void 0;
|
|
6074
|
+
}
|
|
6075
|
+
if (selectedResponses.some((response) => isBinaryFileResponse(response))) {
|
|
6076
|
+
return "document";
|
|
6077
|
+
}
|
|
6078
|
+
if (selectedResponses.some(
|
|
6079
|
+
(response) => isJsonLikeMediaType(
|
|
6080
|
+
getPreferredMediaTypeEntry(response.content).mediaType
|
|
6081
|
+
)
|
|
6082
|
+
)) {
|
|
6083
|
+
return "json";
|
|
6084
|
+
}
|
|
6085
|
+
return void 0;
|
|
5718
6086
|
});
|
|
5719
|
-
__publicField$2(this, "
|
|
5720
|
-
|
|
5721
|
-
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
const
|
|
5750
|
-
const
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
6087
|
+
__publicField$2(this, "projectParameterGroupTypeExpr", (params) => {
|
|
6088
|
+
const properties = Object.entries(params).map(([name, parameter]) => ({
|
|
6089
|
+
name,
|
|
6090
|
+
required: parameter.required,
|
|
6091
|
+
typeExpr: this.typeExprConverter.fromTypeExpr(
|
|
6092
|
+
this.selectParameterSchema(parameter)
|
|
6093
|
+
)
|
|
6094
|
+
}));
|
|
6095
|
+
return {
|
|
6096
|
+
kind: "inline",
|
|
6097
|
+
expr: {
|
|
6098
|
+
node: "object",
|
|
6099
|
+
properties
|
|
6100
|
+
}
|
|
6101
|
+
};
|
|
6102
|
+
});
|
|
6103
|
+
__publicField$2(this, "projectPathParameter", (operation) => {
|
|
6104
|
+
const params = operation.parameters.path;
|
|
6105
|
+
const entries = Object.values(params);
|
|
6106
|
+
if (!entries.length) return null;
|
|
6107
|
+
if (entries.some((parameter) => !parameter.required)) {
|
|
6108
|
+
throw new Error("Unsupported path parameters: all must be required");
|
|
6109
|
+
}
|
|
6110
|
+
return {
|
|
6111
|
+
kind: "path",
|
|
6112
|
+
optional: false,
|
|
6113
|
+
typeExpr: this.projectParameterGroupTypeExpr(params)
|
|
6114
|
+
};
|
|
6115
|
+
});
|
|
6116
|
+
__publicField$2(this, "projectQueryParameter", (operation) => {
|
|
6117
|
+
const params = operation.parameters.query;
|
|
6118
|
+
const entries = Object.values(params);
|
|
6119
|
+
if (!entries.length) return null;
|
|
6120
|
+
return {
|
|
6121
|
+
kind: "query",
|
|
6122
|
+
optional: !entries.some((parameter) => parameter.required),
|
|
6123
|
+
typeExpr: this.projectParameterGroupTypeExpr(params)
|
|
6124
|
+
};
|
|
6125
|
+
});
|
|
6126
|
+
__publicField$2(this, "projectBodyParameter", (operation) => {
|
|
6127
|
+
const requestBody = operation.requestBody;
|
|
6128
|
+
if (!requestBody) return null;
|
|
6129
|
+
return {
|
|
6130
|
+
kind: "body",
|
|
6131
|
+
optional: !requestBody.required,
|
|
6132
|
+
typeExpr: this.typeExprConverter.fromTypeExpr(
|
|
6133
|
+
this.selectRequestBodySchema(requestBody)
|
|
6134
|
+
)
|
|
6135
|
+
};
|
|
6136
|
+
});
|
|
6137
|
+
__publicField$2(this, "projectReturnType", (operation) => {
|
|
6138
|
+
const selectedResponses = selectReturnResponses(operation.responses);
|
|
6139
|
+
if (!selectedResponses.length) {
|
|
6140
|
+
return this.typeExprConverter.scalar("void");
|
|
6141
|
+
}
|
|
6142
|
+
const responseTypes = selectedResponses.map((response) => {
|
|
6143
|
+
const schema = this.selectResponseSchema(response);
|
|
6144
|
+
if (!schema) {
|
|
6145
|
+
return this.typeExprConverter.scalar("void");
|
|
6146
|
+
}
|
|
6147
|
+
return this.typeExprConverter.fromTypeExpr(schema);
|
|
6148
|
+
});
|
|
6149
|
+
return this.typeExprConverter.union(responseTypes);
|
|
6150
|
+
});
|
|
6151
|
+
__publicField$2(this, "selectParameterSchema", (parameter) => {
|
|
6152
|
+
if (parameter.schema) return parameter.schema;
|
|
6153
|
+
return getPreferredMediaTypeEntry(parameter.content).media?.schema;
|
|
6154
|
+
});
|
|
6155
|
+
__publicField$2(this, "selectRequestBodySchema", (requestBody) => {
|
|
6156
|
+
return getPreferredMediaTypeEntry(requestBody.content).media?.schema;
|
|
6157
|
+
});
|
|
6158
|
+
__publicField$2(this, "selectResponseSchema", (response) => {
|
|
6159
|
+
return getPreferredMediaTypeEntry(response.content).media?.schema;
|
|
5754
6160
|
});
|
|
5755
6161
|
}
|
|
5756
|
-
}
|
|
5757
|
-
|
|
5758
|
-
const promiseHttpClientCodegenSpec = () => ({
|
|
5759
|
-
typeName: {
|
|
5760
|
-
kind: "reference",
|
|
5761
|
-
ref: {
|
|
5762
|
-
kind: "internal",
|
|
5763
|
-
name: "HttpClient"
|
|
5764
|
-
}
|
|
5765
|
-
},
|
|
5766
|
-
responseWrapper: {
|
|
5767
|
-
kind: "reference",
|
|
5768
|
-
ref: {
|
|
5769
|
-
kind: "builtin",
|
|
5770
|
-
name: "Promise"
|
|
5771
|
-
}
|
|
5772
|
-
},
|
|
5773
|
-
inferMethodReturnType: true,
|
|
5774
|
-
invocation: {
|
|
5775
|
-
vars: {
|
|
5776
|
-
body: "body",
|
|
5777
|
-
query: "query"
|
|
5778
|
-
},
|
|
5779
|
-
expr: ({
|
|
5780
|
-
responseType,
|
|
5781
|
-
path,
|
|
5782
|
-
method,
|
|
5783
|
-
hasBody,
|
|
5784
|
-
hasQuery,
|
|
5785
|
-
requestContentType,
|
|
5786
|
-
responseFormat,
|
|
5787
|
-
requestParamsVar,
|
|
5788
|
-
unwrap
|
|
5789
|
-
}) => `.request<${responseType}>({
|
|
5790
|
-
method: '${method}',
|
|
5791
|
-
path: ${path}
|
|
5792
|
-
${hasQuery ? `,
|
|
5793
|
-
query` : ""}
|
|
5794
|
-
${hasBody ? `,
|
|
5795
|
-
body` : ""}
|
|
5796
|
-
${requestContentType ? `,
|
|
5797
|
-
requestContentType: '${requestContentType}'` : ""}
|
|
5798
|
-
${responseFormat ? `,
|
|
5799
|
-
format: '${responseFormat}'` : ""}
|
|
5800
|
-
}${requestParamsVar ? `,
|
|
5801
|
-
${requestParamsVar}` : ""})${unwrap ? `.then(res => res.body)` : ""}
|
|
5802
|
-
`
|
|
5803
|
-
}
|
|
5804
|
-
});
|
|
6162
|
+
}
|
|
5805
6163
|
|
|
5806
6164
|
var __defProp$1 = Object.defineProperty;
|
|
5807
6165
|
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
@@ -5817,7 +6175,7 @@ class CodeFormatter {
|
|
|
5817
6175
|
constructor(opts) {
|
|
5818
6176
|
this.opts = opts;
|
|
5819
6177
|
__publicField$1(this, "format", async (code) => {
|
|
5820
|
-
const filePath = Path.resolve(this.opts.resolveConfFrom, "generated.ts");
|
|
6178
|
+
const filePath = Path$1.resolve(this.opts.resolveConfFrom, "generated.ts");
|
|
5821
6179
|
const prettier = await import('prettier');
|
|
5822
6180
|
const prettierConfig = await prettier.resolveConfig(filePath) ?? {};
|
|
5823
6181
|
const formattedCode = await prettier.format(code, {
|
|
@@ -5911,185 +6269,76 @@ class CodeWriter {
|
|
|
5911
6269
|
}
|
|
5912
6270
|
const ensureSingleTrailingNewline = (code) => code.replace(/\n+$/u, "\n");
|
|
5913
6271
|
|
|
5914
|
-
|
|
5915
|
-
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
const orderedChunks = orderByInternalDependencies(chunks);
|
|
5919
|
-
const mergedCode = orderedChunks.map((chunk) => chunk.code).join("\n\n");
|
|
5920
|
-
const exportedSymbols = /* @__PURE__ */ new Set();
|
|
5921
|
-
const mergedExports = [];
|
|
5922
|
-
for (const chunk of orderedChunks) {
|
|
5923
|
-
for (const symbol of chunk.exports) {
|
|
5924
|
-
if (exportedSymbols.has(symbol)) continue;
|
|
5925
|
-
exportedSymbols.add(symbol);
|
|
5926
|
-
mergedExports.push(symbol);
|
|
5927
|
-
}
|
|
5928
|
-
}
|
|
5929
|
-
const seenRefKeys = /* @__PURE__ */ new Set();
|
|
5930
|
-
const mergedRefs = orderedChunks.flatMap((chunk) => chunk.refs).filter((ref) => {
|
|
5931
|
-
if (ref.kind === "internal" && exportedSymbols.has(ref.name)) {
|
|
5932
|
-
return false;
|
|
5933
|
-
}
|
|
5934
|
-
const key = ref.kind === "internal" ? `internal:${ref.name}` : ref.kind === "external" ? `external:${ref.package}:${ref.name}` : `builtin:${ref.name}`;
|
|
5935
|
-
if (seenRefKeys.has(key)) return false;
|
|
5936
|
-
seenRefKeys.add(key);
|
|
5937
|
-
return true;
|
|
5938
|
-
});
|
|
5939
|
-
return {
|
|
5940
|
-
name,
|
|
5941
|
-
code: mergedCode,
|
|
5942
|
-
exports: mergedExports,
|
|
5943
|
-
refs: mergedRefs
|
|
5944
|
-
};
|
|
5945
|
-
};
|
|
5946
|
-
const orderByInternalDependencies = (chunks) => {
|
|
5947
|
-
const chunkIndex = /* @__PURE__ */ new Map();
|
|
5948
|
-
const exportOwnerBySymbol = /* @__PURE__ */ new Map();
|
|
5949
|
-
chunks.forEach((chunk, index) => {
|
|
5950
|
-
chunkIndex.set(chunk.name, index);
|
|
5951
|
-
for (const symbol of chunk.exports) {
|
|
5952
|
-
if (exportOwnerBySymbol.has(symbol)) {
|
|
5953
|
-
throw new Error(
|
|
5954
|
-
`Assertion error. Symbol "${symbol}" exported multiple times while ordering chunks.`
|
|
5955
|
-
);
|
|
5956
|
-
}
|
|
5957
|
-
exportOwnerBySymbol.set(symbol, chunk);
|
|
5958
|
-
}
|
|
5959
|
-
});
|
|
5960
|
-
const dependentsByProvider = /* @__PURE__ */ new Map();
|
|
5961
|
-
const inDegreeByChunk = /* @__PURE__ */ new Map();
|
|
5962
|
-
for (const chunk of chunks) {
|
|
5963
|
-
dependentsByProvider.set(chunk, /* @__PURE__ */ new Set());
|
|
5964
|
-
inDegreeByChunk.set(chunk, 0);
|
|
5965
|
-
}
|
|
5966
|
-
for (const chunk of chunks) {
|
|
5967
|
-
const providers = /* @__PURE__ */ new Set();
|
|
5968
|
-
for (const ref of chunk.refs) {
|
|
5969
|
-
if (ref.kind !== "internal") continue;
|
|
5970
|
-
const provider = exportOwnerBySymbol.get(ref.name);
|
|
5971
|
-
if (!provider || provider === chunk) continue;
|
|
5972
|
-
providers.add(provider);
|
|
5973
|
-
}
|
|
5974
|
-
for (const provider of providers) {
|
|
5975
|
-
dependentsByProvider.get(provider).add(chunk);
|
|
5976
|
-
inDegreeByChunk.set(chunk, inDegreeByChunk.get(chunk) + 1);
|
|
5977
|
-
}
|
|
6272
|
+
class VNextOasClientGenerator {
|
|
6273
|
+
constructor(options, log) {
|
|
6274
|
+
this.options = options;
|
|
6275
|
+
this.log = log;
|
|
5978
6276
|
}
|
|
5979
|
-
|
|
5980
|
-
|
|
5981
|
-
|
|
5982
|
-
const
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
|
|
6277
|
+
async generate(cmd) {
|
|
6278
|
+
const { inputFile, outputDir, apiName } = cmd;
|
|
6279
|
+
this.log(`Will generate API client name=${apiName} to ${outputDir}`);
|
|
6280
|
+
const sourceSchema = await loadSourceSchema(inputFile);
|
|
6281
|
+
const normalizedSchema = this.normalizeSchema(sourceSchema, inputFile);
|
|
6282
|
+
const openApiIr = this.filterOperations(normalizedSchema);
|
|
6283
|
+
const projectResult = new OperationProjector({
|
|
6284
|
+
includeAllSchema: this.options.selection?.allSchemas === true,
|
|
6285
|
+
typeExtraction: {
|
|
6286
|
+
enumStyle: this.options.codegen.enumStyle,
|
|
6287
|
+
uppercaseEnumKeys: this.options.compat?.uppercaseEnumKeys,
|
|
6288
|
+
swaggerTsApiRequiredBooleans: this.options.compat?.swaggerTsApiRequiredBooleans
|
|
5989
6289
|
}
|
|
5990
|
-
}
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
|
|
5995
|
-
|
|
5996
|
-
`Cannot order chunks with cyclic internal dependencies: ${cycleChunkNames}`
|
|
6290
|
+
}).project(openApiIr);
|
|
6291
|
+
const clientName = toPascalCaseIdentifier(apiName);
|
|
6292
|
+
const chunks = generateTsCode(
|
|
6293
|
+
clientName,
|
|
6294
|
+
projectResult,
|
|
6295
|
+
this.options.codegen
|
|
5997
6296
|
);
|
|
6297
|
+
const codeFormatter = new CodeFormatter({
|
|
6298
|
+
resolveConfFrom: outputDir
|
|
6299
|
+
});
|
|
6300
|
+
const writer = new CodeWriter(codeFormatter);
|
|
6301
|
+
await writer.write(outputDir, chunks.all);
|
|
6302
|
+
const tsFilePath = (name) => Path.join(outputDir, `${name}.ts`);
|
|
6303
|
+
return {
|
|
6304
|
+
promiseWrapper: chunks.promiseWrappersNames.map(tsFilePath),
|
|
6305
|
+
types: tsFilePath(chunks.typesName)
|
|
6306
|
+
};
|
|
5998
6307
|
}
|
|
5999
|
-
|
|
6000
|
-
|
|
6001
|
-
|
|
6002
|
-
const generateFromIr = async ({
|
|
6003
|
-
openApiIr,
|
|
6004
|
-
clientName,
|
|
6005
|
-
outputDir,
|
|
6006
|
-
outputFilename,
|
|
6007
|
-
codeFormattingConfigDir,
|
|
6008
|
-
quirks,
|
|
6009
|
-
requestParams: withRequestParams
|
|
6010
|
-
}) => {
|
|
6011
|
-
const tsCodegenFac = new TsCodegenFactory();
|
|
6012
|
-
const projectResult = new OperationProjector({
|
|
6013
|
-
quirks
|
|
6014
|
-
}).project(openApiIr);
|
|
6015
|
-
const { schemaDefinitions } = projectResult;
|
|
6016
|
-
const requestParams = withRequestParams;
|
|
6017
|
-
const types = schemaDefinitions.map((def) => {
|
|
6018
|
-
const cg = tsCodegenFac.forNewChunk();
|
|
6019
|
-
const typeName = def.name;
|
|
6020
|
-
const code = `export type ${typeName} = ${cg.toCode(def.typeExpr)}`;
|
|
6021
|
-
return cg.toChunk(typeName, code, [typeName]);
|
|
6022
|
-
});
|
|
6023
|
-
const clientCodegen = new ApiClientCodegen(
|
|
6024
|
-
{
|
|
6025
|
-
httpClient: promiseHttpClientCodegenSpec(),
|
|
6026
|
-
requestParams,
|
|
6027
|
-
// TODO make configurable
|
|
6028
|
-
pathParamsStyle: "positional"
|
|
6029
|
-
},
|
|
6030
|
-
tsCodegenFac
|
|
6031
|
-
);
|
|
6032
|
-
const generated = clientCodegen.generate(
|
|
6033
|
-
clientName,
|
|
6034
|
-
groupsOpsByTag(projectResult.operations)
|
|
6035
|
-
);
|
|
6036
|
-
const httpClientChunk = tsCodegenFac.forNewChunk().toChunk(
|
|
6037
|
-
"HttpClient",
|
|
6038
|
-
getHttpClientCode(),
|
|
6039
|
-
["HttpRequest", "HttpResponse", "HttpClient"]
|
|
6040
|
-
);
|
|
6041
|
-
const codeFormatter = new CodeFormatter({
|
|
6042
|
-
resolveConfFrom: codeFormattingConfigDir
|
|
6043
|
-
});
|
|
6044
|
-
const writer = new CodeWriter(codeFormatter);
|
|
6045
|
-
let chunks = [...types, httpClientChunk, generated];
|
|
6046
|
-
chunks = [mergeChunks(outputFilename, chunks)];
|
|
6047
|
-
await writer.write(outputDir, chunks);
|
|
6048
|
-
};
|
|
6049
|
-
const groupsOpsByTag = (operations) => {
|
|
6050
|
-
const groupedOps = {};
|
|
6051
|
-
operations.forEach((operation) => {
|
|
6052
|
-
const groupName = operation.tags[0]?.trim() || "root";
|
|
6053
|
-
const bucket = groupedOps[groupName] ?? [];
|
|
6054
|
-
bucket.push(operation.op);
|
|
6055
|
-
groupedOps[groupName] = bucket;
|
|
6056
|
-
});
|
|
6057
|
-
return groupedOps;
|
|
6058
|
-
};
|
|
6059
|
-
const commonTypesCode = httpClientCommonTypesSource;
|
|
6060
|
-
const promiseHttpClientCode = httpClientPromiseTypesSource;
|
|
6061
|
-
const stripImports = (code) => code.replace(/^import[\s\S]*?from\s+['"][^'"]+['"]\s*;?\n?/gm, "").trim();
|
|
6062
|
-
const getHttpClientCode = (opts) => [
|
|
6063
|
-
commonTypesCode,
|
|
6064
|
-
promiseHttpClientCode
|
|
6065
|
-
].map(stripImports).join("\n\n");
|
|
6066
|
-
|
|
6067
|
-
const generateOpenApiClient$1 = async ({ input, name, outputDir, quirks, ignoreOperationsWithTags }, log) => {
|
|
6068
|
-
log(`Will generate API client name=${name} to ${outputDir}`);
|
|
6069
|
-
const sourceSchema = await loadSourceSchema(input);
|
|
6070
|
-
const { result, problems } = new OpenApiNormalizer(sourceSchema).load();
|
|
6071
|
-
problems.forEach((problem) => {
|
|
6072
|
-
log(`[open-api:${problem.level}] ${problem.path}: ${problem.message}`);
|
|
6073
|
-
});
|
|
6074
|
-
const errors = problems.filter((problem) => problem.level === "error");
|
|
6075
|
-
if (errors.length) {
|
|
6076
|
-
throw new Error(
|
|
6077
|
-
`Failed to load OpenAPI IR from ${input}: ${errors.length} error(s).`
|
|
6308
|
+
filterOperations(openApiIr) {
|
|
6309
|
+
const ignoreTags = new Set(
|
|
6310
|
+
this.options.selection?.ignoreOperationsWithTags || []
|
|
6078
6311
|
);
|
|
6312
|
+
return {
|
|
6313
|
+
...openApiIr,
|
|
6314
|
+
operations: openApiIr.operations.filter((operation) => {
|
|
6315
|
+
const hasUsableTags = operation.tags.some((tag) => tag.trim());
|
|
6316
|
+
if (!hasUsableTags) {
|
|
6317
|
+
this.log(
|
|
6318
|
+
`[open-api:warning] Ignoring operation ${operation.method.toUpperCase()} ${operation.path} (${operation.operationId ?? "<missing operationId>"}): no tags`
|
|
6319
|
+
);
|
|
6320
|
+
return false;
|
|
6321
|
+
}
|
|
6322
|
+
return !operation.tags.some((tag) => ignoreTags.has(tag));
|
|
6323
|
+
})
|
|
6324
|
+
};
|
|
6079
6325
|
}
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
}
|
|
6326
|
+
normalizeSchema(rawSchema, inputFile) {
|
|
6327
|
+
const { result, problems } = new OpenApiNormalizer(rawSchema).load();
|
|
6328
|
+
problems.forEach((problem) => {
|
|
6329
|
+
this.log(
|
|
6330
|
+
`[open-api:${problem.level}] ${problem.path}: ${problem.message}`
|
|
6331
|
+
);
|
|
6332
|
+
});
|
|
6333
|
+
const errors = problems.filter((problem) => problem.level === "error");
|
|
6334
|
+
if (errors.length) {
|
|
6335
|
+
throw new Error(
|
|
6336
|
+
`Failed to load OpenAPI IR from ${inputFile}: ${errors.length} error(s).`
|
|
6337
|
+
);
|
|
6338
|
+
}
|
|
6339
|
+
return result;
|
|
6340
|
+
}
|
|
6341
|
+
}
|
|
6093
6342
|
const loadSourceSchema = async (input) => {
|
|
6094
6343
|
let rawContent;
|
|
6095
6344
|
try {
|
|
@@ -6112,109 +6361,60 @@ const loadSourceSchema = async (input) => {
|
|
|
6112
6361
|
}
|
|
6113
6362
|
return parsed;
|
|
6114
6363
|
};
|
|
6115
|
-
const filterOperations = (openApiIr, log, ignoreOperationsWithTags) => {
|
|
6116
|
-
const ignoreTags = new Set(ignoreOperationsWithTags);
|
|
6117
|
-
return {
|
|
6118
|
-
...openApiIr,
|
|
6119
|
-
operations: openApiIr.operations.filter((operation) => {
|
|
6120
|
-
const hasUsableTags = operation.tags.some((tag) => tag.trim());
|
|
6121
|
-
if (!hasUsableTags) {
|
|
6122
|
-
log(
|
|
6123
|
-
`[open-api:warning] Ignoring operation ${operation.method.toUpperCase()} ${operation.path} (${operation.operationId ?? "<missing operationId>"}): no tags`
|
|
6124
|
-
);
|
|
6125
|
-
return false;
|
|
6126
|
-
}
|
|
6127
|
-
return !operation.tags.some((tag) => ignoreTags.has(tag));
|
|
6128
|
-
})
|
|
6129
|
-
};
|
|
6130
|
-
};
|
|
6131
6364
|
const toErrorMessage = (error) => {
|
|
6132
6365
|
return error instanceof Error ? error.message : String(error);
|
|
6133
6366
|
};
|
|
6134
6367
|
|
|
6135
|
-
const
|
|
6136
|
-
|
|
6137
|
-
const
|
|
6138
|
-
|
|
6139
|
-
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
|
|
6159
|
-
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
6164
|
-
|
|
6165
|
-
|
|
6166
|
-
(t) => ignoreTags.has(t)
|
|
6167
|
-
);
|
|
6168
|
-
if (ignore) return false;
|
|
6169
|
-
}
|
|
6170
|
-
return routeData;
|
|
6171
|
-
}
|
|
6172
|
-
},
|
|
6173
|
-
// @ts-ignore
|
|
6174
|
-
codeGenConstructs: () => ({
|
|
6175
|
-
Keyword: {}
|
|
6176
|
-
})
|
|
6177
|
-
// extractRequestParams: true,
|
|
6178
|
-
});
|
|
6179
|
-
return dstFile;
|
|
6180
|
-
};
|
|
6181
|
-
const getThisScriptDirname = () => {
|
|
6182
|
-
return Path$1.dirname(node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('generateApiClient.cjs', document.baseURI).href))));
|
|
6183
|
-
};
|
|
6184
|
-
const getTemplatesDir = () => {
|
|
6185
|
-
const currentDir = getThisScriptDirname();
|
|
6186
|
-
const templatesRelativePath = Path$1.basename(currentDir) === "dist" ? "../templates" : "../../../templates";
|
|
6187
|
-
return Path$1.resolve(currentDir, templatesRelativePath);
|
|
6188
|
-
};
|
|
6189
|
-
|
|
6190
|
-
const SUPPORTED_GENERATORS = [
|
|
6191
|
-
"swagger-ts-api",
|
|
6192
|
-
"api-client-generator"
|
|
6193
|
-
];
|
|
6194
|
-
const OPEN_API_MODEL_GENERATORS = {
|
|
6195
|
-
"swagger-ts-api": generateOpenApiClient,
|
|
6196
|
-
"api-client-generator": generateOpenApiClient$1
|
|
6197
|
-
};
|
|
6198
|
-
const generateOpenApiModel = async (generator, cmd, log) => {
|
|
6199
|
-
const generatorFun = OPEN_API_MODEL_GENERATORS[generator];
|
|
6200
|
-
const outputPath = await generatorFun(cmd, log);
|
|
6201
|
-
const outputModifiers = getOutputModifiers(cmd, log);
|
|
6202
|
-
log(`Will modify the outputs ${JSON.stringify(outputModifiers)}`);
|
|
6203
|
-
await modifyOutput(outputPath, outputModifiers);
|
|
6204
|
-
return outputPath;
|
|
6368
|
+
const generateOpenApiModel = async (spectPath, config, log) => {
|
|
6369
|
+
let outFiles;
|
|
6370
|
+
const { openApiGenerator, responseWrapper } = config;
|
|
6371
|
+
if ("legacy" in openApiGenerator) {
|
|
6372
|
+
outFiles = await generateOpenApiClient(
|
|
6373
|
+
spectPath,
|
|
6374
|
+
{
|
|
6375
|
+
ignoreOperationsWithTags: openApiGenerator.legacy.ignoreOperationsWithTags,
|
|
6376
|
+
name: config.apiName,
|
|
6377
|
+
outputDir: config.dstDir
|
|
6378
|
+
},
|
|
6379
|
+
log
|
|
6380
|
+
);
|
|
6381
|
+
} else {
|
|
6382
|
+
outFiles = await new VNextOasClientGenerator(
|
|
6383
|
+
openApiGenerator,
|
|
6384
|
+
log
|
|
6385
|
+
).generate({
|
|
6386
|
+
inputFile: spectPath,
|
|
6387
|
+
apiName: config.apiName,
|
|
6388
|
+
outputDir: config.dstDir
|
|
6389
|
+
});
|
|
6390
|
+
}
|
|
6391
|
+
if (responseWrapper) {
|
|
6392
|
+
await modifyHttpClientAsyncWrapper(
|
|
6393
|
+
outFiles.promiseWrapper,
|
|
6394
|
+
responseWrapper,
|
|
6395
|
+
log
|
|
6396
|
+
);
|
|
6397
|
+
}
|
|
6398
|
+
return { typesFilePath: outFiles.types };
|
|
6205
6399
|
};
|
|
6206
|
-
const
|
|
6400
|
+
const modifyHttpClientAsyncWrapper = async (filePaths, responseWrapper, log) => {
|
|
6401
|
+
log(
|
|
6402
|
+
`Will use response wrapper '${JSON.stringify(responseWrapper)}' on file ${filePaths.join(", ")}`
|
|
6403
|
+
);
|
|
6207
6404
|
const addImports = [];
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
|
|
6405
|
+
if (responseWrapper.import) {
|
|
6406
|
+
addImports.push(responseWrapper.import);
|
|
6407
|
+
}
|
|
6408
|
+
const replaces = [["Promise", responseWrapper.symbol]];
|
|
6409
|
+
if (responseWrapper.unwrapExpr) {
|
|
6410
|
+
replaces.push([".then(res => res.body)", responseWrapper.unwrapExpr]);
|
|
6411
|
+
}
|
|
6412
|
+
for (const filePath of filePaths) {
|
|
6413
|
+
await modifyOutput(filePath, {
|
|
6414
|
+
addImports,
|
|
6415
|
+
replaces
|
|
6416
|
+
});
|
|
6213
6417
|
}
|
|
6214
|
-
return {
|
|
6215
|
-
addImports,
|
|
6216
|
-
replaces
|
|
6217
|
-
};
|
|
6218
6418
|
};
|
|
6219
6419
|
const modifyOutput = async (path, cmd) => {
|
|
6220
6420
|
let content = await fs.readFile(path).then((r) => r.toString());
|
|
@@ -6280,9 +6480,7 @@ const generateSchemas = async (inputFile, outputFile, opts = {}) => {
|
|
|
6280
6480
|
const { getZodSchemasFile } = tsToZod.generate({
|
|
6281
6481
|
sourceText: removeGenericTypes(code)
|
|
6282
6482
|
});
|
|
6283
|
-
|
|
6284
|
-
let f = getZodSchemasFile(`./${fileName.replace(".ts", "")}`);
|
|
6285
|
-
f = f.replaceAll('"./index";', '"./client"');
|
|
6483
|
+
let f = getZodSchemasFile(getModuleImportPath(inputFile, outputFile));
|
|
6286
6484
|
f = f.replaceAll(".optional()", ".nullable()");
|
|
6287
6485
|
if (opts.localDateTimes) {
|
|
6288
6486
|
f = f.replaceAll(
|
|
@@ -6298,50 +6496,25 @@ const generateSchemas = async (inputFile, outputFile, opts = {}) => {
|
|
|
6298
6496
|
});
|
|
6299
6497
|
fs$2.writeFileSync(outputFile, formatted);
|
|
6300
6498
|
};
|
|
6301
|
-
|
|
6302
|
-
const
|
|
6303
|
-
const
|
|
6304
|
-
|
|
6305
|
-
|
|
6306
|
-
|
|
6307
|
-
generator: _enum(SUPPORTED_GENERATORS).default(DEFAULT_GENERATOR),
|
|
6308
|
-
quirks: strictObject({
|
|
6309
|
-
swaggerTsApiRequiredBooleans: boolean().optional()
|
|
6310
|
-
}).optional(),
|
|
6311
|
-
responseWrapper: strictObject({
|
|
6312
|
-
symbol: string().trim().min(1),
|
|
6313
|
-
import: string().trim().min(1).optional()
|
|
6314
|
-
}).optional(),
|
|
6315
|
-
ignoreOperationsWithTags: array(string()).optional(),
|
|
6316
|
-
zodSchemas: strictObject({
|
|
6317
|
-
enabled: boolean().optional(),
|
|
6318
|
-
localDateTimes: boolean().optional()
|
|
6319
|
-
}).optional()
|
|
6320
|
-
});
|
|
6321
|
-
const parseGenerateApiClientParams = (params) => {
|
|
6322
|
-
const parsed = generateApiClientParamsSchema.safeParse(params);
|
|
6323
|
-
if (parsed.success) return parsed.data;
|
|
6324
|
-
throw new Error(prettifyError(parsed.error));
|
|
6499
|
+
const getModuleImportPath = (inputFile, outputFile) => {
|
|
6500
|
+
const sourceDir = Path.dirname(outputFile);
|
|
6501
|
+
const relativePath = Path.relative(sourceDir, inputFile);
|
|
6502
|
+
const withoutExtension = relativePath.replace(/\.ts$/u, "");
|
|
6503
|
+
const normalizedPath = withoutExtension.split(Path.sep).join("/");
|
|
6504
|
+
return normalizedPath.startsWith(".") ? normalizedPath : `./${normalizedPath}`;
|
|
6325
6505
|
};
|
|
6326
|
-
|
|
6327
|
-
|
|
6328
|
-
|
|
6329
|
-
|
|
6330
|
-
|
|
6331
|
-
quirks,
|
|
6332
|
-
responseWrapper,
|
|
6333
|
-
ignoreOperationsWithTags,
|
|
6334
|
-
zodSchemas,
|
|
6335
|
-
generator
|
|
6336
|
-
} = parseGenerateApiClientParams(params);
|
|
6337
|
-
const dir = Path$1.resolve(dstDir, apiName);
|
|
6506
|
+
|
|
6507
|
+
const generateApiClient = async (params, configFilePath, log = console.log) => {
|
|
6508
|
+
const config = parseConfig(params, configFilePath);
|
|
6509
|
+
const { dstDir, apiName, srcSpec, zodSchemas } = config;
|
|
6510
|
+
const dir = Path.resolve(dstDir, apiName);
|
|
6338
6511
|
if (!fs__namespace.existsSync(dir)) {
|
|
6339
6512
|
log(`Creating dir ${dir}`);
|
|
6340
6513
|
fs__namespace.mkdirSync(dir, {
|
|
6341
6514
|
recursive: true
|
|
6342
6515
|
});
|
|
6343
6516
|
}
|
|
6344
|
-
const clientDir = Path
|
|
6517
|
+
const clientDir = Path.resolve(dir, "client");
|
|
6345
6518
|
const specPath = await getSpecPath(srcSpec, dir, log);
|
|
6346
6519
|
log(`Cleaning client output dir ${clientDir}`);
|
|
6347
6520
|
fs__namespace.rmSync(clientDir, {
|
|
@@ -6351,23 +6524,19 @@ const generateApiClient = async (params, log = console.log) => {
|
|
|
6351
6524
|
fs__namespace.mkdirSync(clientDir, {
|
|
6352
6525
|
recursive: true
|
|
6353
6526
|
});
|
|
6354
|
-
const
|
|
6355
|
-
|
|
6527
|
+
const { typesFilePath } = await generateOpenApiModel(
|
|
6528
|
+
specPath,
|
|
6356
6529
|
{
|
|
6357
|
-
|
|
6358
|
-
|
|
6359
|
-
outputDir: clientDir,
|
|
6360
|
-
quirks,
|
|
6361
|
-
ignoreOperationsWithTags,
|
|
6362
|
-
responseWrapper
|
|
6530
|
+
...config,
|
|
6531
|
+
dstDir: clientDir
|
|
6363
6532
|
},
|
|
6364
6533
|
log
|
|
6365
6534
|
);
|
|
6366
6535
|
if (zodSchemas?.enabled === false) return;
|
|
6367
6536
|
log("Generating Zod schemas");
|
|
6368
6537
|
await generateSchemas(
|
|
6369
|
-
|
|
6370
|
-
Path
|
|
6538
|
+
typesFilePath,
|
|
6539
|
+
Path.resolve(dir, "./zod.ts"),
|
|
6371
6540
|
zodSchemas
|
|
6372
6541
|
);
|
|
6373
6542
|
};
|
|
@@ -6377,7 +6546,7 @@ const getSpecPath = async (urlOrPath, dir, log) => {
|
|
|
6377
6546
|
throw new Error(`Spec file ${urlOrPath} does not exists`);
|
|
6378
6547
|
return urlOrPath;
|
|
6379
6548
|
}
|
|
6380
|
-
const specPath = Path
|
|
6549
|
+
const specPath = Path.resolve(dir, `spec.json`);
|
|
6381
6550
|
log(`Will download the API spec from ${urlOrPath} to ${specPath}`);
|
|
6382
6551
|
await downloadSpec(specPath, urlOrPath);
|
|
6383
6552
|
return specPath;
|