@scalar/postman-to-openapi 0.5.2 → 0.5.3

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.
Files changed (44) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/convert.js +240 -212
  3. package/dist/helpers/auth.js +116 -92
  4. package/dist/helpers/contact.js +24 -20
  5. package/dist/helpers/external-docs.js +33 -25
  6. package/dist/helpers/form-data.js +42 -36
  7. package/dist/helpers/license.js +21 -17
  8. package/dist/helpers/logo.js +22 -21
  9. package/dist/helpers/markdown.js +33 -30
  10. package/dist/helpers/parameters.js +119 -96
  11. package/dist/helpers/path-items.js +244 -202
  12. package/dist/helpers/post-response-scripts.js +12 -12
  13. package/dist/helpers/pre-request-scripts.js +12 -12
  14. package/dist/helpers/prune-document.js +42 -35
  15. package/dist/helpers/request-body.js +102 -92
  16. package/dist/helpers/responses.js +62 -57
  17. package/dist/helpers/schemas.js +43 -37
  18. package/dist/helpers/servers.js +83 -57
  19. package/dist/helpers/status-codes.js +40 -30
  20. package/dist/helpers/urls.js +74 -51
  21. package/dist/index.js +1 -5
  22. package/dist/types.js +1 -1
  23. package/package.json +6 -11
  24. package/dist/convert.js.map +0 -7
  25. package/dist/helpers/auth.js.map +0 -7
  26. package/dist/helpers/contact.js.map +0 -7
  27. package/dist/helpers/external-docs.js.map +0 -7
  28. package/dist/helpers/form-data.js.map +0 -7
  29. package/dist/helpers/license.js.map +0 -7
  30. package/dist/helpers/logo.js.map +0 -7
  31. package/dist/helpers/markdown.js.map +0 -7
  32. package/dist/helpers/parameters.js.map +0 -7
  33. package/dist/helpers/path-items.js.map +0 -7
  34. package/dist/helpers/post-response-scripts.js.map +0 -7
  35. package/dist/helpers/pre-request-scripts.js.map +0 -7
  36. package/dist/helpers/prune-document.js.map +0 -7
  37. package/dist/helpers/request-body.js.map +0 -7
  38. package/dist/helpers/responses.js.map +0 -7
  39. package/dist/helpers/schemas.js.map +0 -7
  40. package/dist/helpers/servers.js.map +0 -7
  41. package/dist/helpers/status-codes.js.map +0 -7
  42. package/dist/helpers/urls.js.map +0 -7
  43. package/dist/index.js.map +0 -7
  44. package/dist/types.js.map +0 -7
@@ -1,218 +1,260 @@
1
- import { processAuth } from "./auth.js";
2
- import { parseMdTable } from "./markdown.js";
3
- import { extractParameters } from "./parameters.js";
4
- import { processPostResponseScripts } from "./post-response-scripts.js";
5
- import { processPreRequestScripts } from "./pre-request-scripts.js";
6
- import { extractRequestBody } from "./request-body.js";
7
- import { extractResponses } from "./responses.js";
8
- import { extractPathFromUrl, extractPathParameterNames, extractServerFromUrl, normalizePath } from "./urls.js";
1
+ import { processAuth } from './auth.js';
2
+ import { parseMdTable } from './markdown.js';
3
+ import { extractParameters } from './parameters.js';
4
+ import { processPostResponseScripts } from './post-response-scripts.js';
5
+ import { processPreRequestScripts } from './pre-request-scripts.js';
6
+ import { extractRequestBody } from './request-body.js';
7
+ import { extractResponses } from './responses.js';
8
+ import { extractPathFromUrl, extractPathParameterNames, extractServerFromUrl, normalizePath } from './urls.js';
9
9
  function ensureRequestBodyContent(requestBody) {
10
- const content = requestBody.content ?? {};
11
- if (Object.keys(content).length === 0) {
12
- requestBody.content = {
13
- "text/plain": {}
14
- };
15
- return;
16
- }
17
- if ("text/plain" in content) {
18
- const textContent = content["text/plain"];
19
- if (!textContent?.schema || textContent.schema && Object.keys(textContent.schema).length === 0) {
20
- content["text/plain"] = {};
21
- }
22
- }
10
+ const content = requestBody.content ?? {};
11
+ if (Object.keys(content).length === 0) {
12
+ requestBody.content = {
13
+ 'text/plain': {},
14
+ };
15
+ return;
16
+ }
17
+ if ('text/plain' in content) {
18
+ const textContent = content['text/plain'];
19
+ if (!textContent?.schema || (textContent.schema && Object.keys(textContent.schema).length === 0)) {
20
+ content['text/plain'] = {};
21
+ }
22
+ }
23
23
  }
