@scalar/postman-to-openapi 0.1.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/CHANGELOG.md +12 -0
- package/README.md +33 -0
- package/dist/convert.d.ts +9 -0
- package/dist/convert.d.ts.map +1 -0
- package/dist/convert.js +156 -0
- package/dist/helpers/authHelpers.d.ts +12 -0
- package/dist/helpers/authHelpers.d.ts.map +1 -0
- package/dist/helpers/authHelpers.js +99 -0
- package/dist/helpers/externalDocsHelper.d.ts +10 -0
- package/dist/helpers/externalDocsHelper.d.ts.map +1 -0
- package/dist/helpers/externalDocsHelper.js +40 -0
- package/dist/helpers/formDataHelpers.d.ts +8 -0
- package/dist/helpers/formDataHelpers.d.ts.map +1 -0
- package/dist/helpers/formDataHelpers.js +42 -0
- package/dist/helpers/itemHelpers.d.ts +13 -0
- package/dist/helpers/itemHelpers.d.ts.map +1 -0
- package/dist/helpers/itemHelpers.js +236 -0
- package/dist/helpers/licenseContactHelper.d.ts +14 -0
- package/dist/helpers/licenseContactHelper.d.ts.map +1 -0
- package/dist/helpers/licenseContactHelper.js +73 -0
- package/dist/helpers/logoHelper.d.ts +13 -0
- package/dist/helpers/logoHelper.d.ts.map +1 -0
- package/dist/helpers/logoHelper.js +24 -0
- package/dist/helpers/md-utils.d.ts +6 -0
- package/dist/helpers/md-utils.d.ts.map +1 -0
- package/dist/helpers/md-utils.js +40 -0
- package/dist/helpers/parameterHelpers.d.ts +12 -0
- package/dist/helpers/parameterHelpers.d.ts.map +1 -0
- package/dist/helpers/parameterHelpers.js +109 -0
- package/dist/helpers/requestBodyHelpers.d.ts +8 -0
- package/dist/helpers/requestBodyHelpers.d.ts.map +1 -0
- package/dist/helpers/requestBodyHelpers.js +79 -0
- package/dist/helpers/responseHelpers.d.ts +9 -0
- package/dist/helpers/responseHelpers.d.ts.map +1 -0
- package/dist/helpers/responseHelpers.js +52 -0
- package/dist/helpers/schemaHelpers.d.ts +15 -0
- package/dist/helpers/schemaHelpers.d.ts.map +1 -0
- package/dist/helpers/schemaHelpers.js +52 -0
- package/dist/helpers/serverHelpers.d.ts +9 -0
- package/dist/helpers/serverHelpers.d.ts.map +1 -0
- package/dist/helpers/serverHelpers.js +29 -0
- package/dist/helpers/statusCodeHelpers.d.ts +10 -0
- package/dist/helpers/statusCodeHelpers.d.ts.map +1 -0
- package/dist/helpers/statusCodeHelpers.js +47 -0
- package/dist/helpers/urlHelpers.d.ts +19 -0
- package/dist/helpers/urlHelpers.d.ts.map +1 -0
- package/dist/helpers/urlHelpers.js +52 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/types.d.ts +229 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { processAuth } from './authHelpers.js';
|
|
2
|
+
import { parseMdTable } from './md-utils.js';
|
|
3
|
+
import { extractParameters } from './parameterHelpers.js';
|
|
4
|
+
import { extractRequestBody } from './requestBodyHelpers.js';
|
|
5
|
+
import { extractResponses } from './responseHelpers.js';
|
|
6
|
+
import { extractStatusCodesFromTests } from './statusCodeHelpers.js';
|
|
7
|
+
import { extractPathFromUrl, extractPathParameterNames, normalizePath, } from './urlHelpers.js';
|
|
8
|
+
/**
|
|
9
|
+
* Processes a Postman collection item or item group and returns
|
|
10
|
+
* the corresponding OpenAPI paths and components.
|
|
11
|
+
* Handles nested item groups, extracts request details, and generates corresponding
|
|
12
|
+
* OpenAPI path items and operations.
|
|
13
|
+
*/
|
|
14
|
+
export function processItem(item, parentTags = [], parentPath = '') {
|
|
15
|
+
const paths = {};
|
|
16
|
+
const components = {};
|
|
17
|
+
if ('item' in item && Array.isArray(item.item)) {
|
|
18
|
+
const newParentTags = item.name ? [...parentTags, item.name] : parentTags;
|
|
19
|
+
item.item.forEach((childItem) => {
|
|
20
|
+
const childResult = processItem(childItem, newParentTags, `${parentPath}/${item.name || ''}`);
|
|
21
|
+
// Merge child paths and components
|
|
22
|
+
for (const [pathKey, pathItem] of Object.entries(childResult.paths)) {
|
|
23
|
+
if (!paths[pathKey]) {
|
|
24
|
+
paths[pathKey] = pathItem;
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
paths[pathKey] = {
|
|
28
|
+
...paths[pathKey],
|
|
29
|
+
...pathItem,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Merge components.securitySchemes
|
|
34
|
+
if (childResult.components.securitySchemes) {
|
|
35
|
+
components.securitySchemes = {
|
|
36
|
+
...components.securitySchemes,
|
|
37
|
+
...childResult.components.securitySchemes,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
return { paths, components };
|
|
42
|
+
}
|
|
43
|
+
if (!('request' in item)) {
|
|
44
|
+
return { paths, components };
|
|
45
|
+
}
|
|
46
|
+
const { request, name, response } = item;
|
|
47
|
+
const method = (typeof request === 'string' ? 'get' : request.method || 'get').toLowerCase();
|
|
48
|
+
const path = extractPathFromUrl(typeof request === 'string'
|
|
49
|
+
? request
|
|
50
|
+
: typeof request.url === 'string'
|
|
51
|
+
? request.url
|
|
52
|
+
: (request.url?.raw ?? ''));
|
|
53
|
+
// Normalize path parameters from ':param' to '{param}'
|
|
54
|
+
const normalizedPath = normalizePath(path);
|
|
55
|
+
// Extract path parameter names
|
|
56
|
+
const pathParameterNames = extractPathParameterNames(normalizedPath);
|
|
57
|
+
// Extract operation ID if present
|
|
58
|
+
const { operationId, summary } = extractOperationInfo(name);
|
|
59
|
+
const description = typeof request === 'string'
|
|
60
|
+
? ''
|
|
61
|
+
: typeof request.description === 'string'
|
|
62
|
+
? request.description
|
|
63
|
+
: (request.description?.content ?? '');
|
|
64
|
+
const operationObject = {
|
|
65
|
+
tags: parentTags.length > 0 ? [parentTags.join(' > ')] : ['default'],
|
|
66
|
+
summary,
|
|
67
|
+
description,
|
|
68
|
+
responses: extractResponses(response || []),
|
|
69
|
+
parameters: [],
|
|
70
|
+
};
|
|
71
|
+
// Only add operationId if it was explicitly provided
|
|
72
|
+
if (operationId) {
|
|
73
|
+
operationObject.operationId = operationId;
|
|
74
|
+
}
|
|
75
|
+
// Parse parameters from the description's Markdown table
|
|
76
|
+
if (operationObject.description) {
|
|
77
|
+
const { descriptionWithoutTable, parametersFromTable } = parseParametersFromDescription(operationObject.description);
|
|
78
|
+
operationObject.description = descriptionWithoutTable.trim();
|
|
79
|
+
// Extract parameters from the request (query, path, header)
|
|
80
|
+
const extractedParameters = extractParameters(request);
|
|
81
|
+
// Merge parameters, giving priority to those from the Markdown table
|
|
82
|
+
const mergedParameters = new Map();
|
|
83
|
+
// Add extracted parameters, filtering out path parameters not in the path
|
|
84
|
+
extractedParameters.forEach((param) => {
|
|
85
|
+
if (param.name) {
|
|
86
|
+
if (param.in === 'path' && !pathParameterNames.includes(param.name)) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
mergedParameters.set(param.name, param);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
// Add parameters from table, filtering out path parameters not in the path
|
|
93
|
+
parametersFromTable.forEach((param) => {
|
|
94
|
+
if (param.name) {
|
|
95
|
+
if (param.in === 'path' && !pathParameterNames.includes(param.name)) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
mergedParameters.set(param.name, param);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
operationObject.parameters = Array.from(mergedParameters.values());
|
|
102
|
+
}
|
|
103
|
+
if (typeof request !== 'string' && request.auth) {
|
|
104
|
+
if (!operationObject.security) {
|
|
105
|
+
operationObject.security = [];
|
|
106
|
+
}
|
|
107
|
+
const { securitySchemes, security } = processAuth(request.auth);
|
|
108
|
+
if (!components.securitySchemes) {
|
|
109
|
+
components.securitySchemes = {};
|
|
110
|
+
}
|
|
111
|
+
components.securitySchemes = {
|
|
112
|
+
...components.securitySchemes,
|
|
113
|
+
...securitySchemes,
|
|
114
|
+
};
|
|
115
|
+
operationObject.security.push(...security);
|
|
116
|
+
}
|
|
117
|
+
if (['post', 'put', 'patch'].includes(method) &&
|
|
118
|
+
typeof request !== 'string' &&
|
|
119
|
+
request.body) {
|
|
120
|
+
operationObject.requestBody = extractRequestBody(request.body);
|
|
121
|
+
}
|
|
122
|
+
if (!paths[path])
|
|
123
|
+
paths[path] = {};
|
|
124
|
+
const pathItem = paths[path];
|
|
125
|
+
pathItem[method] = operationObject;
|
|
126
|
+
// Extract status codes from tests
|
|
127
|
+
const statusCodes = extractStatusCodesFromTests(item);
|
|
128
|
+
// Handle responses
|
|
129
|
+
if (statusCodes.length > 0) {
|
|
130
|
+
const responses = {};
|
|
131
|
+
statusCodes.forEach((code) => {
|
|
132
|
+
responses[code.toString()] = {
|
|
133
|
+
description: 'Successful response',
|
|
134
|
+
content: {
|
|
135
|
+
'application/json': {},
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
});
|
|
139
|
+
if (pathItem[method]) {
|
|
140
|
+
pathItem[method].responses = responses;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
else if (item.response && item.response.length > 0) {
|
|
144
|
+
const firstResponse = item.response[0];
|
|
145
|
+
const statusCode = firstResponse.code || 200;
|
|
146
|
+
if (pathItem[method]) {
|
|
147
|
+
pathItem[method].responses = {
|
|
148
|
+
[statusCode.toString()]: {
|
|
149
|
+
description: firstResponse.status || 'Successful response',
|
|
150
|
+
content: {
|
|
151
|
+
'application/json': {},
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
if (pathItem[method]) {
|
|
159
|
+
pathItem[method].responses = {
|
|
160
|
+
'200': {
|
|
161
|
+
description: 'Successful response',
|
|
162
|
+
content: {
|
|
163
|
+
'application/json': {},
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return { paths, components };
|
|
170
|
+
}
|
|
171
|
+
// Helper function to parse parameters from the description if it is markdown
|
|
172
|
+
function parseParametersFromDescription(description) {
|
|
173
|
+
const lines = description.split('\n');
|
|
174
|
+
let inTable = false;
|
|
175
|
+
const tableLines = [];
|
|
176
|
+
const descriptionLines = [];
|
|
177
|
+
for (let i = 0; i < lines.length; i++) {
|
|
178
|
+
const line = lines[i];
|
|
179
|
+
// Detect the start of the table
|
|
180
|
+
if (line.trim().startsWith('|')) {
|
|
181
|
+
// Remove any preceding headers or empty lines before the table
|
|
182
|
+
while (descriptionLines.length > 0 &&
|
|
183
|
+
(descriptionLines[descriptionLines.length - 1].trim() === '' ||
|
|
184
|
+
descriptionLines[descriptionLines.length - 1].trim().startsWith('#'))) {
|
|
185
|
+
descriptionLines.pop();
|
|
186
|
+
}
|
|
187
|
+
// Start collecting table lines
|
|
188
|
+
inTable = true;
|
|
189
|
+
}
|
|
190
|
+
if (inTable) {
|
|
191
|
+
tableLines.push(line);
|
|
192
|
+
// Detect the end of the table (any line that doesn't start with '|', excluding the alignment line)
|
|
193
|
+
if (!line.trim().startsWith('|') && !line.trim().match(/^-+$/)) {
|
|
194
|
+
inTable = false;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
descriptionLines.push(line);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
const tableMarkdown = tableLines.join('\n');
|
|
202
|
+
const parsedTable = parseMdTable(tableMarkdown);
|
|
203
|
+
const parametersFromTable = Object.values(parsedTable).map((paramData) => {
|
|
204
|
+
const paramIn = paramData.object;
|
|
205
|
+
const param = {
|
|
206
|
+
name: paramData.name,
|
|
207
|
+
in: paramIn,
|
|
208
|
+
description: paramData.description,
|
|
209
|
+
required: paramData.required === 'true',
|
|
210
|
+
schema: {
|
|
211
|
+
type: paramData.type,
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
if (paramData.example) {
|
|
215
|
+
param.example = paramData.example;
|
|
216
|
+
}
|
|
217
|
+
return param;
|
|
218
|
+
});
|
|
219
|
+
const descriptionWithoutTable = descriptionLines.join('\n');
|
|
220
|
+
return { descriptionWithoutTable, parametersFromTable };
|
|
221
|
+
}
|
|
222
|
+
// Instead of using regex with \s*, let's split this into two steps
|
|
223
|
+
function extractOperationInfo(name) {
|
|
224
|
+
if (!name)
|
|
225
|
+
return { operationId: undefined, summary: undefined };
|
|
226
|
+
// First check if the string ends with something in brackets
|
|
227
|
+
const match = name.match(/\[([^[\]]{0,1000})\]$/);
|
|
228
|
+
if (!match)
|
|
229
|
+
return { operationId: undefined, summary: name };
|
|
230
|
+
// Get the operation ID from inside brackets
|
|
231
|
+
const operationId = match[1];
|
|
232
|
+
// Trim the brackets part from the end using string operations instead of regex
|
|
233
|
+
const lastBracketIndex = name.lastIndexOf('[');
|
|
234
|
+
const summary = name.substring(0, lastBracketIndex).trim();
|
|
235
|
+
return { operationId, summary };
|
|
236
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { OpenAPIV3_1 } from '@scalar/openapi-types';
|
|
2
|
+
import type { PostmanCollection } from '../types.js';
|
|
3
|
+
type InfoResult = {
|
|
4
|
+
license?: OpenAPIV3_1.LicenseObject;
|
|
5
|
+
contact?: OpenAPIV3_1.ContactObject;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Processes the license and contact information from a Postman Collection.
|
|
9
|
+
* This function checks for license and contact related variables in the collection
|
|
10
|
+
* and creates corresponding OpenAPI License and Contact Objects if the information is present.
|
|
11
|
+
*/
|
|
12
|
+
export declare function processLicenseAndContact(collection: PostmanCollection): InfoResult;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=licenseContactHelper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"licenseContactHelper.d.ts","sourceRoot":"","sources":["../../src/helpers/licenseContactHelper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAExD,OAAO,KAAK,EAAE,iBAAiB,EAAY,MAAM,UAAU,CAAA;AAe3D,KAAK,UAAU,GAAG;IAChB,OAAO,CAAC,EAAE,WAAW,CAAC,aAAa,CAAA;IACnC,OAAO,CAAC,EAAE,WAAW,CAAC,aAAa,CAAA;CACpC,CAAA;AAmDD;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,iBAAiB,GAC5B,UAAU,CAmBZ"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Constants for variable keys
|
|
2
|
+
const VARIABLE_KEYS = {
|
|
3
|
+
LICENSE: {
|
|
4
|
+
NAME: 'license.name',
|
|
5
|
+
URL: 'license.url',
|
|
6
|
+
},
|
|
7
|
+
CONTACT: {
|
|
8
|
+
NAME: 'contact.name',
|
|
9
|
+
URL: 'contact.url',
|
|
10
|
+
EMAIL: 'contact.email',
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Finds a specific variable in the collection by its key
|
|
15
|
+
*/
|
|
16
|
+
function findVariable(collection, key) {
|
|
17
|
+
return collection.variable?.find((v) => v.key === key);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Processes license information from collection variables
|
|
21
|
+
*/
|
|
22
|
+
function processLicense(collection) {
|
|
23
|
+
const nameVar = findVariable(collection, VARIABLE_KEYS.LICENSE.NAME);
|
|
24
|
+
if (!nameVar?.value || typeof nameVar.value !== 'string')
|
|
25
|
+
return undefined;
|
|
26
|
+
const urlVar = findVariable(collection, VARIABLE_KEYS.LICENSE.URL);
|
|
27
|
+
return {
|
|
28
|
+
name: nameVar.value,
|
|
29
|
+
...(urlVar?.value &&
|
|
30
|
+
typeof urlVar.value === 'string' && { url: urlVar.value }),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Processes contact information from collection variables
|
|
35
|
+
*/
|
|
36
|
+
function processContact(collection) {
|
|
37
|
+
const nameVar = findVariable(collection, VARIABLE_KEYS.CONTACT.NAME);
|
|
38
|
+
const urlVar = findVariable(collection, VARIABLE_KEYS.CONTACT.URL);
|
|
39
|
+
const emailVar = findVariable(collection, VARIABLE_KEYS.CONTACT.EMAIL);
|
|
40
|
+
if (!nameVar?.value && !urlVar?.value && !emailVar?.value)
|
|
41
|
+
return undefined;
|
|
42
|
+
return {
|
|
43
|
+
...(nameVar?.value &&
|
|
44
|
+
typeof nameVar.value === 'string' && { name: nameVar.value }),
|
|
45
|
+
...(urlVar?.value &&
|
|
46
|
+
typeof urlVar.value === 'string' && { url: urlVar.value }),
|
|
47
|
+
...(emailVar?.value &&
|
|
48
|
+
typeof emailVar.value === 'string' && { email: emailVar.value }),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Processes the license and contact information from a Postman Collection.
|
|
53
|
+
* This function checks for license and contact related variables in the collection
|
|
54
|
+
* and creates corresponding OpenAPI License and Contact Objects if the information is present.
|
|
55
|
+
*/
|
|
56
|
+
export function processLicenseAndContact(collection) {
|
|
57
|
+
try {
|
|
58
|
+
const result = {};
|
|
59
|
+
const license = processLicense(collection);
|
|
60
|
+
if (license) {
|
|
61
|
+
result.license = license;
|
|
62
|
+
}
|
|
63
|
+
const contact = processContact(collection);
|
|
64
|
+
if (contact) {
|
|
65
|
+
result.contact = contact;
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
console.error('Error processing license and contact information:', error);
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { PostmanCollection } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Processes logo information from a Postman Collection.
|
|
4
|
+
* This function extracts logo-related variables from the collection
|
|
5
|
+
* and constructs an object with logo properties.
|
|
6
|
+
*/
|
|
7
|
+
export declare function processLogo(postmanCollection: PostmanCollection): {
|
|
8
|
+
url: string;
|
|
9
|
+
backgroundColor: string;
|
|
10
|
+
altText: string;
|
|
11
|
+
href: string;
|
|
12
|
+
} | null;
|
|
13
|
+
//# sourceMappingURL=logoHelper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logoHelper.d.ts","sourceRoot":"","sources":["../../src/helpers/logoHelper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAEjD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,iBAAiB,EAAE,iBAAiB;;;;;SAoB/D"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Processes logo information from a Postman Collection.
|
|
3
|
+
* This function extracts logo-related variables from the collection
|
|
4
|
+
* and constructs an object with logo properties.
|
|
5
|
+
*/
|
|
6
|
+
export function processLogo(postmanCollection) {
|
|
7
|
+
const logoVariables = postmanCollection.variable?.filter((v) => v.key?.startsWith('x-logo.')) ||
|
|
8
|
+
[];
|
|
9
|
+
if (logoVariables.length === 0)
|
|
10
|
+
return null;
|
|
11
|
+
const logo = {};
|
|
12
|
+
logoVariables.forEach((v) => {
|
|
13
|
+
if (v.key) {
|
|
14
|
+
const key = v.key.replace('x-logo.', '').replace('Var', '');
|
|
15
|
+
logo[key] = v.value;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
return {
|
|
19
|
+
url: logo.url,
|
|
20
|
+
backgroundColor: logo.backgroundColor,
|
|
21
|
+
altText: logo.altText,
|
|
22
|
+
href: logo.href,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"md-utils.d.ts","sourceRoot":"","sources":["../../src/helpers/md-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAa,WAAW,EAAE,MAAM,UAAU,CAAA;AAWtD;;GAEG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,WAAW,CAoCpD"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const supHeaders = [
|
|
2
|
+
'object',
|
|
3
|
+
'name',
|
|
4
|
+
'description',
|
|
5
|
+
'example',
|
|
6
|
+
'type',
|
|
7
|
+
'required',
|
|
8
|
+
];
|
|
9
|
+
/**
|
|
10
|
+
* Parses a Markdown table and returns an object representation.
|
|
11
|
+
*/
|
|
12
|
+
export function parseMdTable(md) {
|
|
13
|
+
const lines = md.split('\n').filter((line) => line.trim() !== '');
|
|
14
|
+
if (lines.length < 3)
|
|
15
|
+
return {};
|
|
16
|
+
const header = lines[0]
|
|
17
|
+
.split('|')
|
|
18
|
+
.map((cell) => cell.trim())
|
|
19
|
+
.filter(Boolean);
|
|
20
|
+
if (!header.includes('object') || !header.includes('name'))
|
|
21
|
+
return {};
|
|
22
|
+
const headers = header.map((h) => (supHeaders.includes(h) ? h : false));
|
|
23
|
+
const rows = lines.slice(2).map((line) => line
|
|
24
|
+
.split('|')
|
|
25
|
+
.map((cell) => cell.trim())
|
|
26
|
+
.filter(Boolean));
|
|
27
|
+
const tableObj = rows.reduce((accTable, cell) => {
|
|
28
|
+
const cellObj = cell.reduce((accCell, field, index) => {
|
|
29
|
+
if (headers[index] && typeof headers[index] === 'string') {
|
|
30
|
+
accCell[headers[index]] = field;
|
|
31
|
+
}
|
|
32
|
+
return accCell;
|
|
33
|
+
}, {});
|
|
34
|
+
if (cellObj.name) {
|
|
35
|
+
accTable[cellObj.name] = cellObj;
|
|
36
|
+
}
|
|
37
|
+
return accTable;
|
|
38
|
+
}, {});
|
|
39
|
+
return tableObj;
|
|
40
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { OpenAPIV3_1 } from '@scalar/openapi-types';
|
|
2
|
+
import type { Request } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Extracts parameters from a Postman request and converts them to OpenAPI parameter objects.
|
|
5
|
+
* Processes query, path, and header parameters from the request URL and headers.
|
|
6
|
+
*/
|
|
7
|
+
export declare function extractParameters(request: Request): OpenAPIV3_1.ParameterObject[];
|
|
8
|
+
/**
|
|
9
|
+
* Creates an OpenAPI parameter object from a Postman parameter.
|
|
10
|
+
*/
|
|
11
|
+
export declare function createParameterObject(param: any, paramIn: 'query' | 'path' | 'header'): OpenAPIV3_1.ParameterObject;
|
|
12
|
+
//# sourceMappingURL=parameterHelpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parameterHelpers.d.ts","sourceRoot":"","sources":["../../src/helpers/parameterHelpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAExD,OAAO,KAAK,EAAU,OAAO,EAAE,MAAM,UAAU,CAAA;AAG/C;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,OAAO,GACf,WAAW,CAAC,eAAe,EAAE,CA4D/B;AAsBD;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,GAAG,EACV,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,GACnC,WAAW,CAAC,eAAe,CAmC7B"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { inferSchemaType } from './schemaHelpers.js';
|
|
2
|
+
/**
|
|
3
|
+
* Extracts parameters from a Postman request and converts them to OpenAPI parameter objects.
|
|
4
|
+
* Processes query, path, and header parameters from the request URL and headers.
|
|
5
|
+
*/
|
|
6
|
+
export function extractParameters(request) {
|
|
7
|
+
const parameters = [];
|
|
8
|
+
const parameterMap = new Map();
|
|
9
|
+
if (typeof request === 'string' || !request.url) {
|
|
10
|
+
return parameters;
|
|
11
|
+
}
|
|
12
|
+
const url = typeof request.url === 'string' ? { raw: request.url } : request.url;
|
|
13
|
+
// Process query parameters
|
|
14
|
+
if (url.query) {
|
|
15
|
+
url.query.forEach((param) => {
|
|
16
|
+
const paramObj = createParameterObject(param, 'query');
|
|
17
|
+
if (paramObj.name) {
|
|
18
|
+
parameterMap.set(paramObj.name, paramObj);
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
// Process path parameters
|
|
23
|
+
if (url.variable) {
|
|
24
|
+
url.variable.forEach((param) => {
|
|
25
|
+
const paramObj = createParameterObject(param, 'path');
|
|
26
|
+
if (paramObj.name) {
|
|
27
|
+
parameterMap.set(paramObj.name, paramObj);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
// Include variables extracted from url.path array
|
|
32
|
+
if (url.path) {
|
|
33
|
+
const pathArray = Array.isArray(url.path) ? url.path : [url.path];
|
|
34
|
+
const extractedVariables = extractPathVariablesFromPathArray(pathArray);
|
|
35
|
+
extractedVariables.forEach((varName) => {
|
|
36
|
+
if (!parameterMap.has(varName)) {
|
|
37
|
+
parameterMap.set(varName, {
|
|
38
|
+
name: varName,
|
|
39
|
+
in: 'path',
|
|
40
|
+
required: true,
|
|
41
|
+
schema: {
|
|
42
|
+
type: 'string',
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
// Process header parameters
|
|
49
|
+
if (request.header && Array.isArray(request.header)) {
|
|
50
|
+
request.header.forEach((header) => {
|
|
51
|
+
const paramObj = createParameterObject(header, 'header');
|
|
52
|
+
if (paramObj.name) {
|
|
53
|
+
parameterMap.set(paramObj.name, paramObj);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
return Array.from(parameterMap.values());
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Helper function to extract variables from the url.path array.
|
|
61
|
+
*/
|
|
62
|
+
function extractPathVariablesFromPathArray(pathArray) {
|
|
63
|
+
const variables = [];
|
|
64
|
+
const variableRegex = /{{\s*([\w.-]+)\s*}}/;
|
|
65
|
+
pathArray.forEach((segment) => {
|
|
66
|
+
const segmentString = typeof segment === 'string' ? segment : segment.value;
|
|
67
|
+
const match = segmentString.match(variableRegex);
|
|
68
|
+
if (match?.[1]) {
|
|
69
|
+
variables.push(match[1]);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
return variables;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Creates an OpenAPI parameter object from a Postman parameter.
|
|
76
|
+
*/
|
|
77
|
+
export function createParameterObject(param, paramIn) {
|
|
78
|
+
const parameter = {
|
|
79
|
+
name: param.key || '',
|
|
80
|
+
in: paramIn,
|
|
81
|
+
description: param.description,
|
|
82
|
+
};
|
|
83
|
+
// Path parameters are always required in OpenAPI
|
|
84
|
+
if (paramIn === 'path') {
|
|
85
|
+
parameter.required = true;
|
|
86
|
+
}
|
|
87
|
+
else if (paramIn === 'query') {
|
|
88
|
+
// Check if the parameter is required based on description or name
|
|
89
|
+
const isRequired = param.description?.toLowerCase().includes('[required]') ||
|
|
90
|
+
(param.key && param.key.toLowerCase() === 'required');
|
|
91
|
+
if (isRequired) {
|
|
92
|
+
parameter.required = true;
|
|
93
|
+
// Remove '[required]' from the description
|
|
94
|
+
if (parameter.description) {
|
|
95
|
+
parameter.description = parameter.description
|
|
96
|
+
.replace(/\[required\]/gi, '')
|
|
97
|
+
.trim();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (param.value !== undefined) {
|
|
102
|
+
parameter.example = param.value;
|
|
103
|
+
parameter.schema = inferSchemaType(param.value);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
parameter.schema = { type: 'string' }; // Default to string if no value is provided
|
|
107
|
+
}
|
|
108
|
+
return parameter;
|
|
109
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { OpenAPIV3_1 } from '@scalar/openapi-types';
|
|
2
|
+
import type { RequestBody } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Extracts and converts the request body from a Postman request to an OpenAPI RequestBodyObject.
|
|
5
|
+
* Handles raw JSON, form-data, and URL-encoded body types, creating appropriate schemas and content types.
|
|
6
|
+
*/
|
|
7
|
+
export declare function extractRequestBody(body: RequestBody): OpenAPIV3_1.RequestBodyObject;
|
|
8
|
+
//# sourceMappingURL=requestBodyHelpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requestBodyHelpers.d.ts","sourceRoot":"","sources":["../../src/helpers/requestBodyHelpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAExD,OAAO,KAAK,EAAiB,WAAW,EAAuB,MAAM,UAAU,CAAA;AAI/E;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,WAAW,GAChB,WAAW,CAAC,iBAAiB,CAqB/B"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { processFormDataSchema } from './formDataHelpers.js';
|
|
2
|
+
import { createParameterObject } from './parameterHelpers.js';
|
|
3
|
+
/**
|
|
4
|
+
* Extracts and converts the request body from a Postman request to an OpenAPI RequestBodyObject.
|
|
5
|
+
* Handles raw JSON, form-data, and URL-encoded body types, creating appropriate schemas and content types.
|
|
6
|
+
*/
|
|
7
|
+
export function extractRequestBody(body) {
|
|
8
|
+
const requestBody = {
|
|
9
|
+
content: {},
|
|
10
|
+
};
|
|
11
|
+
if (body.mode === 'raw') {
|
|
12
|
+
handleRawBody(body, requestBody);
|
|
13
|
+
return requestBody;
|
|
14
|
+
}
|
|
15
|
+
if (body.mode === 'formdata' && body.formdata) {
|
|
16
|
+
handleFormDataBody(body.formdata, requestBody);
|
|
17
|
+
return requestBody;
|
|
18
|
+
}
|
|
19
|
+
if (body.mode === 'urlencoded' && body.urlencoded) {
|
|
20
|
+
handleUrlEncodedBody(body.urlencoded, requestBody);
|
|
21
|
+
return requestBody;
|
|
22
|
+
}
|
|
23
|
+
return requestBody;
|
|
24
|
+
}
|
|
25
|
+
function handleRawBody(body, requestBody) {
|
|
26
|
+
try {
|
|
27
|
+
const jsonBody = JSON.parse(body.raw || '');
|
|
28
|
+
requestBody.content = {
|
|
29
|
+
'application/json': {
|
|
30
|
+
schema: {
|
|
31
|
+
type: 'object',
|
|
32
|
+
examples: { default: { value: jsonBody } },
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
requestBody.content = {
|
|
39
|
+
'text/plain': {
|
|
40
|
+
schema: {
|
|
41
|
+
type: 'string',
|
|
42
|
+
examples: [body.raw],
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function handleFormDataBody(formdata, requestBody) {
|
|
49
|
+
requestBody.content = {
|
|
50
|
+
'multipart/form-data': {
|
|
51
|
+
schema: processFormDataSchema(formdata),
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function handleUrlEncodedBody(urlencoded, requestBody) {
|
|
56
|
+
const schema = {
|
|
57
|
+
type: 'object',
|
|
58
|
+
properties: {},
|
|
59
|
+
required: [],
|
|
60
|
+
};
|
|
61
|
+
urlencoded.forEach((item) => {
|
|
62
|
+
if (schema.properties) {
|
|
63
|
+
const paramObject = createParameterObject(item, 'query');
|
|
64
|
+
schema.properties[item.key] = {
|
|
65
|
+
type: 'string',
|
|
66
|
+
examples: [item.value],
|
|
67
|
+
description: paramObject.description,
|
|
68
|
+
};
|
|
69
|
+
if (paramObject.required) {
|
|
70
|
+
schema.required?.push(item.key);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
requestBody.content = {
|
|
75
|
+
'application/x-www-form-urlencoded': {
|
|
76
|
+
schema,
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { OpenAPIV3_1 } from '@scalar/openapi-types';
|
|
2
|
+
import type { Response } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Extracts and converts Postman response objects to OpenAPI response objects.
|
|
5
|
+
* Processes response status codes, descriptions, headers, and body content,
|
|
6
|
+
* inferring schemas from example responses when possible.
|
|
7
|
+
*/
|
|
8
|
+
export declare function extractResponses(responses: Response[]): OpenAPIV3_1.ResponsesObject;
|
|
9
|
+
//# sourceMappingURL=responseHelpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"responseHelpers.d.ts","sourceRoot":"","sources":["../../src/helpers/responseHelpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAExD,OAAO,KAAK,EAAc,QAAQ,EAAE,MAAM,UAAU,CAAA;AAGpD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,QAAQ,EAAE,GACpB,WAAW,CAAC,eAAe,CAsB7B"}
|