@sdk-it/spec 0.22.0 → 0.23.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 +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -2629
- package/dist/index.js.map +4 -4
- package/dist/lib/find-polymorphic-varients.d.ts +1 -1
- package/dist/lib/find-polymorphic-varients.d.ts.map +1 -1
- package/dist/lib/find-polymorphic-varients.js +291 -0
- package/dist/lib/find-polymorphic-varients.js.map +7 -0
- package/dist/lib/find-polymorphic-varients.test.js +615 -0
- package/dist/lib/find-polymorphic-varients.test.js.map +7 -0
- package/dist/lib/find-unique-schema-name.js +16 -0
- package/dist/lib/find-unique-schema-name.js.map +7 -0
- package/dist/lib/for-each-operation.d.ts +3 -0
- package/dist/lib/for-each-operation.d.ts.map +1 -0
- package/dist/lib/for-each-operation.js +30 -0
- package/dist/lib/for-each-operation.js.map +7 -0
- package/dist/lib/format-name.js +86 -0
- package/dist/lib/format-name.js.map +7 -0
- package/dist/lib/get-ref-usage.d.ts +1 -1
- package/dist/lib/get-ref-usage.d.ts.map +1 -1
- package/dist/lib/get-ref-usage.js +52 -0
- package/dist/lib/get-ref-usage.js.map +7 -0
- package/dist/lib/is-primitive-schema.js +12 -0
- package/dist/lib/is-primitive-schema.js.map +7 -0
- package/dist/lib/is.d.ts +14 -0
- package/dist/lib/is.d.ts.map +1 -0
- package/dist/lib/is.js +71 -0
- package/dist/lib/is.js.map +7 -0
- package/dist/lib/loaders/load-spec.js +25 -0
- package/dist/lib/loaders/load-spec.js.map +7 -0
- package/dist/lib/loaders/local-loader.js +20 -0
- package/dist/lib/loaders/local-loader.js.map +7 -0
- package/dist/lib/loaders/postman/postman-converter.js +483 -0
- package/dist/lib/loaders/postman/postman-converter.js.map +7 -0
- package/dist/lib/loaders/postman/spec-types.js +1 -0
- package/dist/lib/loaders/postman/spec-types.js.map +7 -0
- package/dist/lib/loaders/remote-loader.js +29 -0
- package/dist/lib/loaders/remote-loader.js.map +7 -0
- package/dist/lib/metadata.js +51 -0
- package/dist/lib/metadata.js.map +7 -0
- package/dist/lib/operation.d.ts +4 -75
- package/dist/lib/operation.d.ts.map +1 -1
- package/dist/lib/operation.js +402 -0
- package/dist/lib/operation.js.map +7 -0
- package/dist/lib/operation.test.js +261 -0
- package/dist/lib/operation.test.js.map +7 -0
- package/dist/lib/options.d.ts +30 -0
- package/dist/lib/options.d.ts.map +1 -0
- package/dist/lib/options.js +54 -0
- package/dist/lib/options.js.map +7 -0
- package/dist/lib/overview-docs.d.ts +4 -0
- package/dist/lib/overview-docs.d.ts.map +1 -0
- package/dist/lib/overview-docs.js +276 -0
- package/dist/lib/overview-docs.js.map +7 -0
- package/dist/lib/pagination/pagination-result.js +235 -0
- package/dist/lib/pagination/pagination-result.js.map +7 -0
- package/dist/lib/pagination/pagination-result.test.js +548 -0
- package/dist/lib/pagination/pagination-result.test.js.map +7 -0
- package/dist/lib/pagination/pagination.d.ts +1 -1
- package/dist/lib/pagination/pagination.d.ts.map +1 -1
- package/dist/lib/pagination/pagination.js +222 -0
- package/dist/lib/pagination/pagination.js.map +7 -0
- package/dist/lib/pagination/pagination.test.js +378 -0
- package/dist/lib/pagination/pagination.test.js.map +7 -0
- package/dist/lib/reserved-keywords.d.ts +6 -0
- package/dist/lib/reserved-keywords.d.ts.map +1 -0
- package/dist/lib/reserved-keywords.js +71 -0
- package/dist/lib/reserved-keywords.js.map +7 -0
- package/dist/lib/security.d.ts +6 -4
- package/dist/lib/security.d.ts.map +1 -1
- package/dist/lib/security.js +75 -0
- package/dist/lib/security.js.map +7 -0
- package/dist/lib/sidebar.d.ts +5 -3
- package/dist/lib/sidebar.d.ts.map +1 -1
- package/dist/lib/sidebar.js +79 -0
- package/dist/lib/sidebar.js.map +7 -0
- package/dist/lib/tag.d.ts +19 -0
- package/dist/lib/tag.d.ts.map +1 -0
- package/dist/lib/tag.js +132 -0
- package/dist/lib/tag.js.map +7 -0
- package/dist/lib/tune.d.ts +1 -1
- package/dist/lib/tune.d.ts.map +1 -1
- package/dist/lib/tune.js +291 -0
- package/dist/lib/tune.js.map +7 -0
- package/dist/lib/tune.test.js +60 -0
- package/dist/lib/tune.test.js.map +7 -0
- package/dist/lib/types.d.ts +37 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +1 -0
- package/dist/lib/types.js.map +7 -0
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -1,2630 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
var reservedNames = /* @__PURE__ */ new Set(["Function", "Error"]);
|
|
21
|
-
function findUniqueSchemaName(spec, initialName, potentialSuffixList, fallback) {
|
|
22
|
-
spec.components ??= {};
|
|
23
|
-
spec.components.schemas ??= {};
|
|
24
|
-
let name = pascalcase(initialName);
|
|
25
|
-
while (spec.components.schemas[name] || reservedNames.has(name)) {
|
|
26
|
-
const suffix = potentialSuffixList.shift();
|
|
27
|
-
name = pascalcase(joinSkipDigits([name, suffix || ""], " "));
|
|
28
|
-
}
|
|
29
|
-
return initialName === name ? fallback ?? name : name;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// packages/spec/src/lib/format-name.ts
|
|
33
|
-
var reservedWords = /* @__PURE__ */ new Set([
|
|
34
|
-
"abstract",
|
|
35
|
-
"as",
|
|
36
|
-
"assert",
|
|
37
|
-
"async",
|
|
38
|
-
"await",
|
|
39
|
-
"break",
|
|
40
|
-
"case",
|
|
41
|
-
"catch",
|
|
42
|
-
"class",
|
|
43
|
-
"const",
|
|
44
|
-
"continue",
|
|
45
|
-
"default",
|
|
46
|
-
"deferred",
|
|
47
|
-
"do",
|
|
48
|
-
"dynamic",
|
|
49
|
-
"else",
|
|
50
|
-
"enum",
|
|
51
|
-
"export",
|
|
52
|
-
"extends",
|
|
53
|
-
"extension",
|
|
54
|
-
"external",
|
|
55
|
-
"factory",
|
|
56
|
-
"final",
|
|
57
|
-
"finally",
|
|
58
|
-
"for",
|
|
59
|
-
"Function",
|
|
60
|
-
"get",
|
|
61
|
-
"set",
|
|
62
|
-
"hide",
|
|
63
|
-
"if",
|
|
64
|
-
"default",
|
|
65
|
-
"new",
|
|
66
|
-
"implements",
|
|
67
|
-
"import",
|
|
68
|
-
"in",
|
|
69
|
-
"interface",
|
|
70
|
-
"is",
|
|
71
|
-
"library",
|
|
72
|
-
"mixin",
|
|
73
|
-
"new",
|
|
74
|
-
"null",
|
|
75
|
-
"on",
|
|
76
|
-
"operator",
|
|
77
|
-
"part",
|
|
78
|
-
"required",
|
|
79
|
-
"rethrow",
|
|
80
|
-
"return",
|
|
81
|
-
"hide",
|
|
82
|
-
"show"
|
|
83
|
-
]);
|
|
84
|
-
var STARTS_WITH_DIGITS_PATTERN = /^-?\d/;
|
|
85
|
-
var FIRST_DASH = /^_/;
|
|
86
|
-
var LAST_DASH = /_$/;
|
|
87
|
-
var ONLY_ENGLISH = /(^\$)|(\+)|(-)|[^A-Za-z0-9]+/g;
|
|
88
|
-
var formatName = (it) => {
|
|
89
|
-
if (reservedWords.has(it)) {
|
|
90
|
-
return `$${it}`;
|
|
91
|
-
}
|
|
92
|
-
if (typeof it === "number") {
|
|
93
|
-
if (Math.sign(it) === -1) {
|
|
94
|
-
return `$_${Math.abs(it)}`;
|
|
95
|
-
}
|
|
96
|
-
return `$${it}`;
|
|
97
|
-
}
|
|
98
|
-
if (typeof it === "string") {
|
|
99
|
-
if (STARTS_WITH_DIGITS_PATTERN.test(it)) {
|
|
100
|
-
if (Math.sign(parseInt(it, 10)) === -1) {
|
|
101
|
-
return `$_${Math.abs(parseInt(it, 10))}`;
|
|
102
|
-
}
|
|
103
|
-
return `$${it}`;
|
|
104
|
-
}
|
|
105
|
-
return it.replace(ONLY_ENGLISH, (match) => {
|
|
106
|
-
if (match === "-" && match === it[0])
|
|
107
|
-
return "desc_";
|
|
108
|
-
if (match === "+")
|
|
109
|
-
return "_plus_";
|
|
110
|
-
if (match === "$" && match === it[0])
|
|
111
|
-
return "$";
|
|
112
|
-
return "_";
|
|
113
|
-
}).replace(FIRST_DASH, "").replace(LAST_DASH, "");
|
|
114
|
-
}
|
|
115
|
-
return String(it);
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
// packages/spec/src/lib/tune.ts
|
|
119
|
-
function fixSpec(spec, schemas, visited = /* @__PURE__ */ new Set()) {
|
|
120
|
-
for (const schema of schemas) {
|
|
121
|
-
if (isRef(schema))
|
|
122
|
-
continue;
|
|
123
|
-
if (!isEmpty(schema.properties)) {
|
|
124
|
-
schema.type = "object";
|
|
125
|
-
delete schema.oneOf;
|
|
126
|
-
delete schema.anyOf;
|
|
127
|
-
fixSpec(spec, Object.values(schema.properties), visited);
|
|
128
|
-
}
|
|
129
|
-
if (!isEmpty(schema["x-properties"])) {
|
|
130
|
-
fixSpec(spec, Object.values(schema["x-properties"]), visited);
|
|
131
|
-
}
|
|
132
|
-
if (!isEmpty(schema.items)) {
|
|
133
|
-
delete schema.oneOf;
|
|
134
|
-
delete schema.anyOf;
|
|
135
|
-
schema.type = "array";
|
|
136
|
-
fixSpec(spec, [schema.items], visited);
|
|
137
|
-
const items = resolveRef(spec, schema.items);
|
|
138
|
-
if (Array.isArray(items.default)) {
|
|
139
|
-
schema.default ??= structuredClone(items.default);
|
|
140
|
-
}
|
|
141
|
-
delete items.default;
|
|
142
|
-
}
|
|
143
|
-
if (!isEmpty(schema.anyOf) && !isEmpty(schema.oneOf)) {
|
|
144
|
-
delete schema.anyOf;
|
|
145
|
-
}
|
|
146
|
-
if (isEmpty(schema.enum)) {
|
|
147
|
-
delete schema.enum;
|
|
148
|
-
}
|
|
149
|
-
if (!isEmpty(schema.enum)) {
|
|
150
|
-
if (schema.enum.length === 1) {
|
|
151
|
-
schema.const = schema.enum[0];
|
|
152
|
-
delete schema.enum;
|
|
153
|
-
} else {
|
|
154
|
-
const valuesSet = /* @__PURE__ */ new Set();
|
|
155
|
-
const valuesList = [];
|
|
156
|
-
for (const it of schema.enum) {
|
|
157
|
-
const formattedValue = formatName(snakecase(formatName(it)));
|
|
158
|
-
if (!valuesSet.has(formattedValue)) {
|
|
159
|
-
valuesSet.add(formattedValue);
|
|
160
|
-
valuesList.push(it);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
schema.enum = valuesList;
|
|
164
|
-
}
|
|
165
|
-
delete schema.allOf;
|
|
166
|
-
}
|
|
167
|
-
if (schema.const !== void 0) {
|
|
168
|
-
schema.default = schema.const;
|
|
169
|
-
}
|
|
170
|
-
if (!isEmpty(schema.allOf)) {
|
|
171
|
-
const schemas2 = schema.allOf;
|
|
172
|
-
const resolved = schemas2.map((it) => resolveRef(spec, it));
|
|
173
|
-
const hasObjects = resolved.some((it) => it.type === "object");
|
|
174
|
-
const hasOtherTypes = resolved.some(
|
|
175
|
-
(it) => it.type && it.type !== "object"
|
|
176
|
-
);
|
|
177
|
-
if (hasObjects && hasOtherTypes) {
|
|
178
|
-
assert(false, `allOf must be an object`);
|
|
179
|
-
}
|
|
180
|
-
merge(
|
|
181
|
-
schema,
|
|
182
|
-
...resolved.map((it) => {
|
|
183
|
-
fixSpec(spec, [it], visited);
|
|
184
|
-
return it;
|
|
185
|
-
})
|
|
186
|
-
);
|
|
187
|
-
delete schema.allOf;
|
|
188
|
-
} else {
|
|
189
|
-
delete schema.allOf;
|
|
190
|
-
}
|
|
191
|
-
if (schema.type === "object" && isEmpty(schema.properties) && typeof schema.additionalProperties === "object" && !isEmpty(schema.additionalProperties) && notRef(schema.additionalProperties) && !isEmpty(schema.additionalProperties.properties)) {
|
|
192
|
-
fixSpec(
|
|
193
|
-
spec,
|
|
194
|
-
Object.values(schema.additionalProperties.properties),
|
|
195
|
-
visited
|
|
196
|
-
);
|
|
197
|
-
Object.assign(schema, schema.additionalProperties);
|
|
198
|
-
delete schema.additionalProperties;
|
|
199
|
-
}
|
|
200
|
-
for (const kind of ["oneOf", "anyOf"]) {
|
|
201
|
-
if (!isEmpty(schema[kind])) {
|
|
202
|
-
delete schema.type;
|
|
203
|
-
fixSpec(spec, schema[kind], visited);
|
|
204
|
-
if (isEmpty(schema[kind])) {
|
|
205
|
-
continue;
|
|
206
|
-
}
|
|
207
|
-
let enumSchemaIndex = -1;
|
|
208
|
-
const enumValues = [];
|
|
209
|
-
for (let idx = 0; idx < schema[kind].length; idx++) {
|
|
210
|
-
const item = schema[kind][idx];
|
|
211
|
-
if (notRef(item) && item.type === "string") {
|
|
212
|
-
if (item.enum && item.enum.length > 1) {
|
|
213
|
-
enumValues.push(...item.enum);
|
|
214
|
-
if (enumSchemaIndex === -1) {
|
|
215
|
-
enumSchemaIndex = idx;
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
if (enumSchemaIndex !== -1) {
|
|
221
|
-
const enumSchema = schema[kind][enumSchemaIndex];
|
|
222
|
-
if (notRef(enumSchema)) {
|
|
223
|
-
enumSchema.enum = uniq(enumValues);
|
|
224
|
-
}
|
|
225
|
-
schema[kind] = schema[kind].filter(
|
|
226
|
-
(it, idx) => idx === enumSchemaIndex || isRef(it)
|
|
227
|
-
);
|
|
228
|
-
}
|
|
229
|
-
const otherTypes = schema[kind].filter(
|
|
230
|
-
(it) => resolveRef(spec, it).type !== "null"
|
|
231
|
-
);
|
|
232
|
-
if (otherTypes.length === 1) {
|
|
233
|
-
Object.assign(schema, otherTypes[0]);
|
|
234
|
-
delete schema[kind];
|
|
235
|
-
continue;
|
|
236
|
-
}
|
|
237
|
-
schema["x-varients"] = findVarients(spec, schema[kind]);
|
|
238
|
-
} else {
|
|
239
|
-
delete schema[kind];
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
function expandSpec(spec, schemas, refs) {
|
|
245
|
-
for (const [name, schema] of Object.entries(schemas)) {
|
|
246
|
-
if (isRef(schema))
|
|
247
|
-
continue;
|
|
248
|
-
if (!isEmpty(schema.properties)) {
|
|
249
|
-
if (!isEmpty(schema.oneOf)) {
|
|
250
|
-
for (const oneOfIdx in schema.oneOf) {
|
|
251
|
-
const oneOf = schema.oneOf[oneOfIdx];
|
|
252
|
-
if (isRef(oneOf))
|
|
253
|
-
continue;
|
|
254
|
-
for (const key of ["properties", "x-properties"]) {
|
|
255
|
-
if (!isEmpty(oneOf.required) && schema[key]) {
|
|
256
|
-
schema.oneOf[oneOfIdx] = schema[key][oneOf.required[0]];
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
delete schema.type;
|
|
261
|
-
expandSpec(spec, schemas, refs);
|
|
262
|
-
continue;
|
|
263
|
-
}
|
|
264
|
-
if (schema.additionalProperties) {
|
|
265
|
-
continue;
|
|
266
|
-
}
|
|
267
|
-
spec.components.schemas[name] = schema;
|
|
268
|
-
const properties = schema.properties;
|
|
269
|
-
for (const [propName, value] of Object.entries(properties)) {
|
|
270
|
-
if (isRef(value))
|
|
271
|
-
continue;
|
|
272
|
-
const fixedPropName = propName.replace("[]", "");
|
|
273
|
-
const refName = pascalcase2(joinSkipDigits2([name, fixedPropName], " "));
|
|
274
|
-
if (!isEmpty(value.properties)) {
|
|
275
|
-
spec.components.schemas[refName] = value;
|
|
276
|
-
properties[propName] = { $ref: `#/components/schemas/${refName}` };
|
|
277
|
-
expandSpec(spec, { [refName]: value }, refs);
|
|
278
|
-
} else if (!isEmpty(value.oneOf)) {
|
|
279
|
-
expandOneOf(spec, name, value, refs, "oneOf");
|
|
280
|
-
spec.components.schemas[refName] = value;
|
|
281
|
-
properties[propName] = { $ref: `#/components/schemas/${refName}` };
|
|
282
|
-
expandSpec(spec, { [refName]: value }, refs);
|
|
283
|
-
} else if (!isEmpty(value.anyOf)) {
|
|
284
|
-
expandOneOf(spec, name, value, refs, "anyOf");
|
|
285
|
-
spec.components.schemas[refName] = value;
|
|
286
|
-
properties[propName] = { $ref: `#/components/schemas/${refName}` };
|
|
287
|
-
expandSpec(spec, { [refName]: value }, refs);
|
|
288
|
-
} else {
|
|
289
|
-
expandSpec(spec, { [refName]: value }, refs);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
continue;
|
|
293
|
-
}
|
|
294
|
-
if (!isEmpty(schema["x-properties"])) {
|
|
295
|
-
spec.components.schemas[name] = schema;
|
|
296
|
-
const properties = schema["x-properties"];
|
|
297
|
-
for (const [propName, value] of Object.entries(properties)) {
|
|
298
|
-
if (isRef(value))
|
|
299
|
-
continue;
|
|
300
|
-
const fixedPropName = propName.replace("[]", "");
|
|
301
|
-
const refName = pascalcase2(joinSkipDigits2([name, fixedPropName], " "));
|
|
302
|
-
if (!isEmpty(value.properties)) {
|
|
303
|
-
spec.components.schemas[refName] = value;
|
|
304
|
-
properties[propName] = { $ref: `#/components/schemas/${refName}` };
|
|
305
|
-
expandSpec(spec, { [refName]: value }, refs);
|
|
306
|
-
} else if (!isEmpty(value.oneOf)) {
|
|
307
|
-
expandOneOf(spec, name, value, refs, "oneOf");
|
|
308
|
-
spec.components.schemas[refName] = value;
|
|
309
|
-
properties[propName] = { $ref: `#/components/schemas/${refName}` };
|
|
310
|
-
expandSpec(spec, { [refName]: value }, refs);
|
|
311
|
-
} else if (!isEmpty(value.anyOf)) {
|
|
312
|
-
expandOneOf(spec, name, value, refs, "anyOf");
|
|
313
|
-
spec.components.schemas[refName] = value;
|
|
314
|
-
properties[propName] = { $ref: `#/components/schemas/${refName}` };
|
|
315
|
-
expandSpec(spec, { [refName]: value }, refs);
|
|
316
|
-
} else {
|
|
317
|
-
expandSpec(spec, { [refName]: value }, refs);
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
continue;
|
|
321
|
-
}
|
|
322
|
-
if (schema.type === "array") {
|
|
323
|
-
if (isRef(schema.items))
|
|
324
|
-
continue;
|
|
325
|
-
if (isEmpty(schema.items))
|
|
326
|
-
continue;
|
|
327
|
-
const refName = findUniqueSchemaName(spec, name, ["Item", "Entry"]);
|
|
328
|
-
if (schema.items.type === "object") {
|
|
329
|
-
spec.components.schemas[refName] = schema.items;
|
|
330
|
-
expandSpec(spec, { [refName]: schema.items }, refs);
|
|
331
|
-
schema.items = { $ref: `#/components/schemas/${refName}` };
|
|
332
|
-
continue;
|
|
333
|
-
}
|
|
334
|
-
if (schema.items.type === "array") {
|
|
335
|
-
expandSpec(spec, { [refName]: schema.items }, refs);
|
|
336
|
-
continue;
|
|
337
|
-
}
|
|
338
|
-
if (!isEmpty(schema.items.oneOf)) {
|
|
339
|
-
expandOneOf(spec, refName, schema.items, refs, "oneOf");
|
|
340
|
-
continue;
|
|
341
|
-
}
|
|
342
|
-
if (!isEmpty(schema.items.anyOf)) {
|
|
343
|
-
expandOneOf(spec, refName, schema.items, refs, "anyOf");
|
|
344
|
-
continue;
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
if (!isEmpty(schema.oneOf)) {
|
|
348
|
-
expandOneOf(spec, name, schema, refs, "oneOf");
|
|
349
|
-
continue;
|
|
350
|
-
}
|
|
351
|
-
if (!isEmpty(schema.anyOf)) {
|
|
352
|
-
expandOneOf(spec, name, schema, refs, "anyOf");
|
|
353
|
-
continue;
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
function expandOneOf(spec, name, schema, refs, kind) {
|
|
358
|
-
const varients = schema["x-varients"];
|
|
359
|
-
if (!varients || varients.length === 0) {
|
|
360
|
-
console.warn(
|
|
361
|
-
`No varients found for ${name}. This might be an error in the OpenAPI spec.`
|
|
362
|
-
);
|
|
363
|
-
}
|
|
364
|
-
varients.forEach((varient) => {
|
|
365
|
-
const varientSchema = schema[kind][varient.position];
|
|
366
|
-
if (isRef(varientSchema))
|
|
367
|
-
return;
|
|
368
|
-
const refName = findUniqueSchemaName(
|
|
369
|
-
spec,
|
|
370
|
-
pascalcase2(`${name} ${varient.name}`),
|
|
371
|
-
["Varient"]
|
|
372
|
-
);
|
|
373
|
-
if (varientSchema.type === "object") {
|
|
374
|
-
expandSpec(spec, { [refName]: varientSchema }, refs);
|
|
375
|
-
schema[kind][varient.position] = {
|
|
376
|
-
$ref: `#/components/schemas/${refName}`
|
|
377
|
-
};
|
|
378
|
-
} else {
|
|
379
|
-
expandSpec(spec, { [refName]: varientSchema }, refs);
|
|
380
|
-
}
|
|
381
|
-
});
|
|
382
|
-
}
|
|
383
|
-
function coerceTypes(schema, excludeNull = true) {
|
|
384
|
-
const types = Array.isArray(schema.type) ? schema.type : schema.type ? [schema.type] : [];
|
|
385
|
-
if (excludeNull) {
|
|
386
|
-
return types.filter((type) => type !== "null");
|
|
387
|
-
}
|
|
388
|
-
return types;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
// packages/spec/src/lib/find-polymorphic-varients.ts
|
|
392
|
-
var groupSchemasByType = (spec, schemas) => {
|
|
393
|
-
const groups = schemas.reduce((acc, schema, index) => {
|
|
394
|
-
if (isRef2(schema)) {
|
|
395
|
-
const referenced = resolveRef2(spec, schema);
|
|
396
|
-
const [type2] = coerceTypes(referenced, false);
|
|
397
|
-
acc[type2] ??= [];
|
|
398
|
-
acc[type2].push({ schema: referenced, position: index });
|
|
399
|
-
return acc;
|
|
400
|
-
}
|
|
401
|
-
if (isRef2(schema.items)) {
|
|
402
|
-
const referenced = resolveRef2(spec, schema.items);
|
|
403
|
-
acc.array ??= [];
|
|
404
|
-
acc.array.push({
|
|
405
|
-
schema: { ...schema, items: referenced },
|
|
406
|
-
position: index
|
|
407
|
-
});
|
|
408
|
-
return acc;
|
|
409
|
-
}
|
|
410
|
-
if (schema.oneOf) {
|
|
411
|
-
acc["oneOf"] ??= [];
|
|
412
|
-
acc["oneOf"].push({ schema: schema.oneOf, position: index });
|
|
413
|
-
return acc;
|
|
414
|
-
}
|
|
415
|
-
if (schema.anyOf) {
|
|
416
|
-
acc["oneOf"] ??= [];
|
|
417
|
-
acc["oneOf"].push({ schema: schema.anyOf, position: index });
|
|
418
|
-
return acc;
|
|
419
|
-
}
|
|
420
|
-
if (schema.const) {
|
|
421
|
-
switch (typeof schema.const) {
|
|
422
|
-
case "string":
|
|
423
|
-
acc.string ??= [];
|
|
424
|
-
acc.string.push({ schema, position: index });
|
|
425
|
-
return acc;
|
|
426
|
-
case "number":
|
|
427
|
-
acc.number ??= [];
|
|
428
|
-
acc.number.push({ schema, position: index });
|
|
429
|
-
return acc;
|
|
430
|
-
case "boolean":
|
|
431
|
-
acc.boolean ??= [];
|
|
432
|
-
acc.boolean.push({ schema, position: index });
|
|
433
|
-
return acc;
|
|
434
|
-
default:
|
|
435
|
-
throw new Error(
|
|
436
|
-
`Unsupported const type: ${typeof schema.const} for ${schema.const}`
|
|
437
|
-
);
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
const [type] = coerceTypes(schema, false);
|
|
441
|
-
acc[type] ??= [];
|
|
442
|
-
acc[type].push({ schema, position: index });
|
|
443
|
-
return acc;
|
|
444
|
-
}, {});
|
|
445
|
-
return groups;
|
|
446
|
-
};
|
|
447
|
-
function findVarients(spec, schemas) {
|
|
448
|
-
let varients = [];
|
|
449
|
-
const schemasByType = groupSchemasByType(spec, schemas);
|
|
450
|
-
if (!isEmpty2(schemasByType.string)) {
|
|
451
|
-
for (const { schema, position } of schemasByType.string) {
|
|
452
|
-
if (schema.const !== void 0) {
|
|
453
|
-
varients.push({
|
|
454
|
-
name: schema.const || "empty",
|
|
455
|
-
type: "string",
|
|
456
|
-
position,
|
|
457
|
-
priority: 100 - varients.length
|
|
458
|
-
});
|
|
459
|
-
continue;
|
|
460
|
-
}
|
|
461
|
-
if (schema.format) {
|
|
462
|
-
varients.push({
|
|
463
|
-
name: camelcase(schema.format),
|
|
464
|
-
type: "string",
|
|
465
|
-
position,
|
|
466
|
-
priority: 90 - varients.length
|
|
467
|
-
});
|
|
468
|
-
continue;
|
|
469
|
-
}
|
|
470
|
-
varients.push({ name: "textContent", type: "string", position });
|
|
471
|
-
}
|
|
472
|
-
varients = uniqBy(varients, (it) => it.name);
|
|
473
|
-
}
|
|
474
|
-
if (!isEmpty2(schemasByType.number) || !isEmpty2(schemasByType.integer)) {
|
|
475
|
-
const schemas2 = [
|
|
476
|
-
...schemasByType.number ?? [],
|
|
477
|
-
...schemasByType.integer ?? []
|
|
478
|
-
];
|
|
479
|
-
for (const { schema, position } of schemas2) {
|
|
480
|
-
if (schema.format === "int64") {
|
|
481
|
-
varients.push({
|
|
482
|
-
name: "integer",
|
|
483
|
-
type: "number",
|
|
484
|
-
position,
|
|
485
|
-
priority: 90 - varients.length
|
|
486
|
-
});
|
|
487
|
-
continue;
|
|
488
|
-
}
|
|
489
|
-
if (schema.format === "float") {
|
|
490
|
-
varients.push({
|
|
491
|
-
name: "float",
|
|
492
|
-
type: "number",
|
|
493
|
-
position,
|
|
494
|
-
priority: 90 - varients.length
|
|
495
|
-
});
|
|
496
|
-
continue;
|
|
497
|
-
}
|
|
498
|
-
if (schema.format === "double") {
|
|
499
|
-
varients.push({
|
|
500
|
-
name: "double",
|
|
501
|
-
type: "number",
|
|
502
|
-
position,
|
|
503
|
-
priority: 90 - varients.length
|
|
504
|
-
});
|
|
505
|
-
continue;
|
|
506
|
-
}
|
|
507
|
-
varients.push({ name: "number", type: "number", position });
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
if (!isEmpty2(schemasByType.array)) {
|
|
511
|
-
for (const { schema, position } of schemasByType.array) {
|
|
512
|
-
const items = schema.items;
|
|
513
|
-
if (!items) {
|
|
514
|
-
varients.push({ name: "any", type: "array", position });
|
|
515
|
-
continue;
|
|
516
|
-
}
|
|
517
|
-
const [type] = coerceTypes(items);
|
|
518
|
-
if (type === "string") {
|
|
519
|
-
varients.push({
|
|
520
|
-
name: "textList",
|
|
521
|
-
type: "array",
|
|
522
|
-
subtype: "string",
|
|
523
|
-
position
|
|
524
|
-
});
|
|
525
|
-
continue;
|
|
526
|
-
}
|
|
527
|
-
if (type === "number") {
|
|
528
|
-
varients.push({
|
|
529
|
-
name: "numList",
|
|
530
|
-
type: "array",
|
|
531
|
-
subtype: "number",
|
|
532
|
-
position
|
|
533
|
-
});
|
|
534
|
-
continue;
|
|
535
|
-
}
|
|
536
|
-
if (type === "integer") {
|
|
537
|
-
varients.push({
|
|
538
|
-
name: "intList",
|
|
539
|
-
type: "array",
|
|
540
|
-
subtype: "integer",
|
|
541
|
-
position
|
|
542
|
-
});
|
|
543
|
-
continue;
|
|
544
|
-
}
|
|
545
|
-
if (type === "object") {
|
|
546
|
-
const subvarients = findVarients(spec, [items]);
|
|
547
|
-
for (const subvarient of subvarients) {
|
|
548
|
-
varients.push({
|
|
549
|
-
...subvarient,
|
|
550
|
-
type: "array",
|
|
551
|
-
position
|
|
552
|
-
});
|
|
553
|
-
}
|
|
554
|
-
continue;
|
|
555
|
-
}
|
|
556
|
-
if (type === "array") {
|
|
557
|
-
const subvarients = findVarients(spec, [items]);
|
|
558
|
-
for (const subvarient of subvarients) {
|
|
559
|
-
varients.push({
|
|
560
|
-
...subvarient,
|
|
561
|
-
name: `${subvarient.name}Matrix`,
|
|
562
|
-
type: "array",
|
|
563
|
-
position
|
|
564
|
-
});
|
|
565
|
-
}
|
|
566
|
-
continue;
|
|
567
|
-
}
|
|
568
|
-
varients.push({ name: "list", type: "array", position });
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
if (!isEmpty2(schemasByType.$ref)) {
|
|
572
|
-
const subvarients = findVarients(
|
|
573
|
-
spec,
|
|
574
|
-
schemasByType.$ref.map((it) => resolveRef2(spec, it.schema))
|
|
575
|
-
);
|
|
576
|
-
varients.push(
|
|
577
|
-
...subvarients.map((it) => ({
|
|
578
|
-
...it
|
|
579
|
-
}))
|
|
580
|
-
);
|
|
581
|
-
}
|
|
582
|
-
if (!isEmpty2(schemasByType.oneOf)) {
|
|
583
|
-
for (const { schema, position } of schemasByType.oneOf) {
|
|
584
|
-
const subvarients = findVarients(spec, schema);
|
|
585
|
-
varients.push(
|
|
586
|
-
...subvarients.map((it) => ({
|
|
587
|
-
...it,
|
|
588
|
-
position
|
|
589
|
-
}))
|
|
590
|
-
);
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
const matrix = [];
|
|
594
|
-
for (const { schema, position } of schemasByType.object ?? []) {
|
|
595
|
-
if (schema.additionalProperties || isEmpty2({ ...schema.properties, ...schema["x-properties"] })) {
|
|
596
|
-
continue;
|
|
597
|
-
}
|
|
598
|
-
for (const key of ["properties", "x-properties"]) {
|
|
599
|
-
if (!schema[key])
|
|
600
|
-
continue;
|
|
601
|
-
const list = Object.entries(schema[key]).map(([name, schemaOrRef]) => {
|
|
602
|
-
const schema2 = resolveRef2(spec, schemaOrRef);
|
|
603
|
-
name = schema2.const ?? schema2.enum?.[0] ?? name;
|
|
604
|
-
if (schema2.type === "string") {
|
|
605
|
-
return {
|
|
606
|
-
priority: schema2.const !== void 0 || schema2.enum?.[0] !== void 0 ? 100 - matrix.length : void 0,
|
|
607
|
-
static: true,
|
|
608
|
-
subtype: "string",
|
|
609
|
-
source: name,
|
|
610
|
-
name,
|
|
611
|
-
type: "object",
|
|
612
|
-
position
|
|
613
|
-
};
|
|
614
|
-
}
|
|
615
|
-
return {
|
|
616
|
-
subtype: "string",
|
|
617
|
-
source: name,
|
|
618
|
-
name,
|
|
619
|
-
type: "object",
|
|
620
|
-
position
|
|
621
|
-
};
|
|
622
|
-
});
|
|
623
|
-
matrix.push([...new Set(list)].sort((a) => a.static ? -1 : 1));
|
|
624
|
-
}
|
|
625
|
-
if (matrix.length === 0) {
|
|
626
|
-
throw new Error(
|
|
627
|
-
"No valid objects found. Please check your OpenAPI spec."
|
|
628
|
-
);
|
|
629
|
-
}
|
|
630
|
-
let discriminatorProp;
|
|
631
|
-
const firstRow = matrix[0];
|
|
632
|
-
for (const prop of firstRow) {
|
|
633
|
-
const existsCrossAllRows = matrix.every((row) => row.includes(prop));
|
|
634
|
-
if (existsCrossAllRows) {
|
|
635
|
-
discriminatorProp = prop;
|
|
636
|
-
break;
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
for (const row of matrix) {
|
|
641
|
-
for (const prop of row) {
|
|
642
|
-
const isUnique = matrix.every(
|
|
643
|
-
(it) => it === row ? true : !it.some((p) => p.name === prop.name)
|
|
644
|
-
);
|
|
645
|
-
if (isUnique) {
|
|
646
|
-
varients.push(prop);
|
|
647
|
-
break;
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
return varients.sort((a, b) => {
|
|
652
|
-
const aHasPriority = a.priority !== void 0;
|
|
653
|
-
const bHasPriority = b.priority !== void 0;
|
|
654
|
-
if (aHasPriority && bHasPriority) {
|
|
655
|
-
if (a.priority !== b.priority) {
|
|
656
|
-
return b.priority - a.priority;
|
|
657
|
-
}
|
|
658
|
-
return a.position - b.position;
|
|
659
|
-
} else if (aHasPriority) {
|
|
660
|
-
return -1;
|
|
661
|
-
} else if (bHasPriority) {
|
|
662
|
-
return 1;
|
|
663
|
-
} else {
|
|
664
|
-
return 0;
|
|
665
|
-
}
|
|
666
|
-
});
|
|
667
|
-
}
|
|
668
|
-
function findPolymorphicVarients(spec, schemas) {
|
|
669
|
-
const varients = findVarients(spec, schemas);
|
|
670
|
-
return Object.values(groupBy(varients, (it) => "-" + it.position)).map(
|
|
671
|
-
(group) => {
|
|
672
|
-
return (group ?? [])[0];
|
|
673
|
-
}
|
|
674
|
-
);
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
// packages/spec/src/lib/get-ref-usage.ts
|
|
678
|
-
import { isEmpty as isEmpty5, isRef as isRef5, parseRef as parseRef2, pascalcase as pascalcase4 } from "@sdk-it/core";
|
|
679
|
-
|
|
680
|
-
// packages/spec/src/lib/operation.ts
|
|
681
|
-
import { camelcase as camelcase2 } from "stringcase";
|
|
682
|
-
import { methods as methods2 } from "@sdk-it/core/paths.js";
|
|
683
|
-
import { followRef, isRef as isRef4, parseRef, resolveRef as resolveRef4 } from "@sdk-it/core/ref.js";
|
|
684
|
-
import { isEmpty as isEmpty4, pascalcase as pascalcase3, snakecase as snakecase2 } from "@sdk-it/core/utils.js";
|
|
685
|
-
|
|
686
|
-
// packages/spec/src/lib/pagination/pagination.ts
|
|
687
|
-
import { isRef as isRef3 } from "@sdk-it/core/ref.js";
|
|
688
|
-
import { isEmpty as isEmpty3 } from "@sdk-it/core/utils.js";
|
|
689
|
-
|
|
690
|
-
// packages/spec/src/lib/pagination/pagination-result.ts
|
|
691
|
-
import pluralize from "pluralize";
|
|
692
|
-
var PRIMARY_TOP_TIER_KEYWORDS = [
|
|
693
|
-
"data",
|
|
694
|
-
"items",
|
|
695
|
-
"results",
|
|
696
|
-
"value"
|
|
697
|
-
];
|
|
698
|
-
var PRIMARY_OTHER_KEYWORDS = [
|
|
699
|
-
"records",
|
|
700
|
-
"content",
|
|
701
|
-
"list",
|
|
702
|
-
"payload",
|
|
703
|
-
"entities",
|
|
704
|
-
"collection",
|
|
705
|
-
"users",
|
|
706
|
-
"products",
|
|
707
|
-
"orders",
|
|
708
|
-
"bookings",
|
|
709
|
-
"articles",
|
|
710
|
-
"posts",
|
|
711
|
-
"documents",
|
|
712
|
-
"events"
|
|
713
|
-
];
|
|
714
|
-
var SECONDARY_KEYWORDS = ["entries", "rows", "elements"];
|
|
715
|
-
var PLURAL_DEPRIORITIZE_LIST = [
|
|
716
|
-
"status",
|
|
717
|
-
"success",
|
|
718
|
-
"address",
|
|
719
|
-
"details",
|
|
720
|
-
"properties",
|
|
721
|
-
"params",
|
|
722
|
-
"headers",
|
|
723
|
-
"cookies",
|
|
724
|
-
"series",
|
|
725
|
-
"links",
|
|
726
|
-
"meta",
|
|
727
|
-
"metadata",
|
|
728
|
-
"statistics",
|
|
729
|
-
"settings",
|
|
730
|
-
"options",
|
|
731
|
-
"permissions",
|
|
732
|
-
"credentials",
|
|
733
|
-
"diagnostics",
|
|
734
|
-
"warnings",
|
|
735
|
-
"errors",
|
|
736
|
-
"actions",
|
|
737
|
-
"attributes",
|
|
738
|
-
"categories",
|
|
739
|
-
"features",
|
|
740
|
-
"includes",
|
|
741
|
-
"tags"
|
|
742
|
-
];
|
|
743
|
-
var HAS_MORE_PRIMARY_POSITIVE_EXACT = [
|
|
744
|
-
"hasmore",
|
|
745
|
-
"hasnext",
|
|
746
|
-
"hasnextpage",
|
|
747
|
-
"moreitems",
|
|
748
|
-
"moreitemsavailable",
|
|
749
|
-
"nextpage",
|
|
750
|
-
"nextpageexists",
|
|
751
|
-
"nextpageavailable",
|
|
752
|
-
"hasadditionalresults",
|
|
753
|
-
"moreresultsavailable",
|
|
754
|
-
"canloadmore",
|
|
755
|
-
"hasadditional",
|
|
756
|
-
"additionalitems",
|
|
757
|
-
"fetchmore"
|
|
758
|
-
];
|
|
759
|
-
var HAS_MORE_SECONDARY_POSITIVE_EXACT = ["more", "next"];
|
|
760
|
-
var HAS_MORE_PRIMARY_INVERTED_EXACT = [
|
|
761
|
-
"islast",
|
|
762
|
-
"lastpage",
|
|
763
|
-
"endofresults",
|
|
764
|
-
"endoflist",
|
|
765
|
-
"nomoreitems",
|
|
766
|
-
"nomoredata",
|
|
767
|
-
"allitemsloaded",
|
|
768
|
-
"iscomplete",
|
|
769
|
-
"completed"
|
|
770
|
-
];
|
|
771
|
-
var HAS_MORE_POSITIVE_REGEX_PATTERNS = [
|
|
772
|
-
"\\bhas_?more\\b",
|
|
773
|
-
"\\bhas_?next\\b",
|
|
774
|
-
// e.g., itemsHasNext, items_has_next
|
|
775
|
-
"\\bmore_?items\\b",
|
|
776
|
-
"\\bnext_?page\\b",
|
|
777
|
-
// e.g., userNextPageFlag
|
|
778
|
-
"\\badditional\\b",
|
|
779
|
-
// e.g., hasAdditionalData, additional_results_exist
|
|
780
|
-
"\\bcontinuation\\b",
|
|
781
|
-
// e.g., continuationAvailable, has_continuation_token
|
|
782
|
-
"\\bmore_?results\\b",
|
|
783
|
-
"\\bpage_?available\\b",
|
|
784
|
-
"\\bnext(?:_?(page|marker))?\\b"
|
|
785
|
-
];
|
|
786
|
-
var COMPILED_HAS_MORE_POSITIVE_REGEXES = HAS_MORE_POSITIVE_REGEX_PATTERNS.map((p) => new RegExp(p, "i"));
|
|
787
|
-
var HAS_MORE_INVERTED_REGEX_PATTERNS = [
|
|
788
|
-
"\\bis_?last\\b",
|
|
789
|
-
// e.g., pageIsLast
|
|
790
|
-
"\\blast_?page\\b",
|
|
791
|
-
// e.g., resultsAreLastPage
|
|
792
|
-
"\\bend_?of_?(data|results|list|items|stream)\\b",
|
|
793
|
-
"\\bno_?more_?(items|data|results)?\\b",
|
|
794
|
-
"\\ball_?(items_?)?loaded\\b",
|
|
795
|
-
"\\bis_?complete\\b"
|
|
796
|
-
];
|
|
797
|
-
var COMPILED_HAS_MORE_INVERTED_REGEXES = HAS_MORE_INVERTED_REGEX_PATTERNS.map((p) => new RegExp(p, "i"));
|
|
798
|
-
function getItemsName(properties) {
|
|
799
|
-
const arrayPropertyNames = [];
|
|
800
|
-
for (const propName in properties) {
|
|
801
|
-
if (propName in properties) {
|
|
802
|
-
const propSchema = properties[propName];
|
|
803
|
-
if (propSchema && propSchema.type === "array") {
|
|
804
|
-
arrayPropertyNames.push(propName);
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
if (arrayPropertyNames.length === 0) {
|
|
809
|
-
return null;
|
|
810
|
-
}
|
|
811
|
-
if (arrayPropertyNames.length === 1) {
|
|
812
|
-
return arrayPropertyNames[0];
|
|
813
|
-
}
|
|
814
|
-
let bestCandidate = null;
|
|
815
|
-
let candidateRank = Infinity;
|
|
816
|
-
const updateCandidate = (propName, rank) => {
|
|
817
|
-
if (rank < candidateRank) {
|
|
818
|
-
bestCandidate = propName;
|
|
819
|
-
candidateRank = rank;
|
|
820
|
-
}
|
|
821
|
-
};
|
|
822
|
-
for (const propName of arrayPropertyNames) {
|
|
823
|
-
const lowerPropName = propName.toLowerCase();
|
|
824
|
-
if (PRIMARY_TOP_TIER_KEYWORDS.includes(lowerPropName)) {
|
|
825
|
-
updateCandidate(propName, 2);
|
|
826
|
-
continue;
|
|
827
|
-
}
|
|
828
|
-
if (candidateRank > 3 && PRIMARY_OTHER_KEYWORDS.includes(lowerPropName)) {
|
|
829
|
-
updateCandidate(propName, 3);
|
|
830
|
-
continue;
|
|
831
|
-
}
|
|
832
|
-
if (candidateRank > 4 && SECONDARY_KEYWORDS.includes(lowerPropName)) {
|
|
833
|
-
updateCandidate(propName, 4);
|
|
834
|
-
continue;
|
|
835
|
-
}
|
|
836
|
-
if (candidateRank > 5 && pluralize.isPlural(propName) && !PLURAL_DEPRIORITIZE_LIST.includes(lowerPropName)) {
|
|
837
|
-
updateCandidate(propName, 5);
|
|
838
|
-
continue;
|
|
839
|
-
}
|
|
840
|
-
if (candidateRank > 6 && pluralize.isPlural(propName) && PLURAL_DEPRIORITIZE_LIST.includes(lowerPropName)) {
|
|
841
|
-
updateCandidate(propName, 6);
|
|
842
|
-
continue;
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
if (bestCandidate) {
|
|
846
|
-
return bestCandidate;
|
|
847
|
-
}
|
|
848
|
-
return arrayPropertyNames[0];
|
|
849
|
-
}
|
|
850
|
-
function guess(properties) {
|
|
851
|
-
const booleanPropertyNames = [];
|
|
852
|
-
for (const propName in properties) {
|
|
853
|
-
const propSchema = properties[propName];
|
|
854
|
-
if (propSchema.type === "boolean" || propSchema.type === "integer" || propSchema.type === "string") {
|
|
855
|
-
booleanPropertyNames.push(propName);
|
|
856
|
-
}
|
|
857
|
-
}
|
|
858
|
-
if (booleanPropertyNames.length === 0) {
|
|
859
|
-
return null;
|
|
860
|
-
}
|
|
861
|
-
if (booleanPropertyNames.length === 1) {
|
|
862
|
-
return booleanPropertyNames[0];
|
|
863
|
-
}
|
|
864
|
-
let bestCandidate = null;
|
|
865
|
-
let candidateRank = Infinity;
|
|
866
|
-
const updateCandidate = (propName, rank) => {
|
|
867
|
-
if (rank < candidateRank) {
|
|
868
|
-
bestCandidate = propName;
|
|
869
|
-
candidateRank = rank;
|
|
870
|
-
}
|
|
871
|
-
};
|
|
872
|
-
for (const propName of booleanPropertyNames) {
|
|
873
|
-
const normalizedForExactMatch = propName.toLowerCase().replace(/[-_]/g, "");
|
|
874
|
-
let currentPropRank = Infinity;
|
|
875
|
-
if (HAS_MORE_PRIMARY_POSITIVE_EXACT.includes(normalizedForExactMatch)) {
|
|
876
|
-
currentPropRank = 1;
|
|
877
|
-
} else if (HAS_MORE_SECONDARY_POSITIVE_EXACT.includes(normalizedForExactMatch)) {
|
|
878
|
-
currentPropRank = 2;
|
|
879
|
-
} else {
|
|
880
|
-
let foundPositiveRegex = false;
|
|
881
|
-
for (const regex of COMPILED_HAS_MORE_POSITIVE_REGEXES) {
|
|
882
|
-
if (regex.test(propName)) {
|
|
883
|
-
currentPropRank = 3;
|
|
884
|
-
foundPositiveRegex = true;
|
|
885
|
-
break;
|
|
886
|
-
}
|
|
887
|
-
}
|
|
888
|
-
if (!foundPositiveRegex) {
|
|
889
|
-
if (HAS_MORE_PRIMARY_INVERTED_EXACT.includes(normalizedForExactMatch)) {
|
|
890
|
-
currentPropRank = 4;
|
|
891
|
-
} else {
|
|
892
|
-
for (const regex of COMPILED_HAS_MORE_INVERTED_REGEXES) {
|
|
893
|
-
if (regex.test(propName)) {
|
|
894
|
-
currentPropRank = 5;
|
|
895
|
-
break;
|
|
896
|
-
}
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
updateCandidate(propName, currentPropRank);
|
|
902
|
-
}
|
|
903
|
-
return bestCandidate;
|
|
904
|
-
}
|
|
905
|
-
function getHasMoreName(properties) {
|
|
906
|
-
const rootGuess = guess(properties);
|
|
907
|
-
if (rootGuess) {
|
|
908
|
-
return rootGuess;
|
|
909
|
-
}
|
|
910
|
-
for (const propName in properties) {
|
|
911
|
-
const propSchema = properties[propName];
|
|
912
|
-
if (propSchema.type === "object" && propSchema.properties) {
|
|
913
|
-
const nested = getHasMoreName(propSchema.properties);
|
|
914
|
-
if (nested) {
|
|
915
|
-
return propName + "." + nested;
|
|
916
|
-
}
|
|
917
|
-
}
|
|
918
|
-
}
|
|
919
|
-
return null;
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
// packages/spec/src/lib/pagination/pagination.ts
|
|
923
|
-
var OFFSET_PARAM_REGEXES = [
|
|
924
|
-
/\boffset\b/i,
|
|
925
|
-
/\bskip\b/i,
|
|
926
|
-
/\bstart(?:ing_at|_index)?\b/i,
|
|
927
|
-
// e.g., start, starting_at, start_index
|
|
928
|
-
/\bfrom\b/i
|
|
929
|
-
];
|
|
930
|
-
var GENERIC_LIMIT_PARAM_REGEXES = [
|
|
931
|
-
/\blimit\b/i,
|
|
932
|
-
/\bcount\b/i,
|
|
933
|
-
/\b(?:page_?)?size\b/i,
|
|
934
|
-
// e.g., size, page_size, pagesize
|
|
935
|
-
/\bmax_results\b/i,
|
|
936
|
-
/\bnum_results\b/i,
|
|
937
|
-
/\bshow\b/i,
|
|
938
|
-
// Can sometimes mean limit
|
|
939
|
-
/\bper_?page\b/i,
|
|
940
|
-
// e.g., per_page, perpage
|
|
941
|
-
/\bper-page\b/i,
|
|
942
|
-
/\btake\b/i
|
|
943
|
-
];
|
|
944
|
-
var PAGE_NUMBER_REGEXES = [
|
|
945
|
-
// /^(currentPage)?page$/i, // Exact match for "page"
|
|
946
|
-
// /^currentPage$/i, // Exact match for "currentPage"
|
|
947
|
-
/^p$/i,
|
|
948
|
-
// Exact match for "p" (common shorthand)
|
|
949
|
-
// /\bpage_?(?:number|no|num|idx|index)\b/i, // e.g., page_number, pageNumber, page_num, page_idx
|
|
950
|
-
/^(current)?_?page(_?)(?:number|no|num|idx|index)?\b/i
|
|
951
|
-
];
|
|
952
|
-
var PAGE_SIZE_REGEXES = [
|
|
953
|
-
/\bpage_?size\b/i,
|
|
954
|
-
// e.g., page_size, pagesize
|
|
955
|
-
/^size$/i,
|
|
956
|
-
// Exact "size"
|
|
957
|
-
// /\bsize\b/i, // Broader "size" - can be ambiguous, prefer more specific ones first
|
|
958
|
-
/\blimit\b/i,
|
|
959
|
-
// Limit is often used for page size
|
|
960
|
-
/\bcount\b/i,
|
|
961
|
-
// Count can also be used for page size
|
|
962
|
-
/\bper_?page\b/i,
|
|
963
|
-
// e.g., per_page, perpage
|
|
964
|
-
/\bper-page\b/i,
|
|
965
|
-
/\bnum_?(?:items|records|results)\b/i,
|
|
966
|
-
// e.g., num_items, numitems
|
|
967
|
-
/\bresults_?per_?page\b/i
|
|
968
|
-
];
|
|
969
|
-
var CURSOR_REGEXES = [
|
|
970
|
-
/\bmarker\b/i,
|
|
971
|
-
/\bcursor\b/i,
|
|
972
|
-
/\bafter(?:_?cursor)?\b/i,
|
|
973
|
-
// e.g., after, after_cursor
|
|
974
|
-
/\bbefore(?:_?cursor)?\b/i,
|
|
975
|
-
// e.g., before, before_cursor
|
|
976
|
-
/\b(next|prev|previous)_?(?:page_?)?token\b/i,
|
|
977
|
-
// e.g., next_page_token, nextPageToken, prev_token
|
|
978
|
-
/\b(next|prev|previous)_?cursor\b/i,
|
|
979
|
-
// e.g., next_cursor, previousCursor
|
|
980
|
-
/\bcontinuation(?:_?token)?\b/i,
|
|
981
|
-
// e.g., continuation, continuation_token
|
|
982
|
-
/\bpage(?:_?(token|id))?\b/i,
|
|
983
|
-
// e.g., after, after_cursor
|
|
984
|
-
/\bstart_?(?:key|cursor|token|after)\b/i
|
|
985
|
-
// e.g., start_key, startCursor, startToken, startAfter
|
|
986
|
-
];
|
|
987
|
-
var CURSOR_LIMIT_REGEXES = [
|
|
988
|
-
/\blimit\b/i,
|
|
989
|
-
/\bcount\b/i,
|
|
990
|
-
/\bsize\b/i,
|
|
991
|
-
// General size
|
|
992
|
-
/\bfirst\b/i,
|
|
993
|
-
// Common in Relay-style cursor pagination (forward pagination)
|
|
994
|
-
/\blast\b/i,
|
|
995
|
-
// Common in Relay-style cursor pagination (backward pagination)
|
|
996
|
-
/\bpage_?size\b/i,
|
|
997
|
-
// Sometimes page_size is used with cursors
|
|
998
|
-
/\bnum_?(?:items|records|results)\b/i,
|
|
999
|
-
// e.g., num_items
|
|
1000
|
-
/\bmax_?items\b/i,
|
|
1001
|
-
/\btake\b/i
|
|
1002
|
-
];
|
|
1003
|
-
function findParamAndKeyword(queryParams, regexes, excludeParamName) {
|
|
1004
|
-
for (const param of queryParams) {
|
|
1005
|
-
if (param.name === excludeParamName) {
|
|
1006
|
-
continue;
|
|
1007
|
-
}
|
|
1008
|
-
for (const regex of regexes) {
|
|
1009
|
-
const match = param.name.match(regex);
|
|
1010
|
-
if (match) {
|
|
1011
|
-
return { param, keyword: match[0] };
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
return null;
|
|
1016
|
-
}
|
|
1017
|
-
function coearceParameters(parameters) {
|
|
1018
|
-
const cleanedParameters = [];
|
|
1019
|
-
for (const param of parameters) {
|
|
1020
|
-
if (!param.schema) {
|
|
1021
|
-
continue;
|
|
1022
|
-
}
|
|
1023
|
-
if (isRef3(param.schema)) {
|
|
1024
|
-
continue;
|
|
1025
|
-
}
|
|
1026
|
-
if (!param.schema.type) {
|
|
1027
|
-
continue;
|
|
1028
|
-
}
|
|
1029
|
-
if (param.schema) {
|
|
1030
|
-
cleanedParameters.push({
|
|
1031
|
-
...param,
|
|
1032
|
-
schema: {
|
|
1033
|
-
...param.schema,
|
|
1034
|
-
type: Array.isArray(param.schema.type) ? param.schema.type : [param.schema.type]
|
|
1035
|
-
}
|
|
1036
|
-
});
|
|
1037
|
-
}
|
|
1038
|
-
}
|
|
1039
|
-
return cleanedParameters;
|
|
1040
|
-
}
|
|
1041
|
-
function isOffsetPagination(operation, parameters) {
|
|
1042
|
-
const params = coearceParameters(parameters).filter(
|
|
1043
|
-
(it) => it.schema.type.includes("integer") || it.schema.type.includes("number")
|
|
1044
|
-
);
|
|
1045
|
-
const offsetMatch = findParamAndKeyword(params, OFFSET_PARAM_REGEXES);
|
|
1046
|
-
if (!offsetMatch)
|
|
1047
|
-
return null;
|
|
1048
|
-
const limitMatch = findParamAndKeyword(
|
|
1049
|
-
params,
|
|
1050
|
-
GENERIC_LIMIT_PARAM_REGEXES,
|
|
1051
|
-
offsetMatch.param.name
|
|
1052
|
-
);
|
|
1053
|
-
if (!limitMatch)
|
|
1054
|
-
return null;
|
|
1055
|
-
return {
|
|
1056
|
-
type: "offset",
|
|
1057
|
-
offsetParamName: offsetMatch.param.name,
|
|
1058
|
-
offsetKeyword: offsetMatch.keyword,
|
|
1059
|
-
limitParamName: limitMatch.param.name,
|
|
1060
|
-
limitKeyword: limitMatch.keyword
|
|
1061
|
-
};
|
|
1062
|
-
}
|
|
1063
|
-
function isPagePagination(operation, parameters) {
|
|
1064
|
-
const params = coearceParameters(parameters).filter(
|
|
1065
|
-
(it) => it.schema.type.includes("integer") || it.schema.type.includes("number")
|
|
1066
|
-
);
|
|
1067
|
-
if (params.length < 2)
|
|
1068
|
-
return null;
|
|
1069
|
-
const pageNoMatch = findParamAndKeyword(params, PAGE_NUMBER_REGEXES);
|
|
1070
|
-
if (!pageNoMatch)
|
|
1071
|
-
return null;
|
|
1072
|
-
const pageSizeMatch = findParamAndKeyword(
|
|
1073
|
-
params,
|
|
1074
|
-
PAGE_SIZE_REGEXES,
|
|
1075
|
-
pageNoMatch.param.name
|
|
1076
|
-
);
|
|
1077
|
-
if (!pageSizeMatch)
|
|
1078
|
-
return null;
|
|
1079
|
-
return {
|
|
1080
|
-
type: "page",
|
|
1081
|
-
pageNumberParamName: pageNoMatch.param.name,
|
|
1082
|
-
pageNumberKeyword: pageNoMatch.keyword,
|
|
1083
|
-
pageSizeParamName: pageSizeMatch.param.name,
|
|
1084
|
-
pageSizeKeyword: pageSizeMatch.keyword
|
|
1085
|
-
};
|
|
1086
|
-
}
|
|
1087
|
-
function isCursorPagination(operation, parameters = []) {
|
|
1088
|
-
const queryParams = parameters;
|
|
1089
|
-
if (queryParams.length < 2)
|
|
1090
|
-
return null;
|
|
1091
|
-
const cursorMatch = findParamAndKeyword(queryParams, CURSOR_REGEXES);
|
|
1092
|
-
if (!cursorMatch)
|
|
1093
|
-
return null;
|
|
1094
|
-
const limitMatch = findParamAndKeyword(
|
|
1095
|
-
queryParams,
|
|
1096
|
-
CURSOR_LIMIT_REGEXES,
|
|
1097
|
-
cursorMatch.param.name
|
|
1098
|
-
);
|
|
1099
|
-
if (!limitMatch)
|
|
1100
|
-
return null;
|
|
1101
|
-
return {
|
|
1102
|
-
type: "cursor",
|
|
1103
|
-
cursorParamName: cursorMatch.param.name,
|
|
1104
|
-
cursorKeyword: cursorMatch.keyword,
|
|
1105
|
-
limitParamName: limitMatch.param.name,
|
|
1106
|
-
limitKeyword: limitMatch.keyword
|
|
1107
|
-
};
|
|
1108
|
-
}
|
|
1109
|
-
function guessPagination(operation, body, response) {
|
|
1110
|
-
const bodyParameters = body && body.properties ? Object.entries(body.properties).map(([it, schema]) => ({
|
|
1111
|
-
name: it,
|
|
1112
|
-
schema
|
|
1113
|
-
})) : [];
|
|
1114
|
-
const parameters = operation.parameters;
|
|
1115
|
-
if (isEmpty3(operation.parameters) && isEmpty3(bodyParameters)) {
|
|
1116
|
-
return { type: "none", reason: "no parameters" };
|
|
1117
|
-
}
|
|
1118
|
-
if (!response) {
|
|
1119
|
-
return { type: "none", reason: "no response" };
|
|
1120
|
-
}
|
|
1121
|
-
if (!response.properties) {
|
|
1122
|
-
return { type: "none", reason: "empty response" };
|
|
1123
|
-
}
|
|
1124
|
-
const properties = response.properties;
|
|
1125
|
-
const itemsKey = getItemsName(properties);
|
|
1126
|
-
if (!itemsKey) {
|
|
1127
|
-
return { type: "none", reason: "no items key" };
|
|
1128
|
-
}
|
|
1129
|
-
const hasMoreKey = getHasMoreName(excludeKey(properties, itemsKey));
|
|
1130
|
-
if (!hasMoreKey) {
|
|
1131
|
-
return { type: "none", reason: "no hasMore key" };
|
|
1132
|
-
}
|
|
1133
|
-
const pagination = isOffsetPagination(operation, [...parameters, ...bodyParameters]) || isPagePagination(operation, [...parameters, ...bodyParameters]) || isCursorPagination(operation, [...parameters, ...bodyParameters]);
|
|
1134
|
-
return pagination ? { ...pagination, items: itemsKey, hasMore: hasMoreKey } : { type: "none", reason: "no pagination" };
|
|
1135
|
-
}
|
|
1136
|
-
function excludeKey(obj, key) {
|
|
1137
|
-
const { [key]: _, ...rest } = obj;
|
|
1138
|
-
return rest;
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
|
-
// packages/spec/src/lib/security.ts
|
|
1142
|
-
import { methods } from "@sdk-it/core/paths.js";
|
|
1143
|
-
import { resolveRef as resolveRef3 } from "@sdk-it/core/ref.js";
|
|
1144
|
-
function securityToOptions(spec, security2, securitySchemes, staticIn) {
|
|
1145
|
-
const parameters = [];
|
|
1146
|
-
for (const it of security2) {
|
|
1147
|
-
const [name] = Object.keys(it);
|
|
1148
|
-
if (!name) {
|
|
1149
|
-
continue;
|
|
1150
|
-
}
|
|
1151
|
-
const schema = resolveRef3(
|
|
1152
|
-
spec,
|
|
1153
|
-
securitySchemes[name]
|
|
1154
|
-
);
|
|
1155
|
-
if (schema.type === "http") {
|
|
1156
|
-
parameters.push({
|
|
1157
|
-
in: staticIn ?? "header",
|
|
1158
|
-
name: "authorization",
|
|
1159
|
-
schema: { type: "string" },
|
|
1160
|
-
example: schema.scheme === "bearer" ? '"<token>"' : `<${schema.scheme}> <token>`
|
|
1161
|
-
});
|
|
1162
|
-
continue;
|
|
1163
|
-
}
|
|
1164
|
-
if (schema.type === "apiKey") {
|
|
1165
|
-
if (!schema.in) {
|
|
1166
|
-
throw new Error(`apiKey security schema must have an "in" field`);
|
|
1167
|
-
}
|
|
1168
|
-
if (!schema.name) {
|
|
1169
|
-
throw new Error(`apiKey security schema must have a "name" field`);
|
|
1170
|
-
}
|
|
1171
|
-
parameters.push({
|
|
1172
|
-
in: staticIn ?? schema.in,
|
|
1173
|
-
name: schema.name,
|
|
1174
|
-
schema: { type: "string" },
|
|
1175
|
-
example: `"proj-${crypto.randomUUID()}"`
|
|
1176
|
-
});
|
|
1177
|
-
continue;
|
|
1178
|
-
}
|
|
1179
|
-
}
|
|
1180
|
-
return parameters;
|
|
1181
|
-
}
|
|
1182
|
-
function security(spec) {
|
|
1183
|
-
const security2 = spec.security || [];
|
|
1184
|
-
const paths = Object.values(spec.paths ?? {});
|
|
1185
|
-
const options = securityToOptions(
|
|
1186
|
-
spec,
|
|
1187
|
-
security2,
|
|
1188
|
-
spec.components.securitySchemes
|
|
1189
|
-
);
|
|
1190
|
-
for (const it of paths) {
|
|
1191
|
-
for (const method of methods) {
|
|
1192
|
-
const operation = it[method];
|
|
1193
|
-
if (!operation) {
|
|
1194
|
-
continue;
|
|
1195
|
-
}
|
|
1196
|
-
Object.assign(
|
|
1197
|
-
options,
|
|
1198
|
-
securityToOptions(
|
|
1199
|
-
spec,
|
|
1200
|
-
operation.security || [],
|
|
1201
|
-
spec.components.securitySchemes,
|
|
1202
|
-
"input"
|
|
1203
|
-
)
|
|
1204
|
-
);
|
|
1205
|
-
}
|
|
1206
|
-
}
|
|
1207
|
-
return options;
|
|
1208
|
-
}
|
|
1209
|
-
|
|
1210
|
-
// packages/spec/src/lib/operation.ts
|
|
1211
|
-
function findUniqueOperationId(usedOperationIds, initialId, choices, formatter) {
|
|
1212
|
-
let counter = 1;
|
|
1213
|
-
let uniqueOperationId = formatter(initialId);
|
|
1214
|
-
while (usedOperationIds.has(uniqueOperationId)) {
|
|
1215
|
-
const prependIndex = Math.min(counter - 1, choices.length - 1);
|
|
1216
|
-
const prefix = choices[prependIndex];
|
|
1217
|
-
if (prependIndex < choices.length - 1) {
|
|
1218
|
-
uniqueOperationId = formatter(
|
|
1219
|
-
`${prefix}${initialId.charAt(0).toUpperCase() + initialId.slice(1)}`
|
|
1220
|
-
);
|
|
1221
|
-
} else {
|
|
1222
|
-
uniqueOperationId = formatter(
|
|
1223
|
-
`${prefix}${initialId.charAt(0).toUpperCase() + initialId.slice(1)}${counter - choices.length + 1}`
|
|
1224
|
-
);
|
|
1225
|
-
}
|
|
1226
|
-
counter++;
|
|
1227
|
-
}
|
|
1228
|
-
return uniqueOperationId;
|
|
1229
|
-
}
|
|
1230
|
-
function augmentSpec(config, verbose = false) {
|
|
1231
|
-
if ("x-sdk-augmented" in config.spec) {
|
|
1232
|
-
return config.spec;
|
|
1233
|
-
}
|
|
1234
|
-
const spec = {
|
|
1235
|
-
...config.spec,
|
|
1236
|
-
components: {
|
|
1237
|
-
...config.spec.components,
|
|
1238
|
-
schemas: config.spec.components?.schemas ?? {},
|
|
1239
|
-
securitySchemes: config.spec.components?.securitySchemes ?? {}
|
|
1240
|
-
},
|
|
1241
|
-
paths: config.spec.paths ?? {}
|
|
1242
|
-
};
|
|
1243
|
-
const paths = {};
|
|
1244
|
-
const usedOperationIds = /* @__PURE__ */ new Set();
|
|
1245
|
-
for (const [path, pathItem] of Object.entries(spec.paths)) {
|
|
1246
|
-
const fixedPath = path.replace(/:([^/]+)/g, "{$1}");
|
|
1247
|
-
for (const [method, operation] of Object.entries(pathItem)) {
|
|
1248
|
-
if (!methods2.includes(method)) {
|
|
1249
|
-
continue;
|
|
1250
|
-
}
|
|
1251
|
-
const formatOperationId = config.operationId ?? defaults.operationId;
|
|
1252
|
-
const formatTag = config.tag ?? defaults.tag;
|
|
1253
|
-
const operationTag = formatTag(operation, fixedPath);
|
|
1254
|
-
const operationId = findUniqueOperationId(
|
|
1255
|
-
usedOperationIds,
|
|
1256
|
-
formatOperationId(operation, fixedPath, method),
|
|
1257
|
-
[operationTag, method, fixedPath.split("/").filter(Boolean).join("")],
|
|
1258
|
-
(id) => formatOperationId(
|
|
1259
|
-
{ ...operation, operationId: id },
|
|
1260
|
-
fixedPath,
|
|
1261
|
-
method
|
|
1262
|
-
)
|
|
1263
|
-
);
|
|
1264
|
-
usedOperationIds.add(operationId);
|
|
1265
|
-
const parameters = [
|
|
1266
|
-
...pathItem.parameters ?? [],
|
|
1267
|
-
...operation.parameters ?? []
|
|
1268
|
-
].map((it) => resolveRef4(spec, it));
|
|
1269
|
-
const tunedOperation = {
|
|
1270
|
-
...operation,
|
|
1271
|
-
parameters,
|
|
1272
|
-
tags: [snakecase2(operationTag)],
|
|
1273
|
-
operationId,
|
|
1274
|
-
responses: resolveResponses(
|
|
1275
|
-
spec,
|
|
1276
|
-
operationId,
|
|
1277
|
-
operation,
|
|
1278
|
-
config.responses
|
|
1279
|
-
),
|
|
1280
|
-
requestBody: tuneRequestBody(
|
|
1281
|
-
spec,
|
|
1282
|
-
operationId,
|
|
1283
|
-
operation,
|
|
1284
|
-
parameters,
|
|
1285
|
-
operation.security ?? []
|
|
1286
|
-
)
|
|
1287
|
-
};
|
|
1288
|
-
tunedOperation["x-pagination"] = toPagination(spec, tunedOperation);
|
|
1289
|
-
Object.assign(paths, {
|
|
1290
|
-
[fixedPath]: {
|
|
1291
|
-
...paths[fixedPath],
|
|
1292
|
-
[method]: tunedOperation
|
|
1293
|
-
}
|
|
1294
|
-
});
|
|
1295
|
-
}
|
|
1296
|
-
}
|
|
1297
|
-
fixSpec(spec, Object.values(spec.components.schemas));
|
|
1298
|
-
if (verbose) {
|
|
1299
|
-
const newRefs = [];
|
|
1300
|
-
expandSpec(spec, spec.components.schemas, newRefs);
|
|
1301
|
-
}
|
|
1302
|
-
return {
|
|
1303
|
-
...spec,
|
|
1304
|
-
paths,
|
|
1305
|
-
"x-sdk-augmented": true
|
|
1306
|
-
};
|
|
1307
|
-
}
|
|
1308
|
-
function toPagination(spec, tunedOperation) {
|
|
1309
|
-
if (tunedOperation["x-pagination"]) {
|
|
1310
|
-
return tunedOperation["x-pagination"];
|
|
1311
|
-
}
|
|
1312
|
-
const schema = getResponseContentSchema(
|
|
1313
|
-
spec,
|
|
1314
|
-
tunedOperation.responses["200"],
|
|
1315
|
-
"application/json"
|
|
1316
|
-
);
|
|
1317
|
-
const pagination = guessPagination(
|
|
1318
|
-
tunedOperation,
|
|
1319
|
-
tunedOperation.requestBody ? getRequestContentSchema(
|
|
1320
|
-
spec,
|
|
1321
|
-
tunedOperation.requestBody,
|
|
1322
|
-
"application/json"
|
|
1323
|
-
) : void 0,
|
|
1324
|
-
schema
|
|
1325
|
-
);
|
|
1326
|
-
if (pagination && pagination.type !== "none" && schema) {
|
|
1327
|
-
return pagination;
|
|
1328
|
-
}
|
|
1329
|
-
return void 0;
|
|
1330
|
-
}
|
|
1331
|
-
function getResponseContentSchema(spec, response, type) {
|
|
1332
|
-
if (!response) {
|
|
1333
|
-
return void 0;
|
|
1334
|
-
}
|
|
1335
|
-
const content = response.content;
|
|
1336
|
-
if (!content) {
|
|
1337
|
-
return void 0;
|
|
1338
|
-
}
|
|
1339
|
-
for (const contentType in content) {
|
|
1340
|
-
if (contentType.toLowerCase() === type.toLowerCase()) {
|
|
1341
|
-
return isRef4(content[contentType].schema) ? followRef(spec, content[contentType].schema.$ref) : content[contentType].schema;
|
|
1342
|
-
}
|
|
1343
|
-
}
|
|
1344
|
-
return void 0;
|
|
1345
|
-
}
|
|
1346
|
-
function getRequestContentSchema(spec, requestBody, type) {
|
|
1347
|
-
const content = requestBody.content;
|
|
1348
|
-
if (!content) {
|
|
1349
|
-
return void 0;
|
|
1350
|
-
}
|
|
1351
|
-
for (const contentType in content) {
|
|
1352
|
-
if (contentType.toLowerCase() === type.toLowerCase()) {
|
|
1353
|
-
return isRef4(content[contentType].schema) ? followRef(spec, content[contentType].schema.$ref) : content[contentType].schema;
|
|
1354
|
-
}
|
|
1355
|
-
}
|
|
1356
|
-
return void 0;
|
|
1357
|
-
}
|
|
1358
|
-
var defaults = {
|
|
1359
|
-
operationId: (operation, path, method) => {
|
|
1360
|
-
if (operation.operationId) {
|
|
1361
|
-
return camelcase2(
|
|
1362
|
-
operation.operationId.split("#").pop().replace(/-(?=\d)/g, "")
|
|
1363
|
-
);
|
|
1364
|
-
}
|
|
1365
|
-
const metadata = operation["x-oaiMeta"];
|
|
1366
|
-
if (metadata && metadata.name) {
|
|
1367
|
-
return camelcase2(metadata.name);
|
|
1368
|
-
}
|
|
1369
|
-
return camelcase2(
|
|
1370
|
-
[method, ...path.replace(/[\\/\\{\\}]/g, " ").split(" ")].filter(Boolean).join(" ").trim()
|
|
1371
|
-
);
|
|
1372
|
-
},
|
|
1373
|
-
tag: (operation, path) => {
|
|
1374
|
-
return operation.tags?.[0] ? sanitizeTag(operation.tags?.[0]) : determineGenericTag(path, operation);
|
|
1375
|
-
}
|
|
1376
|
-
};
|
|
1377
|
-
function resolveResponses(spec, operationId, operation, responsesConfig) {
|
|
1378
|
-
const responses = operation.responses ?? {};
|
|
1379
|
-
operation.responses ??= {};
|
|
1380
|
-
let foundSuccessResponse = false;
|
|
1381
|
-
for (const status in responses) {
|
|
1382
|
-
if (status === "default") {
|
|
1383
|
-
delete responses[status];
|
|
1384
|
-
continue;
|
|
1385
|
-
}
|
|
1386
|
-
operation.responses[status] = structuredClone(
|
|
1387
|
-
resolveRef4(spec, responses[status])
|
|
1388
|
-
);
|
|
1389
|
-
if (isSuccessStatusCode(status)) {
|
|
1390
|
-
foundSuccessResponse = true;
|
|
1391
|
-
}
|
|
1392
|
-
}
|
|
1393
|
-
if (!foundSuccessResponse) {
|
|
1394
|
-
operation.responses["200"] = {
|
|
1395
|
-
description: "OK",
|
|
1396
|
-
content: {
|
|
1397
|
-
"application/json": {
|
|
1398
|
-
schema: {}
|
|
1399
|
-
}
|
|
1400
|
-
}
|
|
1401
|
-
};
|
|
1402
|
-
}
|
|
1403
|
-
for (const status in operation.responses) {
|
|
1404
|
-
const response = operation.responses[status];
|
|
1405
|
-
const statusCode = +status;
|
|
1406
|
-
const outputName = statusCode !== 200 ? pascalcase3(operationId) + status : findUniqueSchemaName(spec, operationId, [
|
|
1407
|
-
"output",
|
|
1408
|
-
"payload",
|
|
1409
|
-
"result"
|
|
1410
|
-
]);
|
|
1411
|
-
if (!responsesConfig?.flattenErrorResponses) {
|
|
1412
|
-
if (!isSuccessStatusCode(status)) {
|
|
1413
|
-
continue;
|
|
1414
|
-
}
|
|
1415
|
-
}
|
|
1416
|
-
if (isEmpty4(response.content)) {
|
|
1417
|
-
response.content = {
|
|
1418
|
-
"application/octet-stream": {}
|
|
1419
|
-
};
|
|
1420
|
-
}
|
|
1421
|
-
response["x-response-name"] = outputName;
|
|
1422
|
-
for (const [contentType, mediaType] of Object.entries(
|
|
1423
|
-
response.content
|
|
1424
|
-
)) {
|
|
1425
|
-
if (isRef4(mediaType.schema)) {
|
|
1426
|
-
const { model } = parseRef(mediaType.schema.$ref);
|
|
1427
|
-
Object.assign(spec.components.schemas[model], {
|
|
1428
|
-
"x-responsebody": true
|
|
1429
|
-
// do not assign response ref to a group
|
|
1430
|
-
// because they are supposed to be in a separate file only inlined
|
|
1431
|
-
// schemas are grouped by operationId
|
|
1432
|
-
});
|
|
1433
|
-
response["x-response-name"] = model;
|
|
1434
|
-
continue;
|
|
1435
|
-
}
|
|
1436
|
-
if (isSseContentType(contentType)) {
|
|
1437
|
-
continue;
|
|
1438
|
-
}
|
|
1439
|
-
if (parseJsonContentType(contentType)) {
|
|
1440
|
-
if (isEmpty4(mediaType.schema)) {
|
|
1441
|
-
spec.components.schemas[outputName] = {
|
|
1442
|
-
type: "object",
|
|
1443
|
-
additionalProperties: true
|
|
1444
|
-
};
|
|
1445
|
-
}
|
|
1446
|
-
} else {
|
|
1447
|
-
spec.components.schemas[outputName] = {
|
|
1448
|
-
...spec.components.schemas[outputName],
|
|
1449
|
-
"x-stream": !isTextContentType(contentType)
|
|
1450
|
-
};
|
|
1451
|
-
}
|
|
1452
|
-
spec.components.schemas[outputName] = {
|
|
1453
|
-
...spec.components.schemas[outputName],
|
|
1454
|
-
...mediaType.schema,
|
|
1455
|
-
"x-responsebody": true,
|
|
1456
|
-
"x-response-group": operationId
|
|
1457
|
-
};
|
|
1458
|
-
operation.responses[status].content[contentType].schema = {
|
|
1459
|
-
$ref: `#/components/schemas/${outputName}`
|
|
1460
|
-
};
|
|
1461
|
-
}
|
|
1462
|
-
}
|
|
1463
|
-
return operation.responses;
|
|
1464
|
-
}
|
|
1465
|
-
function forEachOperation(spec, callback) {
|
|
1466
|
-
const result = [];
|
|
1467
|
-
for (const [path, pathItem] of Object.entries(spec.paths)) {
|
|
1468
|
-
for (const [method, operation] of Object.entries(pathItem)) {
|
|
1469
|
-
if (!methods2.includes(method)) {
|
|
1470
|
-
continue;
|
|
1471
|
-
}
|
|
1472
|
-
const metadata = operation["x-oaiMeta"] ?? {};
|
|
1473
|
-
const operationTag = operation.tags?.[0];
|
|
1474
|
-
result.push(
|
|
1475
|
-
callback(
|
|
1476
|
-
{
|
|
1477
|
-
name: metadata.name,
|
|
1478
|
-
method,
|
|
1479
|
-
path,
|
|
1480
|
-
groupName: operationTag,
|
|
1481
|
-
tag: operationTag
|
|
1482
|
-
},
|
|
1483
|
-
operation
|
|
1484
|
-
)
|
|
1485
|
-
);
|
|
1486
|
-
}
|
|
1487
|
-
}
|
|
1488
|
-
return result;
|
|
1489
|
-
}
|
|
1490
|
-
var reservedKeywords = /* @__PURE__ */ new Set([
|
|
1491
|
-
"await",
|
|
1492
|
-
// Reserved in async functions
|
|
1493
|
-
"break",
|
|
1494
|
-
"case",
|
|
1495
|
-
"catch",
|
|
1496
|
-
"class",
|
|
1497
|
-
"const",
|
|
1498
|
-
"continue",
|
|
1499
|
-
"debugger",
|
|
1500
|
-
"default",
|
|
1501
|
-
"delete",
|
|
1502
|
-
"do",
|
|
1503
|
-
"else",
|
|
1504
|
-
"enum",
|
|
1505
|
-
"export",
|
|
1506
|
-
"extends",
|
|
1507
|
-
"false",
|
|
1508
|
-
"finally",
|
|
1509
|
-
"for",
|
|
1510
|
-
"function",
|
|
1511
|
-
"if",
|
|
1512
|
-
"implements",
|
|
1513
|
-
// Strict mode
|
|
1514
|
-
"import",
|
|
1515
|
-
"in",
|
|
1516
|
-
"instanceof",
|
|
1517
|
-
"interface",
|
|
1518
|
-
// Strict mode
|
|
1519
|
-
"let",
|
|
1520
|
-
// Strict mode
|
|
1521
|
-
"new",
|
|
1522
|
-
"null",
|
|
1523
|
-
"package",
|
|
1524
|
-
// Strict mode
|
|
1525
|
-
"private",
|
|
1526
|
-
// Strict mode
|
|
1527
|
-
"protected",
|
|
1528
|
-
// Strict mode
|
|
1529
|
-
"public",
|
|
1530
|
-
// Strict mode
|
|
1531
|
-
"return",
|
|
1532
|
-
"static",
|
|
1533
|
-
// Strict mode
|
|
1534
|
-
"super",
|
|
1535
|
-
"switch",
|
|
1536
|
-
"this",
|
|
1537
|
-
"throw",
|
|
1538
|
-
"true",
|
|
1539
|
-
"try",
|
|
1540
|
-
"typeof",
|
|
1541
|
-
"var",
|
|
1542
|
-
"void",
|
|
1543
|
-
"while",
|
|
1544
|
-
"with",
|
|
1545
|
-
"yield",
|
|
1546
|
-
// Strict mode / Generator functions
|
|
1547
|
-
// 'arguments' is not technically a reserved word, but it's a special identifier within functions
|
|
1548
|
-
// and assigning to it or declaring it can cause issues or unexpected behavior.
|
|
1549
|
-
"arguments"
|
|
1550
|
-
]);
|
|
1551
|
-
var reservedSdkKeywords = /* @__PURE__ */ new Set(["ClientError", "Error", "ConflictError"]);
|
|
1552
|
-
function sanitizeTag(tag) {
|
|
1553
|
-
if (/^\d/.test(tag)) {
|
|
1554
|
-
return `_${tag}`;
|
|
1555
|
-
}
|
|
1556
|
-
if (reservedKeywords.has(tag)) {
|
|
1557
|
-
return `$${tag}`;
|
|
1558
|
-
}
|
|
1559
|
-
if (reservedSdkKeywords.has(tag)) {
|
|
1560
|
-
return `$${tag}`;
|
|
1561
|
-
}
|
|
1562
|
-
return tag.replace(/[()]/g, "").replace(/--/g, "").split(/\s+/).filter(Boolean).join(" ");
|
|
1563
|
-
}
|
|
1564
|
-
function determineGenericTag(pathString, operation) {
|
|
1565
|
-
const operationId = operation.operationId || "";
|
|
1566
|
-
const VERSION_REGEX = /^[vV]\d+$/;
|
|
1567
|
-
const commonVerbs = /* @__PURE__ */ new Set([
|
|
1568
|
-
// Verbs to potentially strip from operationId prefix
|
|
1569
|
-
"get",
|
|
1570
|
-
"list",
|
|
1571
|
-
"create",
|
|
1572
|
-
"update",
|
|
1573
|
-
"delete",
|
|
1574
|
-
"post",
|
|
1575
|
-
"put",
|
|
1576
|
-
"patch",
|
|
1577
|
-
"do",
|
|
1578
|
-
"send",
|
|
1579
|
-
"add",
|
|
1580
|
-
"remove",
|
|
1581
|
-
"set",
|
|
1582
|
-
"find",
|
|
1583
|
-
"search",
|
|
1584
|
-
"check",
|
|
1585
|
-
"make"
|
|
1586
|
-
]);
|
|
1587
|
-
const segments = pathString.split("/").filter(Boolean);
|
|
1588
|
-
const potentialCandidates = segments.filter(
|
|
1589
|
-
(segment) => segment && !segment.startsWith("{") && !segment.endsWith("}") && !VERSION_REGEX.test(segment)
|
|
1590
|
-
);
|
|
1591
|
-
for (let i = potentialCandidates.length - 1; i >= 0; i--) {
|
|
1592
|
-
const segment = potentialCandidates[i];
|
|
1593
|
-
if (!segment.startsWith("@")) {
|
|
1594
|
-
return sanitizeTag(camelcase2(segment));
|
|
1595
|
-
}
|
|
1596
|
-
}
|
|
1597
|
-
const canFallbackToPathSegment = potentialCandidates.length > 0;
|
|
1598
|
-
if (operationId) {
|
|
1599
|
-
const lowerOpId = operationId.toLowerCase();
|
|
1600
|
-
const parts = operationId.replace(/([a-z])([A-Z])/g, "$1_$2").replace(/([A-Z])([A-Z][a-z])/g, "$1_$2").replace(/([a-zA-Z])(\d)/g, "$1_$2").replace(/(\d)([a-zA-Z])/g, "$1_$2").toLowerCase().split(/[_-\s]+/);
|
|
1601
|
-
const validParts = parts.filter(Boolean);
|
|
1602
|
-
if (commonVerbs.has(lowerOpId) && validParts.length === 1 && canFallbackToPathSegment) {
|
|
1603
|
-
} else if (validParts.length > 0) {
|
|
1604
|
-
const firstPart = validParts[0];
|
|
1605
|
-
const isFirstPartVerb = commonVerbs.has(firstPart);
|
|
1606
|
-
if (isFirstPartVerb && validParts.length > 1) {
|
|
1607
|
-
const verbPrefixLength = firstPart.length;
|
|
1608
|
-
let nextPartStartIndex = -1;
|
|
1609
|
-
if (operationId.length > verbPrefixLength) {
|
|
1610
|
-
const charAfterPrefix = operationId[verbPrefixLength];
|
|
1611
|
-
if (charAfterPrefix >= "A" && charAfterPrefix <= "Z") {
|
|
1612
|
-
nextPartStartIndex = verbPrefixLength;
|
|
1613
|
-
} else if (charAfterPrefix >= "0" && charAfterPrefix <= "9") {
|
|
1614
|
-
nextPartStartIndex = verbPrefixLength;
|
|
1615
|
-
} else if (["_", "-"].includes(charAfterPrefix)) {
|
|
1616
|
-
nextPartStartIndex = verbPrefixLength + 1;
|
|
1617
|
-
} else {
|
|
1618
|
-
const match = operationId.substring(verbPrefixLength).match(/[A-Z0-9]/);
|
|
1619
|
-
if (match && match.index !== void 0) {
|
|
1620
|
-
nextPartStartIndex = verbPrefixLength + match.index;
|
|
1621
|
-
}
|
|
1622
|
-
if (nextPartStartIndex === -1 && operationId.length > verbPrefixLength) {
|
|
1623
|
-
nextPartStartIndex = verbPrefixLength;
|
|
1624
|
-
}
|
|
1625
|
-
}
|
|
1626
|
-
}
|
|
1627
|
-
if (nextPartStartIndex !== -1 && nextPartStartIndex < operationId.length) {
|
|
1628
|
-
const remainingOriginalSubstring = operationId.substring(nextPartStartIndex);
|
|
1629
|
-
const potentialTag = camelcase2(remainingOriginalSubstring);
|
|
1630
|
-
if (potentialTag) {
|
|
1631
|
-
return sanitizeTag(potentialTag);
|
|
1632
|
-
}
|
|
1633
|
-
}
|
|
1634
|
-
const potentialTagJoined = camelcase2(validParts.slice(1).join("_"));
|
|
1635
|
-
if (potentialTagJoined) {
|
|
1636
|
-
return sanitizeTag(potentialTagJoined);
|
|
1637
|
-
}
|
|
1638
|
-
}
|
|
1639
|
-
const potentialTagFull = camelcase2(operationId);
|
|
1640
|
-
if (potentialTagFull) {
|
|
1641
|
-
const isResultSingleVerb = validParts.length === 1 && isFirstPartVerb;
|
|
1642
|
-
if (!(isResultSingleVerb && canFallbackToPathSegment)) {
|
|
1643
|
-
if (potentialTagFull.length > 0) {
|
|
1644
|
-
return sanitizeTag(potentialTagFull);
|
|
1645
|
-
}
|
|
1646
|
-
}
|
|
1647
|
-
}
|
|
1648
|
-
const firstPartCamel = camelcase2(firstPart);
|
|
1649
|
-
if (firstPartCamel) {
|
|
1650
|
-
const isFirstPartCamelVerb = commonVerbs.has(firstPartCamel);
|
|
1651
|
-
if (!isFirstPartCamelVerb || validParts.length === 1 || !canFallbackToPathSegment) {
|
|
1652
|
-
return sanitizeTag(firstPartCamel);
|
|
1653
|
-
}
|
|
1654
|
-
}
|
|
1655
|
-
if (isFirstPartVerb && validParts.length > 1 && validParts[1] && canFallbackToPathSegment) {
|
|
1656
|
-
const secondPartCamel = camelcase2(validParts[1]);
|
|
1657
|
-
if (secondPartCamel) {
|
|
1658
|
-
return sanitizeTag(secondPartCamel);
|
|
1659
|
-
}
|
|
1660
|
-
}
|
|
1661
|
-
}
|
|
1662
|
-
}
|
|
1663
|
-
if (potentialCandidates.length > 0) {
|
|
1664
|
-
let firstCandidate = potentialCandidates[0];
|
|
1665
|
-
if (firstCandidate.startsWith("@")) {
|
|
1666
|
-
firstCandidate = firstCandidate.substring(1);
|
|
1667
|
-
}
|
|
1668
|
-
if (firstCandidate) {
|
|
1669
|
-
return sanitizeTag(camelcase2(firstCandidate));
|
|
1670
|
-
}
|
|
1671
|
-
}
|
|
1672
|
-
console.warn(
|
|
1673
|
-
`Could not determine a suitable tag for path: ${pathString}, operationId: ${operationId}. Using 'unknown'.`
|
|
1674
|
-
);
|
|
1675
|
-
return "unknown";
|
|
1676
|
-
}
|
|
1677
|
-
function parseJsonContentType(contentType) {
|
|
1678
|
-
if (!contentType) {
|
|
1679
|
-
return null;
|
|
1680
|
-
}
|
|
1681
|
-
let mainType = contentType.trim();
|
|
1682
|
-
const semicolonIndex = mainType.indexOf(";");
|
|
1683
|
-
if (semicolonIndex !== -1) {
|
|
1684
|
-
mainType = mainType.substring(0, semicolonIndex).trim();
|
|
1685
|
-
}
|
|
1686
|
-
mainType = mainType.toLowerCase();
|
|
1687
|
-
if (mainType.endsWith("/json")) {
|
|
1688
|
-
return mainType.split("/")[1];
|
|
1689
|
-
} else if (mainType.endsWith("+json")) {
|
|
1690
|
-
return mainType.split("+")[1];
|
|
1691
|
-
}
|
|
1692
|
-
return null;
|
|
1693
|
-
}
|
|
1694
|
-
function isTextContentType(contentType) {
|
|
1695
|
-
if (!contentType) {
|
|
1696
|
-
return false;
|
|
1697
|
-
}
|
|
1698
|
-
let mainType = contentType.trim();
|
|
1699
|
-
const semicolonIndex = mainType.indexOf(";");
|
|
1700
|
-
if (semicolonIndex !== -1) {
|
|
1701
|
-
mainType = mainType.substring(0, semicolonIndex).trim();
|
|
1702
|
-
}
|
|
1703
|
-
mainType = mainType.toLowerCase();
|
|
1704
|
-
return mainType.startsWith("text/");
|
|
1705
|
-
}
|
|
1706
|
-
function isSseContentType(contentType) {
|
|
1707
|
-
if (!contentType) {
|
|
1708
|
-
return false;
|
|
1709
|
-
}
|
|
1710
|
-
let mainType = contentType.trim();
|
|
1711
|
-
const semicolonIndex = mainType.indexOf(";");
|
|
1712
|
-
if (semicolonIndex !== -1) {
|
|
1713
|
-
mainType = mainType.substring(0, semicolonIndex).trim();
|
|
1714
|
-
}
|
|
1715
|
-
mainType = mainType.toLowerCase();
|
|
1716
|
-
return mainType === "text/event-stream";
|
|
1717
|
-
}
|
|
1718
|
-
function isStreamingContentType(contentType) {
|
|
1719
|
-
return contentType === "application/octet-stream";
|
|
1720
|
-
}
|
|
1721
|
-
function isSuccessStatusCode(statusCode) {
|
|
1722
|
-
if (typeof statusCode === "string") {
|
|
1723
|
-
const statusGroup = +statusCode.slice(0, 1);
|
|
1724
|
-
const status = Number(statusCode);
|
|
1725
|
-
return status >= 200 && status < 300 || status >= 2 && statusGroup <= 3;
|
|
1726
|
-
}
|
|
1727
|
-
statusCode = Number(statusCode);
|
|
1728
|
-
return statusCode >= 200 && statusCode < 300;
|
|
1729
|
-
}
|
|
1730
|
-
function isErrorStatusCode(statusCode) {
|
|
1731
|
-
if (typeof statusCode === "string") {
|
|
1732
|
-
const statusGroup = +statusCode.slice(0, 1);
|
|
1733
|
-
const status = Number(statusCode);
|
|
1734
|
-
return status < 200 || status >= 300 || statusGroup >= 4 || statusGroup === 0 || statusGroup === 1;
|
|
1735
|
-
}
|
|
1736
|
-
statusCode = Number(statusCode);
|
|
1737
|
-
return statusCode < 200 || statusCode >= 300;
|
|
1738
|
-
}
|
|
1739
|
-
function patchParameters(spec, schema, parameters, security2) {
|
|
1740
|
-
const securitySchemes = spec.components?.securitySchemes ?? {};
|
|
1741
|
-
const securityOptions = securityToOptions(spec, security2, securitySchemes);
|
|
1742
|
-
let required = Array.isArray(schema.required) ? schema.required : [];
|
|
1743
|
-
schema["x-properties"] ??= {};
|
|
1744
|
-
for (const param of parameters) {
|
|
1745
|
-
if (param.required) {
|
|
1746
|
-
required.push(param.name);
|
|
1747
|
-
}
|
|
1748
|
-
schema["x-properties"][param.name] = {
|
|
1749
|
-
"x-in": param.in,
|
|
1750
|
-
...isRef4(param.schema) ? followRef(spec, param.schema.$ref) : param.schema ?? { type: "string" }
|
|
1751
|
-
};
|
|
1752
|
-
}
|
|
1753
|
-
for (const param of securityOptions) {
|
|
1754
|
-
required = required.filter((name) => name !== param.name);
|
|
1755
|
-
schema["x-properties"][param.name] = {
|
|
1756
|
-
"x-in": "header",
|
|
1757
|
-
...isRef4(param.schema) ? followRef(spec, param.schema.$ref) : param.schema ?? { type: "string" }
|
|
1758
|
-
};
|
|
1759
|
-
}
|
|
1760
|
-
schema["x-required"] = required;
|
|
1761
|
-
}
|
|
1762
|
-
function createOperation(options) {
|
|
1763
|
-
const parameters = [];
|
|
1764
|
-
if (!isEmpty4(options.parameters)) {
|
|
1765
|
-
const locations = ["query", "path", "header", "cookie"];
|
|
1766
|
-
for (const location of locations) {
|
|
1767
|
-
const locationParams = options.parameters[location];
|
|
1768
|
-
if (locationParams) {
|
|
1769
|
-
for (const [name, param] of Object.entries(locationParams)) {
|
|
1770
|
-
parameters.push({
|
|
1771
|
-
name,
|
|
1772
|
-
in: location,
|
|
1773
|
-
required: param.required ?? false,
|
|
1774
|
-
schema: param.schema
|
|
1775
|
-
});
|
|
1776
|
-
}
|
|
1777
|
-
}
|
|
1778
|
-
}
|
|
1779
|
-
}
|
|
1780
|
-
const responses = {};
|
|
1781
|
-
for (const [key, schema] of Object.entries(options.response)) {
|
|
1782
|
-
const [statusCode, contentType] = key.split(/-(.*)/);
|
|
1783
|
-
if (!contentType) {
|
|
1784
|
-
throw new Error(
|
|
1785
|
-
`Response key "${key}" must be in the format "statusCode-contentType"`
|
|
1786
|
-
);
|
|
1787
|
-
}
|
|
1788
|
-
responses[statusCode] ??= {
|
|
1789
|
-
description: `Response for ${statusCode}`,
|
|
1790
|
-
content: {}
|
|
1791
|
-
};
|
|
1792
|
-
if (contentType === "headers") {
|
|
1793
|
-
responses[statusCode].headers = schema;
|
|
1794
|
-
} else {
|
|
1795
|
-
responses[statusCode].content[contentType] = {
|
|
1796
|
-
schema
|
|
1797
|
-
};
|
|
1798
|
-
}
|
|
1799
|
-
}
|
|
1800
|
-
let requestBody = void 0;
|
|
1801
|
-
if (options.request) {
|
|
1802
|
-
requestBody = { description: "Request body", content: {} };
|
|
1803
|
-
for (const [contentType, schema] of Object.entries(options.request)) {
|
|
1804
|
-
requestBody.content[contentType] = { schema };
|
|
1805
|
-
}
|
|
1806
|
-
}
|
|
1807
|
-
return {
|
|
1808
|
-
security: (options.security ?? []).map((name) => ({
|
|
1809
|
-
[name]: []
|
|
1810
|
-
})),
|
|
1811
|
-
operationId: options.name,
|
|
1812
|
-
tags: [options.group],
|
|
1813
|
-
parameters,
|
|
1814
|
-
responses,
|
|
1815
|
-
requestBody
|
|
1816
|
-
};
|
|
1817
|
-
}
|
|
1818
|
-
function tuneRequestBody(spec, operationId, operation, parameters, security2) {
|
|
1819
|
-
const inputName = findUniqueSchemaName(spec, operationId, [
|
|
1820
|
-
"input",
|
|
1821
|
-
"payload",
|
|
1822
|
-
"request"
|
|
1823
|
-
]);
|
|
1824
|
-
const requestBody = isRef4(operation.requestBody) ? followRef(spec, operation.requestBody.$ref) : operation.requestBody ?? {
|
|
1825
|
-
content: {},
|
|
1826
|
-
required: false
|
|
1827
|
-
};
|
|
1828
|
-
if (isEmpty4(requestBody.content)) {
|
|
1829
|
-
const schema = {
|
|
1830
|
-
"x-inputname": inputName,
|
|
1831
|
-
"x-requestbody": true
|
|
1832
|
-
};
|
|
1833
|
-
patchParameters(spec, schema, parameters, security2);
|
|
1834
|
-
const tuned = {
|
|
1835
|
-
...requestBody,
|
|
1836
|
-
content: {
|
|
1837
|
-
"application/empty": {
|
|
1838
|
-
schema: { $ref: `#/components/schemas/${inputName}` }
|
|
1839
|
-
}
|
|
1840
|
-
}
|
|
1841
|
-
};
|
|
1842
|
-
spec.components.schemas[inputName] = schema;
|
|
1843
|
-
return tuned;
|
|
1844
|
-
}
|
|
1845
|
-
for (const contentType in requestBody.content) {
|
|
1846
|
-
const mediaType = requestBody.content[contentType];
|
|
1847
|
-
let schema;
|
|
1848
|
-
switch (true) {
|
|
1849
|
-
case isRef4(mediaType.schema):
|
|
1850
|
-
schema = followRef(spec, mediaType.schema.$ref);
|
|
1851
|
-
break;
|
|
1852
|
-
case isEmpty4(mediaType.schema):
|
|
1853
|
-
schema ??= {};
|
|
1854
|
-
console.warn(
|
|
1855
|
-
`Request body schema for content type "${contentType}" is empty.`
|
|
1856
|
-
);
|
|
1857
|
-
break;
|
|
1858
|
-
default:
|
|
1859
|
-
schema = mediaType.schema;
|
|
1860
|
-
break;
|
|
1861
|
-
}
|
|
1862
|
-
patchParameters(spec, schema, parameters, security2);
|
|
1863
|
-
spec.components.schemas[inputName] = {
|
|
1864
|
-
...schema,
|
|
1865
|
-
"x-requestbody": true,
|
|
1866
|
-
"x-inputname": inputName
|
|
1867
|
-
};
|
|
1868
|
-
requestBody.content[contentType].schema = {
|
|
1869
|
-
$ref: `#/components/schemas/${inputName}`
|
|
1870
|
-
};
|
|
1871
|
-
}
|
|
1872
|
-
return requestBody;
|
|
1873
|
-
}
|
|
1874
|
-
|
|
1875
|
-
// packages/spec/src/lib/get-ref-usage.ts
|
|
1876
|
-
function getRefUsage(spec, schemaName, list = []) {
|
|
1877
|
-
const checkSchema = (schema, withRefCheck = false) => {
|
|
1878
|
-
if (isRef5(schema)) {
|
|
1879
|
-
if (!withRefCheck)
|
|
1880
|
-
return false;
|
|
1881
|
-
const { model } = parseRef2(schema.$ref);
|
|
1882
|
-
return model === schemaName;
|
|
1883
|
-
}
|
|
1884
|
-
if (!isEmpty5(schema.oneOf)) {
|
|
1885
|
-
return schema.oneOf.some((it) => checkSchema(it, true));
|
|
1886
|
-
}
|
|
1887
|
-
if (schema.type === "array" && schema.items) {
|
|
1888
|
-
if (isRef5(schema.items)) {
|
|
1889
|
-
return checkSchema(schema.items, withRefCheck);
|
|
1890
|
-
}
|
|
1891
|
-
if (schema.items.oneOf) {
|
|
1892
|
-
return schema.items.oneOf.some((it) => checkSchema(it, true));
|
|
1893
|
-
}
|
|
1894
|
-
return checkSchema(schema.items, withRefCheck);
|
|
1895
|
-
}
|
|
1896
|
-
if (schema.type === "object") {
|
|
1897
|
-
const properties = schema.properties;
|
|
1898
|
-
if (!isEmpty5(properties)) {
|
|
1899
|
-
let found = false;
|
|
1900
|
-
let propertyName = "";
|
|
1901
|
-
for (const [key, it] of Object.entries(properties)) {
|
|
1902
|
-
found = checkSchema(it, false);
|
|
1903
|
-
if (found) {
|
|
1904
|
-
propertyName = key;
|
|
1905
|
-
}
|
|
1906
|
-
}
|
|
1907
|
-
return propertyName;
|
|
1908
|
-
}
|
|
1909
|
-
}
|
|
1910
|
-
return false;
|
|
1911
|
-
};
|
|
1912
|
-
for (const [key, value] of Object.entries(spec.components.schemas)) {
|
|
1913
|
-
const thisWouldBeTheObjectPropertyKeyName = checkSchema(value);
|
|
1914
|
-
if (thisWouldBeTheObjectPropertyKeyName) {
|
|
1915
|
-
if (typeof thisWouldBeTheObjectPropertyKeyName === "string") {
|
|
1916
|
-
list.push(pascalcase4(`${key} ${thisWouldBeTheObjectPropertyKeyName}`));
|
|
1917
|
-
} else {
|
|
1918
|
-
list.push(pascalcase4(key));
|
|
1919
|
-
}
|
|
1920
|
-
}
|
|
1921
|
-
}
|
|
1922
|
-
return list;
|
|
1923
|
-
}
|
|
1924
|
-
|
|
1925
|
-
// packages/spec/src/lib/is-primitive-schema.ts
|
|
1926
|
-
function isPrimitiveSchema(schema) {
|
|
1927
|
-
const types = coerceTypes(schema, false);
|
|
1928
|
-
if (!types || types.length === 0) {
|
|
1929
|
-
return false;
|
|
1930
|
-
}
|
|
1931
|
-
return types.includes("object") === false;
|
|
1932
|
-
}
|
|
1933
|
-
|
|
1934
|
-
// packages/spec/src/lib/loaders/local-loader.ts
|
|
1935
|
-
import { readFile } from "node:fs/promises";
|
|
1936
|
-
import { extname } from "node:path";
|
|
1937
|
-
import { parse } from "yaml";
|
|
1938
|
-
async function loadLocal(location) {
|
|
1939
|
-
const extName = extname(location);
|
|
1940
|
-
const text = await readFile(location, "utf-8");
|
|
1941
|
-
switch (extName) {
|
|
1942
|
-
case ".json":
|
|
1943
|
-
return JSON.parse(text);
|
|
1944
|
-
case ".yaml":
|
|
1945
|
-
case ".yml":
|
|
1946
|
-
return parse(text);
|
|
1947
|
-
default:
|
|
1948
|
-
throw new Error(`Unsupported file extension: ${extName}`);
|
|
1949
|
-
}
|
|
1950
|
-
}
|
|
1951
|
-
|
|
1952
|
-
// packages/spec/src/lib/loaders/postman/postman-converter.ts
|
|
1953
|
-
import { parse as parse2 } from "fast-content-type-parse";
|
|
1954
|
-
function descriptionToText(description) {
|
|
1955
|
-
if (!description) {
|
|
1956
|
-
return void 0;
|
|
1957
|
-
}
|
|
1958
|
-
if (typeof description === "string") {
|
|
1959
|
-
return description;
|
|
1960
|
-
}
|
|
1961
|
-
if (description.content) {
|
|
1962
|
-
return description.content;
|
|
1963
|
-
}
|
|
1964
|
-
return void 0;
|
|
1965
|
-
}
|
|
1966
|
-
function isFolder(item) {
|
|
1967
|
-
return "item" in item;
|
|
1968
|
-
}
|
|
1969
|
-
function isRequest(item) {
|
|
1970
|
-
return !!item.request;
|
|
1971
|
-
}
|
|
1972
|
-
function processItems(items, parentTags, globalTags, paths, securitySchemes) {
|
|
1973
|
-
for (const item of items) {
|
|
1974
|
-
if (!isFolder(item) && !isRequest(item)) {
|
|
1975
|
-
console.warn(
|
|
1976
|
-
`Skipping item ${item.name} because it is not a folder or request`
|
|
1977
|
-
);
|
|
1978
|
-
continue;
|
|
1979
|
-
}
|
|
1980
|
-
if (isFolder(item)) {
|
|
1981
|
-
if (item.auth) {
|
|
1982
|
-
processAuthScheme(item.auth, securitySchemes);
|
|
1983
|
-
}
|
|
1984
|
-
if (!globalTags.some((tag) => tag.name === item.name)) {
|
|
1985
|
-
globalTags.push({
|
|
1986
|
-
name: item.name,
|
|
1987
|
-
description: descriptionToText(item.description)
|
|
1988
|
-
});
|
|
1989
|
-
}
|
|
1990
|
-
const currentTags = [...parentTags, item.name];
|
|
1991
|
-
processItems(item.item, currentTags, globalTags, paths, securitySchemes);
|
|
1992
|
-
} else if (isRequest(item)) {
|
|
1993
|
-
let operation;
|
|
1994
|
-
if (typeof item.request === "string") {
|
|
1995
|
-
const url = new URL(item.request);
|
|
1996
|
-
operation = requestToOperation(
|
|
1997
|
-
{
|
|
1998
|
-
name: url.pathname,
|
|
1999
|
-
response: [{ code: 200 }],
|
|
2000
|
-
request: {
|
|
2001
|
-
method: "get",
|
|
2002
|
-
url: {
|
|
2003
|
-
path: url.pathname.split("/").slice(1)
|
|
2004
|
-
}
|
|
2005
|
-
}
|
|
2006
|
-
},
|
|
2007
|
-
securitySchemes
|
|
2008
|
-
);
|
|
2009
|
-
} else {
|
|
2010
|
-
const auth = item.request.auth;
|
|
2011
|
-
operation = requestToOperation(
|
|
2012
|
-
{
|
|
2013
|
-
name: item.name,
|
|
2014
|
-
request: { ...item.request, auth },
|
|
2015
|
-
response: item.response
|
|
2016
|
-
},
|
|
2017
|
-
securitySchemes
|
|
2018
|
-
);
|
|
2019
|
-
}
|
|
2020
|
-
paths[operation.path] ??= {};
|
|
2021
|
-
Object.assign(paths[operation.path], {
|
|
2022
|
-
[operation.method]: {
|
|
2023
|
-
tags: parentTags.length ? parentTags : void 0,
|
|
2024
|
-
...operation.operation
|
|
2025
|
-
}
|
|
2026
|
-
});
|
|
2027
|
-
}
|
|
2028
|
-
}
|
|
2029
|
-
}
|
|
2030
|
-
function coerceVariable(query) {
|
|
2031
|
-
if (!query.key) {
|
|
2032
|
-
throw new Error("Invalid query parameter format");
|
|
2033
|
-
}
|
|
2034
|
-
return {
|
|
2035
|
-
key: query.key,
|
|
2036
|
-
description: descriptionToText(query.description)
|
|
2037
|
-
};
|
|
2038
|
-
}
|
|
2039
|
-
function coerceQuery(query) {
|
|
2040
|
-
if (!query.key) {
|
|
2041
|
-
throw new Error("Invalid query parameter format");
|
|
2042
|
-
}
|
|
2043
|
-
return {
|
|
2044
|
-
key: query.key,
|
|
2045
|
-
description: descriptionToText(query.description)
|
|
2046
|
-
};
|
|
2047
|
-
}
|
|
2048
|
-
function coerceUrl(url) {
|
|
2049
|
-
if (!url) {
|
|
2050
|
-
throw new Error("Invalid URL format");
|
|
2051
|
-
}
|
|
2052
|
-
if (typeof url === "string") {
|
|
2053
|
-
return {
|
|
2054
|
-
path: url.split("/").slice(1),
|
|
2055
|
-
query: [],
|
|
2056
|
-
variable: []
|
|
2057
|
-
};
|
|
2058
|
-
}
|
|
2059
|
-
if (typeof url.path === "string") {
|
|
2060
|
-
return {
|
|
2061
|
-
path: url.path.split("/").slice(1),
|
|
2062
|
-
query: (url.query ?? []).map(coerceQuery),
|
|
2063
|
-
variable: (url.variable ?? []).map(coerceVariable)
|
|
2064
|
-
};
|
|
2065
|
-
}
|
|
2066
|
-
return {
|
|
2067
|
-
path: (url.path ?? []).map((p) => {
|
|
2068
|
-
if (typeof p === "string") {
|
|
2069
|
-
return p;
|
|
2070
|
-
}
|
|
2071
|
-
throw new Error("Invalid URL path format");
|
|
2072
|
-
}),
|
|
2073
|
-
query: (url.query ?? []).map(coerceQuery),
|
|
2074
|
-
variable: (url.variable ?? []).map(coerceVariable)
|
|
2075
|
-
};
|
|
2076
|
-
}
|
|
2077
|
-
function coerceResponseHeader(header) {
|
|
2078
|
-
if (!header) {
|
|
2079
|
-
return [];
|
|
2080
|
-
}
|
|
2081
|
-
if (typeof header === "string") {
|
|
2082
|
-
throw new Error(`Invalid header format: ${header}`);
|
|
2083
|
-
}
|
|
2084
|
-
return header.map((h) => {
|
|
2085
|
-
if (typeof h === "string") {
|
|
2086
|
-
return {
|
|
2087
|
-
key: h,
|
|
2088
|
-
value: null
|
|
2089
|
-
};
|
|
2090
|
-
}
|
|
2091
|
-
return h;
|
|
2092
|
-
});
|
|
2093
|
-
}
|
|
2094
|
-
function processAuthScheme(auth, securitySchemes) {
|
|
2095
|
-
if (!auth || auth.type === "noauth")
|
|
2096
|
-
return null;
|
|
2097
|
-
const getAuthAttr = (key) => {
|
|
2098
|
-
if (!auth || auth.type === "noauth")
|
|
2099
|
-
return void 0;
|
|
2100
|
-
const authType = auth[auth.type];
|
|
2101
|
-
if (!authType)
|
|
2102
|
-
return void 0;
|
|
2103
|
-
const attr = authType.find((a) => a.key === key);
|
|
2104
|
-
return attr ? String(attr.value) : void 0;
|
|
2105
|
-
};
|
|
2106
|
-
const schemeId = `${auth.type}Auth`;
|
|
2107
|
-
switch (auth.type) {
|
|
2108
|
-
case "apikey": {
|
|
2109
|
-
const key = getAuthAttr("key") || "api_key";
|
|
2110
|
-
const in_ = getAuthAttr("in") || "header";
|
|
2111
|
-
securitySchemes[schemeId] = {
|
|
2112
|
-
type: "apiKey",
|
|
2113
|
-
name: key,
|
|
2114
|
-
in: in_,
|
|
2115
|
-
description: "API key authentication"
|
|
2116
|
-
};
|
|
2117
|
-
break;
|
|
2118
|
-
}
|
|
2119
|
-
case "basic":
|
|
2120
|
-
securitySchemes[schemeId] = {
|
|
2121
|
-
type: "http",
|
|
2122
|
-
scheme: "basic",
|
|
2123
|
-
description: "Basic HTTP authentication"
|
|
2124
|
-
};
|
|
2125
|
-
break;
|
|
2126
|
-
case "bearer": {
|
|
2127
|
-
const token = getAuthAttr("token");
|
|
2128
|
-
securitySchemes[schemeId] = {
|
|
2129
|
-
type: "http",
|
|
2130
|
-
scheme: "bearer",
|
|
2131
|
-
description: "Bearer token authentication",
|
|
2132
|
-
...token ? { bearerFormat: "JWT" } : {}
|
|
2133
|
-
};
|
|
2134
|
-
break;
|
|
2135
|
-
}
|
|
2136
|
-
case "oauth2": {
|
|
2137
|
-
const flowType = getAuthAttr("grant_type") || "implicit";
|
|
2138
|
-
securitySchemes[schemeId] = {
|
|
2139
|
-
type: "oauth2",
|
|
2140
|
-
description: "OAuth 2.0 authentication",
|
|
2141
|
-
flows: {
|
|
2142
|
-
[flowType === "authorization_code" ? "authorizationCode" : flowType]: {
|
|
2143
|
-
authorizationUrl: getAuthAttr("authUrl") || "https://example.com/oauth/authorize",
|
|
2144
|
-
tokenUrl: getAuthAttr("tokenUrl") || "https://example.com/oauth/token",
|
|
2145
|
-
scopes: {}
|
|
2146
|
-
}
|
|
2147
|
-
}
|
|
2148
|
-
};
|
|
2149
|
-
break;
|
|
2150
|
-
}
|
|
2151
|
-
case "digest":
|
|
2152
|
-
securitySchemes[schemeId] = {
|
|
2153
|
-
type: "http",
|
|
2154
|
-
scheme: "digest",
|
|
2155
|
-
description: "Digest authentication"
|
|
2156
|
-
};
|
|
2157
|
-
break;
|
|
2158
|
-
case "awsv4":
|
|
2159
|
-
securitySchemes[schemeId] = {
|
|
2160
|
-
type: "apiKey",
|
|
2161
|
-
name: "Authorization",
|
|
2162
|
-
in: "header",
|
|
2163
|
-
description: "AWS Signature v4 authentication"
|
|
2164
|
-
};
|
|
2165
|
-
break;
|
|
2166
|
-
default:
|
|
2167
|
-
securitySchemes[schemeId] = {
|
|
2168
|
-
type: "apiKey",
|
|
2169
|
-
name: "Authorization",
|
|
2170
|
-
in: "header",
|
|
2171
|
-
description: `${auth.type} authentication`
|
|
2172
|
-
};
|
|
2173
|
-
}
|
|
2174
|
-
return schemeId;
|
|
2175
|
-
}
|
|
2176
|
-
function generateSecurityRequirement(auth, securitySchemes) {
|
|
2177
|
-
if (!auth || auth.type === "noauth")
|
|
2178
|
-
return [];
|
|
2179
|
-
const schemeId = `${auth.type}Auth`;
|
|
2180
|
-
if (securitySchemes[schemeId]) {
|
|
2181
|
-
return [{ [schemeId]: [] }];
|
|
2182
|
-
}
|
|
2183
|
-
return [];
|
|
2184
|
-
}
|
|
2185
|
-
function requestToOperation(item, securitySchemes) {
|
|
2186
|
-
const url = coerceUrl(item.request.url);
|
|
2187
|
-
const headers = Array.isArray(item.request.header) ? item.request.header : [];
|
|
2188
|
-
const parameters = [
|
|
2189
|
-
...url.query.filter((param) => !param.disabled).map((param) => {
|
|
2190
|
-
return {
|
|
2191
|
-
in: "query",
|
|
2192
|
-
name: param.key,
|
|
2193
|
-
required: false,
|
|
2194
|
-
description: param.description,
|
|
2195
|
-
schema: {
|
|
2196
|
-
...param.value && !isNaN(Number(param.value)) ? { type: "number" } : param.value === "true" || param.value === "false" ? { type: "boolean" } : { type: "string" }
|
|
2197
|
-
}
|
|
2198
|
-
};
|
|
2199
|
-
}),
|
|
2200
|
-
...url.variable.map((param) => {
|
|
2201
|
-
return {
|
|
2202
|
-
in: "path",
|
|
2203
|
-
name: param.key,
|
|
2204
|
-
required: true,
|
|
2205
|
-
description: param.description,
|
|
2206
|
-
schema: {
|
|
2207
|
-
...param.value && !isNaN(Number(param.value)) ? { type: "number" } : param.value === "true" || param.value === "false" ? { type: "boolean" } : { type: "string" }
|
|
2208
|
-
}
|
|
2209
|
-
};
|
|
2210
|
-
}),
|
|
2211
|
-
...headers.filter(
|
|
2212
|
-
(h) => !h.disabled && h.key.toLowerCase() !== "accept" && h.key.toLowerCase() !== "content-type"
|
|
2213
|
-
).map((header) => {
|
|
2214
|
-
return {
|
|
2215
|
-
in: "header",
|
|
2216
|
-
name: header.key,
|
|
2217
|
-
required: false,
|
|
2218
|
-
description: descriptionToText(header.description),
|
|
2219
|
-
schema: {
|
|
2220
|
-
type: "string"
|
|
2221
|
-
}
|
|
2222
|
-
};
|
|
2223
|
-
})
|
|
2224
|
-
];
|
|
2225
|
-
const acceptHeaderIdx = headers.findIndex(
|
|
2226
|
-
(h) => h.key.toLowerCase() === "accept"
|
|
2227
|
-
);
|
|
2228
|
-
const contentTypeIdx = headers.findIndex(
|
|
2229
|
-
(h) => h.key.toLowerCase() === "content-type"
|
|
2230
|
-
);
|
|
2231
|
-
const [acceptHeaderValue] = acceptHeaderIdx !== -1 ? headers.splice(acceptHeaderIdx, 1).map((h) => h.value) : [];
|
|
2232
|
-
const [contentTypeValue] = contentTypeIdx !== -1 ? headers.splice(contentTypeIdx, 1).map((h) => h.value) : [];
|
|
2233
|
-
let security2;
|
|
2234
|
-
if (item.request.auth) {
|
|
2235
|
-
const schemeId = processAuthScheme(item.request.auth, securitySchemes);
|
|
2236
|
-
if (schemeId) {
|
|
2237
|
-
security2 = [{ [schemeId]: [] }];
|
|
2238
|
-
}
|
|
2239
|
-
}
|
|
2240
|
-
return {
|
|
2241
|
-
path: `/${url.path.join("/")}`.replace(/:([^/]+)/g, "{$1}"),
|
|
2242
|
-
method: (item.request.method ?? "get").toLowerCase(),
|
|
2243
|
-
operation: {
|
|
2244
|
-
summary: item.name,
|
|
2245
|
-
description: descriptionToText(item.request.description),
|
|
2246
|
-
parameters,
|
|
2247
|
-
security: security2,
|
|
2248
|
-
responses: !item.response || item.response.length === 0 ? {
|
|
2249
|
-
200: {
|
|
2250
|
-
description: "Successful response"
|
|
2251
|
-
}
|
|
2252
|
-
} : item.response.reduce((acc, response) => {
|
|
2253
|
-
const headers2 = coerceResponseHeader(response.header);
|
|
2254
|
-
const contentTypeIdx2 = headers2.findIndex(
|
|
2255
|
-
(h) => h.key.toLowerCase() === "content-type"
|
|
2256
|
-
);
|
|
2257
|
-
const [contentTypeValue2] = contentTypeIdx2 !== -1 ? headers2.splice(contentTypeIdx2).map((h) => h.value) : [];
|
|
2258
|
-
let contentType = response.body ? parse2(contentTypeValue2 || "application/json")?.type : null;
|
|
2259
|
-
contentType ??= "application/octet-stream";
|
|
2260
|
-
return {
|
|
2261
|
-
...acc,
|
|
2262
|
-
[response.code ?? 200]: {
|
|
2263
|
-
description: response.name ? response.name : `Response for ${response.code}`,
|
|
2264
|
-
content: {
|
|
2265
|
-
[contentType]: {
|
|
2266
|
-
schema: bodyToSchema(response.body)
|
|
2267
|
-
}
|
|
2268
|
-
}
|
|
2269
|
-
}
|
|
2270
|
-
};
|
|
2271
|
-
}, {}),
|
|
2272
|
-
requestBody: item.request.body ? requestBodyToOperationBody(
|
|
2273
|
-
contentTypeValue || "application/json",
|
|
2274
|
-
item.request.body
|
|
2275
|
-
) : void 0
|
|
2276
|
-
}
|
|
2277
|
-
};
|
|
2278
|
-
}
|
|
2279
|
-
function requestBodyToOperationBody(contentType, body) {
|
|
2280
|
-
if (body.mode === "raw") {
|
|
2281
|
-
return {
|
|
2282
|
-
content: {
|
|
2283
|
-
[contentType]: {
|
|
2284
|
-
schema: bodyToSchema(body.raw)
|
|
2285
|
-
}
|
|
2286
|
-
}
|
|
2287
|
-
};
|
|
2288
|
-
} else if (body.mode === "urlencoded") {
|
|
2289
|
-
const properties = {};
|
|
2290
|
-
(body.urlencoded || []).filter((param) => !param.disabled).forEach((param) => {
|
|
2291
|
-
properties[param.key] = {
|
|
2292
|
-
type: "string",
|
|
2293
|
-
description: descriptionToText(param.description)
|
|
2294
|
-
};
|
|
2295
|
-
});
|
|
2296
|
-
return {
|
|
2297
|
-
content: {
|
|
2298
|
-
"application/x-www-form-urlencoded": {
|
|
2299
|
-
schema: {
|
|
2300
|
-
type: "object",
|
|
2301
|
-
properties
|
|
2302
|
-
}
|
|
2303
|
-
}
|
|
2304
|
-
}
|
|
2305
|
-
};
|
|
2306
|
-
} else if (body.mode === "formdata") {
|
|
2307
|
-
const properties = {};
|
|
2308
|
-
(body.formdata || []).filter((param) => !param.disabled).forEach((param) => {
|
|
2309
|
-
if (param.type === "text") {
|
|
2310
|
-
properties[param.key] = {
|
|
2311
|
-
type: "string",
|
|
2312
|
-
description: descriptionToText(param.description)
|
|
2313
|
-
};
|
|
2314
|
-
} else if (param.type === "file") {
|
|
2315
|
-
properties[param.key] = {
|
|
2316
|
-
type: "string",
|
|
2317
|
-
format: "binary",
|
|
2318
|
-
description: descriptionToText(param.description)
|
|
2319
|
-
};
|
|
2320
|
-
}
|
|
2321
|
-
});
|
|
2322
|
-
return {
|
|
2323
|
-
content: {
|
|
2324
|
-
"multipart/form-data": {
|
|
2325
|
-
schema: {
|
|
2326
|
-
type: "object",
|
|
2327
|
-
properties
|
|
2328
|
-
}
|
|
2329
|
-
}
|
|
2330
|
-
}
|
|
2331
|
-
};
|
|
2332
|
-
}
|
|
2333
|
-
throw new Error(
|
|
2334
|
-
`Unsupported request body mode: ${body.mode}. Supported modes are: raw, urlencoded, formdata`
|
|
2335
|
-
);
|
|
2336
|
-
}
|
|
2337
|
-
function bodyToSchema(bodyString) {
|
|
2338
|
-
if (!bodyString) {
|
|
2339
|
-
return void 0;
|
|
2340
|
-
}
|
|
2341
|
-
let body;
|
|
2342
|
-
try {
|
|
2343
|
-
body = JSON.parse(bodyString.normalize("NFKD"));
|
|
2344
|
-
} catch (error) {
|
|
2345
|
-
console.warn(
|
|
2346
|
-
`Failed to parse JSON body: ${bodyString}. Treating as plain string.`,
|
|
2347
|
-
error
|
|
2348
|
-
);
|
|
2349
|
-
return { type: "string", example: bodyString };
|
|
2350
|
-
}
|
|
2351
|
-
return toSchema(body);
|
|
2352
|
-
}
|
|
2353
|
-
function toSchema(body) {
|
|
2354
|
-
const typeMap = {
|
|
2355
|
-
"<number>": "number",
|
|
2356
|
-
"<string>": "string",
|
|
2357
|
-
"<boolean>": "boolean",
|
|
2358
|
-
false: "boolean",
|
|
2359
|
-
true: "boolean"
|
|
2360
|
-
};
|
|
2361
|
-
if (Array.isArray(body)) {
|
|
2362
|
-
return {
|
|
2363
|
-
type: "array",
|
|
2364
|
-
items: toSchema(body[0])
|
|
2365
|
-
};
|
|
2366
|
-
}
|
|
2367
|
-
if (typeof body === "object" && body !== null) {
|
|
2368
|
-
const properties = {};
|
|
2369
|
-
for (const [key, value] of Object.entries(body)) {
|
|
2370
|
-
properties[key] = toSchema(value);
|
|
2371
|
-
}
|
|
2372
|
-
return {
|
|
2373
|
-
type: "object",
|
|
2374
|
-
properties
|
|
2375
|
-
};
|
|
2376
|
-
}
|
|
2377
|
-
if (typeof body === "string") {
|
|
2378
|
-
return {
|
|
2379
|
-
type: typeMap[body] ?? "string"
|
|
2380
|
-
};
|
|
2381
|
-
}
|
|
2382
|
-
if (typeof body === "number") {
|
|
2383
|
-
return {
|
|
2384
|
-
type: "number"
|
|
2385
|
-
};
|
|
2386
|
-
}
|
|
2387
|
-
if (typeof body === "boolean") {
|
|
2388
|
-
return {
|
|
2389
|
-
type: "boolean"
|
|
2390
|
-
};
|
|
2391
|
-
}
|
|
2392
|
-
if (body === null) {
|
|
2393
|
-
return {
|
|
2394
|
-
type: "null"
|
|
2395
|
-
};
|
|
2396
|
-
}
|
|
2397
|
-
console.warn(`Unknown type for body: ${body}. Defaulting to string.`, body);
|
|
2398
|
-
return {
|
|
2399
|
-
type: "string"
|
|
2400
|
-
};
|
|
2401
|
-
}
|
|
2402
|
-
function convertPostmanToOpenAPI(collection) {
|
|
2403
|
-
const tags = [];
|
|
2404
|
-
const paths = {};
|
|
2405
|
-
const securitySchemes = {};
|
|
2406
|
-
if (collection.auth) {
|
|
2407
|
-
processAuthScheme(collection.auth, securitySchemes);
|
|
2408
|
-
}
|
|
2409
|
-
processItems(collection.item, [], tags, paths, securitySchemes);
|
|
2410
|
-
return {
|
|
2411
|
-
openapi: "3.1.0",
|
|
2412
|
-
info: {
|
|
2413
|
-
title: collection.info.name,
|
|
2414
|
-
version: "1.0.0",
|
|
2415
|
-
description: descriptionToText(collection.info.description)
|
|
2416
|
-
},
|
|
2417
|
-
tags,
|
|
2418
|
-
paths,
|
|
2419
|
-
// Only add security at top level if there's collection auth
|
|
2420
|
-
security: collection.auth ? generateSecurityRequirement(collection.auth, securitySchemes) : void 0,
|
|
2421
|
-
components: Object.keys(securitySchemes).length > 0 ? {
|
|
2422
|
-
securitySchemes
|
|
2423
|
-
} : void 0
|
|
2424
|
-
};
|
|
2425
|
-
}
|
|
2426
|
-
|
|
2427
|
-
// packages/spec/src/lib/loaders/remote-loader.ts
|
|
2428
|
-
import { extname as extname2 } from "node:path";
|
|
2429
|
-
import { parse as parse3 } from "yaml";
|
|
2430
|
-
async function loadRemote(location) {
|
|
2431
|
-
const extName = extname2(location);
|
|
2432
|
-
const response = await fetch(location);
|
|
2433
|
-
if (!response.ok) {
|
|
2434
|
-
throw new Error(`Failed to fetch ${location}: ${response.statusText}`);
|
|
2435
|
-
}
|
|
2436
|
-
switch (extName) {
|
|
2437
|
-
case ".json":
|
|
2438
|
-
return response.json();
|
|
2439
|
-
case ".yaml":
|
|
2440
|
-
case ".yml": {
|
|
2441
|
-
const text = await response.text();
|
|
2442
|
-
return parse3(text);
|
|
2443
|
-
}
|
|
2444
|
-
default:
|
|
2445
|
-
try {
|
|
2446
|
-
return response.json();
|
|
2447
|
-
} catch {
|
|
2448
|
-
const text = await response.text();
|
|
2449
|
-
return parse3(text);
|
|
2450
|
-
}
|
|
2451
|
-
}
|
|
2452
|
-
}
|
|
2453
|
-
|
|
2454
|
-
// packages/spec/src/lib/loaders/load-spec.ts
|
|
2455
|
-
function isPostman(content) {
|
|
2456
|
-
return typeof content === "object" && content !== null && "info" in content && typeof content.info === "object" && content.info !== null && "item" in content && Array.isArray(content.item) && "schema" in content.info && typeof content.info.schema === "string" && content.info.schema.includes("//schema.getpostman.com/");
|
|
2457
|
-
}
|
|
2458
|
-
async function loadSpec(location) {
|
|
2459
|
-
let content = await loadFile(location);
|
|
2460
|
-
if (isPostman(content)) {
|
|
2461
|
-
content = convertPostmanToOpenAPI(content);
|
|
2462
|
-
}
|
|
2463
|
-
return content;
|
|
2464
|
-
}
|
|
2465
|
-
function loadFile(location) {
|
|
2466
|
-
const [protocol] = location.split(":");
|
|
2467
|
-
if (protocol === "http" || protocol === "https") {
|
|
2468
|
-
return loadRemote(location);
|
|
2469
|
-
}
|
|
2470
|
-
return loadLocal(location);
|
|
2471
|
-
}
|
|
2472
|
-
|
|
2473
|
-
// packages/spec/src/lib/metadata.ts
|
|
2474
|
-
import deubg from "debug";
|
|
2475
|
-
import { readFile as readFile2, unlink, writeFile } from "node:fs/promises";
|
|
2476
|
-
import { availableParallelism } from "node:os";
|
|
2477
|
-
import { join } from "node:path";
|
|
2478
|
-
import pLimit from "p-limit";
|
|
2479
|
-
import {
|
|
2480
|
-
addLeadingSlash,
|
|
2481
|
-
exist,
|
|
2482
|
-
readFolder
|
|
2483
|
-
} from "@sdk-it/core/file-system.js";
|
|
2484
|
-
var log = deubg("sdk-it:metdata");
|
|
2485
|
-
async function readWriteJson(path) {
|
|
2486
|
-
const content = await exist(path) ? JSON.parse(await readFile2(path, "utf-8")) : {};
|
|
2487
|
-
return {
|
|
2488
|
-
content,
|
|
2489
|
-
write: (value = content) => writeFile(path, JSON.stringify(value, null, 2), "utf-8")
|
|
2490
|
-
};
|
|
2491
|
-
}
|
|
2492
|
-
async function readWriteMetadata(output, files) {
|
|
2493
|
-
const metadata = await readWriteJson(join(output, "metadata.json"));
|
|
2494
|
-
metadata.content.generatedFiles = files;
|
|
2495
|
-
metadata.content.userFiles ??= ["/dist/**", "/build/**", "/readme.md"];
|
|
2496
|
-
await metadata.write(metadata.content);
|
|
2497
|
-
return metadata;
|
|
2498
|
-
}
|
|
2499
|
-
async function cleanFiles(metadata, output, alwaysAvailableFiles = []) {
|
|
2500
|
-
const { default: micromatch } = await import("micromatch");
|
|
2501
|
-
const generated = metadata.generatedFiles ?? [];
|
|
2502
|
-
const user = metadata.userFiles ?? [];
|
|
2503
|
-
const keep = [...generated, ...user, ...alwaysAvailableFiles];
|
|
2504
|
-
const actualFiles = (await readFolder(output, true)).map(addLeadingSlash);
|
|
2505
|
-
const filesToDelete = actualFiles.filter(
|
|
2506
|
-
(file) => !micromatch.isMatch(file, keep, { cwd: join(process.cwd(), output) })
|
|
2507
|
-
);
|
|
2508
|
-
const limit = pLimit(availableParallelism());
|
|
2509
|
-
await Promise.all(
|
|
2510
|
-
filesToDelete.map(
|
|
2511
|
-
(file) => limit(async () => {
|
|
2512
|
-
const filePath = join(output, file);
|
|
2513
|
-
await unlink(filePath);
|
|
2514
|
-
log(`Deleted file: ${filePath}`);
|
|
2515
|
-
})
|
|
2516
|
-
)
|
|
2517
|
-
);
|
|
2518
|
-
}
|
|
2519
|
-
|
|
2520
|
-
// packages/spec/src/lib/sidebar.ts
|
|
2521
|
-
import { camelcase as camelcase3 } from "stringcase";
|
|
2522
|
-
function createOAIMeta(spec) {
|
|
2523
|
-
spec.paths ??= {};
|
|
2524
|
-
const navigationGroups = {
|
|
2525
|
-
default: { id: "default", title: "General" }
|
|
2526
|
-
};
|
|
2527
|
-
const groups = {};
|
|
2528
|
-
forEachOperation(spec, (entry, operation) => {
|
|
2529
|
-
const tag = entry.tag;
|
|
2530
|
-
groups[tag] ??= {
|
|
2531
|
-
id: tag,
|
|
2532
|
-
title: tag,
|
|
2533
|
-
description: spec.tags?.find((t) => t.name === tag)?.description,
|
|
2534
|
-
navigationGroup: "default",
|
|
2535
|
-
sections: []
|
|
2536
|
-
};
|
|
2537
|
-
groups[tag].sections.push({
|
|
2538
|
-
type: "endpoint",
|
|
2539
|
-
key: operation.operationId,
|
|
2540
|
-
path: entry.path
|
|
2541
|
-
});
|
|
2542
|
-
});
|
|
2543
|
-
return {
|
|
2544
|
-
groups: Object.values(groups),
|
|
2545
|
-
navigationGroups: Object.values(navigationGroups)
|
|
2546
|
-
};
|
|
2547
|
-
}
|
|
2548
|
-
function getOperationById(spec, operationId) {
|
|
2549
|
-
let operation;
|
|
2550
|
-
forEachOperation(spec, (entry, op) => {
|
|
2551
|
-
if (op.operationId === operationId) {
|
|
2552
|
-
operation = op;
|
|
2553
|
-
}
|
|
2554
|
-
});
|
|
2555
|
-
return operation;
|
|
2556
|
-
}
|
|
2557
|
-
function toSidebar(spec) {
|
|
2558
|
-
const openapi = spec;
|
|
2559
|
-
const sidebar = [];
|
|
2560
|
-
const oaiMeta = openapi["x-oaiMeta"] ?? createOAIMeta(spec);
|
|
2561
|
-
for (const navGroup of oaiMeta.navigationGroups) {
|
|
2562
|
-
const group = oaiMeta.groups.filter(
|
|
2563
|
-
(group2) => group2.navigationGroup === navGroup.id
|
|
2564
|
-
);
|
|
2565
|
-
if (group.length === 0)
|
|
2566
|
-
continue;
|
|
2567
|
-
const groupItems = [];
|
|
2568
|
-
for (const item of group) {
|
|
2569
|
-
const subitems = (item.sections ?? []).filter((it) => it.type === "endpoint").map((section) => {
|
|
2570
|
-
const operation = getOperationById(spec, section.key);
|
|
2571
|
-
const title = operation?.["x-oaiMeta"]?.name || operation?.summary || section.key;
|
|
2572
|
-
return {
|
|
2573
|
-
id: section.key,
|
|
2574
|
-
title,
|
|
2575
|
-
url: `${item.id}/${camelcase3(section.key)}`
|
|
2576
|
-
};
|
|
2577
|
-
});
|
|
2578
|
-
if (subitems.length === 0)
|
|
2579
|
-
continue;
|
|
2580
|
-
groupItems.push({
|
|
2581
|
-
id: item.id,
|
|
2582
|
-
title: item.title,
|
|
2583
|
-
url: `/${item.id}`,
|
|
2584
|
-
description: item.description,
|
|
2585
|
-
items: subitems
|
|
2586
|
-
});
|
|
2587
|
-
}
|
|
2588
|
-
if (groupItems.length === 0)
|
|
2589
|
-
continue;
|
|
2590
|
-
sidebar.push({
|
|
2591
|
-
category: navGroup.title,
|
|
2592
|
-
items: groupItems
|
|
2593
|
-
});
|
|
2594
|
-
}
|
|
2595
|
-
return sidebar;
|
|
2596
|
-
}
|
|
2597
|
-
export {
|
|
2598
|
-
augmentSpec,
|
|
2599
|
-
cleanFiles,
|
|
2600
|
-
coerceTypes,
|
|
2601
|
-
createOperation,
|
|
2602
|
-
defaults,
|
|
2603
|
-
determineGenericTag,
|
|
2604
|
-
expandSpec,
|
|
2605
|
-
findPolymorphicVarients,
|
|
2606
|
-
findVarients,
|
|
2607
|
-
fixSpec,
|
|
2608
|
-
forEachOperation,
|
|
2609
|
-
formatName,
|
|
2610
|
-
getRefUsage,
|
|
2611
|
-
isErrorStatusCode,
|
|
2612
|
-
isPrimitiveSchema,
|
|
2613
|
-
isSseContentType,
|
|
2614
|
-
isStreamingContentType,
|
|
2615
|
-
isSuccessStatusCode,
|
|
2616
|
-
isTextContentType,
|
|
2617
|
-
loadFile,
|
|
2618
|
-
loadLocal,
|
|
2619
|
-
loadRemote,
|
|
2620
|
-
loadSpec,
|
|
2621
|
-
parseJsonContentType,
|
|
2622
|
-
patchParameters,
|
|
2623
|
-
readWriteJson,
|
|
2624
|
-
readWriteMetadata,
|
|
2625
|
-
sanitizeTag,
|
|
2626
|
-
security,
|
|
2627
|
-
securityToOptions,
|
|
2628
|
-
toSidebar
|
|
2629
|
-
};
|
|
1
|
+
export * from "./lib/find-polymorphic-varients.js";
|
|
2
|
+
export * from "./lib/find-unique-schema-name.js";
|
|
3
|
+
export * from "./lib/for-each-operation.js";
|
|
4
|
+
export * from "./lib/format-name.js";
|
|
5
|
+
export * from "./lib/get-ref-usage.js";
|
|
6
|
+
export * from "./lib/is-primitive-schema.js";
|
|
7
|
+
export * from "./lib/is.js";
|
|
8
|
+
export * from "./lib/loaders/load-spec.js";
|
|
9
|
+
export * from "./lib/loaders/local-loader.js";
|
|
10
|
+
export * from "./lib/loaders/remote-loader.js";
|
|
11
|
+
export * from "./lib/metadata.js";
|
|
12
|
+
export * from "./lib/operation.js";
|
|
13
|
+
export * from "./lib/options.js";
|
|
14
|
+
export * from "./lib/reserved-keywords.js";
|
|
15
|
+
export * from "./lib/security.js";
|
|
16
|
+
export * from "./lib/sidebar.js";
|
|
17
|
+
export * from "./lib/tag.js";
|
|
18
|
+
export * from "./lib/tune.js";
|
|
19
|
+
export * from "./lib/types.js";
|
|
2630
20
|
//# sourceMappingURL=index.js.map
|