24
- function processItem(item, parentTags = [], parentPath = "") {
25
- const paths = {};
26
- const components = {};
27
- const serverUsage = [];
28
- if ("item" in item && Array.isArray(item.item)) {
29
- const newParentTags = item.name ? [...parentTags, item.name] : parentTags;
30
- item.item.forEach((childItem) => {
31
- const childResult = processItem(childItem, newParentTags, `${parentPath}/${item.name || ""}`);
32
- for (const [pathKey, pathItem2] of Object.entries(childResult.paths)) {
33
- if (!paths[pathKey]) {
34
- paths[pathKey] = pathItem2;
35
- } else {
36
- paths[pathKey] = {
37
- ...paths[pathKey],
38
- ...pathItem2
39
- };
24
+ /**
25
+ * Processes a Postman collection item or item group and returns
26
+ * the corresponding OpenAPI paths and components.
27
+ * Handles nested item groups, extracts request details, and generates corresponding
28
+ * OpenAPI path items and operations.
29
+ */
30
+ export function processItem(item, parentTags = [], parentPath = '') {
31
+ const paths = {};
32
+ const components = {};
33
+ const serverUsage = [];
34
+ if ('item' in item && Array.isArray(item.item)) {
35
+ const newParentTags = item.name ? [...parentTags, item.name] : parentTags;
36
+ item.item.forEach((childItem) => {
37
+ const childResult = processItem(childItem, newParentTags, `${parentPath}/${item.name || ''}`);
38
+ // Merge child paths and components
39
+ for (const [pathKey, pathItem] of Object.entries(childResult.paths)) {
40
+ if (!paths[pathKey]) {
41
+ paths[pathKey] = pathItem;
42
+ }
43
+ else {
44
+ paths[pathKey] = {
45
+ ...paths[pathKey],
46
+ ...pathItem,
47
+ };
48
+ }
49
+ }
50
+ // Merge components.securitySchemes
51
+ if (childResult.components.securitySchemes) {
52
+ components.securitySchemes = {
53
+ ...components.securitySchemes,
54
+ ...childResult.components.securitySchemes,
55
+ };
56
+ }
57
+ // Merge server usage
58
+ serverUsage.push(...childResult.serverUsage);
59
+ });
60
+ return { paths, components, serverUsage };
61
+ }
62
+ if (!('request' in item)) {
63
+ return { paths, components, serverUsage };
64
+ }
65
+ const { request, name, response } = item;
66
+ const method = (typeof request === 'string' ? 'get' : request.method || 'get').toLowerCase();
67
+ const requestUrl = typeof request === 'string' ? request : typeof request.url === 'string' ? request.url : (request.url?.raw ?? '');
68
+ const path = extractPathFromUrl(requestUrl);
69
+ // Normalize path parameters from ':param' to '{param}'
70
+ const normalizedPath = normalizePath(path);
71
+ // Extract server URL from request URL
72
+ const serverUrl = extractServerFromUrl(requestUrl);
73
+ if (serverUrl) {
74
+ serverUsage.push({
75
+ serverUrl,
76
+ path: normalizedPath,
77
+ method,
78
+ });
79
+ }
80
+ // Extract path parameter names
81
+ const pathParameterNames = extractPathParameterNames(normalizedPath);
82
+ // Extract operation ID if present
83
+ const { operationId, summary } = extractOperationInfo(name);
84
+ const description = typeof request === 'string'
85
+ ? ''
86
+ : typeof request.description === 'string'
87
+ ? request.description
88
+ : (request.description?.content ?? '');
89
+ const operationObject = {
90
+ tags: parentTags.length > 0 ? [parentTags.join(' > ')] : undefined,
91
+ summary,
92
+ description,
93
+ responses: extractResponses(response || [], item),
94
+ parameters: [],
95
+ };
96
+ // Add pre-request scripts if present
97
+ const preRequestScript = processPreRequestScripts(item.event);
98
+ if (preRequestScript) {
99
+ operationObject['x-pre-request'] = preRequestScript;
100
+ }
101
+ // Add post-response scripts if present
102
+ const postResponseScript = processPostResponseScripts(item.event);
103
+ if (postResponseScript) {
104
+ operationObject['x-post-response'] = postResponseScript;
105
+ }
106
+ // Only add operationId if it was explicitly provided
107
+ if (operationId) {
108
+ operationObject.operationId = operationId;
109
+ }
110
+ // Extract parameters from the request (query, path, header)
111
+ // This should always happen, regardless of whether a description exists
112
+ const extractedParameters = extractParameters(request);
113
+ // Merge parameters, giving priority to those from the Markdown table if description exists
114
+ const mergedParameters = new Map();
115
+ // Add extracted parameters, filtering out path parameters not in the path
116
+ extractedParameters.forEach((param) => {
117
+ if (param.name) {
118
+ if (param.in === 'path' && !pathParameterNames.includes(param.name)) {
119
+ return;
120
+ }
121
+ mergedParameters.set(param.name, param);
122
+ }
123
+ });
124
+ // Parse parameters from the description's Markdown table if description exists
125
+ if (operationObject.description) {
126
+ const { descriptionWithoutTable, parametersFromTable } = parseParametersFromDescription(operationObject.description);
127
+ operationObject.description = descriptionWithoutTable.trim();
128
+ // Add parameters from table, filtering out path parameters not in the path
129
+ // These take priority over extracted parameters
130
+ parametersFromTable.forEach((param) => {
131
+ if (param.name) {
132
+ if (param.in === 'path' && !pathParameterNames.includes(param.name)) {
133
+ return;
134
+ }
135
+ mergedParameters.set(param.name, param);
136
+ }
137
+ });
138
+ }
139
+ // Set parameters if we have any
140
+ if (mergedParameters.size > 0) {
141
+ operationObject.parameters = Array.from(mergedParameters.values());
142
+ }
143
+ if (typeof request !== 'string' && request.auth) {
144
+ if (!operationObject.security) {
145
+ operationObject.security = [];
146
+ }
147
+ const { securitySchemes, security } = processAuth(request.auth);
148
+ if (!components.securitySchemes) {
149
+ components.securitySchemes = {};
40
150
  }
41
- }
42
- if (childResult.components.securitySchemes) {
43
151
  components.securitySchemes = {
44
- ...components.securitySchemes,
45
- ...childResult.components.securitySchemes
152
+ ...components.securitySchemes,
153
+ ...securitySchemes,
46
154
  };
47
- }
48
- serverUsage.push(...childResult.serverUsage);
49
- });
50
- return { paths, components, serverUsage };
51
- }
52
- if (!("request" in item)) {
53
- return { paths, components, serverUsage };
54
- }
55
- const { request, name, response } = item;
56
- const method = (typeof request === "string" ? "get" : request.method || "get").toLowerCase();
57
- const requestUrl = typeof request === "string" ? request : typeof request.url === "string" ? request.url : request.url?.raw ?? "";
58
- const path = extractPathFromUrl(requestUrl);
59
- const normalizedPath = normalizePath(path);
60
- const serverUrl = extractServerFromUrl(requestUrl);
61
- if (serverUrl) {
62
- serverUsage.push({
63
- serverUrl,
64
- path: normalizedPath,
65
- method
66
- });
67
- }
68
- const pathParameterNames = extractPathParameterNames(normalizedPath);
69
- const { operationId, summary } = extractOperationInfo(name);
70
- const description = typeof request === "string" ? "" : typeof request.description === "string" ? request.description : request.description?.content ?? "";
71
- const operationObject = {
72
- tags: parentTags.length > 0 ? [parentTags.join(" > ")] : void 0,
73
- summary,
74
- description,
75
- responses: extractResponses(response || [], item),
76
- parameters: []
77
- };
78
- const preRequestScript = processPreRequestScripts(item.event);
79
- if (preRequestScript) {
80
- operationObject["x-pre-request"] = preRequestScript;
81
- }
82
- const postResponseScript = processPostResponseScripts(item.event);
83
- if (postResponseScript) {
84
- operationObject["x-post-response"] = postResponseScript;
85
- }
86
- if (operationId) {
87
- operationObject.operationId = operationId;
88
- }
89
- const extractedParameters = extractParameters(request);
90
- const mergedParameters = /* @__PURE__ */ new Map();
91
- extractedParameters.forEach((param) => {
92
- if (param.name) {
93
- if (param.in === "path" && !pathParameterNames.includes(param.name)) {
94
- return;
95
- }
96
- mergedParameters.set(param.name, param);
97
- }
98
- });
99
- if (operationObject.description) {
100
- const { descriptionWithoutTable, parametersFromTable } = parseParametersFromDescription(operationObject.description);
101
- operationObject.description = descriptionWithoutTable.trim();
102
- parametersFromTable.forEach((param) => {
103
- if (param.name) {
104
- if (param.in === "path" && !pathParameterNames.includes(param.name)) {
105
- return;
155
+ operationObject.security.push(...security);
156
+ }
157
+ // Allow request bodies for all methods (including GET) if body is present
158
+ if (typeof request !== 'string' && request.body) {
159
+ const requestBody = extractRequestBody(request.body);
160
+ ensureRequestBodyContent(requestBody);
161
+ // Only add requestBody if it has content
162
+ if (requestBody.content && Object.keys(requestBody.content).length > 0) {
163
+ operationObject.requestBody = requestBody;
106
164
  }
107
- mergedParameters.set(param.name, param);
108
- }
109
- });
110
- }
111
- if (mergedParameters.size > 0) {
112
- operationObject.parameters = Array.from(mergedParameters.values());
113
- }
114
- if (typeof request !== "string" && request.auth) {
115
- if (!operationObject.security) {
116
- operationObject.security = [];
117
- }
118
- const { securitySchemes, security } = processAuth(request.auth);
119
- if (!components.securitySchemes) {
120
- components.securitySchemes = {};
121
- }
122
- components.securitySchemes = {
123
- ...components.securitySchemes,
124
- ...securitySchemes
125
- };
126
- operationObject.security.push(...security);
127
- }
128
- if (typeof request !== "string" && request.body) {
129
- const requestBody = extractRequestBody(request.body);
130
- ensureRequestBodyContent(requestBody);
131
- if (requestBody.content && Object.keys(requestBody.content).length > 0) {
132
- operationObject.requestBody = requestBody;
133
- }
134
- }
135
- if (!paths[path]) {
136
- paths[path] = {};
137
- }
138
- const pathItem = paths[path];
139
- pathItem[method] = operationObject;
140
- return { paths, components, serverUsage };
165
+ }
166
+ if (!paths[path]) {
167
+ paths[path] = {};
168
+ }
169
+ const pathItem = paths[path];
170
+ pathItem[method] = operationObject;
171
+ return { paths, components, serverUsage };
141
172
  }
142
- const OPENAPI_PARAM_SCHEMA_TYPES = ["string", "number", "integer", "boolean", "object", "array"];
173
+ /** OpenAPI 3.1 parameter schema types (ParameterObject uses OpenAPIV3_1). */
174
+ const OPENAPI_PARAM_SCHEMA_TYPES = ['string', 'number', 'integer', 'boolean', 'object', 'array'];
143
175
  function toOpenApiParamSchemaType(s) {
144
- const value = s ?? "string";
145
- for (const t of OPENAPI_PARAM_SCHEMA_TYPES) {
146
- if (t === value) return t;
147
- }
148
- return "string";
176
+ const value = s ?? 'string';
177
+ for (const t of OPENAPI_PARAM_SCHEMA_TYPES) {
178
+ if (t === value)
179
+ return t;
180
+ }
181
+ return 'string';
149
182
  }
150
183
  function parameterSchemaFromType(type) {
151
- if (type === "array") {
152
- return { type: "array" };
153
- }
154
- return { type };
184
+ if (type === 'array') {
185
+ return { type: 'array' };
186
+ }
187
+ return { type };
155
188
  }
156
189
  function parseParametersFromDescription(description) {
157
- const lines = description.split("\n");
158
- let inTable = false;
159
- const tableLines = [];
160
- const descriptionLines = [];
161
- for (const line of lines) {
162
- if (line.trim().startsWith("|")) {
163
- while (descriptionLines.length > 0 && (descriptionLines[descriptionLines.length - 1]?.trim() === "" || descriptionLines[descriptionLines.length - 1]?.trim().startsWith("#"))) {
164
- descriptionLines.pop();
165
- }
166
- inTable = true;
167
- }
168
- if (inTable) {
169
- tableLines.push(line);
170
- if (!line.trim().startsWith("|") && !line.trim().match(/^-+$/)) {
171
- inTable = false;
172
- }
173
- } else {
174
- descriptionLines.push(line);
175
- }
176
- }
177
- const tableMarkdown = tableLines.join("\n");
178
- const parsedTable = parseMdTable(tableMarkdown);
179
- const parametersFromTable = Object.values(parsedTable).map((paramData) => {
180
- const row = paramData;
181
- if (row.object !== "query" && row.object !== "header" && row.object !== "path") {
182
- return void 0;
183
- }
184
- if (!row.name) {
185
- return void 0;
186
- }
187
- const param = {
188
- name: row.name,
189
- in: row.object,
190
- description: row.description,
191
- required: row.required === "true",
192
- schema: parameterSchemaFromType(toOpenApiParamSchemaType(row.type))
193
- };
194
- if (row.example) {
195
- param.example = row.example;
190
+ const lines = description.split('\n');
191
+ let inTable = false;
192
+ const tableLines = [];
193
+ const descriptionLines = [];
194
+ for (const line of lines) {
195
+ // Detect the start of the table
196
+ if (line.trim().startsWith('|')) {
197
+ // Remove any preceding headers or empty lines before the table
198
+ while (descriptionLines.length > 0 &&
199
+ (descriptionLines[descriptionLines.length - 1]?.trim() === '' ||
200
+ descriptionLines[descriptionLines.length - 1]?.trim().startsWith('#'))) {
201
+ descriptionLines.pop();
202
+ }
203
+ // Start collecting table lines
204
+ inTable = true;
205
+ }
206
+ if (inTable) {
207
+ tableLines.push(line);
208
+ // Detect the end of the table (any line that doesn't start with '|', excluding the alignment line)
209
+ if (!line.trim().startsWith('|') && !line.trim().match(/^-+$/)) {
210
+ inTable = false;
211
+ }
212
+ }
213
+ else {
214
+ descriptionLines.push(line);
215
+ }
196
216
  }
197
- return param;
198
- }).filter((param) => Boolean(param));
199
- const descriptionWithoutTable = descriptionLines.join("\n");
200
- return { descriptionWithoutTable, parametersFromTable };
217
+ const tableMarkdown = tableLines.join('\n');
218
+ const parsedTable = parseMdTable(tableMarkdown);
219
+ const parametersFromTable = Object.values(parsedTable)
220
+ .map((paramData) => {
221
+ const row = paramData;
222
+ if (row.object !== 'query' && row.object !== 'header' && row.object !== 'path') {
223
+ return undefined;
224
+ }
225
+ if (!row.name) {
226
+ return undefined;
227
+ }
228
+ const param = {
229
+ name: row.name,
230
+ in: row.object,
231
+ description: row.description,
232
+ required: row.required === 'true',
233
+ schema: parameterSchemaFromType(toOpenApiParamSchemaType(row.type)),
234
+ };
235
+ if (row.example) {
236
+ param.example = row.example;
237
+ }
238
+ return param;
239
+ })
240
+ .filter((param) => Boolean(param));
241
+ const descriptionWithoutTable = descriptionLines.join('\n');
242
+ return { descriptionWithoutTable, parametersFromTable };
201
243
  }
244
+ // Instead of using regex with \s*, let's split this into two steps
202
245
  function extractOperationInfo(name) {
203
- if (!name) {
204
- return { operationId: void 0, summary: void 0 };
205
- }
206
- const match = name.match(/\[([^[\]]{0,1000})\]$/);
207
- if (!match) {
208
- return { operationId: void 0, summary: name };
209
- }
210
- const operationId = match[1];
211
- const lastBracketIndex = name.lastIndexOf("[");
212
- const summary = name.substring(0, lastBracketIndex).trim();
213
- return { operationId, summary };
246
+ if (!name) {
247
+ return { operationId: undefined, summary: undefined };
248
+ }
249
+ // First check if the string ends with something in brackets
250
+ const match = name.match(/\[([^[\]]{0,1000})\]$/);
251
+ if (!match) {
252
+ return { operationId: undefined, summary: name };
253
+ }
254
+ // Get the operation ID from inside brackets
255
+ const operationId = match[1];
256
+ // Trim the brackets part from the end using string operations instead of regex
257
+ const lastBracketIndex = name.lastIndexOf('[');
258
+ const summary = name.substring(0, lastBracketIndex).trim();
259
+ return { operationId, summary };
214
260
  }
215
- export {
216
- processItem
217
- };
218
- //# sourceMappingURL=path-items.js.map
@@ -1,13 +1,13 @@
1
- function processPostResponseScripts(events = []) {
2
- const testEvent = events.find((event) => event.listen === "test");
3
- const exec = testEvent?.script?.exec;
4
- if (!exec) {
5
- return void 0;
6
- }
7
- const content = typeof exec === "string" ? exec : exec.join("\n");
8
- return content.trim() || void 0;
1
+ /**
2
+ * Processes Postman test scripts and converts them to OpenAPI x-post-response extension.
3
+ * Extracts the test script from Postman events and returns it as a single string.
4
+ */
5
+ export function processPostResponseScripts(events = []) {
6
+ const testEvent = events.find((event) => event.listen === 'test');
7
+ const exec = testEvent?.script?.exec;
8
+ if (!exec) {
9
+ return undefined;
10
+ }
11
+ const content = typeof exec === 'string' ? exec : exec.join('\n');
12
+ return content.trim() || undefined;
9
13
  }
10
- export {
11
- processPostResponseScripts
12
- };
13
- //# sourceMappingURL=post-response-scripts.js.map
@@ -1,13 +1,13 @@
1
- function processPreRequestScripts(events = []) {
2
- const preRequestEvent = events.find((event) => event.listen === "prerequest");
3
- const exec = preRequestEvent?.script?.exec;
4
- if (!exec) {
5
- return void 0;
6
- }
7
- const content = typeof exec === "string" ? exec : exec.join("\n");
8
- return content.trim() || void 0;
1
+ /**
2
+ * Processes Postman pre-request scripts and converts them to OpenAPI x-pre-request extension.
3
+ * Extracts the pre-request script from Postman events and returns it as a single string.
4
+ */
5
+ export function processPreRequestScripts(events = []) {
6
+ const preRequestEvent = events.find((event) => event.listen === 'prerequest');
7
+ const exec = preRequestEvent?.script?.exec;
8
+ if (!exec) {
9
+ return undefined;
10
+ }
11
+ const content = typeof exec === 'string' ? exec : exec.join('\n');
12
+ return content.trim() || undefined;
9
13
  }
10
- export {
11
- processPreRequestScripts
12
- };
13
- //# sourceMappingURL=pre-request-scripts.js.map
@@ -1,39 +1,46 @@
1
+ /**
2
+ * Recursively removes all undefined values from an object or array.
3
+ * Preserves null values as they are valid in JSON/OpenAPI.
4
+ * Returns a new object/array with undefined values removed.
5
+ */
1
6
  const removeUndefinedValues = (value) => {
2
- if (value === null || value === void 0) {
3
- return value === null ? null : void 0;
4
- }
5
- if (Array.isArray(value)) {
6
- return value.map(removeUndefinedValues).filter((item) => item !== void 0);
7
- }
8
- if (typeof value === "object") {
9
- const cleaned = {};
10
- for (const [key, val] of Object.entries(value)) {
11
- const cleanedValue = removeUndefinedValues(val);
12
- if (cleanedValue !== void 0) {
13
- cleaned[key] = cleanedValue;
14
- }
7
+ if (value === null || value === undefined) {
8
+ return value === null ? null : undefined;
15
9
  }
16
- return cleaned;
17
- }
18
- return value;
19
- };
20
- const pruneDocument = (document) => {
21
- const cleaned = { ...document };
22
- if (cleaned.tags?.length === 0) {
23
- delete cleaned.tags;
24
- }
25
- if (cleaned.security?.length === 0) {
26
- delete cleaned.security;
27
- }
28
- if (cleaned.components && Object.keys(cleaned.components).length === 0) {
29
- delete cleaned.components;
30
- }
31
- if (cleaned.externalDocs === void 0) {
32
- delete cleaned.externalDocs;
33
- }
34
- return removeUndefinedValues(cleaned);
10
+ if (Array.isArray(value)) {
11
+ return value.map(removeUndefinedValues).filter((item) => item !== undefined);
12
+ }
13
+ if (typeof value === 'object') {
14
+ const cleaned = {};
15
+ for (const [key, val] of Object.entries(value)) {
16
+ const cleanedValue = removeUndefinedValues(val);
17
+ if (cleanedValue !== undefined) {
18
+ cleaned[key] = cleanedValue;
19
+ }
20
+ }
21
+ return cleaned;
22
+ }
23
+ return value;
35
24
  };
36
- export {
37
- pruneDocument
25
+ /**
26
+ * Prunes an OpenAPI document by removing empty arrays/objects and undefined values.
27
+ * Removes empty tags, security arrays, and empty components objects.
28
+ * Recursively removes all undefined values throughout the document structure.
29
+ */
30
+ export const pruneDocument = (document) => {
31
+ const cleaned = { ...document };
32
+ if (cleaned.tags?.length === 0) {
33
+ delete cleaned.tags;
34
+ }
35
+ if (cleaned.security?.length === 0) {
36
+ delete cleaned.security;
37
+ }
38
+ if (cleaned.components && Object.keys(cleaned.components).length === 0) {
39
+ delete cleaned.components;
40
+ }
41
+ if (cleaned.externalDocs === undefined) {
42
+ delete cleaned.externalDocs;
43
+ }
44
+ // Recursively remove all undefined values from the document
45
+ return removeUndefinedValues(cleaned);
38
46
  };
39
- //# sourceMappingURL=prune-document.js.map