@scalar/openapi-parser 0.25.4 → 0.25.5
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/CHANGELOG.md +6 -0
- package/dist/configuration/index.js +24 -24
- package/dist/index.js +24 -45
- package/dist/lib/Validator/Validator.js +125 -108
- package/dist/plugins/fetch-urls/fetch-urls.js +39 -38
- package/dist/plugins/fetch-urls/index.js +1 -6
- package/dist/plugins/read-files/index.js +1 -5
- package/dist/plugins/read-files/read-files.js +53 -51
- package/dist/schemas/v2.0/schema.js +1373 -1377
- package/dist/schemas/v3.0/schema.js +1411 -1415
- package/dist/schemas/v3.1/schema.js +1196 -1200
- package/dist/schemas/v3.2/schema.js +1417 -1421
- package/dist/types/index.js +1 -1
- package/dist/utils/betterAjvErrors/helpers.js +138 -112
- package/dist/utils/betterAjvErrors/index.js +11 -15
- package/dist/utils/betterAjvErrors/utils.js +17 -30
- package/dist/utils/betterAjvErrors/validation-errors/additional-prop.js +14 -18
- package/dist/utils/betterAjvErrors/validation-errors/base.js +20 -21
- package/dist/utils/betterAjvErrors/validation-errors/default.js +14 -18
- package/dist/utils/betterAjvErrors/validation-errors/enum.js +33 -37
- package/dist/utils/betterAjvErrors/validation-errors/index.js +6 -15
- package/dist/utils/betterAjvErrors/validation-errors/pattern.js +14 -18
- package/dist/utils/betterAjvErrors/validation-errors/required.js +13 -17
- package/dist/utils/betterAjvErrors/validation-errors/unevaluated-prop.js +14 -18
- package/dist/utils/dereference.js +22 -18
- package/dist/utils/details.js +28 -29
- package/dist/utils/filter.js +13 -14
- package/dist/utils/get-entrypoint.js +5 -6
- package/dist/utils/get-list-of-references.js +20 -16
- package/dist/utils/get-segments-from-path.js +13 -9
- package/dist/utils/is-filesystem.js +8 -6
- package/dist/utils/is-json.js +8 -11
- package/dist/utils/is-yaml.js +15 -17
- package/dist/utils/join/index.js +1 -5
- package/dist/utils/join/join.js +267 -153
- package/dist/utils/join/merge-objects.js +35 -18
- package/dist/utils/load/index.js +1 -5
- package/dist/utils/load/load.js +114 -90
- package/dist/utils/make-filesystem.js +20 -21
- package/dist/utils/normalize.js +32 -29
- package/dist/utils/openapi/actions/details.js +9 -10
- package/dist/utils/openapi/actions/files.js +7 -8
- package/dist/utils/openapi/actions/get.js +9 -10
- package/dist/utils/openapi/actions/toJson.js +9 -10
- package/dist/utils/openapi/actions/toYaml.js +9 -10
- package/dist/utils/openapi/commands/dereferenceCommand.js +25 -26
- package/dist/utils/openapi/commands/filterCommand.js +24 -25
- package/dist/utils/openapi/commands/loadCommand.js +40 -41
- package/dist/utils/openapi/commands/upgradeCommand.js +27 -28
- package/dist/utils/openapi/commands/validateCommand.js +31 -32
- package/dist/utils/openapi/openapi.js +16 -14
- package/dist/utils/openapi/openapi.test-d.js +13 -13
- package/dist/utils/openapi/utils/queueTask.js +8 -9
- package/dist/utils/openapi/utils/workThroughQueue.js +64 -50
- package/dist/utils/resolve-references.js +168 -110
- package/dist/utils/to-json.js +1 -5
- package/dist/utils/to-yaml.js +2 -6
- package/dist/utils/transform/sanitize.js +22 -22
- package/dist/utils/transform/utils/addInfoObject.js +9 -14
- package/dist/utils/transform/utils/addLatestOpenApiVersion.js +4 -9
- package/dist/utils/transform/utils/addMissingTags.js +26 -27
- package/dist/utils/transform/utils/normalizeSecuritySchemes.js +39 -37
- package/dist/utils/transform/utils/rejectSwaggerDocuments.js +5 -9
- package/dist/utils/transform-errors.js +62 -55
- package/dist/utils/traverse.js +21 -22
- package/dist/utils/unescape-json-pointer.js +8 -6
- package/dist/utils/upgrade.js +19 -22
- package/dist/utils/validate-path-parameters.js +76 -80
- package/dist/utils/validate.js +23 -19
- package/package.json +9 -14
- package/dist/configuration/index.js.map +0 -7
- package/dist/declarations.d.js +0 -1
- package/dist/declarations.d.js.map +0 -7
- package/dist/index.js.map +0 -7
- package/dist/lib/Validator/Validator.js.map +0 -7
- package/dist/plugins/fetch-urls/fetch-urls.js.map +0 -7
- package/dist/plugins/fetch-urls/index.js.map +0 -7
- package/dist/plugins/read-files/index.js.map +0 -7
- package/dist/plugins/read-files/read-files.js.map +0 -7
- package/dist/schemas/v2.0/schema.js.map +0 -7
- package/dist/schemas/v3.0/schema.js.map +0 -7
- package/dist/schemas/v3.1/schema.js.map +0 -7
- package/dist/schemas/v3.2/schema.js.map +0 -7
- package/dist/types/index.js.map +0 -7
- package/dist/utils/betterAjvErrors/helpers.js.map +0 -7
- package/dist/utils/betterAjvErrors/index.js.map +0 -7
- package/dist/utils/betterAjvErrors/utils.js.map +0 -7
- package/dist/utils/betterAjvErrors/validation-errors/additional-prop.js.map +0 -7
- package/dist/utils/betterAjvErrors/validation-errors/base.js.map +0 -7
- package/dist/utils/betterAjvErrors/validation-errors/default.js.map +0 -7
- package/dist/utils/betterAjvErrors/validation-errors/enum.js.map +0 -7
- package/dist/utils/betterAjvErrors/validation-errors/index.js.map +0 -7
- package/dist/utils/betterAjvErrors/validation-errors/pattern.js.map +0 -7
- package/dist/utils/betterAjvErrors/validation-errors/required.js.map +0 -7
- package/dist/utils/betterAjvErrors/validation-errors/unevaluated-prop.js.map +0 -7
- package/dist/utils/dereference.js.map +0 -7
- package/dist/utils/details.js.map +0 -7
- package/dist/utils/filter.js.map +0 -7
- package/dist/utils/get-entrypoint.js.map +0 -7
- package/dist/utils/get-list-of-references.js.map +0 -7
- package/dist/utils/get-segments-from-path.js.map +0 -7
- package/dist/utils/is-filesystem.js.map +0 -7
- package/dist/utils/is-json.js.map +0 -7
- package/dist/utils/is-yaml.js.map +0 -7
- package/dist/utils/join/index.js.map +0 -7
- package/dist/utils/join/join.js.map +0 -7
- package/dist/utils/join/merge-objects.js.map +0 -7
- package/dist/utils/load/index.js.map +0 -7
- package/dist/utils/load/load.js.map +0 -7
- package/dist/utils/make-filesystem.js.map +0 -7
- package/dist/utils/normalize.js.map +0 -7
- package/dist/utils/openapi/actions/details.js.map +0 -7
- package/dist/utils/openapi/actions/files.js.map +0 -7
- package/dist/utils/openapi/actions/get.js.map +0 -7
- package/dist/utils/openapi/actions/toJson.js.map +0 -7
- package/dist/utils/openapi/actions/toYaml.js.map +0 -7
- package/dist/utils/openapi/commands/dereferenceCommand.js.map +0 -7
- package/dist/utils/openapi/commands/filterCommand.js.map +0 -7
- package/dist/utils/openapi/commands/loadCommand.js.map +0 -7
- package/dist/utils/openapi/commands/upgradeCommand.js.map +0 -7
- package/dist/utils/openapi/commands/validateCommand.js.map +0 -7
- package/dist/utils/openapi/openapi.js.map +0 -7
- package/dist/utils/openapi/openapi.test-d.js.map +0 -7
- package/dist/utils/openapi/utils/queueTask.js.map +0 -7
- package/dist/utils/openapi/utils/workThroughQueue.js.map +0 -7
- package/dist/utils/resolve-references.js.map +0 -7
- package/dist/utils/to-json.js.map +0 -7
- package/dist/utils/to-yaml.js.map +0 -7
- package/dist/utils/transform/sanitize.js.map +0 -7
- package/dist/utils/transform/utils/addInfoObject.js.map +0 -7
- package/dist/utils/transform/utils/addLatestOpenApiVersion.js.map +0 -7
- package/dist/utils/transform/utils/addMissingTags.js.map +0 -7
- package/dist/utils/transform/utils/normalizeSecuritySchemes.js.map +0 -7
- package/dist/utils/transform/utils/rejectSwaggerDocuments.js.map +0 -7
- package/dist/utils/transform-errors.js.map +0 -7
- package/dist/utils/traverse.js.map +0 -7
- package/dist/utils/unescape-json-pointer.js.map +0 -7
- package/dist/utils/upgrade.js.map +0 -7
- package/dist/utils/validate-path-parameters.js.map +0 -7
- package/dist/utils/validate.js.map +0 -7
package/dist/utils/join/join.js
CHANGED
|
@@ -1,178 +1,292 @@
|
|
|
1
|
-
import { bundle } from
|
|
2
|
-
import { mergeObjects } from
|
|
3
|
-
import { upgrade } from
|
|
1
|
+
import { bundle } from '@scalar/json-magic/bundle';
|
|
2
|
+
import { mergeObjects } from '../../utils/join/merge-objects.js';
|
|
3
|
+
import { upgrade } from '../../utils/upgrade.js';
|
|
4
|
+
/**
|
|
5
|
+
* Returns the intersection of two sets as an array.
|
|
6
|
+
*
|
|
7
|
+
* @param a - The first set
|
|
8
|
+
* @param b - The second set
|
|
9
|
+
* @returns An array containing the elements present in both sets
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* const setA = new Set([1, 2, 3, 4])
|
|
13
|
+
* const setB = new Set([3, 4, 5, 6])
|
|
14
|
+
* const intersection = getSetIntersection(setA, setB)
|
|
15
|
+
* // intersection: [3, 4]
|
|
16
|
+
*/
|
|
4
17
|
const getSetIntersection = (a, b) => {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
18
|
+
const result = [];
|
|
19
|
+
for (const value of a) {
|
|
20
|
+
if (b.has(value)) {
|
|
21
|
+
result.push(value);
|
|
22
|
+
}
|
|
9
23
|
}
|
|
10
|
-
|
|
11
|
-
return result;
|
|
24
|
+
return result;
|
|
12
25
|
};
|
|
26
|
+
/**
|
|
27
|
+
* Returns the value if it is not nullish (or, for arrays, not empty), otherwise returns the provided default value.
|
|
28
|
+
* Useful for handling OpenAPI fields that may be missing or empty.
|
|
29
|
+
*/
|
|
13
30
|
const withDefault = (value, defaultValue) => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
31
|
+
if (Array.isArray(value)) {
|
|
32
|
+
return value.length ? value : defaultValue;
|
|
33
|
+
}
|
|
34
|
+
if (typeof value === 'object' && value !== null) {
|
|
35
|
+
return Object.keys(value).length ? value : defaultValue;
|
|
36
|
+
}
|
|
37
|
+
return value ?? defaultValue;
|
|
21
38
|
};
|
|
39
|
+
/**
|
|
40
|
+
* Merges multiple OpenAPI PathsObjects into a single PathsObject.
|
|
41
|
+
* - If a path does not exist in the result, it is added directly.
|
|
42
|
+
* - If a path already exists, its operations (get, post, etc.) are merged.
|
|
43
|
+
* - If the same operation (e.g., "get") exists for the same path in multiple inputs,
|
|
44
|
+
* a conflict is recorded for that path and method.
|
|
45
|
+
*
|
|
46
|
+
* @param inputs - Array of OpenAPIV3_1.PathsObject to merge
|
|
47
|
+
* @returns An object containing the merged paths and a list of conflicts
|
|
48
|
+
*/
|
|
22
49
|
const mergePaths = (inputs) => {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
50
|
+
const result = {};
|
|
51
|
+
const conflicts = [];
|
|
52
|
+
for (const paths of inputs) {
|
|
53
|
+
if (typeof paths !== 'object') {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
for (const [path, pathItem] of Object.entries(paths)) {
|
|
57
|
+
if (!result[path]) {
|
|
58
|
+
// If the path does not exist, add it directly
|
|
59
|
+
result[path] = pathItem;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
// Find intersecting operation keys (e.g., "get", "post") for this path
|
|
63
|
+
const intersectingKeys = getSetIntersection(new Set(Object.keys(result[path])), new Set(Object.keys(pathItem)));
|
|
64
|
+
// If the path exists, merge the operations (get, post, etc.)
|
|
65
|
+
result[path] = { ...result[path], ...pathItem };
|
|
66
|
+
// Record conflicts for each intersecting operation key
|
|
67
|
+
intersectingKeys.forEach((key) => conflicts.push({ method: key, path }));
|
|
68
|
+
}
|
|
37
69
|
}
|
|
38
|
-
|
|
39
|
-
return { paths: result, conflicts };
|
|
70
|
+
return { paths: result, conflicts };
|
|
40
71
|
};
|
|
72
|
+
/**
|
|
73
|
+
* Merges multiple arrays of OpenAPI TagObjects into a single array, ensuring uniqueness by tag name.
|
|
74
|
+
* - If a tag with the same name appears in multiple arrays, only the first occurrence is included in the result.
|
|
75
|
+
*
|
|
76
|
+
* @param inputs - Array of arrays of OpenAPIV3_1.TagObject to merge
|
|
77
|
+
* @returns An array of unique TagObjects (by name)
|
|
78
|
+
*/
|
|
41
79
|
const mergeTags = (inputs) => {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
80
|
+
const cache = new Set();
|
|
81
|
+
const result = [];
|
|
82
|
+
for (const tags of inputs) {
|
|
83
|
+
for (const tag of tags) {
|
|
84
|
+
if (!cache.has(tag.name)) {
|
|
85
|
+
result.push(tag);
|
|
86
|
+
}
|
|
87
|
+
cache.add(tag.name);
|
|
88
|
+
}
|
|
50
89
|
}
|
|
51
|
-
|
|
52
|
-
return result;
|
|
90
|
+
return result;
|
|
53
91
|
};
|
|
92
|
+
/**
|
|
93
|
+
* Merges multiple arrays of OpenAPI ServerObjects into a single array, ensuring uniqueness by server URL.
|
|
94
|
+
* - If a server with the same URL appears in multiple arrays, only the first occurrence is included in the result.
|
|
95
|
+
*
|
|
96
|
+
* @param inputs - Array of arrays of OpenAPIV3_1.ServerObject to merge
|
|
97
|
+
* @returns An array of unique ServerObjects (by url)
|
|
98
|
+
*/
|
|
54
99
|
const mergeServers = (inputs) => {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
100
|
+
const cache = new Set();
|
|
101
|
+
const result = [];
|
|
102
|
+
for (const servers of inputs) {
|
|
103
|
+
for (const server of servers) {
|
|
104
|
+
if (!cache.has(server.url)) {
|
|
105
|
+
result.push(server);
|
|
106
|
+
}
|
|
107
|
+
cache.add(server.url);
|
|
108
|
+
}
|
|
63
109
|
}
|
|
64
|
-
|
|
65
|
-
return result;
|
|
110
|
+
return result;
|
|
66
111
|
};
|
|
112
|
+
/**
|
|
113
|
+
* Merges multiple OpenAPI ComponentsObject instances into a single components object.
|
|
114
|
+
* - If a component with the same type and name appears in multiple inputs, only the first occurrence is included.
|
|
115
|
+
* - Any conflicts (duplicate component names within the same type) are recorded in the `conflicts` array.
|
|
116
|
+
*
|
|
117
|
+
* @param inputs - Array of OpenAPIV3_1.ComponentsObject to merge
|
|
118
|
+
* @returns An object containing the merged components and an array of conflicts
|
|
119
|
+
*/
|
|
67
120
|
const mergeComponents = (inputs) => {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
for (const [key, value] of Object.entries(components)) {
|
|
75
|
-
for (const [name, component] of Object.entries(value)) {
|
|
76
|
-
if (!result[key]) {
|
|
77
|
-
result[key] = {};
|
|
121
|
+
const result = {};
|
|
122
|
+
const conflicts = [];
|
|
123
|
+
for (const components of inputs) {
|
|
124
|
+
if (typeof components !== 'object') {
|
|
125
|
+
continue;
|
|
78
126
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
127
|
+
// Merge each component type (schemas, responses, parameters, etc.)
|
|
128
|
+
for (const [key, value] of Object.entries(components)) {
|
|
129
|
+
for (const [name, component] of Object.entries(value)) {
|
|
130
|
+
if (!result[key]) {
|
|
131
|
+
result[key] = {};
|
|
132
|
+
}
|
|
133
|
+
if (result[key][name]) {
|
|
134
|
+
// If the component already exists, record a conflict
|
|
135
|
+
conflicts.push({ componentType: key, name });
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
// Otherwise, add the component
|
|
139
|
+
result[key][name] = component;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
83
142
|
}
|
|
84
|
-
}
|
|
85
143
|
}
|
|
86
|
-
|
|
87
|
-
return { components: result, conflicts };
|
|
144
|
+
return { components: result, conflicts };
|
|
88
145
|
};
|
|
146
|
+
/**
|
|
147
|
+
* Prefixes component names and their references in multiple OpenAPI documents.
|
|
148
|
+
*
|
|
149
|
+
* This function mutates each input document in-place by:
|
|
150
|
+
* 1. Prefixing all component names (e.g., schema names) with the corresponding prefix.
|
|
151
|
+
* 2. Updating all $ref values that point to components to use the prefixed names.
|
|
152
|
+
*
|
|
153
|
+
* This is useful when merging multiple OpenAPI documents to avoid component name collisions.
|
|
154
|
+
*
|
|
155
|
+
* @param inputs - Array of OpenAPI documents to mutate.
|
|
156
|
+
* @param prefixes - Array of prefixes to apply to each document's components.
|
|
157
|
+
*/
|
|
89
158
|
const prefixComponents = async (inputs, prefixes) => {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
159
|
+
for (const index of inputs.keys()) {
|
|
160
|
+
await bundle(inputs[index], {
|
|
161
|
+
treeShake: false,
|
|
162
|
+
urlMap: false,
|
|
163
|
+
plugins: [
|
|
164
|
+
// Plugin to update $ref values to use the prefixed component names
|
|
165
|
+
{
|
|
166
|
+
type: 'lifecycle',
|
|
167
|
+
onBeforeNodeProcess: (node) => {
|
|
168
|
+
const ref = node['$ref'];
|
|
169
|
+
if (typeof ref !== 'string') {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
// Only process refs that point to components
|
|
173
|
+
if (!ref.startsWith('#/components/')) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const parts = ref.split('/');
|
|
177
|
+
// Ensure the ref has the expected structure: #/components/{type}/{name}
|
|
178
|
+
if (parts.length < 4) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
// Prefix the component name (parts[3]) with the provided prefix
|
|
182
|
+
parts[3] = `${prefixes[index] ?? ''}${parts[3]}`;
|
|
183
|
+
node['$ref'] = parts.join('/');
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
// Plugin to rename component keys with the prefix
|
|
187
|
+
{
|
|
188
|
+
type: 'lifecycle',
|
|
189
|
+
onBeforeNodeProcess: (node, context) => {
|
|
190
|
+
// Check if the node is a component type object (e.g., schemas, responses) under "components"
|
|
191
|
+
if (context.path.length === 2 && context.path[0] === 'components') {
|
|
192
|
+
const prefix = prefixes[index];
|
|
193
|
+
Object.keys(node).forEach((key) => {
|
|
194
|
+
const newKey = `${prefix ?? ''}${key}`;
|
|
195
|
+
const childNode = node[key];
|
|
196
|
+
delete node[key];
|
|
197
|
+
node[newKey] = childNode;
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
],
|
|
203
|
+
});
|
|
204
|
+
}
|
|
132
205
|
};
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
206
|
+
/**
|
|
207
|
+
* Joins multiple OpenAPI documents into a single document.
|
|
208
|
+
*
|
|
209
|
+
* - Merges the "info" object, paths, webhooks, tags, and servers from all input documents.
|
|
210
|
+
* - If there are conflicting paths or webhooks (same path and method), returns a list of conflicts.
|
|
211
|
+
* - Only the first occurrence of a tag (by name) or server (by url) is included.
|
|
212
|
+
* - The merge is performed in reverse order, so the first document in the input array has the highest precedence.
|
|
213
|
+
*
|
|
214
|
+
* @param inputs - Array of OpenAPI documents (UnknownObject) to join
|
|
215
|
+
* @returns {JoinResult} - { ok: true, document } if successful, or { ok: false, conflicts } if there are conflicts
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* const doc1 = {
|
|
219
|
+
* info: { title: "API 1", version: "1.0.0" },
|
|
220
|
+
* paths: { "/foo": { get: { summary: "Get Foo" } } },
|
|
221
|
+
* tags: [{ name: "foo" }],
|
|
222
|
+
* servers: [{ url: "https://api1.example.com" }]
|
|
223
|
+
* }
|
|
224
|
+
* const doc2 = {
|
|
225
|
+
* info: { description: "Second API" },
|
|
226
|
+
* paths: { "/bar": { get: { summary: "Get Bar" } } },
|
|
227
|
+
* tags: [{ name: "bar" }],
|
|
228
|
+
* servers: [{ url: "https://api2.example.com" }]
|
|
229
|
+
* }
|
|
230
|
+
* const result = join([doc1, doc2])
|
|
231
|
+
* // result.ok === true
|
|
232
|
+
* // result.document.info.title === "API 1"
|
|
233
|
+
* // result.document.info.description === "Second API"
|
|
234
|
+
* // result.document.paths has both "/foo" and "/bar"
|
|
235
|
+
* // result.document.tags contains both "foo" and "bar"
|
|
236
|
+
* // result.document.servers contains both server URLs
|
|
237
|
+
*/
|
|
238
|
+
export const join = async (inputs, config) => {
|
|
239
|
+
// Reverse the input list and upgrade them (first input has highest precedence)
|
|
240
|
+
const upgraded = inputs.map((it) => upgrade(it).specification);
|
|
241
|
+
// Preprocess documents by prefixing components if specified
|
|
242
|
+
if (config?.prefixComponents) {
|
|
243
|
+
await prefixComponents(upgraded, config.prefixComponents);
|
|
244
|
+
}
|
|
245
|
+
// Reverse the upgraded documents to ensure the first document has the highest precedence
|
|
246
|
+
upgraded.reverse();
|
|
247
|
+
// Merge only the "info" object from all inputs
|
|
248
|
+
const info = upgraded.reduce((acc, curr) => {
|
|
249
|
+
if (curr.info && typeof curr.info === 'object') {
|
|
250
|
+
return mergeObjects(acc, curr.info);
|
|
251
|
+
}
|
|
252
|
+
return acc;
|
|
253
|
+
}, {});
|
|
254
|
+
// Merge paths from all documents, collecting conflicts
|
|
255
|
+
const { paths, conflicts: pathConflicts } = mergePaths(upgraded.map((it) => it.paths ?? {}));
|
|
256
|
+
// Merge webhooks from all documents, collecting conflicts
|
|
257
|
+
const { paths: webhooks, conflicts: webhookConflicts } = mergePaths(upgraded.map((it) => it.webhooks ?? {}));
|
|
258
|
+
// Merge tags, ensuring uniqueness by tag name
|
|
259
|
+
const tags = mergeTags(upgraded.map((it) => it.tags ?? []));
|
|
260
|
+
// Merge servers, ensuring uniqueness by server url
|
|
261
|
+
const servers = mergeServers(upgraded.map((it) => it.servers ?? []));
|
|
262
|
+
// Merge components, collecting conflicts
|
|
263
|
+
const { components, conflicts: componentConflicts } = mergeComponents(upgraded.map((it) => it.components ?? {}));
|
|
264
|
+
// Merge all documents in the upgraded array into a single object (shallow merge)
|
|
265
|
+
const result = upgraded.reduce((acc, curr) => ({ ...acc, ...curr }), {});
|
|
266
|
+
// Collect all conflicts (paths and webhooks)
|
|
267
|
+
const conflicts = [
|
|
268
|
+
...pathConflicts.map((it) => ({ type: 'path', ...it })),
|
|
269
|
+
...webhookConflicts.map((it) => ({ type: 'webhook', ...it })),
|
|
270
|
+
...componentConflicts.map((it) => ({ type: 'component', ...it })),
|
|
271
|
+
];
|
|
272
|
+
// If there are any conflicts, return them
|
|
273
|
+
if (conflicts.length) {
|
|
274
|
+
return {
|
|
275
|
+
ok: false,
|
|
276
|
+
conflicts,
|
|
277
|
+
};
|
|
142
278
|
}
|
|
143
|
-
|
|
144
|
-
}, {});
|
|
145
|
-
const { paths, conflicts: pathConflicts } = mergePaths(upgraded.map((it) => it.paths ?? {}));
|
|
146
|
-
const { paths: webhooks, conflicts: webhookConflicts } = mergePaths(upgraded.map((it) => it.webhooks ?? {}));
|
|
147
|
-
const tags = mergeTags(upgraded.map((it) => it.tags ?? []));
|
|
148
|
-
const servers = mergeServers(upgraded.map((it) => it.servers ?? []));
|
|
149
|
-
const { components, conflicts: componentConflicts } = mergeComponents(upgraded.map((it) => it.components ?? {}));
|
|
150
|
-
const result = upgraded.reduce((acc, curr) => ({ ...acc, ...curr }), {});
|
|
151
|
-
const conflicts = [
|
|
152
|
-
...pathConflicts.map((it) => ({ type: "path", ...it })),
|
|
153
|
-
...webhookConflicts.map((it) => ({ type: "webhook", ...it })),
|
|
154
|
-
...componentConflicts.map((it) => ({ type: "component", ...it }))
|
|
155
|
-
];
|
|
156
|
-
if (conflicts.length) {
|
|
279
|
+
// Return the merged OpenAPI document
|
|
157
280
|
return {
|
|
158
|
-
|
|
159
|
-
|
|
281
|
+
ok: true,
|
|
282
|
+
document: {
|
|
283
|
+
...result,
|
|
284
|
+
info,
|
|
285
|
+
paths,
|
|
286
|
+
webhooks: withDefault(webhooks, undefined),
|
|
287
|
+
tags: withDefault(tags, undefined),
|
|
288
|
+
servers: withDefault(servers, undefined),
|
|
289
|
+
components: withDefault(components, undefined),
|
|
290
|
+
},
|
|
160
291
|
};
|
|
161
|
-
}
|
|
162
|
-
return {
|
|
163
|
-
ok: true,
|
|
164
|
-
document: {
|
|
165
|
-
...result,
|
|
166
|
-
info,
|
|
167
|
-
paths,
|
|
168
|
-
webhooks: withDefault(webhooks, void 0),
|
|
169
|
-
tags: withDefault(tags, void 0),
|
|
170
|
-
servers: withDefault(servers, void 0),
|
|
171
|
-
components: withDefault(components, void 0)
|
|
172
|
-
}
|
|
173
|
-
};
|
|
174
|
-
};
|
|
175
|
-
export {
|
|
176
|
-
join
|
|
177
292
|
};
|
|
178
|
-
//# sourceMappingURL=join.js.map
|
|
@@ -1,20 +1,37 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Deep merges two objects, combining their properties recursively.
|
|
3
|
+
*
|
|
4
|
+
* ⚠️ Note: This operation assumes there are no key collisions between the objects.
|
|
5
|
+
* @param a - Target object to merge into
|
|
6
|
+
* @param b - Source object to merge from
|
|
7
|
+
* @returns The merged object (mutates and returns a)
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* // Simple merge
|
|
11
|
+
* const a = { name: 'John' }
|
|
12
|
+
* const b = { age: 30 }
|
|
13
|
+
* mergeObjects(a, b) // { name: 'John', age: 30 }
|
|
14
|
+
*
|
|
15
|
+
* // Nested merge
|
|
16
|
+
* const a = { user: { name: 'John' } }
|
|
17
|
+
* const b = { user: { age: 30 } }
|
|
18
|
+
* mergeObjects(a, b) // { user: { name: 'John', age: 30 } }
|
|
19
|
+
*/
|
|
20
|
+
export const mergeObjects = (a, b) => {
|
|
21
|
+
for (const key in b) {
|
|
22
|
+
if (!(key in a)) {
|
|
23
|
+
a[key] = b[key];
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
const aValue = a[key];
|
|
27
|
+
const bValue = b[key];
|
|
28
|
+
if (typeof aValue === 'object' && aValue !== null && typeof bValue === 'object' && bValue !== null) {
|
|
29
|
+
mergeObjects(aValue, bValue);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
a[key] = bValue; // Overwrite with b's value if not an object
|
|
33
|
+
}
|
|
34
|
+
}
|
|
13
35
|
}
|
|
14
|
-
|
|
15
|
-
return a;
|
|
36
|
+
return a;
|
|
16
37
|
};
|
|
17
|
-
export {
|
|
18
|
-
mergeObjects
|
|
19
|
-
};
|
|
20
|
-
//# sourceMappingURL=merge-objects.js.map
|
package/dist/utils/load/index.js
CHANGED