@sdk-it/spec 0.24.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +2 -2
- package/dist/lib/create-operation.d.ts +28 -0
- package/dist/lib/create-operation.d.ts.map +1 -0
- package/dist/lib/create-operation.js +62 -0
- package/dist/lib/create-operation.js.map +7 -0
- package/dist/lib/for-each-operation.d.ts.map +1 -1
- package/dist/lib/for-each-operation.js +0 -3
- package/dist/lib/for-each-operation.js.map +2 -2
- package/dist/lib/get-ref-usage.d.ts +1 -1
- package/dist/lib/get-ref-usage.js.map +1 -1
- package/dist/lib/guess/guess-resource.d.ts +7 -0
- package/dist/lib/guess/guess-resource.d.ts.map +1 -0
- package/dist/lib/guess/guess-resource.js +76 -0
- package/dist/lib/guess/guess-resource.js.map +7 -0
- package/dist/lib/metadata.d.ts +2 -2
- package/dist/lib/metadata.d.ts.map +1 -1
- package/dist/lib/metadata.js +17 -5
- package/dist/lib/metadata.js.map +2 -2
- package/dist/lib/metadata.test.d.ts +2 -0
- package/dist/lib/metadata.test.d.ts.map +1 -0
- package/dist/lib/metadata.test.js +359 -0
- package/dist/lib/metadata.test.js.map +7 -0
- package/dist/lib/operation.d.ts +1 -32
- package/dist/lib/operation.d.ts.map +1 -1
- package/dist/lib/operation.js +32 -312
- package/dist/lib/operation.js.map +2 -2
- package/dist/lib/options.d.ts +42 -1
- package/dist/lib/options.d.ts.map +1 -1
- package/dist/lib/options.js +23 -8
- package/dist/lib/options.js.map +2 -2
- package/dist/lib/pagination/guess-pagination.d.ts +50 -0
- package/dist/lib/pagination/guess-pagination.d.ts.map +1 -0
- package/dist/lib/pagination/guess-pagination.js +222 -0
- package/dist/lib/pagination/guess-pagination.js.map +7 -0
- package/dist/lib/pagination/pagination.d.ts +2 -49
- package/dist/lib/pagination/pagination.d.ts.map +1 -1
- package/dist/lib/pagination/pagination.js +43 -209
- package/dist/lib/pagination/pagination.js.map +2 -2
- package/dist/lib/pagination/pagination.test.js +2 -1
- package/dist/lib/pagination/pagination.test.js.map +2 -2
- package/dist/lib/security.d.ts +1 -1
- package/dist/lib/security.js.map +1 -1
- package/dist/lib/sidebar.d.ts +5 -24
- package/dist/lib/sidebar.d.ts.map +1 -1
- package/dist/lib/sidebar.js +22 -65
- package/dist/lib/sidebar.js.map +3 -3
- package/dist/lib/tune-request-body.d.ts +5 -0
- package/dist/lib/tune-request-body.d.ts.map +1 -0
- package/dist/lib/tune-request-body.js +88 -0
- package/dist/lib/tune-request-body.js.map +7 -0
- package/dist/lib/tune-response.d.ts +5 -0
- package/dist/lib/tune-response.d.ts.map +1 -0
- package/dist/lib/tune-response.js +102 -0
- package/dist/lib/tune-response.js.map +7 -0
- package/dist/lib/types.d.ts +8 -3
- package/dist/lib/types.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/lib/operation.js
CHANGED
|
@@ -1,21 +1,12 @@
|
|
|
1
1
|
import { methods } from "@sdk-it/core/paths.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
isSseContentType,
|
|
7
|
-
isSuccessStatusCode,
|
|
8
|
-
isTextContentType,
|
|
9
|
-
parseJsonContentType
|
|
10
|
-
} from "./is.js";
|
|
11
|
-
import {
|
|
12
|
-
coeraceConfig
|
|
13
|
-
} from "./options.js";
|
|
2
|
+
import { resolveRef } from "@sdk-it/core/ref.js";
|
|
3
|
+
import { snakecase } from "@sdk-it/core/utils.js";
|
|
4
|
+
import { toResource } from "./guess/guess-resource.js";
|
|
5
|
+
import { coeraceConfig } from "./options.js";
|
|
14
6
|
import { extractOverviewDocs } from "./overview-docs.js";
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
} from "./
|
|
18
|
-
import { securityToOptions } from "./security.js";
|
|
7
|
+
import { toPagination } from "./pagination/pagination.js";
|
|
8
|
+
import { tuneRequestBody } from "./tune-request-body.js";
|
|
9
|
+
import { resolveResponses } from "./tune-response.js";
|
|
19
10
|
import { expandSpec, fixSpec } from "./tune.js";
|
|
20
11
|
function findUniqueOperationId(usedOperationIds, initialId, choices, formatter) {
|
|
21
12
|
let counter = 1;
|
|
@@ -41,24 +32,15 @@ function augmentSpec(config, verbose = false) {
|
|
|
41
32
|
if ("x-sdk-augmented" in config.spec) {
|
|
42
33
|
return config.spec;
|
|
43
34
|
}
|
|
44
|
-
const spec = {
|
|
45
|
-
...config.spec,
|
|
46
|
-
components: {
|
|
47
|
-
...config.spec.components,
|
|
48
|
-
schemas: config.spec.components?.schemas ?? {},
|
|
49
|
-
securitySchemes: config.spec.components?.securitySchemes ?? {}
|
|
50
|
-
},
|
|
51
|
-
paths: config.spec.paths ?? {},
|
|
52
|
-
"x-docs": []
|
|
53
|
-
};
|
|
54
35
|
const paths = {};
|
|
55
36
|
const usedOperationIds = /* @__PURE__ */ new Set();
|
|
56
|
-
for (const [path, pathItem] of Object.entries(spec.paths)) {
|
|
37
|
+
for (const [path, pathItem] of Object.entries(coearcedConfig.spec.paths)) {
|
|
57
38
|
const fixedPath = path.replace(/:([^/]+)/g, "{$1}");
|
|
58
39
|
for (const [method, operation] of Object.entries(pathItem)) {
|
|
59
40
|
if (!methods.includes(method)) {
|
|
60
41
|
continue;
|
|
61
42
|
}
|
|
43
|
+
const { name } = toResource(operation, fixedPath, method);
|
|
62
44
|
const operationTag = coearcedConfig.tag(operation, fixedPath);
|
|
63
45
|
const operationId = findUniqueOperationId(
|
|
64
46
|
usedOperationIds,
|
|
@@ -74,21 +56,24 @@ function augmentSpec(config, verbose = false) {
|
|
|
74
56
|
const parameters = [
|
|
75
57
|
...pathItem.parameters ?? [],
|
|
76
58
|
...operation.parameters ?? []
|
|
77
|
-
].map((it) => resolveRef(spec, it));
|
|
59
|
+
].map((it) => resolveRef(coearcedConfig.spec, it));
|
|
78
60
|
const tunedOperation = {
|
|
79
61
|
...operation,
|
|
80
62
|
parameters,
|
|
63
|
+
"x-fn-name": name,
|
|
64
|
+
"x-fn-group": operationTag,
|
|
81
65
|
tags: [snakecase(operationTag)],
|
|
82
66
|
operationId,
|
|
83
67
|
responses: resolveResponses(
|
|
84
|
-
spec,
|
|
68
|
+
coearcedConfig.spec,
|
|
85
69
|
operationId,
|
|
86
70
|
operation,
|
|
87
|
-
|
|
71
|
+
coearcedConfig.responses
|
|
88
72
|
),
|
|
89
73
|
requestBody: tuneRequestBody(
|
|
90
|
-
spec,
|
|
74
|
+
coearcedConfig.spec,
|
|
91
75
|
operationId,
|
|
76
|
+
// camelcase(`${name} ${operationTag}`),
|
|
92
77
|
operation,
|
|
93
78
|
parameters,
|
|
94
79
|
operation.security ?? []
|
|
@@ -96,7 +81,10 @@ function augmentSpec(config, verbose = false) {
|
|
|
96
81
|
};
|
|
97
82
|
if (coearcedConfig.pagination.enabled) {
|
|
98
83
|
if (coearcedConfig.pagination.guess) {
|
|
99
|
-
tunedOperation["x-pagination"] = toPagination(
|
|
84
|
+
tunedOperation["x-pagination"] = toPagination(
|
|
85
|
+
coearcedConfig.spec,
|
|
86
|
+
tunedOperation
|
|
87
|
+
);
|
|
100
88
|
}
|
|
101
89
|
} else {
|
|
102
90
|
delete tunedOperation["x-pagination"];
|
|
@@ -109,294 +97,26 @@ function augmentSpec(config, verbose = false) {
|
|
|
109
97
|
});
|
|
110
98
|
}
|
|
111
99
|
}
|
|
112
|
-
fixSpec(
|
|
100
|
+
fixSpec(
|
|
101
|
+
coearcedConfig.spec,
|
|
102
|
+
Object.values(coearcedConfig.spec.components.schemas)
|
|
103
|
+
);
|
|
113
104
|
if (verbose) {
|
|
114
105
|
const newRefs = [];
|
|
115
|
-
expandSpec(
|
|
106
|
+
expandSpec(
|
|
107
|
+
coearcedConfig.spec,
|
|
108
|
+
coearcedConfig.spec.components.schemas,
|
|
109
|
+
newRefs
|
|
110
|
+
);
|
|
116
111
|
}
|
|
117
112
|
return {
|
|
118
|
-
...spec,
|
|
113
|
+
...coearcedConfig.spec,
|
|
119
114
|
paths,
|
|
120
|
-
"x-docs": extractOverviewDocs(spec),
|
|
115
|
+
"x-docs": extractOverviewDocs(coearcedConfig.spec),
|
|
121
116
|
"x-sdk-augmented": true
|
|
122
117
|
};
|
|
123
118
|
}
|
|
124
|
-
function toPagination(spec, tunedOperation) {
|
|
125
|
-
if (tunedOperation["x-pagination"]) {
|
|
126
|
-
return tunedOperation["x-pagination"];
|
|
127
|
-
}
|
|
128
|
-
const schema = getResponseContentSchema(
|
|
129
|
-
spec,
|
|
130
|
-
tunedOperation.responses["200"],
|
|
131
|
-
"application/json"
|
|
132
|
-
);
|
|
133
|
-
const pagination = guessPagination(
|
|
134
|
-
tunedOperation,
|
|
135
|
-
tunedOperation.requestBody ? getRequestContentSchema(
|
|
136
|
-
spec,
|
|
137
|
-
tunedOperation.requestBody,
|
|
138
|
-
"application/json"
|
|
139
|
-
) : void 0,
|
|
140
|
-
schema
|
|
141
|
-
);
|
|
142
|
-
if (pagination && pagination.type !== "none" && schema) {
|
|
143
|
-
return pagination;
|
|
144
|
-
}
|
|
145
|
-
return void 0;
|
|
146
|
-
}
|
|
147
|
-
function getResponseContentSchema(spec, response, type) {
|
|
148
|
-
if (!response) {
|
|
149
|
-
return void 0;
|
|
150
|
-
}
|
|
151
|
-
const content = response.content;
|
|
152
|
-
if (!content) {
|
|
153
|
-
return void 0;
|
|
154
|
-
}
|
|
155
|
-
for (const contentType in content) {
|
|
156
|
-
if (contentType.toLowerCase() === type.toLowerCase()) {
|
|
157
|
-
return isRef(content[contentType].schema) ? followRef(spec, content[contentType].schema.$ref) : content[contentType].schema;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
return void 0;
|
|
161
|
-
}
|
|
162
|
-
function getRequestContentSchema(spec, requestBody, type) {
|
|
163
|
-
const content = requestBody.content;
|
|
164
|
-
if (!content) {
|
|
165
|
-
return void 0;
|
|
166
|
-
}
|
|
167
|
-
for (const contentType in content) {
|
|
168
|
-
if (contentType.toLowerCase() === type.toLowerCase()) {
|
|
169
|
-
return isRef(content[contentType].schema) ? followRef(spec, content[contentType].schema.$ref) : content[contentType].schema;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
return void 0;
|
|
173
|
-
}
|
|
174
|
-
function resolveResponses(spec, operationId, operation, responsesConfig) {
|
|
175
|
-
const responses = operation.responses ?? {};
|
|
176
|
-
operation.responses ??= {};
|
|
177
|
-
let foundSuccessResponse = false;
|
|
178
|
-
for (const status in responses) {
|
|
179
|
-
if (status === "default") {
|
|
180
|
-
delete responses[status];
|
|
181
|
-
continue;
|
|
182
|
-
}
|
|
183
|
-
operation.responses[status] = structuredClone(
|
|
184
|
-
resolveRef(spec, responses[status])
|
|
185
|
-
);
|
|
186
|
-
if (isSuccessStatusCode(status)) {
|
|
187
|
-
foundSuccessResponse = true;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
if (!foundSuccessResponse) {
|
|
191
|
-
operation.responses["200"] = {
|
|
192
|
-
description: "OK",
|
|
193
|
-
content: {
|
|
194
|
-
"application/json": {
|
|
195
|
-
schema: {}
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
for (const status in operation.responses) {
|
|
201
|
-
const response = operation.responses[status];
|
|
202
|
-
const statusCode = +status;
|
|
203
|
-
const outputName = statusCode !== 200 ? pascalcase(operationId) + status : findUniqueSchemaName(spec, operationId, [
|
|
204
|
-
"output",
|
|
205
|
-
"payload",
|
|
206
|
-
"result"
|
|
207
|
-
]);
|
|
208
|
-
if (!responsesConfig?.flattenErrorResponses) {
|
|
209
|
-
if (!isSuccessStatusCode(status)) {
|
|
210
|
-
continue;
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
if (isEmpty(response.content)) {
|
|
214
|
-
response.content = {
|
|
215
|
-
"application/octet-stream": {}
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
response["x-response-name"] = outputName;
|
|
219
|
-
for (const [contentType, mediaType] of Object.entries(
|
|
220
|
-
response.content
|
|
221
|
-
)) {
|
|
222
|
-
if (isRef(mediaType.schema)) {
|
|
223
|
-
const { model } = parseRef(mediaType.schema.$ref);
|
|
224
|
-
Object.assign(spec.components.schemas[model], {
|
|
225
|
-
"x-responsebody": true
|
|
226
|
-
// do not assign response ref to a group
|
|
227
|
-
// because they are supposed to be in a separate file only inlined
|
|
228
|
-
// schemas are grouped by operationId
|
|
229
|
-
});
|
|
230
|
-
response["x-response-name"] = model;
|
|
231
|
-
continue;
|
|
232
|
-
}
|
|
233
|
-
if (isSseContentType(contentType)) {
|
|
234
|
-
continue;
|
|
235
|
-
}
|
|
236
|
-
if (parseJsonContentType(contentType)) {
|
|
237
|
-
if (isEmpty(mediaType.schema)) {
|
|
238
|
-
spec.components.schemas[outputName] = {
|
|
239
|
-
type: "object",
|
|
240
|
-
additionalProperties: true
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
} else {
|
|
244
|
-
spec.components.schemas[outputName] = {
|
|
245
|
-
...spec.components.schemas[outputName],
|
|
246
|
-
"x-stream": !isTextContentType(contentType)
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
spec.components.schemas[outputName] = {
|
|
250
|
-
...spec.components.schemas[outputName],
|
|
251
|
-
...mediaType.schema,
|
|
252
|
-
"x-responsebody": true,
|
|
253
|
-
"x-response-group": operationId
|
|
254
|
-
};
|
|
255
|
-
operation.responses[status].content[contentType].schema = {
|
|
256
|
-
$ref: `#/components/schemas/${outputName}`
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
return operation.responses;
|
|
261
|
-
}
|
|
262
|
-
function patchParameters(spec, schema, parameters, security) {
|
|
263
|
-
const securitySchemes = spec.components?.securitySchemes ?? {};
|
|
264
|
-
const securityOptions = securityToOptions(spec, security, securitySchemes);
|
|
265
|
-
let required = Array.isArray(schema.required) ? schema.required : [];
|
|
266
|
-
schema["x-properties"] ??= {};
|
|
267
|
-
for (const param of parameters) {
|
|
268
|
-
if (param.required) {
|
|
269
|
-
required.push(param.name);
|
|
270
|
-
}
|
|
271
|
-
schema["x-properties"][param.name] = {
|
|
272
|
-
"x-in": param.in,
|
|
273
|
-
...isRef(param.schema) ? followRef(spec, param.schema.$ref) : param.schema ?? { type: "string" }
|
|
274
|
-
};
|
|
275
|
-
}
|
|
276
|
-
for (const param of securityOptions) {
|
|
277
|
-
required = required.filter((name) => name !== param.name);
|
|
278
|
-
schema["x-properties"][param.name] = {
|
|
279
|
-
"x-in": "header",
|
|
280
|
-
...isRef(param.schema) ? followRef(spec, param.schema.$ref) : param.schema ?? { type: "string" }
|
|
281
|
-
};
|
|
282
|
-
}
|
|
283
|
-
schema["x-required"] = required;
|
|
284
|
-
}
|
|
285
|
-
function createOperation(options) {
|
|
286
|
-
const parameters = [];
|
|
287
|
-
if (!isEmpty(options.parameters)) {
|
|
288
|
-
const locations = ["query", "path", "header", "cookie"];
|
|
289
|
-
for (const location of locations) {
|
|
290
|
-
const locationParams = options.parameters[location];
|
|
291
|
-
if (locationParams) {
|
|
292
|
-
for (const [name, param] of Object.entries(locationParams)) {
|
|
293
|
-
parameters.push({
|
|
294
|
-
name,
|
|
295
|
-
in: location,
|
|
296
|
-
required: param.required ?? false,
|
|
297
|
-
schema: param.schema
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
const responses = {};
|
|
304
|
-
for (const [key, schema] of Object.entries(options.response)) {
|
|
305
|
-
const [statusCode, contentType] = key.split(/-(.*)/);
|
|
306
|
-
if (!contentType) {
|
|
307
|
-
throw new Error(
|
|
308
|
-
`Response key "${key}" must be in the format "statusCode-contentType"`
|
|
309
|
-
);
|
|
310
|
-
}
|
|
311
|
-
responses[statusCode] ??= {
|
|
312
|
-
description: `Response for ${statusCode}`,
|
|
313
|
-
content: {}
|
|
314
|
-
};
|
|
315
|
-
if (contentType === "headers") {
|
|
316
|
-
responses[statusCode].headers = schema;
|
|
317
|
-
} else {
|
|
318
|
-
responses[statusCode].content[contentType] = {
|
|
319
|
-
schema
|
|
320
|
-
};
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
let requestBody = void 0;
|
|
324
|
-
if (options.request) {
|
|
325
|
-
requestBody = { description: "Request body", content: {} };
|
|
326
|
-
for (const [contentType, schema] of Object.entries(options.request)) {
|
|
327
|
-
requestBody.content[contentType] = { schema };
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
return {
|
|
331
|
-
security: (options.security ?? []).map((name) => ({
|
|
332
|
-
[name]: []
|
|
333
|
-
})),
|
|
334
|
-
operationId: options.name,
|
|
335
|
-
tags: [options.group],
|
|
336
|
-
parameters,
|
|
337
|
-
responses,
|
|
338
|
-
requestBody
|
|
339
|
-
};
|
|
340
|
-
}
|
|
341
|
-
function tuneRequestBody(spec, operationId, operation, parameters, security) {
|
|
342
|
-
const inputName = findUniqueSchemaName(spec, operationId, [
|
|
343
|
-
"input",
|
|
344
|
-
"payload",
|
|
345
|
-
"request"
|
|
346
|
-
]);
|
|
347
|
-
const requestBody = isRef(operation.requestBody) ? followRef(spec, operation.requestBody.$ref) : operation.requestBody ?? {
|
|
348
|
-
content: {},
|
|
349
|
-
required: false
|
|
350
|
-
};
|
|
351
|
-
if (isEmpty(requestBody.content)) {
|
|
352
|
-
const schema = {
|
|
353
|
-
"x-inputname": inputName,
|
|
354
|
-
"x-requestbody": true
|
|
355
|
-
};
|
|
356
|
-
patchParameters(spec, schema, parameters, security);
|
|
357
|
-
const tuned = {
|
|
358
|
-
...requestBody,
|
|
359
|
-
content: {
|
|
360
|
-
"application/empty": {
|
|
361
|
-
schema: { $ref: `#/components/schemas/${inputName}` }
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
};
|
|
365
|
-
spec.components.schemas[inputName] = schema;
|
|
366
|
-
return tuned;
|
|
367
|
-
}
|
|
368
|
-
for (const contentType in requestBody.content) {
|
|
369
|
-
const mediaType = requestBody.content[contentType];
|
|
370
|
-
let schema;
|
|
371
|
-
switch (true) {
|
|
372
|
-
case isRef(mediaType.schema):
|
|
373
|
-
schema = followRef(spec, mediaType.schema.$ref);
|
|
374
|
-
break;
|
|
375
|
-
case isEmpty(mediaType.schema):
|
|
376
|
-
schema ??= {};
|
|
377
|
-
console.warn(
|
|
378
|
-
`Request body schema for content type "${contentType}" is empty.`
|
|
379
|
-
);
|
|
380
|
-
break;
|
|
381
|
-
default:
|
|
382
|
-
schema = mediaType.schema;
|
|
383
|
-
break;
|
|
384
|
-
}
|
|
385
|
-
patchParameters(spec, schema, parameters, security);
|
|
386
|
-
spec.components.schemas[inputName] = {
|
|
387
|
-
...schema,
|
|
388
|
-
"x-requestbody": true,
|
|
389
|
-
"x-inputname": inputName
|
|
390
|
-
};
|
|
391
|
-
requestBody.content[contentType].schema = {
|
|
392
|
-
$ref: `#/components/schemas/${inputName}`
|
|
393
|
-
};
|
|
394
|
-
}
|
|
395
|
-
return requestBody;
|
|
396
|
-
}
|
|
397
119
|
export {
|
|
398
|
-
augmentSpec
|
|
399
|
-
createOperation,
|
|
400
|
-
patchParameters
|
|
120
|
+
augmentSpec
|
|
401
121
|
};
|
|
402
122
|
//# sourceMappingURL=operation.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/operation.ts"],
|
|
4
|
-
"sourcesContent": ["import type {\n MediaTypeObject,\n OperationObject,\n ParameterObject,\n PathsObject,\n ReferenceObject,\n RequestBodyObject,\n ResponseObject,\n ResponsesObject,\n SchemaObject,\n SecurityRequirementObject,\n} from 'openapi3-ts/oas31';\n\nimport { type Method, methods } from '@sdk-it/core/paths.js';\nimport { followRef, isRef, parseRef, resolveRef } from '@sdk-it/core/ref.js';\nimport { isEmpty, pascalcase, snakecase } from '@sdk-it/core/utils.js';\n\nimport { findUniqueSchemaName } from './find-unique-schema-name.js';\nimport {\n isSseContentType,\n isSuccessStatusCode,\n isTextContentType,\n parseJsonContentType,\n} from './is.js';\nimport {\n type GenerateSdkConfig,\n type ResponsesConfig,\n coeraceConfig,\n} from './options.js';\nimport { extractOverviewDocs } from './overview-docs.js';\nimport {\n type PaginationGuess,\n guessPagination,\n} from './pagination/pagination.js';\nimport { securityToOptions } from './security.js';\nimport { expandSpec, fixSpec } from './tune.js';\nimport type {\n OurOpenAPIObject,\n OurRequestBodyObject,\n TunedOperationObject,\n} from './types.js';\n\nfunction findUniqueOperationId(\n usedOperationIds: Set<string>,\n initialId: string,\n choices: string[],\n formatter: (id: string) => string,\n) {\n let counter = 1;\n let uniqueOperationId = formatter(initialId);\n\n while (usedOperationIds.has(uniqueOperationId)) {\n // Try each prepend option\n const prependIndex = Math.min(counter - 1, choices.length - 1);\n const prefix = choices[prependIndex];\n\n if (prependIndex < choices.length - 1) {\n // Using one of the prepend options\n uniqueOperationId = formatter(\n `${prefix}${initialId.charAt(0).toUpperCase() + initialId.slice(1)}`,\n );\n } else {\n // If we've exhausted all prepend options, start adding numbers\n uniqueOperationId = formatter(\n `${prefix}${initialId.charAt(0).toUpperCase() + initialId.slice(1)}${counter - choices.length + 1}`,\n );\n }\n counter++;\n }\n\n return uniqueOperationId;\n}\n\nexport function augmentSpec(\n config: GenerateSdkConfig,\n verbose = false,\n): OurOpenAPIObject {\n const coearcedConfig = coeraceConfig(config);\n if ('x-sdk-augmented' in config.spec) {\n return config.spec as OurOpenAPIObject; // Already augmented\n }\n const spec: OurOpenAPIObject = {\n ...config.spec,\n components: {\n ...config.spec.components,\n schemas: config.spec.components?.schemas ?? {},\n securitySchemes: config.spec.components?.securitySchemes ?? {},\n },\n paths: config.spec.paths ?? {},\n 'x-docs': [],\n };\n\n const paths: PathsObject = {};\n const usedOperationIds = new Set<string>();\n\n for (const [path, pathItem] of Object.entries(spec.paths)) {\n // Convert Express-style routes (:param) to OpenAPI-style routes ({param})\n const fixedPath = path.replace(/:([^/]+)/g, '{$1}');\n for (const [method, operation] of Object.entries(pathItem) as [\n Method,\n OperationObject,\n ][]) {\n if (!methods.includes(method)) {\n continue;\n }\n const operationTag = coearcedConfig.tag(operation, fixedPath);\n const operationId = findUniqueOperationId(\n usedOperationIds,\n coearcedConfig.operationId(operation, fixedPath, method),\n [operationTag, method, fixedPath.split('/').filter(Boolean).join('')],\n (id) =>\n coearcedConfig.operationId(\n { ...operation, operationId: id },\n fixedPath,\n method,\n ),\n );\n usedOperationIds.add(operationId);\n\n const parameters = [\n ...(pathItem.parameters ?? []),\n ...(operation.parameters ?? []),\n ].map((it) => resolveRef<ParameterObject>(spec, it));\n\n const tunedOperation: TunedOperationObject = {\n ...operation,\n parameters,\n tags: [snakecase(operationTag)],\n operationId: operationId,\n responses: resolveResponses(\n spec,\n operationId,\n operation,\n config.responses,\n ),\n requestBody: tuneRequestBody(\n spec,\n operationId,\n operation,\n parameters,\n operation.security ?? [],\n ),\n };\n\n if (coearcedConfig.pagination.enabled) {\n if (coearcedConfig.pagination.guess) {\n tunedOperation['x-pagination'] = toPagination(spec, tunedOperation);\n }\n } else {\n delete tunedOperation['x-pagination'];\n }\n\n Object.assign(paths, {\n [fixedPath]: {\n ...paths[fixedPath],\n [method]: tunedOperation,\n },\n });\n }\n }\n\n fixSpec(spec, Object.values(spec.components.schemas));\n\n if (verbose) {\n const newRefs: { name: string; value: SchemaObject }[] = [];\n expandSpec(spec, spec.components.schemas, newRefs);\n }\n\n return {\n ...(spec as OurOpenAPIObject),\n paths,\n 'x-docs': extractOverviewDocs(spec),\n 'x-sdk-augmented': true,\n };\n}\n\nexport type OperationPagination = PaginationGuess & {\n items: string;\n};\n\nfunction toPagination(\n spec: OurOpenAPIObject,\n tunedOperation: TunedOperationObject,\n) {\n if (tunedOperation['x-pagination']) {\n return tunedOperation['x-pagination'];\n }\n const schema = getResponseContentSchema(\n spec,\n tunedOperation.responses['200'],\n 'application/json',\n );\n const pagination = guessPagination(\n tunedOperation,\n tunedOperation.requestBody\n ? getRequestContentSchema(\n spec,\n tunedOperation.requestBody,\n 'application/json',\n )\n : undefined,\n schema,\n );\n if (pagination && pagination.type !== 'none' && schema) {\n return pagination;\n }\n return undefined;\n}\n\nfunction getResponseContentSchema(\n spec: OurOpenAPIObject,\n response: ResponseObject,\n type: string,\n) {\n if (!response) {\n return undefined;\n }\n const content = response.content;\n if (!content) {\n return undefined;\n }\n for (const contentType in content) {\n if (contentType.toLowerCase() === type.toLowerCase()) {\n return isRef(content[contentType].schema)\n ? followRef<SchemaObject>(spec, content[contentType].schema.$ref)\n : content[contentType].schema;\n }\n }\n return undefined;\n}\n\nfunction getRequestContentSchema(\n spec: OurOpenAPIObject,\n requestBody: RequestBodyObject,\n type: string,\n) {\n const content = requestBody.content;\n if (!content) {\n return undefined;\n }\n for (const contentType in content) {\n if (contentType.toLowerCase() === type.toLowerCase()) {\n return isRef(content[contentType].schema)\n ? followRef<SchemaObject>(spec, content[contentType].schema.$ref)\n : content[contentType].schema;\n }\n }\n return undefined;\n}\n\nfunction resolveResponses(\n spec: OurOpenAPIObject,\n operationId: string,\n operation: OperationObject,\n responsesConfig?: ResponsesConfig,\n) {\n const responses = operation.responses ?? {};\n operation.responses ??= {};\n let foundSuccessResponse = false;\n for (const status in responses) {\n if (status === 'default') {\n delete responses[status]; // Skip default response\n continue;\n }\n // use structuredClone to avoid mutating a response object\n // that is referenced by multiple operations\n operation.responses[status] = structuredClone(\n resolveRef<ResponseObject>(spec, responses[status]),\n );\n\n if (isSuccessStatusCode(status)) {\n foundSuccessResponse = true;\n }\n }\n\n if (!foundSuccessResponse) {\n operation.responses['200'] = {\n description: 'OK',\n content: {\n 'application/json': {\n schema: {},\n },\n },\n };\n }\n\n for (const status in operation.responses) {\n const response = operation.responses[status] as ResponseObject;\n const statusCode = +status;\n\n const outputName =\n statusCode !== 200\n ? pascalcase(operationId) + status\n : findUniqueSchemaName(spec, operationId, [\n 'output',\n 'payload',\n 'result',\n ]);\n\n if (!responsesConfig?.flattenErrorResponses) {\n if (!isSuccessStatusCode(status)) {\n continue;\n }\n }\n\n if (isEmpty(response.content)) {\n response.content = {\n 'application/octet-stream': {},\n };\n }\n\n response['x-response-name'] = outputName;\n\n // if (isErrorStatusCode(status)) {\n // const mediaType = response.content?.['application/json'];\n // if (mediaType && isRef(mediaType.schema)) {\n // const { model } = parseRef(mediaType.schema.$ref);\n // Object.assign(config.spec.components.schemas[model], {\n // 'x-responsebody': true,\n // // do not assign response ref to a group\n // // because they are supposed to be in a separate file only inlined\n // // schemas are grouped by operationId\n // });\n // response['x-response-name'] = model;\n // }\n // continue;\n // }\n\n for (const [contentType, mediaType] of Object.entries(\n response.content as Record<string, MediaTypeObject>,\n )) {\n if (isRef(mediaType.schema)) {\n const { model } = parseRef(mediaType.schema.$ref);\n Object.assign(spec.components.schemas[model], {\n 'x-responsebody': true,\n // do not assign response ref to a group\n // because they are supposed to be in a separate file only inlined\n // schemas are grouped by operationId\n });\n response['x-response-name'] = model;\n continue;\n }\n if (isSseContentType(contentType)) {\n continue; // Skip SSE content types\n }\n if (parseJsonContentType(contentType)) {\n if (isEmpty(mediaType.schema)) {\n // add \"additionalProperties\" because we're certain this is a response body is of json type\n spec.components.schemas[outputName] = {\n type: 'object',\n additionalProperties: true,\n };\n }\n } else {\n spec.components.schemas[outputName] = {\n ...spec.components.schemas[outputName],\n 'x-stream': !isTextContentType(contentType),\n };\n }\n\n spec.components.schemas[outputName] = {\n ...spec.components.schemas[outputName],\n ...mediaType.schema,\n 'x-responsebody': true,\n 'x-response-group': operationId,\n };\n operation.responses[status].content[contentType].schema = {\n $ref: `#/components/schemas/${outputName}`,\n };\n }\n }\n\n return operation.responses;\n}\n\nexport function patchParameters(\n spec: OurOpenAPIObject,\n schema: SchemaObject,\n parameters: ParameterObject[],\n security: SecurityRequirementObject[],\n) {\n const securitySchemes = spec.components?.securitySchemes ?? {};\n const securityOptions = securityToOptions(spec, security, securitySchemes);\n\n let required = Array.isArray(schema.required) ? schema.required : [];\n schema['x-properties'] ??= {};\n for (const param of parameters) {\n if (param.required) {\n required.push(param.name);\n }\n schema['x-properties'][param.name] = {\n 'x-in': param.in,\n ...(isRef(param.schema)\n ? followRef<SchemaObject>(spec, param.schema.$ref)\n : (param.schema ?? { type: 'string' })),\n };\n }\n for (const param of securityOptions) {\n required = required.filter((name) => name !== param.name);\n schema['x-properties'][param.name] = {\n 'x-in': 'header',\n ...(isRef(param.schema)\n ? followRef<SchemaObject>(spec, param.schema.$ref)\n : (param.schema ?? { type: 'string' })),\n };\n }\n schema['x-required'] = required;\n}\n\nexport function createOperation(options: {\n name: string;\n group: string;\n security?: string[];\n parameters?: {\n query?: Record<string, { schema: SchemaObject; required?: boolean }>;\n path?: Record<string, { schema: SchemaObject; required?: boolean }>;\n header?: Record<string, { schema: SchemaObject; required?: boolean }>;\n cookie?: Record<string, { schema: SchemaObject; required?: boolean }>;\n };\n response: Record<string, SchemaObject | Record<string, SchemaObject>>; // Key is statusCode-contentType\n request?: Record<string, ReferenceObject>;\n}): TunedOperationObject {\n const parameters: ParameterObject[] = [];\n if (!isEmpty(options.parameters)) {\n const locations = ['query', 'path', 'header', 'cookie'] as const;\n for (const location of locations) {\n const locationParams = options.parameters[location];\n if (locationParams) {\n for (const [name, param] of Object.entries(locationParams)) {\n parameters.push({\n name,\n in: location,\n required: param.required ?? false,\n schema: param.schema,\n });\n }\n }\n }\n }\n const responses: ResponsesObject = {};\n\n for (const [key, schema] of Object.entries(options.response)) {\n const [statusCode, contentType] = key.split(/-(.*)/); // Split on the first dash\n if (!contentType) {\n throw new Error(\n `Response key \"${key}\" must be in the format \"statusCode-contentType\"`,\n );\n }\n responses[statusCode] ??= {\n description: `Response for ${statusCode}`,\n content: {},\n };\n\n if (contentType === 'headers') {\n responses[statusCode].headers = schema;\n } else {\n responses[statusCode].content[contentType] = {\n schema: schema,\n };\n }\n }\n\n let requestBody: OurRequestBodyObject | undefined = undefined;\n\n if (options.request) {\n requestBody = { description: 'Request body', content: {} };\n\n for (const [contentType, schema] of Object.entries(options.request)) {\n requestBody.content[contentType] = { schema: schema };\n }\n }\n return {\n security: (options.security ?? []).map((name) => ({\n [name]: [],\n })),\n operationId: options.name,\n tags: [options.group],\n parameters,\n responses,\n requestBody: requestBody as any,\n };\n}\n\nfunction tuneRequestBody(\n spec: OurOpenAPIObject,\n operationId: string,\n operation: OperationObject,\n parameters: ParameterObject[],\n security: SecurityRequirementObject[],\n): OurRequestBodyObject {\n const inputName = findUniqueSchemaName(spec, operationId, [\n 'input',\n 'payload',\n 'request',\n ]);\n const requestBody = isRef(operation.requestBody)\n ? followRef<RequestBodyObject>(spec, operation.requestBody.$ref)\n : (operation.requestBody ?? {\n content: {},\n required: false,\n });\n if (isEmpty(requestBody.content)) {\n const schema: SchemaObject = {\n 'x-inputname': inputName,\n 'x-requestbody': true,\n };\n patchParameters(spec, schema, parameters, security);\n const tuned: OurRequestBodyObject = {\n ...requestBody,\n content: {\n 'application/empty': {\n schema: { $ref: `#/components/schemas/${inputName}` },\n },\n },\n };\n\n spec.components.schemas[inputName] = schema;\n return tuned;\n }\n for (const contentType in requestBody.content) {\n const mediaType = requestBody.content[contentType];\n let schema: SchemaObject | undefined;\n\n switch (true) {\n case isRef(mediaType.schema):\n schema = followRef<SchemaObject>(spec, mediaType.schema.$ref);\n // we cannot use the model name as inputName as it might be used in other places that are not request bodies\n // inputName = parseRef(mediaType.schema.$ref).model;\n break;\n case isEmpty(mediaType.schema):\n schema ??= {}; // default to empty schema if not defined\n console.warn(\n `Request body schema for content type \"${contentType}\" is empty.`,\n );\n break;\n default:\n schema = mediaType.schema;\n break;\n }\n\n patchParameters(spec, schema, parameters, security);\n spec.components.schemas[inputName] = {\n ...schema,\n 'x-requestbody': true,\n 'x-inputname': inputName,\n };\n\n requestBody.content[contentType].schema = {\n $ref: `#/components/schemas/${inputName}`,\n };\n }\n return requestBody as OurRequestBodyObject;\n}\n"],
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import type {\n OperationObject,\n ParameterObject,\n PathsObject,\n SchemaObject,\n} from 'openapi3-ts/oas31';\n\nimport { type Method, methods } from '@sdk-it/core/paths.js';\nimport { resolveRef } from '@sdk-it/core/ref.js';\nimport { snakecase } from '@sdk-it/core/utils.js';\n\nimport { toResource } from './guess/guess-resource.js';\nimport { type GenerateSdkConfig, coeraceConfig } from './options.js';\nimport { extractOverviewDocs } from './overview-docs.js';\nimport { toPagination } from './pagination/pagination.js';\nimport { tuneRequestBody } from './tune-request-body.js';\nimport { resolveResponses } from './tune-response.js';\nimport { expandSpec, fixSpec } from './tune.js';\nimport type { OurOpenAPIObject, TunedOperationObject } from './types.js';\n\nfunction findUniqueOperationId(\n usedOperationIds: Set<string>,\n initialId: string,\n choices: string[],\n formatter: (id: string) => string,\n) {\n let counter = 1;\n let uniqueOperationId = formatter(initialId);\n\n while (usedOperationIds.has(uniqueOperationId)) {\n // Try each prepend option\n const prependIndex = Math.min(counter - 1, choices.length - 1);\n const prefix = choices[prependIndex];\n\n if (prependIndex < choices.length - 1) {\n // Using one of the prepend options\n uniqueOperationId = formatter(\n `${prefix}${initialId.charAt(0).toUpperCase() + initialId.slice(1)}`,\n );\n } else {\n // If we've exhausted all prepend options, start adding numbers\n uniqueOperationId = formatter(\n `${prefix}${initialId.charAt(0).toUpperCase() + initialId.slice(1)}${counter - choices.length + 1}`,\n );\n }\n counter++;\n }\n\n return uniqueOperationId;\n}\n\nexport function augmentSpec(\n config: GenerateSdkConfig,\n verbose = false,\n): OurOpenAPIObject {\n const coearcedConfig = coeraceConfig(config);\n if ('x-sdk-augmented' in config.spec) {\n return config.spec as OurOpenAPIObject; // Already augmented\n }\n\n const paths: PathsObject = {};\n const usedOperationIds = new Set<string>();\n\n for (const [path, pathItem] of Object.entries(coearcedConfig.spec.paths)) {\n // Convert Express-style routes (:param) to OpenAPI-style routes ({param})\n const fixedPath = path.replace(/:([^/]+)/g, '{$1}');\n for (const [method, operation] of Object.entries(pathItem) as [\n Method,\n OperationObject,\n ][]) {\n if (!methods.includes(method)) {\n continue;\n }\n const { name } = toResource(operation, fixedPath, method);\n const operationTag = coearcedConfig.tag(operation, fixedPath);\n const operationId = findUniqueOperationId(\n usedOperationIds,\n coearcedConfig.operationId(operation, fixedPath, method),\n [operationTag, method, fixedPath.split('/').filter(Boolean).join('')],\n (id) =>\n coearcedConfig.operationId(\n { ...operation, operationId: id },\n fixedPath,\n method,\n ),\n );\n usedOperationIds.add(operationId);\n\n const parameters = [\n ...(pathItem.parameters ?? []),\n ...(operation.parameters ?? []),\n ].map((it) => resolveRef<ParameterObject>(coearcedConfig.spec, it));\n\n const tunedOperation: TunedOperationObject = {\n ...operation,\n parameters,\n 'x-fn-name': name,\n 'x-fn-group': operationTag,\n tags: [snakecase(operationTag)],\n operationId: operationId,\n responses: resolveResponses(\n coearcedConfig.spec,\n operationId,\n operation,\n coearcedConfig.responses,\n ),\n requestBody: tuneRequestBody(\n coearcedConfig.spec,\n operationId,\n // camelcase(`${name} ${operationTag}`),\n operation,\n parameters,\n operation.security ?? [],\n ),\n };\n\n if (coearcedConfig.pagination.enabled) {\n if (coearcedConfig.pagination.guess) {\n tunedOperation['x-pagination'] = toPagination(\n coearcedConfig.spec,\n tunedOperation,\n );\n }\n } else {\n delete tunedOperation['x-pagination'];\n }\n\n Object.assign(paths, {\n [fixedPath]: {\n ...paths[fixedPath],\n [method]: tunedOperation,\n },\n });\n }\n }\n\n fixSpec(\n coearcedConfig.spec,\n Object.values(coearcedConfig.spec.components.schemas),\n );\n\n if (verbose) {\n const newRefs: { name: string; value: SchemaObject }[] = [];\n expandSpec(\n coearcedConfig.spec,\n coearcedConfig.spec.components.schemas,\n newRefs,\n );\n }\n\n return {\n ...coearcedConfig.spec,\n paths,\n 'x-docs': extractOverviewDocs(coearcedConfig.spec),\n 'x-sdk-augmented': true,\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/lib/options.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export interface ResponsesConfig {
|
|
|
12
12
|
export type PaginationConfig = {
|
|
13
13
|
guess?: boolean;
|
|
14
14
|
};
|
|
15
|
+
export declare function cleanOperationId(operationId: string): string;
|
|
15
16
|
export declare const defaults: Partial<GenerateSdkConfig> & Required<Pick<GenerateSdkConfig, 'operationId' | 'tag'>>;
|
|
16
17
|
export declare function coeraceConfig(config: GenerateSdkConfig): {
|
|
17
18
|
pagination: {
|
|
@@ -19,7 +20,47 @@ export declare function coeraceConfig(config: GenerateSdkConfig): {
|
|
|
19
20
|
enabled: boolean;
|
|
20
21
|
};
|
|
21
22
|
responses: ResponsesConfig;
|
|
22
|
-
spec:
|
|
23
|
+
spec: {
|
|
24
|
+
components: {
|
|
25
|
+
schemas: {
|
|
26
|
+
[schema: string]: import("openapi3-ts/oas31").SchemaObject | import("openapi3-ts/oas31").ReferenceObject;
|
|
27
|
+
};
|
|
28
|
+
securitySchemes: {
|
|
29
|
+
[securityScheme: string]: import("openapi3-ts/oas31").ReferenceObject | import("openapi3-ts/oas31").SecuritySchemeObject;
|
|
30
|
+
};
|
|
31
|
+
responses?: {
|
|
32
|
+
[response: string]: import("openapi3-ts/oas31").ResponseObject | import("openapi3-ts/oas31").ReferenceObject;
|
|
33
|
+
};
|
|
34
|
+
parameters?: {
|
|
35
|
+
[parameter: string]: import("openapi3-ts/oas31").ParameterObject | import("openapi3-ts/oas31").ReferenceObject;
|
|
36
|
+
};
|
|
37
|
+
examples?: {
|
|
38
|
+
[example: string]: import("openapi3-ts/oas31").ExampleObject | import("openapi3-ts/oas31").ReferenceObject;
|
|
39
|
+
};
|
|
40
|
+
requestBodies?: {
|
|
41
|
+
[request: string]: import("openapi3-ts/oas31").RequestBodyObject | import("openapi3-ts/oas31").ReferenceObject;
|
|
42
|
+
};
|
|
43
|
+
headers?: {
|
|
44
|
+
[header: string]: import("openapi3-ts/oas31").HeaderObject | import("openapi3-ts/oas31").ReferenceObject;
|
|
45
|
+
};
|
|
46
|
+
links?: {
|
|
47
|
+
[link: string]: import("openapi3-ts/oas31").LinkObject | import("openapi3-ts/oas31").ReferenceObject;
|
|
48
|
+
};
|
|
49
|
+
callbacks?: {
|
|
50
|
+
[callback: string]: import("openapi3-ts/oas31").CallbackObject | import("openapi3-ts/oas31").ReferenceObject;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
paths: import("openapi3-ts/oas31").PathsObject;
|
|
54
|
+
'x-docs': never[];
|
|
55
|
+
'x-tagGroups': any;
|
|
56
|
+
openapi: string;
|
|
57
|
+
info: import("openapi3-ts/oas31").InfoObject;
|
|
58
|
+
servers?: import("openapi3-ts/oas31").ServerObject[];
|
|
59
|
+
security?: import("openapi3-ts/oas31").SecurityRequirementObject[];
|
|
60
|
+
tags?: import("openapi3-ts/oas31").TagObject[];
|
|
61
|
+
externalDocs?: import("openapi3-ts/oas31").ExternalDocumentationObject;
|
|
62
|
+
webhooks?: import("openapi3-ts/oas31").PathsObject;
|
|
63
|
+
};
|
|
23
64
|
operationId: (operation: OperationObject, path: string, method: string) => string;
|
|
24
65
|
tag: (operation: OperationObject, path: string) => string;
|
|
25
66
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/lib/options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/lib/options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAMxE,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,aAAa,CAAC;IACpB,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,UAAU,CAAC,EAAE,gBAAgB,GAAG,KAAK,CAAC;IACtC,WAAW,CAAC,EAAE,CACZ,SAAS,EAAE,eAAe,EAC1B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,KACX,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,CAAC,SAAS,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CAC5D;AACD,MAAM,WAAW,eAAe;IAC9B,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,UAOnD;AAED,eAAO,MAAM,QAAQ,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAC/C,QAAQ,CAAC,IAAI,CAAC,iBAAiB,EAAE,aAAa,GAAG,KAAK,CAAC,CAkBxD,CAAC;AAEF,wBAAgB,aAAa,CAAC,MAAM,EAAE,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6BA5CxC,eAAe,QACpB,MAAM,UACJ,MAAM,KACX,MAAM;qBACO,eAAe,QAAQ,MAAM,KAAK,MAAM;EA+D3D;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,gBAAgB,GAAG,SAAS,GAAG,KAAK;;;EAmB9C"}
|
package/dist/lib/options.js
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { camelcase } from "stringcase";
|
|
2
2
|
import { determineGenericTag, sanitizeTag } from "./tag.js";
|
|
3
|
+
function cleanOperationId(operationId) {
|
|
4
|
+
return camelcase(
|
|
5
|
+
operationId.split("#").pop().replace(/-(?=\d)/g, "")
|
|
6
|
+
);
|
|
7
|
+
}
|
|
3
8
|
const defaults = {
|
|
4
9
|
operationId: (operation, path, method) => {
|
|
5
10
|
if (operation.operationId) {
|
|
6
|
-
return
|
|
7
|
-
operation.operationId.split("#").pop().replace(/-(?=\d)/g, "")
|
|
8
|
-
);
|
|
9
|
-
}
|
|
10
|
-
const metadata = operation["x-oaiMeta"];
|
|
11
|
-
if (metadata && metadata.name) {
|
|
12
|
-
return camelcase(metadata.name);
|
|
11
|
+
return cleanOperationId(operation.operationId);
|
|
13
12
|
}
|
|
14
13
|
return camelcase(
|
|
15
14
|
[method, ...path.replace(/[\\/\\{\\}]/g, " ").split(" ")].filter(Boolean).join(" ").trim()
|
|
@@ -23,7 +22,22 @@ function coeraceConfig(config) {
|
|
|
23
22
|
return {
|
|
24
23
|
pagination: coearcePaginationConfig(config.pagination),
|
|
25
24
|
responses: config.responses ?? {},
|
|
26
|
-
spec:
|
|
25
|
+
spec: {
|
|
26
|
+
...config.spec,
|
|
27
|
+
components: {
|
|
28
|
+
...config.spec.components,
|
|
29
|
+
schemas: config.spec.components?.schemas ?? {},
|
|
30
|
+
securitySchemes: config.spec.components?.securitySchemes ?? {}
|
|
31
|
+
},
|
|
32
|
+
paths: config.spec.paths ?? {},
|
|
33
|
+
"x-docs": [],
|
|
34
|
+
"x-tagGroups": config.spec["x-tagGroups"] ?? [
|
|
35
|
+
{
|
|
36
|
+
name: "API",
|
|
37
|
+
tags: config.spec.tags?.map((tag) => tag.name) ?? []
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
},
|
|
27
41
|
operationId: config.operationId ?? defaults.operationId,
|
|
28
42
|
tag: config.tag ?? defaults.tag
|
|
29
43
|
};
|
|
@@ -47,6 +61,7 @@ function coearcePaginationConfig(options) {
|
|
|
47
61
|
};
|
|
48
62
|
}
|
|
49
63
|
export {
|
|
64
|
+
cleanOperationId,
|
|
50
65
|
coearcePaginationConfig,
|
|
51
66
|
coeraceConfig,
|
|
52
67
|
defaults
|
package/dist/lib/options.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/lib/options.ts"],
|
|
4
|
-
"sourcesContent": ["import type { OpenAPIObject, OperationObject } from 'openapi3-ts/oas31';\nimport { camelcase } from 'stringcase';\n\nimport { determineGenericTag, sanitizeTag } from './tag.js';\n\nexport interface GenerateSdkConfig {\n spec: OpenAPIObject;\n responses?: ResponsesConfig;\n pagination?: PaginationConfig | false;\n operationId?: (\n operation: OperationObject,\n path: string,\n method: string,\n ) => string;\n tag?: (operation: OperationObject, path: string) => string;\n}\nexport interface ResponsesConfig {\n flattenErrorResponses?: boolean;\n}\n\nexport type PaginationConfig = {\n guess?: boolean;\n};\n\nexport
|
|
5
|
-
"mappings": "AACA,SAAS,iBAAiB;AAE1B,SAAS,qBAAqB,mBAAmB;
|
|
4
|
+
"sourcesContent": ["import type { OpenAPIObject, OperationObject } from 'openapi3-ts/oas31';\nimport { camelcase } from 'stringcase';\n\nimport { determineGenericTag, sanitizeTag } from './tag.js';\nimport type { OurOpenAPIObject } from './types.js';\n\nexport interface GenerateSdkConfig {\n spec: OpenAPIObject;\n responses?: ResponsesConfig;\n pagination?: PaginationConfig | false;\n operationId?: (\n operation: OperationObject,\n path: string,\n method: string,\n ) => string;\n tag?: (operation: OperationObject, path: string) => string;\n}\nexport interface ResponsesConfig {\n flattenErrorResponses?: boolean;\n}\n\nexport type PaginationConfig = {\n guess?: boolean;\n};\n\nexport function cleanOperationId(operationId: string) {\n return camelcase(\n operationId\n .split('#')\n .pop()!\n .replace(/-(?=\\d)/g, ''),\n );\n}\n\nexport const defaults: Partial<GenerateSdkConfig> &\n Required<Pick<GenerateSdkConfig, 'operationId' | 'tag'>> = {\n operationId: (operation, path, method) => {\n if (operation.operationId) {\n return cleanOperationId(operation.operationId);\n }\n\n return camelcase(\n [method, ...path.replace(/[\\\\/\\\\{\\\\}]/g, ' ').split(' ')]\n .filter(Boolean)\n .join(' ')\n .trim(),\n );\n },\n tag: (operation, path) => {\n return operation.tags?.[0]\n ? sanitizeTag(operation.tags?.[0])\n : determineGenericTag(path, operation);\n },\n};\n\nexport function coeraceConfig(config: GenerateSdkConfig) {\n return {\n pagination: coearcePaginationConfig(config.pagination),\n responses: config.responses ?? {},\n spec: {\n ...config.spec,\n components: {\n ...config.spec.components,\n schemas: config.spec.components?.schemas ?? {},\n securitySchemes: config.spec.components?.securitySchemes ?? {},\n },\n paths: config.spec.paths ?? {},\n 'x-docs': [],\n 'x-tagGroups': config.spec['x-tagGroups'] ?? [\n {\n name: 'API',\n tags: config.spec.tags?.map((tag) => tag.name) ?? [],\n },\n ],\n } satisfies OurOpenAPIObject,\n operationId: config.operationId ?? defaults.operationId,\n tag: config.tag ?? defaults.tag,\n };\n}\n\nexport function coearcePaginationConfig(\n options: PaginationConfig | undefined | false,\n) {\n if (options === undefined) {\n return {\n guess: true,\n enabled: true,\n };\n }\n if (options === false) {\n return {\n enabled: false,\n guess: false,\n };\n }\n // If options is true, we assume pagination is enabled with guessing\n return {\n guess: options.guess ?? true,\n enabled: true,\n };\n}\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,iBAAiB;AAE1B,SAAS,qBAAqB,mBAAmB;AAsB1C,SAAS,iBAAiB,aAAqB;AACpD,SAAO;AAAA,IACL,YACG,MAAM,GAAG,EACT,IAAI,EACJ,QAAQ,YAAY,EAAE;AAAA,EAC3B;AACF;AAEO,MAAM,WACgD;AAAA,EAC3D,aAAa,CAAC,WAAW,MAAM,WAAW;AACxC,QAAI,UAAU,aAAa;AACzB,aAAO,iBAAiB,UAAU,WAAW;AAAA,IAC/C;AAEA,WAAO;AAAA,MACL,CAAC,QAAQ,GAAG,KAAK,QAAQ,gBAAgB,GAAG,EAAE,MAAM,GAAG,CAAC,EACrD,OAAO,OAAO,EACd,KAAK,GAAG,EACR,KAAK;AAAA,IACV;AAAA,EACF;AAAA,EACA,KAAK,CAAC,WAAW,SAAS;AACxB,WAAO,UAAU,OAAO,CAAC,IACrB,YAAY,UAAU,OAAO,CAAC,CAAC,IAC/B,oBAAoB,MAAM,SAAS;AAAA,EACzC;AACF;AAEO,SAAS,cAAc,QAA2B;AACvD,SAAO;AAAA,IACL,YAAY,wBAAwB,OAAO,UAAU;AAAA,IACrD,WAAW,OAAO,aAAa,CAAC;AAAA,IAChC,MAAM;AAAA,MACJ,GAAG,OAAO;AAAA,MACV,YAAY;AAAA,QACV,GAAG,OAAO,KAAK;AAAA,QACf,SAAS,OAAO,KAAK,YAAY,WAAW,CAAC;AAAA,QAC7C,iBAAiB,OAAO,KAAK,YAAY,mBAAmB,CAAC;AAAA,MAC/D;AAAA,MACA,OAAO,OAAO,KAAK,SAAS,CAAC;AAAA,MAC7B,UAAU,CAAC;AAAA,MACX,eAAe,OAAO,KAAK,aAAa,KAAK;AAAA,QAC3C;AAAA,UACE,MAAM;AAAA,UACN,MAAM,OAAO,KAAK,MAAM,IAAI,CAAC,QAAQ,IAAI,IAAI,KAAK,CAAC;AAAA,QACrD;AAAA,MACF;AAAA,IACF;AAAA,IACA,aAAa,OAAO,eAAe,SAAS;AAAA,IAC5C,KAAK,OAAO,OAAO,SAAS;AAAA,EAC9B;AACF;AAEO,SAAS,wBACd,SACA;AACA,MAAI,YAAY,QAAW;AACzB,WAAO;AAAA,MACL,OAAO;AAAA,MACP,SAAS;AAAA,IACX;AAAA,EACF;AACA,MAAI,YAAY,OAAO;AACrB,WAAO;AAAA,MACL,SAAS;AAAA,MACT,OAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AAAA,IACL,OAAO,QAAQ,SAAS;AAAA,IACxB,SAAS;AAAA,EACX;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|