@zuplo/cli 6.62.7 → 6.62.9
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 +36 -4
- package/dist/__tests__/import-openapi-utils.test.js +1 -2
- package/dist/__tests__/import-openapi-utils.test.js.map +1 -1
- package/dist/__tests__/import-openapi.test.js +1 -5
- package/dist/__tests__/import-openapi.test.js.map +1 -1
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/cmds/open-api/convert.d.ts +9 -0
- package/dist/cmds/open-api/convert.d.ts.map +1 -0
- package/dist/cmds/open-api/convert.js +66 -0
- package/dist/cmds/open-api/convert.js.map +1 -0
- package/dist/cmds/open-api/index.d.ts +4 -0
- package/dist/cmds/open-api/index.d.ts.map +1 -0
- package/dist/cmds/open-api/index.js +15 -0
- package/dist/cmds/open-api/index.js.map +1 -0
- package/dist/cmds/open-api/merge.d.ts +9 -0
- package/dist/cmds/open-api/merge.d.ts.map +1 -0
- package/dist/cmds/open-api/merge.js +57 -0
- package/dist/cmds/open-api/merge.js.map +1 -0
- package/dist/cmds/open-api/overlay.d.ts +9 -0
- package/dist/cmds/open-api/overlay.d.ts.map +1 -0
- package/dist/cmds/open-api/overlay.js +85 -0
- package/dist/cmds/open-api/overlay.js.map +1 -0
- package/dist/cmds/source/import-openapi.d.ts +1 -0
- package/dist/cmds/source/import-openapi.d.ts.map +1 -1
- package/dist/cmds/source/import-openapi.js +6 -9
- package/dist/cmds/source/import-openapi.js.map +1 -1
- package/dist/cmds/source/migrate.js +1 -1
- package/dist/cmds/source/migrate.js.map +1 -1
- package/dist/common/file-format.d.ts +25 -0
- package/dist/common/file-format.d.ts.map +1 -0
- package/dist/common/file-format.js +72 -0
- package/dist/common/file-format.js.map +1 -0
- package/dist/common/runner.d.ts.map +1 -0
- package/dist/common/runner.js.map +1 -0
- package/dist/common/utils/stringify-config.d.ts.map +1 -0
- package/dist/common/utils/stringify-config.js.map +1 -0
- package/dist/common/utils/stringify-config.test.d.ts.map +1 -0
- package/dist/common/utils/stringify-config.test.js.map +1 -0
- package/dist/open-api/convert/convert-engine.d.ts +26 -0
- package/dist/open-api/convert/convert-engine.d.ts.map +1 -0
- package/dist/open-api/convert/convert-engine.js +20 -0
- package/dist/open-api/convert/convert-engine.js.map +1 -0
- package/dist/open-api/convert/convert-engine.spec.d.ts +2 -0
- package/dist/open-api/convert/convert-engine.spec.d.ts.map +1 -0
- package/dist/open-api/convert/convert-engine.spec.js +268 -0
- package/dist/open-api/convert/convert-engine.spec.js.map +1 -0
- package/dist/open-api/convert/handler.d.ts +9 -0
- package/dist/open-api/convert/handler.d.ts.map +1 -0
- package/dist/open-api/convert/handler.js +54 -0
- package/dist/open-api/convert/handler.js.map +1 -0
- package/dist/open-api/convert/handler.spec.d.ts +2 -0
- package/dist/open-api/convert/handler.spec.d.ts.map +1 -0
- package/dist/open-api/convert/handler.spec.js +291 -0
- package/dist/open-api/convert/handler.spec.js.map +1 -0
- package/dist/open-api/merge/ajv.d.ts.map +1 -0
- package/dist/open-api/merge/ajv.js.map +1 -0
- package/dist/open-api/merge/handler.d.ts +9 -0
- package/dist/open-api/merge/handler.d.ts.map +1 -0
- package/dist/{source/import-openapi → open-api/merge}/handler.js +16 -42
- package/dist/open-api/merge/handler.js.map +1 -0
- package/dist/open-api/merge/handler.spec.d.ts +2 -0
- package/dist/open-api/merge/handler.spec.d.ts.map +1 -0
- package/dist/open-api/merge/handler.spec.js +335 -0
- package/dist/open-api/merge/handler.spec.js.map +1 -0
- package/dist/open-api/merge/interfaces.d.ts.map +1 -0
- package/dist/open-api/merge/interfaces.js.map +1 -0
- package/dist/open-api/merge/merge-engine.d.ts +23 -0
- package/dist/open-api/merge/merge-engine.d.ts.map +1 -0
- package/dist/open-api/merge/merge-engine.js +33 -0
- package/dist/open-api/merge/merge-engine.js.map +1 -0
- package/dist/open-api/merge/merge-engine.spec.d.ts +2 -0
- package/dist/open-api/merge/merge-engine.spec.d.ts.map +1 -0
- package/dist/open-api/merge/merge-engine.spec.js +117 -0
- package/dist/open-api/merge/merge-engine.spec.js.map +1 -0
- package/dist/open-api/merge/utils.d.ts.map +1 -0
- package/dist/open-api/merge/utils.js.map +1 -0
- package/dist/open-api/overlay/handler.d.ts +10 -0
- package/dist/open-api/overlay/handler.d.ts.map +1 -0
- package/dist/open-api/overlay/handler.js +92 -0
- package/dist/open-api/overlay/handler.js.map +1 -0
- package/dist/open-api/overlay/handler.spec.d.ts +2 -0
- package/dist/open-api/overlay/handler.spec.d.ts.map +1 -0
- package/dist/open-api/overlay/handler.spec.js +304 -0
- package/dist/open-api/overlay/handler.spec.js.map +1 -0
- package/dist/open-api/overlay/overlay-engine.d.ts +55 -0
- package/dist/open-api/overlay/overlay-engine.d.ts.map +1 -0
- package/dist/open-api/overlay/overlay-engine.js +280 -0
- package/dist/open-api/overlay/overlay-engine.js.map +1 -0
- package/dist/open-api/overlay/overlay-engine.spec.d.ts +2 -0
- package/dist/open-api/overlay/overlay-engine.spec.d.ts.map +1 -0
- package/dist/open-api/overlay/overlay-engine.spec.js +609 -0
- package/dist/open-api/overlay/overlay-engine.spec.js.map +1 -0
- package/dist/source/migrate/dev-portal/handler.d.ts.map +1 -0
- package/dist/{cmds/source/migrate → source/migrate/dev-portal}/handler.js +53 -4
- package/dist/source/migrate/dev-portal/handler.js.map +1 -0
- package/dist/{cmds/source/migrate → source/migrate/dev-portal}/types.d.ts +12 -1
- package/dist/source/migrate/dev-portal/types.d.ts.map +1 -0
- package/dist/source/migrate/dev-portal/types.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -4
- package/dist/cmds/source/migrate/handler.d.ts.map +0 -1
- package/dist/cmds/source/migrate/handler.js.map +0 -1
- package/dist/cmds/source/migrate/runner.d.ts.map +0 -1
- package/dist/cmds/source/migrate/runner.js.map +0 -1
- package/dist/cmds/source/migrate/stringify-config.d.ts.map +0 -1
- package/dist/cmds/source/migrate/stringify-config.js.map +0 -1
- package/dist/cmds/source/migrate/stringify-config.test.d.ts.map +0 -1
- package/dist/cmds/source/migrate/stringify-config.test.js.map +0 -1
- package/dist/cmds/source/migrate/types.d.ts.map +0 -1
- package/dist/cmds/source/migrate/types.js.map +0 -1
- package/dist/source/import-openapi/ajv.d.ts.map +0 -1
- package/dist/source/import-openapi/ajv.js.map +0 -1
- package/dist/source/import-openapi/handler.d.ts +0 -13
- package/dist/source/import-openapi/handler.d.ts.map +0 -1
- package/dist/source/import-openapi/handler.js.map +0 -1
- package/dist/source/import-openapi/interfaces.d.ts.map +0 -1
- package/dist/source/import-openapi/interfaces.js.map +0 -1
- package/dist/source/import-openapi/utils.d.ts.map +0 -1
- package/dist/source/import-openapi/utils.js.map +0 -1
- /package/dist/{cmds/source/migrate → common}/runner.d.ts +0 -0
- /package/dist/{cmds/source/migrate → common}/runner.js +0 -0
- /package/dist/{cmds/source/migrate → common/utils}/stringify-config.d.ts +0 -0
- /package/dist/{cmds/source/migrate → common/utils}/stringify-config.js +0 -0
- /package/dist/{cmds/source/migrate → common/utils}/stringify-config.test.d.ts +0 -0
- /package/dist/{cmds/source/migrate → common/utils}/stringify-config.test.js +0 -0
- /package/dist/{source/import-openapi → open-api/merge}/ajv.d.ts +0 -0
- /package/dist/{source/import-openapi → open-api/merge}/ajv.js +0 -0
- /package/dist/{source/import-openapi → open-api/merge}/interfaces.d.ts +0 -0
- /package/dist/{source/import-openapi → open-api/merge}/interfaces.js +0 -0
- /package/dist/{source/import-openapi → open-api/merge}/utils.d.ts +0 -0
- /package/dist/{source/import-openapi → open-api/merge}/utils.js +0 -0
- /package/dist/{cmds/source/migrate → source/migrate/dev-portal}/handler.d.ts +0 -0
- /package/dist/{cmds/source/migrate → source/migrate/dev-portal}/types.js +0 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { JSONPath } from "jsonpath-plus";
|
|
2
|
+
export function deepClone(obj) {
|
|
3
|
+
if (typeof structuredClone === "function") {
|
|
4
|
+
return structuredClone(obj);
|
|
5
|
+
}
|
|
6
|
+
return JSON.parse(JSON.stringify(obj));
|
|
7
|
+
}
|
|
8
|
+
export function deepMerge(target, source) {
|
|
9
|
+
if (!source || typeof source !== "object") {
|
|
10
|
+
return source;
|
|
11
|
+
}
|
|
12
|
+
if (!target || typeof target !== "object") {
|
|
13
|
+
return deepClone(source);
|
|
14
|
+
}
|
|
15
|
+
if (Array.isArray(source)) {
|
|
16
|
+
if (!Array.isArray(target)) {
|
|
17
|
+
return deepClone(source);
|
|
18
|
+
}
|
|
19
|
+
if (source.length > 0 &&
|
|
20
|
+
source[0].name !== undefined &&
|
|
21
|
+
source[0].in !== undefined) {
|
|
22
|
+
const merged = [...target];
|
|
23
|
+
for (const sourceParam of source) {
|
|
24
|
+
const existingIndex = merged.findIndex((p) => p.name === sourceParam.name && p.in === sourceParam.in);
|
|
25
|
+
if (existingIndex >= 0) {
|
|
26
|
+
merged[existingIndex] = deepMerge(merged[existingIndex], sourceParam);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
merged.push(deepClone(sourceParam));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return merged;
|
|
33
|
+
}
|
|
34
|
+
return [...target, ...deepClone(source)];
|
|
35
|
+
}
|
|
36
|
+
const result = { ...target };
|
|
37
|
+
for (const [key, value] of Object.entries(source)) {
|
|
38
|
+
if (key in result) {
|
|
39
|
+
result[key] = deepMerge(result[key], value);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
result[key] = deepClone(value);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
export function checkCondition(value, condition) {
|
|
48
|
+
if (condition.empty) {
|
|
49
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
50
|
+
return Object.keys(value).length === 0;
|
|
51
|
+
}
|
|
52
|
+
if (Array.isArray(value)) {
|
|
53
|
+
return value.length === 0;
|
|
54
|
+
}
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
if (condition.missing) {
|
|
58
|
+
const parts = condition.missing.split(".");
|
|
59
|
+
let current = value;
|
|
60
|
+
for (const part of parts) {
|
|
61
|
+
if (current && typeof current === "object" && part in current) {
|
|
62
|
+
current = current[part];
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
const JSONPATH_SEGMENT_PATTERN = /[^.[]+|\[[^\]]+\]/g;
|
|
73
|
+
function extractBracketKey(bracketNotation) {
|
|
74
|
+
const inner = bracketNotation.slice(1, -1);
|
|
75
|
+
if (inner.startsWith("'") && inner.endsWith("'")) {
|
|
76
|
+
return inner.slice(1, -1);
|
|
77
|
+
}
|
|
78
|
+
if (inner.startsWith('"') && inner.endsWith('"')) {
|
|
79
|
+
return inner.slice(1, -1);
|
|
80
|
+
}
|
|
81
|
+
return inner;
|
|
82
|
+
}
|
|
83
|
+
export function setValueAtPath(doc, target, value) {
|
|
84
|
+
const targetPath = target.replace(/^\$\./, "");
|
|
85
|
+
const parts = targetPath.match(JSONPATH_SEGMENT_PATTERN) || [];
|
|
86
|
+
let current = doc;
|
|
87
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
88
|
+
const part = parts[i];
|
|
89
|
+
if (part.startsWith("[")) {
|
|
90
|
+
const key = extractBracketKey(part);
|
|
91
|
+
if (!(key in current)) {
|
|
92
|
+
current[key] = {};
|
|
93
|
+
}
|
|
94
|
+
current = current[key];
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
if (!(part in current)) {
|
|
98
|
+
current[part] = {};
|
|
99
|
+
}
|
|
100
|
+
current = current[part];
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const lastPart = parts[parts.length - 1];
|
|
104
|
+
if (lastPart.startsWith("[")) {
|
|
105
|
+
const key = extractBracketKey(lastPart);
|
|
106
|
+
current[key] = value;
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
current[lastPart] = value;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
export function applyAction(doc, action) {
|
|
113
|
+
if (action.update) {
|
|
114
|
+
if (action.target === "$") {
|
|
115
|
+
Object.assign(doc, deepMerge(doc, action.update));
|
|
116
|
+
return { applied: true, count: 1 };
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
const results = JSONPath({
|
|
120
|
+
path: action.target,
|
|
121
|
+
json: doc,
|
|
122
|
+
resultType: "all",
|
|
123
|
+
});
|
|
124
|
+
if (results.length > 0) {
|
|
125
|
+
let count = 0;
|
|
126
|
+
for (const result of results) {
|
|
127
|
+
const { parent, parentProperty } = result;
|
|
128
|
+
if (parent && parentProperty !== undefined) {
|
|
129
|
+
parent[parentProperty] = deepMerge(parent[parentProperty], action.update);
|
|
130
|
+
count++;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return { applied: true, count };
|
|
134
|
+
}
|
|
135
|
+
setValueAtPath(doc, action.target, action.update);
|
|
136
|
+
return { applied: true, count: 1 };
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
throw new Error(`Failed to apply update: ${error.message}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
else if (action.remove) {
|
|
143
|
+
try {
|
|
144
|
+
const results = JSONPath({
|
|
145
|
+
path: action.target,
|
|
146
|
+
json: doc,
|
|
147
|
+
resultType: "all",
|
|
148
|
+
});
|
|
149
|
+
if (results.length === 0) {
|
|
150
|
+
return { applied: false, count: 0 };
|
|
151
|
+
}
|
|
152
|
+
let count = 0;
|
|
153
|
+
for (let i = results.length - 1; i >= 0; i--) {
|
|
154
|
+
const result = results[i];
|
|
155
|
+
const { parent, parentProperty, value } = result;
|
|
156
|
+
const shouldRemove = typeof action.remove === "object"
|
|
157
|
+
? checkCondition(value, action.remove)
|
|
158
|
+
: true;
|
|
159
|
+
if (shouldRemove && parent && parentProperty !== undefined) {
|
|
160
|
+
if (Array.isArray(parent)) {
|
|
161
|
+
parent.splice(parentProperty, 1);
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
delete parent[parentProperty];
|
|
165
|
+
}
|
|
166
|
+
count++;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return { applied: true, count };
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
throw new Error(`Failed to remove: ${error.message}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return { applied: false, count: 0 };
|
|
176
|
+
}
|
|
177
|
+
export function extractPathOrder(overlay) {
|
|
178
|
+
const pathOrder = [];
|
|
179
|
+
const seenPaths = new Set();
|
|
180
|
+
for (const action of overlay.actions) {
|
|
181
|
+
const match = action.target.match(/^\$\.paths\['([^']+)'\]/);
|
|
182
|
+
if (match) {
|
|
183
|
+
const targetPath = match[1];
|
|
184
|
+
if (!seenPaths.has(targetPath)) {
|
|
185
|
+
pathOrder.push(targetPath);
|
|
186
|
+
seenPaths.add(targetPath);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return pathOrder;
|
|
191
|
+
}
|
|
192
|
+
export function reorderOperationProperties(operation) {
|
|
193
|
+
const priorityKeys = ["summary", "description", "operationId"];
|
|
194
|
+
const reordered = {};
|
|
195
|
+
for (const key of priorityKeys) {
|
|
196
|
+
if (key in operation) {
|
|
197
|
+
reordered[key] = operation[key];
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
for (const [key, value] of Object.entries(operation)) {
|
|
201
|
+
if (!priorityKeys.includes(key)) {
|
|
202
|
+
reordered[key] = value;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return reordered;
|
|
206
|
+
}
|
|
207
|
+
export function reorderPaths(doc, pathOrder) {
|
|
208
|
+
if (!doc.paths || pathOrder.length === 0) {
|
|
209
|
+
return doc;
|
|
210
|
+
}
|
|
211
|
+
const originalPaths = doc.paths;
|
|
212
|
+
const reorderedPaths = {};
|
|
213
|
+
for (const targetPath of pathOrder) {
|
|
214
|
+
if (targetPath in originalPaths) {
|
|
215
|
+
reorderedPaths[targetPath] = originalPaths[targetPath];
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
for (const [targetPath, value] of Object.entries(originalPaths)) {
|
|
219
|
+
if (!(targetPath in reorderedPaths)) {
|
|
220
|
+
reorderedPaths[targetPath] = value;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
for (const pathItem of Object.values(reorderedPaths)) {
|
|
224
|
+
const httpMethods = [
|
|
225
|
+
"get",
|
|
226
|
+
"post",
|
|
227
|
+
"put",
|
|
228
|
+
"patch",
|
|
229
|
+
"delete",
|
|
230
|
+
"options",
|
|
231
|
+
"head",
|
|
232
|
+
"trace",
|
|
233
|
+
];
|
|
234
|
+
for (const method of httpMethods) {
|
|
235
|
+
if (pathItem[method] &&
|
|
236
|
+
typeof pathItem[method] === "object") {
|
|
237
|
+
pathItem[method] = reorderOperationProperties(pathItem[method]);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
doc.paths = reorderedPaths;
|
|
242
|
+
return doc;
|
|
243
|
+
}
|
|
244
|
+
export function reorderDocumentProperties(doc) {
|
|
245
|
+
const priorityKeys = ["openapi", "info", "tags"];
|
|
246
|
+
const reordered = {};
|
|
247
|
+
for (const key of priorityKeys) {
|
|
248
|
+
if (key in doc) {
|
|
249
|
+
reordered[key] = doc[key];
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
for (const [key, value] of Object.entries(doc)) {
|
|
253
|
+
if (!priorityKeys.includes(key)) {
|
|
254
|
+
reordered[key] = value;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return reordered;
|
|
258
|
+
}
|
|
259
|
+
export function applyOverlay(openapi, overlay) {
|
|
260
|
+
if (!overlay.overlay || !overlay.actions) {
|
|
261
|
+
throw new Error("Invalid overlay format. Must include 'overlay' version and 'actions' array.");
|
|
262
|
+
}
|
|
263
|
+
const result = deepClone(openapi);
|
|
264
|
+
const stats = { applied: 0, skipped: 0, totalNodes: 0 };
|
|
265
|
+
for (const action of overlay.actions) {
|
|
266
|
+
const { applied, count } = applyAction(result, action);
|
|
267
|
+
if (applied) {
|
|
268
|
+
stats.applied++;
|
|
269
|
+
stats.totalNodes += count;
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
stats.skipped++;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
const pathOrder = extractPathOrder(overlay);
|
|
276
|
+
reorderPaths(result, pathOrder);
|
|
277
|
+
const reordered = reorderDocumentProperties(result);
|
|
278
|
+
return { result: reordered, stats };
|
|
279
|
+
}
|
|
280
|
+
//# sourceMappingURL=overlay-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlay-engine.js","sourceRoot":"","sources":["../../../src/open-api/overlay/overlay-engine.ts"],"names":[],"mappings":"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","sourcesContent":["/** biome-ignore-all lint/suspicious/noExplicitAny: Complex overlay algorithm working with dynamic JSON structures */\nimport { JSONPath } from \"jsonpath-plus\";\n\nexport interface OverlayDocument {\n overlay: string;\n info: {\n title: string;\n version: string;\n };\n actions: OverlayAction[];\n extends?: string;\n}\n\nexport interface OverlayAction {\n target: string;\n description?: string;\n update?: any;\n remove?: boolean | RemoveCondition;\n}\n\nexport interface RemoveCondition {\n empty?: boolean;\n missing?: string;\n}\n\nexport interface ApplyResult {\n applied: boolean;\n count: number;\n}\n\nexport interface OverlayStats {\n applied: number;\n skipped: number;\n totalNodes: number;\n}\n\n/**\n * Deep clone an object using structuredClone for better performance and correctness\n * Falls back to JSON parse/stringify for environments without structuredClone\n */\nexport function deepClone<T>(obj: T): T {\n if (typeof structuredClone === \"function\") {\n return structuredClone(obj);\n }\n return JSON.parse(JSON.stringify(obj));\n}\n\n/**\n * Deep merge two objects\n * Special handling for OpenAPI parameters (merge by name+in)\n */\nexport function deepMerge(target: any, source: any): any {\n if (!source || typeof source !== \"object\") {\n return source;\n }\n\n if (!target || typeof target !== \"object\") {\n return deepClone(source);\n }\n\n if (Array.isArray(source)) {\n if (!Array.isArray(target)) {\n return deepClone(source);\n }\n\n // Special case for OpenAPI parameters - merge by name+in\n if (\n source.length > 0 &&\n source[0].name !== undefined &&\n source[0].in !== undefined\n ) {\n const merged = [...target];\n for (const sourceParam of source) {\n const existingIndex = merged.findIndex(\n (p) => p.name === sourceParam.name && p.in === sourceParam.in\n );\n if (existingIndex >= 0) {\n merged[existingIndex] = deepMerge(merged[existingIndex], sourceParam);\n } else {\n merged.push(deepClone(sourceParam));\n }\n }\n return merged;\n }\n\n // For other arrays, concatenate\n return [...target, ...deepClone(source)];\n }\n\n const result = { ...target };\n for (const [key, value] of Object.entries(source)) {\n if (key in result) {\n result[key] = deepMerge(result[key], value);\n } else {\n result[key] = deepClone(value);\n }\n }\n\n return result;\n}\n\n/**\n * Check if a condition is met for conditional removal\n */\nexport function checkCondition(\n value: any,\n condition: RemoveCondition\n): boolean {\n // Support \"empty\" condition to check if object has no keys\n if (condition.empty) {\n if (typeof value === \"object\" && value !== null && !Array.isArray(value)) {\n return Object.keys(value).length === 0;\n }\n if (Array.isArray(value)) {\n return value.length === 0;\n }\n return false;\n }\n\n // Support \"missing\" condition to check if a property doesn't exist\n if (condition.missing) {\n const parts = condition.missing.split(\".\");\n let current = value;\n for (const part of parts) {\n if (current && typeof current === \"object\" && part in current) {\n current = current[part];\n } else {\n return true; // Property is missing\n }\n }\n return false; // Property exists\n }\n\n return false;\n}\n\n/**\n * Regular expression for parsing JSONPath segments\n * Matches: word, or [quoted], or ['quoted'], or [\"quoted\"], or numeric indices [0]\n */\nconst JSONPATH_SEGMENT_PATTERN = /[^.[]+|\\[[^\\]]+\\]/g;\n\n/**\n * Extract key from bracket notation, handling various formats:\n * - ['key'] -> key\n * - [\"key\"] -> key\n * - [key] -> key\n * - [0] -> 0 (as string for consistency)\n */\nfunction extractBracketKey(bracketNotation: string): string {\n const inner = bracketNotation.slice(1, -1); // Remove [ and ]\n\n // Handle single-quoted: ['key'] -> key\n if (inner.startsWith(\"'\") && inner.endsWith(\"'\")) {\n return inner.slice(1, -1);\n }\n\n // Handle double-quoted: [\"key\"] -> key\n if (inner.startsWith('\"') && inner.endsWith('\"')) {\n return inner.slice(1, -1);\n }\n\n // Handle unquoted: [key] or [0] -> key or 0\n return inner;\n}\n\n/**\n * Set a value at a JSONPath, creating parent paths as needed\n * Handles patterns like:\n * $.paths['/route'].method\n * $.components.parameters['name']\n * $.paths[\"/users\"].get\n * $.paths[users].get\n * $.items[0].value\n */\nexport function setValueAtPath(doc: any, target: string, value: any): void {\n // Parse the path manually to handle both bracket and dot notation\n // Example: $.paths['/route'].post -> [\"paths\", \"['/route']\", \"post\"]\n\n // Remove leading $.\n const targetPath = target.replace(/^\\$\\./, \"\");\n\n // Split by dots, but keep bracket notation together\n const parts = targetPath.match(JSONPATH_SEGMENT_PATTERN) || [];\n\n let current = doc;\n\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i];\n\n // Handle bracket notation\n if (part.startsWith(\"[\")) {\n const key = extractBracketKey(part);\n if (!(key in current)) {\n current[key] = {};\n }\n current = current[key];\n } else {\n // Handle regular property\n if (!(part in current)) {\n current[part] = {};\n }\n current = current[part];\n }\n }\n\n // Set the final value\n const lastPart = parts[parts.length - 1];\n if (lastPart.startsWith(\"[\")) {\n const key = extractBracketKey(lastPart);\n current[key] = value;\n } else {\n current[lastPart] = value;\n }\n}\n\n/**\n * Apply a single overlay action to the document\n */\nexport function applyAction(doc: any, action: OverlayAction): ApplyResult {\n if (action.update) {\n // Handle root level updates specially\n if (action.target === \"$\") {\n Object.assign(doc, deepMerge(doc, action.update));\n return { applied: true, count: 1 };\n }\n\n try {\n // Try to find existing nodes\n const results = JSONPath({\n path: action.target,\n json: doc,\n resultType: \"all\",\n });\n\n if (results.length > 0) {\n // Path exists, merge the update\n let count = 0;\n for (const result of results) {\n const { parent, parentProperty } = result;\n if (parent && parentProperty !== undefined) {\n parent[parentProperty] = deepMerge(\n parent[parentProperty],\n action.update\n );\n count++;\n }\n }\n return { applied: true, count };\n }\n\n // Path doesn't exist, create it manually\n setValueAtPath(doc, action.target, action.update);\n return { applied: true, count: 1 };\n } catch (error) {\n throw new Error(`Failed to apply update: ${(error as Error).message}`);\n }\n } else if (action.remove) {\n try {\n const results = JSONPath({\n path: action.target,\n json: doc,\n resultType: \"all\",\n });\n\n if (results.length === 0) {\n return { applied: false, count: 0 };\n }\n\n let count = 0;\n // Process in reverse to avoid index issues when removing\n for (let i = results.length - 1; i >= 0; i--) {\n const result = results[i];\n const { parent, parentProperty, value } = result;\n\n // Check condition if specified\n const shouldRemove =\n typeof action.remove === \"object\"\n ? checkCondition(value, action.remove)\n : true;\n\n if (shouldRemove && parent && parentProperty !== undefined) {\n if (Array.isArray(parent)) {\n parent.splice(parentProperty as number, 1);\n } else {\n delete parent[parentProperty];\n }\n count++;\n }\n }\n\n return { applied: true, count };\n } catch (error) {\n throw new Error(`Failed to remove: ${(error as Error).message}`);\n }\n }\n\n return { applied: false, count: 0 };\n}\n\n/**\n * Extract path order from overlay actions\n * Returns array of paths in the order they appear in overlay\n */\nexport function extractPathOrder(overlay: OverlayDocument): string[] {\n const pathOrder: string[] = [];\n const seenPaths = new Set<string>();\n\n for (const action of overlay.actions) {\n // Match patterns like $.paths['/route'].method\n const match = action.target.match(/^\\$\\.paths\\['([^']+)'\\]/);\n if (match) {\n const targetPath = match[1];\n if (!seenPaths.has(targetPath)) {\n pathOrder.push(targetPath);\n seenPaths.add(targetPath);\n }\n }\n }\n\n return pathOrder;\n}\n\n/**\n * Reorder operation properties so summary, description, operationId come first\n */\nexport function reorderOperationProperties(operation: any): any {\n const priorityKeys = [\"summary\", \"description\", \"operationId\"];\n const reordered: any = {};\n\n // Add priority keys first (in order)\n for (const key of priorityKeys) {\n if (key in operation) {\n reordered[key] = operation[key];\n }\n }\n\n // Add remaining keys\n for (const [key, value] of Object.entries(operation)) {\n if (!priorityKeys.includes(key)) {\n reordered[key] = value;\n }\n }\n\n return reordered;\n}\n\n/**\n * Reorder paths object to match the order from overlay\n */\nexport function reorderPaths(doc: any, pathOrder: string[]): any {\n if (!doc.paths || pathOrder.length === 0) {\n return doc;\n }\n\n const originalPaths = doc.paths;\n const reorderedPaths: any = {};\n\n // First, add paths in the order specified by overlay\n for (const targetPath of pathOrder) {\n if (targetPath in originalPaths) {\n reorderedPaths[targetPath] = originalPaths[targetPath];\n }\n }\n\n // Then add any remaining paths that weren't in the overlay\n for (const [targetPath, value] of Object.entries(originalPaths)) {\n if (!(targetPath in reorderedPaths)) {\n reorderedPaths[targetPath] = value;\n }\n }\n\n // Reorder operation properties within each path\n for (const pathItem of Object.values(reorderedPaths)) {\n const httpMethods = [\n \"get\",\n \"post\",\n \"put\",\n \"patch\",\n \"delete\",\n \"options\",\n \"head\",\n \"trace\",\n ];\n\n for (const method of httpMethods) {\n if (\n (pathItem as any)[method] &&\n typeof (pathItem as any)[method] === \"object\"\n ) {\n (pathItem as any)[method] = reorderOperationProperties(\n (pathItem as any)[method]\n );\n }\n }\n }\n\n doc.paths = reorderedPaths;\n return doc;\n}\n\n/**\n * Reorder top-level document properties to ensure proper ordering\n * OpenAPI spec should have: openapi, info, tags, servers, paths, components, etc.\n */\nexport function reorderDocumentProperties(doc: any): any {\n const priorityKeys = [\"openapi\", \"info\", \"tags\"];\n const reordered: any = {};\n\n // Add priority keys first\n for (const key of priorityKeys) {\n if (key in doc) {\n reordered[key] = doc[key];\n }\n }\n\n // Add remaining keys\n for (const [key, value] of Object.entries(doc)) {\n if (!priorityKeys.includes(key)) {\n reordered[key] = value;\n }\n }\n\n return reordered;\n}\n\n/**\n * Apply overlay to OpenAPI document\n * @param openapi The OpenAPI document to modify\n * @param overlay The overlay document to apply\n * @returns The modified OpenAPI document and statistics\n */\nexport function applyOverlay(\n openapi: any,\n overlay: OverlayDocument\n): { result: any; stats: OverlayStats } {\n // Validate overlay format\n if (!overlay.overlay || !overlay.actions) {\n throw new Error(\n \"Invalid overlay format. Must include 'overlay' version and 'actions' array.\"\n );\n }\n\n const result = deepClone(openapi);\n const stats: OverlayStats = { applied: 0, skipped: 0, totalNodes: 0 };\n\n for (const action of overlay.actions) {\n const { applied, count } = applyAction(result, action);\n\n if (applied) {\n stats.applied++;\n stats.totalNodes += count;\n } else {\n stats.skipped++;\n }\n }\n\n // Reorder paths to match overlay order\n const pathOrder = extractPathOrder(overlay);\n reorderPaths(result, pathOrder);\n\n // Reorder top-level document properties\n const reordered = reorderDocumentProperties(result);\n\n return { result: reordered, stats };\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlay-engine.spec.d.ts","sourceRoot":"","sources":["../../../src/open-api/overlay/overlay-engine.spec.ts"],"names":[],"mappings":""}
|