oas 38.3.0 → 38.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -4
- package/dist/chunk-4CWJNFV5.js +709 -0
- package/dist/chunk-4CWJNFV5.js.map +1 -0
- package/dist/{chunk-XXX6U72S.js → chunk-5ZBXVQYR.js} +17 -3
- package/dist/chunk-5ZBXVQYR.js.map +1 -0
- package/dist/{chunk-3ZXZMZXT.cjs → chunk-Y64TRECN.cjs} +18 -4
- package/dist/chunk-Y64TRECN.cjs.map +1 -0
- package/dist/chunk-Z2WDQGCU.cjs +709 -0
- package/dist/chunk-Z2WDQGCU.cjs.map +1 -0
- package/dist/index-Borb1VXh.d.cts +174 -0
- package/dist/index-Br-eJMCh.d.ts +174 -0
- package/dist/index.cjs +29 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +12 -5
- package/dist/index.js.map +1 -1
- package/dist/operation/index.cjs +2 -2
- package/dist/operation/index.js +1 -1
- package/dist/pruner/index.cjs +87 -0
- package/dist/pruner/index.cjs.map +1 -0
- package/dist/pruner/index.d.cts +72 -0
- package/dist/pruner/index.d.ts +72 -0
- package/dist/pruner/index.js +87 -0
- package/dist/pruner/index.js.map +1 -0
- package/dist/reducer/index.cjs +39 -520
- package/dist/reducer/index.cjs.map +1 -1
- package/dist/reducer/index.d.cts +22 -108
- package/dist/reducer/index.d.ts +22 -108
- package/dist/reducer/index.js +37 -518
- package/dist/reducer/index.js.map +1 -1
- package/package.json +6 -2
- package/dist/chunk-3ZXZMZXT.cjs.map +0 -1
- package/dist/chunk-XXX6U72S.js.map +0 -1
|
@@ -0,0 +1,709 @@
|
|
|
1
|
+
import {
|
|
2
|
+
query
|
|
3
|
+
} from "./chunk-QTPMJRIE.js";
|
|
4
|
+
import {
|
|
5
|
+
Operation
|
|
6
|
+
} from "./chunk-5ZBXVQYR.js";
|
|
7
|
+
import {
|
|
8
|
+
decodePointer,
|
|
9
|
+
supportedMethods
|
|
10
|
+
} from "./chunk-ANZIKNVX.js";
|
|
11
|
+
import {
|
|
12
|
+
isOpenAPI31,
|
|
13
|
+
isRef
|
|
14
|
+
} from "./chunk-XG4HGNCN.js";
|
|
15
|
+
|
|
16
|
+
// src/lib/transformer/index.ts
|
|
17
|
+
import jsonPointer from "jsonpointer";
|
|
18
|
+
|
|
19
|
+
// src/lib/transformer/operation-selection.ts
|
|
20
|
+
var OperationSelection = class {
|
|
21
|
+
selections = /* @__PURE__ */ new Map();
|
|
22
|
+
/** Whether any paths or webhooks have been selected. */
|
|
23
|
+
get hasSelections() {
|
|
24
|
+
return this.selections.size > 0;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Select every operation within a path or webhook.
|
|
28
|
+
*
|
|
29
|
+
* @param key Path or webhook key whose operations should be selected.
|
|
30
|
+
*/
|
|
31
|
+
addAll(key) {
|
|
32
|
+
this.selections.set(this.normalize(key), "*");
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Add an operation to the selection for a path or webhook. If every operation for the key is
|
|
36
|
+
* already selected, that selection remains unchanged.
|
|
37
|
+
*
|
|
38
|
+
* @param key Path or webhook key that contains the operation.
|
|
39
|
+
* @param method HTTP method of the operation to select.
|
|
40
|
+
*/
|
|
41
|
+
addOperation(key, method) {
|
|
42
|
+
const normalizedKey = this.normalize(key);
|
|
43
|
+
const normalizedMethod = this.normalize(method);
|
|
44
|
+
const selection = this.selections.get(normalizedKey);
|
|
45
|
+
if (selection === "*") {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (selection instanceof Set) {
|
|
49
|
+
selection.add(normalizedMethod);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
this.selections.set(normalizedKey, /* @__PURE__ */ new Set([normalizedMethod]));
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Clear every operation selection for a path or webhook.
|
|
56
|
+
*
|
|
57
|
+
* @param key Path or webhook key to clear.
|
|
58
|
+
*/
|
|
59
|
+
clear(key) {
|
|
60
|
+
this.selections.delete(this.normalize(key));
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Determine whether a path or webhook has any selected operations.
|
|
64
|
+
*
|
|
65
|
+
* @param key Path or webhook key to check.
|
|
66
|
+
*/
|
|
67
|
+
has(key) {
|
|
68
|
+
return this.selections.has(this.normalize(key));
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Determine whether every operation within a path or webhook is selected.
|
|
72
|
+
*
|
|
73
|
+
* @param key Path or webhook key to check.
|
|
74
|
+
*/
|
|
75
|
+
matchesAll(key) {
|
|
76
|
+
return this.selections.get(this.normalize(key)) === "*";
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Determine whether an operation is selected.
|
|
80
|
+
*
|
|
81
|
+
* @param key Path or webhook key that contains the operation.
|
|
82
|
+
* @param method HTTP method of the operation to check.
|
|
83
|
+
*/
|
|
84
|
+
matches(key, method) {
|
|
85
|
+
const selection = this.selections.get(this.normalize(key));
|
|
86
|
+
return selection === "*" || Boolean(selection?.has(this.normalize(method)));
|
|
87
|
+
}
|
|
88
|
+
/** Normalize path, webhook, and method casing for comparisons. */
|
|
89
|
+
normalize(value) {
|
|
90
|
+
return value.toLowerCase();
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// src/lib/transformer/index.ts
|
|
95
|
+
var OpenAPITransformer = class {
|
|
96
|
+
definition;
|
|
97
|
+
mode;
|
|
98
|
+
/**
|
|
99
|
+
* A collection of `$ref` pointers that are used within our transformed API definition. This is
|
|
100
|
+
* used to ensure that all referenced schemas are retained in our resulting API definition. Not
|
|
101
|
+
* retaining them would result in an invalid OpenAPI definition.
|
|
102
|
+
*/
|
|
103
|
+
$refs = /* @__PURE__ */ new Set();
|
|
104
|
+
/**
|
|
105
|
+
* A collection of OpenAPI tags that are used within the transformed API definition.
|
|
106
|
+
*/
|
|
107
|
+
usedTags = /* @__PURE__ */ new Set();
|
|
108
|
+
/**
|
|
109
|
+
* A collection of OpenAPI paths and operations that are cross-referenced from any other paths
|
|
110
|
+
* and operations that we're retaining. This collection is used to ensure that those operations
|
|
111
|
+
* are retained in our resulting API definition. Not retaining them would result in an invalid
|
|
112
|
+
* OpenAPI definition.
|
|
113
|
+
*/
|
|
114
|
+
retainPathMethods = /* @__PURE__ */ new Set();
|
|
115
|
+
/**
|
|
116
|
+
* A collection of OpenAPI webhook names and methods that are cross-referenced from any other
|
|
117
|
+
* schemas. This collection, like `retainPathMethods`, is used to ensure that those operations are
|
|
118
|
+
* retained in our resulting API definition. Not retaining them would result in an invalid
|
|
119
|
+
* OpenAPI definition.
|
|
120
|
+
*/
|
|
121
|
+
retainWebhookMethods = /* @__PURE__ */ new Set();
|
|
122
|
+
/** An array of OpenAPI tags selected for the current transformation. */
|
|
123
|
+
tagSelection = [];
|
|
124
|
+
/** A collection of operation IDs selected for the current transformation. */
|
|
125
|
+
operationIdSelection = /* @__PURE__ */ new Set();
|
|
126
|
+
/** A collection of OpenAPI paths and operations selected for the current transformation. */
|
|
127
|
+
pathSelection = new OperationSelection();
|
|
128
|
+
/** A collection of OpenAPI webhooks selected for the current transformation. */
|
|
129
|
+
webhookSelection = new OperationSelection();
|
|
130
|
+
hasTagSelection = false;
|
|
131
|
+
hasOperationIdSelection = false;
|
|
132
|
+
hasPathSelection = false;
|
|
133
|
+
hasWebhookSelection = false;
|
|
134
|
+
/**
|
|
135
|
+
* @param definition OpenAPI definition to transform.
|
|
136
|
+
* @param options Transformation mode.
|
|
137
|
+
*/
|
|
138
|
+
constructor(definition, options) {
|
|
139
|
+
this.definition = structuredClone(definition);
|
|
140
|
+
this.mode = options.mode;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Select an OpenAPI tag. Operations with this tag are retained when reducing and removed when
|
|
144
|
+
* pruning. Tag casing does not matter.
|
|
145
|
+
*
|
|
146
|
+
* @param tag Tag to select.
|
|
147
|
+
*/
|
|
148
|
+
selectTag(tag) {
|
|
149
|
+
this.tagSelection.push(tag.toLowerCase());
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Select an entire OpenAPI path and all operations that it contains. Selected paths are retained
|
|
153
|
+
* when reducing and removed when pruning. Path casing does not matter.
|
|
154
|
+
*
|
|
155
|
+
* @param path Path to select.
|
|
156
|
+
*/
|
|
157
|
+
selectPath(path) {
|
|
158
|
+
this.pathSelection.addAll(path);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Select a single OpenAPI operation. Selected operations are retained when reducing and removed
|
|
162
|
+
* when pruning. Path and method casing does not matter.
|
|
163
|
+
*
|
|
164
|
+
* In reduce mode, selecting an operation after its entire path replaces the all-operation
|
|
165
|
+
* selection with that operation and any operations selected afterward. Prune mode keeps the
|
|
166
|
+
* entire path selected.
|
|
167
|
+
*
|
|
168
|
+
* @param path Path containing the operation.
|
|
169
|
+
* @param method HTTP method of the operation to select.
|
|
170
|
+
*/
|
|
171
|
+
selectOperation(path, method) {
|
|
172
|
+
if (this.mode === "reduce" && this.pathSelection.matchesAll(path)) {
|
|
173
|
+
this.pathSelection.clear(path);
|
|
174
|
+
}
|
|
175
|
+
this.pathSelection.addOperation(path, method);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Select an OpenAPI operation by its operation ID. IDs are matched exactly and are generated
|
|
179
|
+
* from the operation path and method when one is not authored in the definition.
|
|
180
|
+
*
|
|
181
|
+
* @param operationId Operation ID to select.
|
|
182
|
+
*/
|
|
183
|
+
selectOperationId(operationId) {
|
|
184
|
+
this.operationIdSelection.add(operationId);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Select an OpenAPI webhook or one of its operations. Selected webhooks are retained when
|
|
188
|
+
* reducing and removed when pruning. Webhook and method casing does not matter.
|
|
189
|
+
*
|
|
190
|
+
* @param webhookName Webhook to select.
|
|
191
|
+
* @param method Optional HTTP method of an individual webhook operation to select.
|
|
192
|
+
*/
|
|
193
|
+
selectWebhook(webhookName, method) {
|
|
194
|
+
if (!method) {
|
|
195
|
+
this.webhookSelection.addAll(webhookName);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (this.mode === "reduce" && this.webhookSelection.matchesAll(webhookName)) {
|
|
199
|
+
this.webhookSelection.clear(webhookName);
|
|
200
|
+
}
|
|
201
|
+
this.webhookSelection.addOperation(webhookName, method);
|
|
202
|
+
}
|
|
203
|
+
/** Transform the OpenAPI definition according to the configured mode and selections. */
|
|
204
|
+
transform() {
|
|
205
|
+
if (!this.definition.openapi) {
|
|
206
|
+
throw new Error("Sorry, only OpenAPI definitions are supported.");
|
|
207
|
+
}
|
|
208
|
+
this.hasTagSelection = Boolean(this.tagSelection.length);
|
|
209
|
+
this.hasOperationIdSelection = this.operationIdSelection.size > 0;
|
|
210
|
+
this.hasPathSelection = this.pathSelection.hasSelections;
|
|
211
|
+
this.hasWebhookSelection = this.webhookSelection.hasSelections;
|
|
212
|
+
if ("security" in this.definition) {
|
|
213
|
+
Object.values(this.definition.security || {}).forEach((sec) => {
|
|
214
|
+
Object.keys(sec).forEach((scheme) => {
|
|
215
|
+
this.$refs.add(`#/components/securitySchemes/${scheme}`);
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
this.walkPaths();
|
|
220
|
+
this.walkWebhooks();
|
|
221
|
+
this.$refs.forEach(($ref) => {
|
|
222
|
+
this.accumulateUsedRefs(this.definition, this.$refs, $ref);
|
|
223
|
+
});
|
|
224
|
+
this.$refs.forEach((ref) => {
|
|
225
|
+
const usedPathRef = this.parsePathRef(ref);
|
|
226
|
+
if (usedPathRef) {
|
|
227
|
+
this.retainPathMethods.add(`${usedPathRef.path.toLowerCase()}|${usedPathRef.method.toLowerCase()}`);
|
|
228
|
+
}
|
|
229
|
+
const usedWebhookRef = this.parseWebhookRef(ref);
|
|
230
|
+
if (usedWebhookRef) {
|
|
231
|
+
this.retainWebhookMethods.add(`${usedWebhookRef.name.toLowerCase()}|${usedWebhookRef.method.toLowerCase()}`);
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
this.transformPaths();
|
|
235
|
+
this.transformWebhooks();
|
|
236
|
+
const hasPaths = Boolean(this.definition.paths && Object.keys(this.definition.paths).length);
|
|
237
|
+
const hasWebhooks = Boolean(
|
|
238
|
+
"webhooks" in this.definition && this.definition.webhooks && Object.keys(this.definition.webhooks).length
|
|
239
|
+
);
|
|
240
|
+
if (this.mode === "reduce" && !hasPaths && !hasWebhooks) {
|
|
241
|
+
throw new Error(
|
|
242
|
+
"All paths and webhooks in the API definition were removed. Did you supply the right path, operation, or webhook to reduce by?"
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
if ("components" in this.definition) {
|
|
246
|
+
Object.keys(this.definition.components || {}).forEach((componentType) => {
|
|
247
|
+
Object.keys(this.definition.components?.[componentType] || {}).forEach((component) => {
|
|
248
|
+
const refIsUsed = this.$refs.has(`#/components/${componentType}/${component}`) || Array.from(this.$refs).some((ref) => {
|
|
249
|
+
return ref.startsWith(`#/components/${componentType}/${component}/`);
|
|
250
|
+
});
|
|
251
|
+
if (!refIsUsed) {
|
|
252
|
+
delete this.definition.components?.[componentType]?.[component];
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
if (!Object.keys(this.definition.components?.[componentType] || {}).length) {
|
|
256
|
+
delete this.definition.components?.[componentType];
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
if (!Object.keys(this.definition.components || {}).length) {
|
|
260
|
+
delete this.definition.components;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
if ("tags" in this.definition) {
|
|
264
|
+
this.definition.tags = (this.definition.tags ?? []).filter((tag) => {
|
|
265
|
+
return Boolean(tag) && this.usedTags.has(tag.name);
|
|
266
|
+
});
|
|
267
|
+
if (!this.definition.tags?.length) {
|
|
268
|
+
delete this.definition.tags;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return this.definition;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Recursively process a `$ref` pointer and accumulate any other `$ref` pointers that it or its
|
|
275
|
+
* children use. This handles circular references by skipping `$ref` pointers we have already seen.
|
|
276
|
+
* Additionally when a `$ref` points to `#/paths` we record the used path + method so we can
|
|
277
|
+
* retain cross-operation references within the transformed definition.
|
|
278
|
+
*
|
|
279
|
+
* @param schema JSON Schema object to look for and accumulate any `$ref` pointers that it may have.
|
|
280
|
+
* @param $refs Known set of `$ref` pointers.
|
|
281
|
+
* @param $ref `$ref` pointer to fetch a schema from out of the supplied schema.
|
|
282
|
+
*/
|
|
283
|
+
accumulateUsedRefs(schema, $refs, $ref) {
|
|
284
|
+
const pathRef = this.parsePathRef($ref);
|
|
285
|
+
if (pathRef) {
|
|
286
|
+
this.retainPathMethods.add(`${pathRef.path.toLowerCase()}|${pathRef.method.toLowerCase()}`);
|
|
287
|
+
}
|
|
288
|
+
const webhookRef = this.parseWebhookRef($ref);
|
|
289
|
+
if (webhookRef) {
|
|
290
|
+
this.retainWebhookMethods.add(`${webhookRef.name.toLowerCase()}|${webhookRef.method.toLowerCase()}`);
|
|
291
|
+
}
|
|
292
|
+
let $refSchema;
|
|
293
|
+
if (typeof $ref === "string") $refSchema = jsonPointer.get(schema, $ref.substring(1));
|
|
294
|
+
if ($refSchema === void 0) {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
this.queryForRefPointers($refSchema).forEach(({ value: currRef }) => {
|
|
298
|
+
const foundRef = this.toRefString(currRef);
|
|
299
|
+
if (!foundRef) {
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
if ($refs.has(foundRef)) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
$refs.add(foundRef);
|
|
306
|
+
this.accumulateUsedRefs(schema, $refs, foundRef);
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Query a JSON Schema object for any `$ref` pointers using JSONPath and return any pointers that
|
|
311
|
+
* exist.
|
|
312
|
+
*
|
|
313
|
+
* @see {@link https://datatracker.ietf.org/doc/html/rfc9535}
|
|
314
|
+
* @param schema JSON Schema object to look for any `$ref` pointers within it.
|
|
315
|
+
*/
|
|
316
|
+
queryForRefPointers(schema) {
|
|
317
|
+
return query(["$..['$ref']"], schema);
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Normalize a value from a `jsonpath-plus` `$ref` query to a `$ref` pointer because JSONPath
|
|
321
|
+
* queries may return the property value or the parent.
|
|
322
|
+
*
|
|
323
|
+
*/
|
|
324
|
+
toRefString(value) {
|
|
325
|
+
if (typeof value === "string") {
|
|
326
|
+
return value;
|
|
327
|
+
} else if (value && typeof value === "object" && "$ref" in value && typeof value.$ref === "string") {
|
|
328
|
+
return value.$ref;
|
|
329
|
+
}
|
|
330
|
+
return null;
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* If the given `$ref` points into a path (e.g. `#/paths/~1anything/post/...`), return the path
|
|
334
|
+
* and method so the transformer can retain cross-operation references.
|
|
335
|
+
*
|
|
336
|
+
*/
|
|
337
|
+
parsePathRef($ref) {
|
|
338
|
+
if (typeof $ref !== "string" || !$ref.startsWith("#/paths/")) {
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
const match = $ref.match(/^#\/paths\/([^/]+)\/([^/]+)(?:\/|$)/);
|
|
342
|
+
if (match) {
|
|
343
|
+
const pathSegment = match[1];
|
|
344
|
+
const method = match[2];
|
|
345
|
+
if (pathSegment && method) {
|
|
346
|
+
return { path: decodePointer(pathSegment), method };
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return null;
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* If the given `$ref` points into webhooks (e.g. `#/webhooks/newBooking/post/...`), return the
|
|
353
|
+
* webhook name and method so the transformer can retain cross-referenced webhook operations.
|
|
354
|
+
*
|
|
355
|
+
*/
|
|
356
|
+
parseWebhookRef($ref) {
|
|
357
|
+
if (typeof $ref !== "string" || !$ref.startsWith("#/webhooks/")) {
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
const match = $ref.match(/^#\/webhooks\/([^/]+)\/([^/]+)(?:\/|$)/);
|
|
361
|
+
if (match) {
|
|
362
|
+
const webhookName = match[1];
|
|
363
|
+
const method = match[2];
|
|
364
|
+
if (webhookName && method) {
|
|
365
|
+
return { name: decodePointer(webhookName), method };
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
370
|
+
/** Determine whether a path or webhook is excluded in the current transformation mode. */
|
|
371
|
+
isContainerExcluded(selection, key) {
|
|
372
|
+
if (this.mode === "prune") {
|
|
373
|
+
return selection.matchesAll(key);
|
|
374
|
+
}
|
|
375
|
+
return !selection.has(key);
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Determine whether an operation passes the configured filters before cross-operation references
|
|
379
|
+
* are considered.
|
|
380
|
+
*/
|
|
381
|
+
shouldRetainOperation(selection, key, method, operation) {
|
|
382
|
+
if (this.mode === "reduce") {
|
|
383
|
+
if (selection.hasSelections && !selection.matches(key, method)) return false;
|
|
384
|
+
if (this.hasOperationIdSelection && !this.operationIdSelection.has(Operation.getOperationId(key, method, operation))) {
|
|
385
|
+
return false;
|
|
386
|
+
}
|
|
387
|
+
if (this.hasTagSelection && !(operation.tags || []).some((tag) => this.tagSelection.includes(tag.toLowerCase()))) {
|
|
388
|
+
return false;
|
|
389
|
+
}
|
|
390
|
+
} else {
|
|
391
|
+
if (selection.hasSelections && selection.matches(key, method)) return false;
|
|
392
|
+
if (this.hasOperationIdSelection && this.operationIdSelection.has(Operation.getOperationId(key, method, operation))) {
|
|
393
|
+
return false;
|
|
394
|
+
}
|
|
395
|
+
if (this.hasTagSelection && (operation.tags || []).some((tag) => this.tagSelection.includes(tag.toLowerCase()))) {
|
|
396
|
+
return false;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
return true;
|
|
400
|
+
}
|
|
401
|
+
/** Determine whether a path item contains at least one HTTP operation. */
|
|
402
|
+
hasOperations(pathItem) {
|
|
403
|
+
return Boolean(pathItem && supportedMethods.some((method) => method in pathItem));
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Accumulate any `$ref` pointers that are used by common Path Item parameters.
|
|
407
|
+
*
|
|
408
|
+
* @param parameters Common Path Item parameters to inspect for `$ref` pointers.
|
|
409
|
+
*/
|
|
410
|
+
accumulateParameterRefs(parameters) {
|
|
411
|
+
if (!parameters) {
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
this.queryForRefPointers(parameters).forEach(({ value: ref }) => {
|
|
415
|
+
const refStr = this.toRefString(ref);
|
|
416
|
+
if (!refStr) {
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
this.$refs.add(refStr);
|
|
420
|
+
this.accumulateUsedRefs(this.definition, this.$refs, refStr);
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Walk through the `paths` in our OpenAPI definition and determine which operations we want to
|
|
425
|
+
* retain. Accumulate any `$ref` pointers that they use so their referenced schemas can also be
|
|
426
|
+
* retained in our resulting API definition.
|
|
427
|
+
*
|
|
428
|
+
*/
|
|
429
|
+
walkPaths() {
|
|
430
|
+
if (!("paths" in this.definition) || !this.definition.paths) {
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
Object.keys(this.definition.paths).forEach((path) => {
|
|
434
|
+
if (this.mode === "reduce" && this.hasWebhookSelection && !this.hasPathSelection) {
|
|
435
|
+
return;
|
|
436
|
+
} else if (this.hasPathSelection && this.isContainerExcluded(this.pathSelection, path)) {
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
const pathItem = this.definition.paths?.[path];
|
|
440
|
+
if (isRef(pathItem)) {
|
|
441
|
+
this.$refs.add(pathItem.$ref);
|
|
442
|
+
this.accumulateUsedRefs(this.definition, this.$refs, pathItem.$ref);
|
|
443
|
+
}
|
|
444
|
+
if (!this.hasOperations(pathItem)) {
|
|
445
|
+
this.accumulateParameterRefs(pathItem?.parameters);
|
|
446
|
+
}
|
|
447
|
+
Object.keys(pathItem || {}).forEach((method) => {
|
|
448
|
+
if (method === "parameters" || !supportedMethods.includes(method.toLowerCase())) {
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
const operation = this.definition.paths?.[path]?.[method];
|
|
452
|
+
if (!operation) {
|
|
453
|
+
throw new Error(`Operation \`${method} ${path}\` not found`);
|
|
454
|
+
}
|
|
455
|
+
if (!this.shouldRetainOperation(this.pathSelection, path, method, operation)) {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
(operation.tags || []).forEach((tag) => {
|
|
459
|
+
this.usedTags.add(tag);
|
|
460
|
+
});
|
|
461
|
+
this.accumulateParameterRefs(pathItem?.parameters);
|
|
462
|
+
this.queryForRefPointers(operation).forEach(({ value: ref }) => {
|
|
463
|
+
const refStr = this.toRefString(ref);
|
|
464
|
+
if (!refStr) {
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
this.$refs.add(refStr);
|
|
468
|
+
const pathRef = this.parsePathRef(refStr);
|
|
469
|
+
if (pathRef) {
|
|
470
|
+
this.retainPathMethods.add(`${pathRef.path.toLowerCase()}|${pathRef.method.toLowerCase()}`);
|
|
471
|
+
}
|
|
472
|
+
this.accumulateUsedRefs(this.definition, this.$refs, refStr);
|
|
473
|
+
});
|
|
474
|
+
Object.values(operation.security || {}).forEach((sec) => {
|
|
475
|
+
Object.keys(sec).forEach((scheme) => {
|
|
476
|
+
this.$refs.add(`#/components/securitySchemes/${scheme}`);
|
|
477
|
+
});
|
|
478
|
+
});
|
|
479
|
+
});
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Walk through the `webhooks` in our OpenAPI definition and determine which operations we want
|
|
484
|
+
* to retain. Accumulate any `$ref` pointers that they use so their referenced schemas can also be
|
|
485
|
+
* retained in our resulting API definition.
|
|
486
|
+
*
|
|
487
|
+
*/
|
|
488
|
+
walkWebhooks() {
|
|
489
|
+
if (!isOpenAPI31(this.definition)) {
|
|
490
|
+
return;
|
|
491
|
+
} else if (!("webhooks" in this.definition) || !this.definition.webhooks) {
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
const definition = this.definition;
|
|
495
|
+
Object.keys(definition.webhooks || {}).forEach((webhookName) => {
|
|
496
|
+
if (this.hasWebhookSelection && this.isContainerExcluded(this.webhookSelection, webhookName)) {
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
const webhook = definition.webhooks?.[webhookName];
|
|
500
|
+
if (!webhook || typeof webhook !== "object") {
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
if (typeof webhook.$ref === "string") {
|
|
504
|
+
this.$refs.add(webhook.$ref);
|
|
505
|
+
this.accumulateUsedRefs(definition, this.$refs, webhook.$ref);
|
|
506
|
+
}
|
|
507
|
+
if (!this.hasOperations(webhook)) {
|
|
508
|
+
this.accumulateParameterRefs(webhook.parameters);
|
|
509
|
+
}
|
|
510
|
+
Object.keys(webhook).forEach((method) => {
|
|
511
|
+
if (method === "parameters" || !supportedMethods.includes(method.toLowerCase())) {
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
const operation = webhook[method];
|
|
515
|
+
if (!operation) {
|
|
516
|
+
throw new Error(`Webhook operation \`${method} ${webhookName}\` not found`);
|
|
517
|
+
}
|
|
518
|
+
if (!this.shouldRetainOperation(this.webhookSelection, webhookName, method, operation)) {
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
(operation.tags || []).forEach((tag) => {
|
|
522
|
+
this.usedTags.add(tag);
|
|
523
|
+
});
|
|
524
|
+
this.accumulateParameterRefs(webhook.parameters);
|
|
525
|
+
this.queryForRefPointers(operation).forEach(({ value: ref }) => {
|
|
526
|
+
const refStr = this.toRefString(ref);
|
|
527
|
+
if (!refStr) {
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
this.$refs.add(refStr);
|
|
531
|
+
const pathRef = this.parsePathRef(refStr);
|
|
532
|
+
if (pathRef) {
|
|
533
|
+
this.retainPathMethods.add(`${pathRef.path.toLowerCase()}|${pathRef.method.toLowerCase()}`);
|
|
534
|
+
}
|
|
535
|
+
const webhookRef = this.parseWebhookRef(refStr);
|
|
536
|
+
if (webhookRef) {
|
|
537
|
+
this.retainWebhookMethods.add(`${webhookRef.name.toLowerCase()}|${webhookRef.method.toLowerCase()}`);
|
|
538
|
+
}
|
|
539
|
+
this.accumulateUsedRefs(definition, this.$refs, refStr);
|
|
540
|
+
});
|
|
541
|
+
Object.values(operation.security || {}).forEach((sec) => {
|
|
542
|
+
Object.keys(sec).forEach((scheme) => {
|
|
543
|
+
this.$refs.add(`#/components/securitySchemes/${scheme}`);
|
|
544
|
+
});
|
|
545
|
+
});
|
|
546
|
+
});
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Transform our `paths` object according to the paths and operations that we've selected.
|
|
551
|
+
*/
|
|
552
|
+
transformPaths() {
|
|
553
|
+
if (!("paths" in this.definition) || !this.definition.paths) {
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
Object.keys(this.definition.paths).forEach((path) => {
|
|
557
|
+
const pathLC = path.toLowerCase();
|
|
558
|
+
const excludePathItem = this.hasPathSelection && this.isContainerExcluded(this.pathSelection, path) || this.mode === "reduce" && this.hasWebhookSelection && !this.hasPathSelection;
|
|
559
|
+
if (this.mode === "prune" && excludePathItem) {
|
|
560
|
+
if (Array.from(this.retainPathMethods).some((key) => key.startsWith(`${pathLC}|`))) {
|
|
561
|
+
throw new Error(`Cannot remove path \`${path}\` because one of its operations is referenced.`);
|
|
562
|
+
}
|
|
563
|
+
delete this.definition.paths?.[path];
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
const pathItem = this.definition.paths?.[path];
|
|
567
|
+
if (isRef(pathItem)) {
|
|
568
|
+
if (excludePathItem) {
|
|
569
|
+
const retainedByRef = Array.from(this.retainPathMethods).some((key) => key.startsWith(`${pathLC}|`));
|
|
570
|
+
if (!retainedByRef) {
|
|
571
|
+
delete this.definition.paths?.[path];
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
let removedOperation = false;
|
|
577
|
+
Object.keys(pathItem || {}).forEach((method) => {
|
|
578
|
+
const methodLC = method.toLowerCase();
|
|
579
|
+
if (method === "parameters" || !supportedMethods.includes(methodLC)) {
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
const retainedByRef = this.retainPathMethods.has(`${pathLC}|${methodLC}`) || Array.from(this.$refs).some((ref) => {
|
|
583
|
+
const pathRef = this.parsePathRef(ref);
|
|
584
|
+
return pathRef?.path.toLowerCase() === pathLC && pathRef?.method.toLowerCase() === methodLC;
|
|
585
|
+
});
|
|
586
|
+
const operation = this.definition.paths?.[path]?.[method];
|
|
587
|
+
if (!operation) {
|
|
588
|
+
throw new Error(`Operation \`${method} ${path}\` not found`);
|
|
589
|
+
}
|
|
590
|
+
if (excludePathItem || !this.shouldRetainOperation(this.pathSelection, path, method, operation)) {
|
|
591
|
+
if (this.mode === "prune" && retainedByRef) {
|
|
592
|
+
throw new Error(`Cannot remove operation \`${method.toUpperCase()} ${path}\` because it is referenced.`);
|
|
593
|
+
}
|
|
594
|
+
if (!retainedByRef) {
|
|
595
|
+
delete this.definition.paths?.[path]?.[method];
|
|
596
|
+
removedOperation = true;
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
this.accumulateParameterRefs(pathItem?.parameters);
|
|
601
|
+
if ("tags" in operation) {
|
|
602
|
+
operation.tags?.forEach((tag) => {
|
|
603
|
+
this.usedTags.add(tag);
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
if ("security" in operation) {
|
|
607
|
+
Object.values(operation.security || {}).forEach((sec) => {
|
|
608
|
+
Object.keys(sec).forEach((scheme) => {
|
|
609
|
+
this.$refs.add(`#/components/securitySchemes/${scheme}`);
|
|
610
|
+
});
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
if ((removedOperation || this.mode === "reduce" && excludePathItem) && !this.hasOperations(pathItem) || this.mode === "reduce" && !Object.keys(this.definition.paths?.[path] || {}).length) {
|
|
615
|
+
delete this.definition.paths?.[path];
|
|
616
|
+
}
|
|
617
|
+
});
|
|
618
|
+
if (!Object.keys(this.definition.paths || {}).length) {
|
|
619
|
+
if (this.mode === "reduce" && !(this.definition.webhooks && Object.keys(this.definition.webhooks).length)) {
|
|
620
|
+
throw new Error(
|
|
621
|
+
"All paths in the API definition were removed. Did you supply the right path name to reduce by?"
|
|
622
|
+
);
|
|
623
|
+
}
|
|
624
|
+
if (this.definition.webhooks && Object.keys(this.definition.webhooks).length) {
|
|
625
|
+
delete this.definition.paths;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Transform our `webhooks` object according to the webhooks and operations that we've selected.
|
|
631
|
+
*/
|
|
632
|
+
transformWebhooks() {
|
|
633
|
+
if (!isOpenAPI31(this.definition)) {
|
|
634
|
+
return;
|
|
635
|
+
} else if (!("webhooks" in this.definition) || !this.definition.webhooks) {
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
const definition = this.definition;
|
|
639
|
+
Object.keys(definition.webhooks || {}).forEach((webhookName) => {
|
|
640
|
+
const nameLC = webhookName.toLowerCase();
|
|
641
|
+
const excludeWebhook = this.hasWebhookSelection && this.isContainerExcluded(this.webhookSelection, webhookName);
|
|
642
|
+
if (this.mode === "prune" && excludeWebhook) {
|
|
643
|
+
const retainedByRef = Array.from(this.retainWebhookMethods).some((key) => key.startsWith(`${nameLC}|`));
|
|
644
|
+
if (retainedByRef) {
|
|
645
|
+
throw new Error(`Cannot remove webhook \`${webhookName}\` because one of its operations is referenced.`);
|
|
646
|
+
}
|
|
647
|
+
delete definition.webhooks?.[webhookName];
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
const webhook = definition.webhooks?.[webhookName];
|
|
651
|
+
if (!webhook || typeof webhook !== "object") {
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
if (isRef(webhook)) {
|
|
655
|
+
if (excludeWebhook) {
|
|
656
|
+
const retainedByRef = Array.from(this.retainWebhookMethods).some((key) => key.startsWith(`${nameLC}|`));
|
|
657
|
+
if (!retainedByRef) {
|
|
658
|
+
delete definition.webhooks?.[webhookName];
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
let removedOperation = false;
|
|
664
|
+
Object.keys(webhook).forEach((method) => {
|
|
665
|
+
const methodLC = method.toLowerCase();
|
|
666
|
+
if (method === "parameters" || !supportedMethods.includes(methodLC)) {
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
const operation = webhook[method];
|
|
670
|
+
if (!operation) {
|
|
671
|
+
throw new Error(`Webhook operation \`${method} ${webhookName}\` not found`);
|
|
672
|
+
}
|
|
673
|
+
const retainedByRef = this.retainWebhookMethods.has(`${nameLC}|${methodLC}`);
|
|
674
|
+
if (excludeWebhook || !this.shouldRetainOperation(this.webhookSelection, webhookName, method, operation)) {
|
|
675
|
+
if (this.mode === "prune" && retainedByRef) {
|
|
676
|
+
throw new Error(
|
|
677
|
+
`Cannot remove operation \`${method.toUpperCase()} ${webhookName}\` because it is referenced.`
|
|
678
|
+
);
|
|
679
|
+
}
|
|
680
|
+
if (!retainedByRef) {
|
|
681
|
+
delete webhook[method];
|
|
682
|
+
removedOperation = true;
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
this.accumulateParameterRefs(webhook.parameters);
|
|
687
|
+
(operation.tags || []).forEach((tag) => {
|
|
688
|
+
this.usedTags.add(tag);
|
|
689
|
+
});
|
|
690
|
+
Object.values(operation.security || {}).forEach((sec) => {
|
|
691
|
+
Object.keys(sec).forEach((scheme) => {
|
|
692
|
+
this.$refs.add(`#/components/securitySchemes/${scheme}`);
|
|
693
|
+
});
|
|
694
|
+
});
|
|
695
|
+
});
|
|
696
|
+
if ((removedOperation || this.mode === "reduce" && excludeWebhook) && !this.hasOperations(webhook) || this.mode === "reduce" && !Object.keys(definition.webhooks?.[webhookName] || {}).length) {
|
|
697
|
+
delete definition.webhooks?.[webhookName];
|
|
698
|
+
}
|
|
699
|
+
});
|
|
700
|
+
if (definition.webhooks && !Object.keys(definition.webhooks).length) {
|
|
701
|
+
delete definition.webhooks;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
};
|
|
705
|
+
|
|
706
|
+
export {
|
|
707
|
+
OpenAPITransformer
|
|
708
|
+
};
|
|
709
|
+
//# sourceMappingURL=chunk-4CWJNFV5.js.map
|