wrekenfile-converter 2.0.7 → 2.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/README.md +45 -26
- package/dist/example-usage.d.ts +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.js +53 -20
- package/dist/v1/index.d.ts +3 -0
- package/dist/v1/index.js +21 -0
- package/dist/{wrekenfile-validator.js → v1/wrekenfile-validator.js} +30 -8
- package/dist/v2/cli/cli-mini-wrekenfile-generator.d.ts +2 -0
- package/dist/v2/cli/cli-mini-wrekenfile-generator.js +107 -0
- package/dist/v2/cli/cli-openapi-to-wrekenfile.d.ts +2 -0
- package/dist/v2/cli/cli-openapi-to-wrekenfile.js +115 -0
- package/dist/v2/cli/cli-openapi-v2-to-wrekenfile.d.ts +2 -0
- package/dist/v2/cli/cli-openapi-v2-to-wrekenfile.js +115 -0
- package/dist/v2/cli/cli-postman-to-wrekenfile.d.ts +2 -0
- package/dist/v2/cli/cli-postman-to-wrekenfile.js +54 -0
- package/dist/v2/index.d.ts +4 -0
- package/dist/v2/index.js +25 -0
- package/dist/v2/mini-wrekenfile-generator.d.ts +13 -0
- package/dist/v2/mini-wrekenfile-generator.js +289 -0
- package/dist/v2/openapi-to-wreken.d.ts +2 -0
- package/dist/v2/openapi-to-wreken.js +829 -0
- package/dist/v2/openapi-v2-to-wrekenfile.d.ts +2 -0
- package/dist/v2/openapi-v2-to-wrekenfile.js +806 -0
- package/dist/v2/postman-to-wrekenfile.d.ts +11 -0
- package/dist/v2/postman-to-wrekenfile.js +742 -0
- package/dist/v2/utils/constants.d.ts +80 -0
- package/dist/v2/utils/constants.js +104 -0
- package/dist/v2/utils/response-utils.d.ts +11 -0
- package/dist/v2/utils/response-utils.js +39 -0
- package/dist/v2/utils/yaml-utils.d.ts +4 -0
- package/dist/v2/utils/yaml-utils.js +96 -0
- package/dist/versions.d.ts +9 -0
- package/dist/versions.js +12 -0
- package/package.json +12 -14
- package/dist/openapi-to-wrekenfile.d.ts +0 -0
- package/dist/openapi-to-wrekenfile.js +0 -10
- package/wrekenfile.md +0 -726
- /package/dist/{cli → v1/cli}/cli-mini-wrekenfile-generator.d.ts +0 -0
- /package/dist/{cli → v1/cli}/cli-mini-wrekenfile-generator.js +0 -0
- /package/dist/{cli → v1/cli}/cli-openapi-to-wrekenfile.d.ts +0 -0
- /package/dist/{cli → v1/cli}/cli-openapi-to-wrekenfile.js +0 -0
- /package/dist/{cli → v1/cli}/cli-postman-to-wrekenfile.d.ts +0 -0
- /package/dist/{cli → v1/cli}/cli-postman-to-wrekenfile.js +0 -0
- /package/dist/{mini-wrekenfile-generator.d.ts → v1/mini-wrekenfile-generator.d.ts} +0 -0
- /package/dist/{mini-wrekenfile-generator.js → v1/mini-wrekenfile-generator.js} +0 -0
- /package/dist/{openapi-to-wreken.d.ts → v1/openapi-to-wreken.d.ts} +0 -0
- /package/dist/{openapi-to-wreken.js → v1/openapi-to-wreken.js} +0 -0
- /package/dist/{openapi-v2-to-wrekenfile.d.ts → v1/openapi-v2-to-wrekenfile.d.ts} +0 -0
- /package/dist/{openapi-v2-to-wrekenfile.js → v1/openapi-v2-to-wrekenfile.js} +0 -0
- /package/dist/{postman-to-wrekenfile.d.ts → v1/postman-to-wrekenfile.d.ts} +0 -0
- /package/dist/{postman-to-wrekenfile.js → v1/postman-to-wrekenfile.js} +0 -0
- /package/dist/{wrekenfile-validator.d.ts → v1/wrekenfile-validator.d.ts} +0 -0
|
@@ -0,0 +1,742 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
36
|
+
var t = {};
|
|
37
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
38
|
+
t[p] = s[p];
|
|
39
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
40
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
41
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
42
|
+
t[p[i]] = s[p[i]];
|
|
43
|
+
}
|
|
44
|
+
return t;
|
|
45
|
+
};
|
|
46
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
+
exports.generateWrekenfile = generateWrekenfile;
|
|
48
|
+
exports.extractStructs = extractStructs;
|
|
49
|
+
exports.extractOperations = extractOperations;
|
|
50
|
+
exports.mapType = mapType;
|
|
51
|
+
exports.parseJsonExample = parseJsonExample;
|
|
52
|
+
exports.extractFieldsFromObject = extractFieldsFromObject;
|
|
53
|
+
exports.loadEnvironmentFile = loadEnvironmentFile;
|
|
54
|
+
exports.extractCollectionVariables = extractCollectionVariables;
|
|
55
|
+
exports.resolveVariables = resolveVariables;
|
|
56
|
+
// postman-to-wrekenfile.ts
|
|
57
|
+
// Converts Postman collections to Wrekenfile v2.0.1 format
|
|
58
|
+
const fs = __importStar(require("fs"));
|
|
59
|
+
const js_yaml_1 = require("js-yaml");
|
|
60
|
+
const yaml_utils_1 = require("./utils/yaml-utils");
|
|
61
|
+
const constants_1 = require("./utils/constants");
|
|
62
|
+
const response_utils_1 = require("./utils/response-utils");
|
|
63
|
+
function mapType(value) {
|
|
64
|
+
if (typeof value === 'string') {
|
|
65
|
+
// Check for common patterns
|
|
66
|
+
if (/^\d{4}-\d{2}-\d{2}/.test(value))
|
|
67
|
+
return 'DATE';
|
|
68
|
+
if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value))
|
|
69
|
+
return 'STRING'; // UUID as string
|
|
70
|
+
if (/^\d+$/.test(value))
|
|
71
|
+
return 'INT';
|
|
72
|
+
if (/^\d+\.\d+$/.test(value))
|
|
73
|
+
return 'FLOAT';
|
|
74
|
+
if (value === 'true' || value === 'false')
|
|
75
|
+
return 'BOOL';
|
|
76
|
+
return 'STRING';
|
|
77
|
+
}
|
|
78
|
+
if (typeof value === 'number') {
|
|
79
|
+
return Number.isInteger(value) ? 'INT' : 'FLOAT';
|
|
80
|
+
}
|
|
81
|
+
if (typeof value === 'boolean')
|
|
82
|
+
return 'BOOL';
|
|
83
|
+
if (Array.isArray(value))
|
|
84
|
+
return 'ANY'; // Arrays will be handled specially
|
|
85
|
+
if (value === null || value === undefined)
|
|
86
|
+
return 'ANY';
|
|
87
|
+
return 'ANY';
|
|
88
|
+
}
|
|
89
|
+
function getItemDescription(item) {
|
|
90
|
+
var _a, _b, _c, _d, _e;
|
|
91
|
+
let description = (_c = (_a = item === null || item === void 0 ? void 0 : item.description) !== null && _a !== void 0 ? _a : (_b = item === null || item === void 0 ? void 0 : item.request) === null || _b === void 0 ? void 0 : _b.description) !== null && _c !== void 0 ? _c : (_e = (_d = item === null || item === void 0 ? void 0 : item.request) === null || _d === void 0 ? void 0 : _d.body) === null || _e === void 0 ? void 0 : _e.description;
|
|
92
|
+
if (!description)
|
|
93
|
+
return '';
|
|
94
|
+
// Postman can store description as an object with { content: string }
|
|
95
|
+
if (typeof description === 'object' && typeof description.content === 'string') {
|
|
96
|
+
description = description.content;
|
|
97
|
+
}
|
|
98
|
+
if (typeof description !== 'string')
|
|
99
|
+
return '';
|
|
100
|
+
return description.replace(/<[^>]*>/g, '').replace(/\n+/g, ' ').trim();
|
|
101
|
+
}
|
|
102
|
+
function generateSummary(item, method, path) {
|
|
103
|
+
const cleaned = getItemDescription(item);
|
|
104
|
+
if (cleaned) {
|
|
105
|
+
// Use first sentence as summary
|
|
106
|
+
const firstSentence = cleaned.split(/[.!?]\s/)[0];
|
|
107
|
+
return firstSentence || cleaned.substring(0, 100);
|
|
108
|
+
}
|
|
109
|
+
const verb = {
|
|
110
|
+
get: 'Fetch',
|
|
111
|
+
post: 'Create',
|
|
112
|
+
put: 'Update',
|
|
113
|
+
delete: 'Delete',
|
|
114
|
+
patch: 'Modify',
|
|
115
|
+
}[method.toLowerCase()] || 'Call';
|
|
116
|
+
const entity = path.split('/').filter(p => p && !p.startsWith('{')).pop() || 'resource';
|
|
117
|
+
return `${verb} ${entity}`;
|
|
118
|
+
}
|
|
119
|
+
function generateStructName(itemName, method, path, suffix) {
|
|
120
|
+
const cleanName = itemName.replace(/[^a-zA-Z0-9]/g, '');
|
|
121
|
+
const cleanPath = path.replace(/[\/{}]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');
|
|
122
|
+
return `${method.toLowerCase()}-${cleanPath}-${suffix}`;
|
|
123
|
+
}
|
|
124
|
+
function parseJsonExample(jsonStr) {
|
|
125
|
+
try {
|
|
126
|
+
return JSON.parse(jsonStr);
|
|
127
|
+
}
|
|
128
|
+
catch (_a) {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function extractFieldsFromObject(obj, depth = 0, prefix = '') {
|
|
133
|
+
if (depth > 3)
|
|
134
|
+
return [];
|
|
135
|
+
if (!obj)
|
|
136
|
+
return [];
|
|
137
|
+
// If the root object is an array, extract fields from the first object
|
|
138
|
+
if (Array.isArray(obj) && obj.length > 0 && typeof obj[0] === 'object' && obj[0] !== null) {
|
|
139
|
+
return extractFieldsFromObject(obj[0], depth + 1, prefix);
|
|
140
|
+
}
|
|
141
|
+
if (Array.isArray(obj))
|
|
142
|
+
return [];
|
|
143
|
+
if (typeof obj !== 'object')
|
|
144
|
+
return [];
|
|
145
|
+
const fields = [];
|
|
146
|
+
const keyCount = {};
|
|
147
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
148
|
+
let type = 'ANY';
|
|
149
|
+
let required = false;
|
|
150
|
+
// Handle duplicate keys
|
|
151
|
+
let fieldName = key;
|
|
152
|
+
if (keyCount[key] === undefined) {
|
|
153
|
+
keyCount[key] = 1;
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
keyCount[key] += 1;
|
|
157
|
+
fieldName = `${key} ${keyCount[key]}`;
|
|
158
|
+
}
|
|
159
|
+
if (Array.isArray(value)) {
|
|
160
|
+
if (value.length > 0) {
|
|
161
|
+
const firstItem = value[0];
|
|
162
|
+
if (typeof firstItem === 'object' && firstItem !== null) {
|
|
163
|
+
type = `[]STRUCT(${prefix}${fieldName}Item)`;
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
type = `[]${mapType(firstItem)}`;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
type = '[]ANY';
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
else if (typeof value === 'object' && value !== null) {
|
|
174
|
+
type = `STRUCT(${prefix}${fieldName})`;
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
type = mapType(value);
|
|
178
|
+
}
|
|
179
|
+
fields.push({
|
|
180
|
+
name: fieldName,
|
|
181
|
+
type,
|
|
182
|
+
REQUIRED: required,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return fields;
|
|
186
|
+
}
|
|
187
|
+
function loadEnvironmentFile(envPath) {
|
|
188
|
+
try {
|
|
189
|
+
const envData = fs.readFileSync(envPath, 'utf8');
|
|
190
|
+
const env = JSON.parse(envData);
|
|
191
|
+
const variables = {};
|
|
192
|
+
if (env.values) {
|
|
193
|
+
for (const variable of env.values) {
|
|
194
|
+
if (variable.key && variable.value) {
|
|
195
|
+
variables[variable.key] = variable.value;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return variables;
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
return {};
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
function extractCollectionVariables(collection) {
|
|
206
|
+
const variables = {};
|
|
207
|
+
// Extract collection-level variables
|
|
208
|
+
if (collection.variable) {
|
|
209
|
+
for (const variable of collection.variable) {
|
|
210
|
+
if (variable.key && variable.value) {
|
|
211
|
+
variables[variable.key] = variable.value;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return variables;
|
|
216
|
+
}
|
|
217
|
+
function resolveVariables(value, variables) {
|
|
218
|
+
if (typeof value !== 'string')
|
|
219
|
+
return value;
|
|
220
|
+
// Replace {{variable}} patterns with actual values
|
|
221
|
+
return value.replace(/\{\{([^}]+)\}\}/g, (match, varName) => {
|
|
222
|
+
return variables[varName] || match;
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
function extractStructs(collection, variables) {
|
|
226
|
+
const structs = {};
|
|
227
|
+
const structNameCount = {};
|
|
228
|
+
function getUniqueStructName(name) {
|
|
229
|
+
if (structs[name] === undefined) {
|
|
230
|
+
structNameCount[name] = 1;
|
|
231
|
+
return name;
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
structNameCount[name] = (structNameCount[name] || 1) + 1;
|
|
235
|
+
return `${name} ${structNameCount[name]}`;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
function processItem(item) {
|
|
239
|
+
var _a;
|
|
240
|
+
if (item.request) {
|
|
241
|
+
const method = item.request.method || 'GET';
|
|
242
|
+
const url = item.request.url;
|
|
243
|
+
const path = extractPathFromUrl(url, variables);
|
|
244
|
+
const itemName = item.name || 'unknown';
|
|
245
|
+
// Extract request body structs
|
|
246
|
+
if (((_a = item.request.body) === null || _a === void 0 ? void 0 : _a.mode) === 'raw' && item.request.body.raw) {
|
|
247
|
+
const bodyData = parseJsonExample(item.request.body.raw);
|
|
248
|
+
if (bodyData) {
|
|
249
|
+
let requestStructName = generateStructName(itemName, method, path, 'Request');
|
|
250
|
+
requestStructName = getUniqueStructName(requestStructName);
|
|
251
|
+
const fields = extractFieldsFromObject(bodyData, 0, requestStructName);
|
|
252
|
+
if (fields.length > 0) {
|
|
253
|
+
structs[requestStructName] = fields;
|
|
254
|
+
}
|
|
255
|
+
extractNestedStructs(bodyData, structs, requestStructName);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
// Extract response structs from examples
|
|
259
|
+
if (item.response) {
|
|
260
|
+
for (const response of item.response) {
|
|
261
|
+
let responseStructName = generateStructName(itemName, method, path, `Response${response.code || '200'}`);
|
|
262
|
+
responseStructName = getUniqueStructName(responseStructName);
|
|
263
|
+
if (response.body) {
|
|
264
|
+
const responseData = parseJsonExample(response.body);
|
|
265
|
+
if (responseData) {
|
|
266
|
+
const fields = extractFieldsFromObject(responseData, 0, responseStructName);
|
|
267
|
+
if (fields.length > 0) {
|
|
268
|
+
structs[responseStructName] = fields;
|
|
269
|
+
}
|
|
270
|
+
extractNestedStructs(responseData, structs, responseStructName);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
if (item.item) {
|
|
277
|
+
for (const subItem of item.item) {
|
|
278
|
+
processItem(subItem);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
for (const item of collection.item) {
|
|
283
|
+
processItem(item);
|
|
284
|
+
}
|
|
285
|
+
return structs;
|
|
286
|
+
}
|
|
287
|
+
function extractNestedStructs(obj, structs, prefix = '') {
|
|
288
|
+
if (!obj || typeof obj !== 'object' || Array.isArray(obj))
|
|
289
|
+
return;
|
|
290
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
291
|
+
if (Array.isArray(value) && value.length > 0) {
|
|
292
|
+
const firstItem = value[0];
|
|
293
|
+
if (typeof firstItem === 'object' && firstItem !== null) {
|
|
294
|
+
const structName = `${prefix}${key}Item`;
|
|
295
|
+
const fields = extractFieldsFromObject(firstItem, 0, structName);
|
|
296
|
+
if (fields.length > 0) {
|
|
297
|
+
structs[structName] = fields;
|
|
298
|
+
}
|
|
299
|
+
extractNestedStructs(firstItem, structs, `${prefix}${key}Item`);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
else if (typeof value === 'object' && value !== null) {
|
|
303
|
+
const structName = `${prefix}${key}`;
|
|
304
|
+
const fields = extractFieldsFromObject(value, 0, structName);
|
|
305
|
+
if (fields.length > 0) {
|
|
306
|
+
structs[structName] = fields;
|
|
307
|
+
}
|
|
308
|
+
extractNestedStructs(value, structs, `${prefix}${key}`);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
function extractPathFromUrl(url, variables) {
|
|
313
|
+
if (url === null || url === void 0 ? void 0 : url.raw) {
|
|
314
|
+
// Remove base URL and protocol
|
|
315
|
+
let path = url.raw;
|
|
316
|
+
path = resolveVariables(path, variables); // Resolve variables first
|
|
317
|
+
path = path.replace(/^https?:\/\/[^\/]+/, ''); // Remove protocol and host
|
|
318
|
+
path = path.replace(/\{\{.*?\}\}/g, ''); // Remove any remaining Postman variables
|
|
319
|
+
path = path.replace(/\/+/g, '/'); // Normalize slashes
|
|
320
|
+
path = path.replace(/^\/|\/$/g, ''); // Remove leading/trailing slashes
|
|
321
|
+
return path;
|
|
322
|
+
}
|
|
323
|
+
if (url === null || url === void 0 ? void 0 : url.path) {
|
|
324
|
+
const resolvedPath = url.path.map((segment) => resolveVariables(segment, variables));
|
|
325
|
+
return resolvedPath.join('/');
|
|
326
|
+
}
|
|
327
|
+
return '';
|
|
328
|
+
}
|
|
329
|
+
function getContentTypeAndBodyType(request) {
|
|
330
|
+
const headers = request.header || [];
|
|
331
|
+
const contentTypeHeader = headers.find((h) => { var _a; return ((_a = h.key) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === 'content-type'; });
|
|
332
|
+
let contentType = 'application/json';
|
|
333
|
+
if (contentTypeHeader) {
|
|
334
|
+
contentType = contentTypeHeader.value || 'application/json';
|
|
335
|
+
}
|
|
336
|
+
let bodyType = 'raw';
|
|
337
|
+
if (contentType === 'multipart/form-data') {
|
|
338
|
+
bodyType = 'form-data';
|
|
339
|
+
}
|
|
340
|
+
else if (contentType === 'application/x-www-form-urlencoded') {
|
|
341
|
+
bodyType = 'x-www-form-urlencoded';
|
|
342
|
+
}
|
|
343
|
+
return { contentType, bodyType };
|
|
344
|
+
}
|
|
345
|
+
function getHeadersForOperation(request, variables) {
|
|
346
|
+
var _a;
|
|
347
|
+
const { contentType } = getContentTypeAndBodyType(request);
|
|
348
|
+
const headerMap = new Map();
|
|
349
|
+
// Add Content-Type header for POST/PUT/PATCH requests
|
|
350
|
+
if (['post', 'put', 'patch'].includes(((_a = request.method) === null || _a === void 0 ? void 0 : _a.toLowerCase()) || '')) {
|
|
351
|
+
headerMap.set('Content-Type', contentType);
|
|
352
|
+
}
|
|
353
|
+
// Add authentication headers
|
|
354
|
+
const authHeaders = request.header || [];
|
|
355
|
+
for (const header of authHeaders) {
|
|
356
|
+
if (header.key && header.value) {
|
|
357
|
+
const key = header.key.toLowerCase();
|
|
358
|
+
if (key === 'x-api-key' || key === 'authorization' || key === 'x-signature') {
|
|
359
|
+
let value = resolveVariables(header.value, variables);
|
|
360
|
+
if (key === 'x-api-key')
|
|
361
|
+
value = 'api_key';
|
|
362
|
+
else if (key === 'authorization')
|
|
363
|
+
value = 'bearer_token';
|
|
364
|
+
else if (key === 'x-signature')
|
|
365
|
+
value = 'signature';
|
|
366
|
+
headerMap.set(header.key, value);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
// Convert Map to object
|
|
371
|
+
const headers = {};
|
|
372
|
+
for (const [key, value] of headerMap.entries()) {
|
|
373
|
+
headers[key] = value;
|
|
374
|
+
}
|
|
375
|
+
return headers;
|
|
376
|
+
}
|
|
377
|
+
function extractParameters(request, variables) {
|
|
378
|
+
const inputParams = [];
|
|
379
|
+
// Extract URL parameters
|
|
380
|
+
const url = request.url;
|
|
381
|
+
if (url === null || url === void 0 ? void 0 : url.variable) {
|
|
382
|
+
for (const variable of url.variable) {
|
|
383
|
+
const isRequired = !variable.disabled;
|
|
384
|
+
const inputParam = {};
|
|
385
|
+
if (isRequired) {
|
|
386
|
+
// Simple form
|
|
387
|
+
inputParam[variable.key] = 'STRING';
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
// Extended form
|
|
391
|
+
inputParam[variable.key] = {
|
|
392
|
+
TYPE: 'STRING',
|
|
393
|
+
REQUIRED: false,
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
inputParams.push(inputParam);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
// Extract query parameters
|
|
400
|
+
if (url === null || url === void 0 ? void 0 : url.query) {
|
|
401
|
+
for (const query of url.query) {
|
|
402
|
+
const isRequired = !query.disabled;
|
|
403
|
+
const inputParam = {};
|
|
404
|
+
if (isRequired) {
|
|
405
|
+
// Simple form
|
|
406
|
+
inputParam[query.key] = 'STRING';
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
// Extended form
|
|
410
|
+
inputParam[query.key] = {
|
|
411
|
+
TYPE: 'STRING',
|
|
412
|
+
REQUIRED: false,
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
inputParams.push(inputParam);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return inputParams;
|
|
419
|
+
}
|
|
420
|
+
function extractRequestBody(request, itemName, method, path) {
|
|
421
|
+
var _a, _b, _c;
|
|
422
|
+
const inputParams = [];
|
|
423
|
+
if (((_a = request.body) === null || _a === void 0 ? void 0 : _a.mode) === 'raw' && request.body.raw) {
|
|
424
|
+
const bodyData = parseJsonExample(request.body.raw);
|
|
425
|
+
if (bodyData) {
|
|
426
|
+
const requestStructName = generateStructName(itemName, method, path, 'Request');
|
|
427
|
+
const inputParam = {};
|
|
428
|
+
inputParam.body = {
|
|
429
|
+
TYPE: `STRUCT(${requestStructName})`,
|
|
430
|
+
REQUIRED: true,
|
|
431
|
+
};
|
|
432
|
+
inputParams.push(inputParam);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
// Handle form-data and urlencoded
|
|
436
|
+
if (((_b = request.body) === null || _b === void 0 ? void 0 : _b.mode) === 'formdata' || ((_c = request.body) === null || _c === void 0 ? void 0 : _c.mode) === 'urlencoded') {
|
|
437
|
+
const formData = request.body.formdata || request.body.urlencoded || [];
|
|
438
|
+
for (const field of formData) {
|
|
439
|
+
const type = field.type === 'file' ? 'STRING' : 'STRING';
|
|
440
|
+
const isRequired = !field.disabled;
|
|
441
|
+
const inputParam = {};
|
|
442
|
+
if (isRequired) {
|
|
443
|
+
inputParam[field.key] = type;
|
|
444
|
+
}
|
|
445
|
+
else {
|
|
446
|
+
inputParam[field.key] = {
|
|
447
|
+
TYPE: type,
|
|
448
|
+
REQUIRED: false,
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
inputParams.push(inputParam);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
return inputParams;
|
|
455
|
+
}
|
|
456
|
+
function extractResponses(item, itemName, method, path) {
|
|
457
|
+
var _a;
|
|
458
|
+
const returns = [];
|
|
459
|
+
const seenCodes = new Set();
|
|
460
|
+
if (item.response) {
|
|
461
|
+
for (const response of item.response) {
|
|
462
|
+
const code = ((_a = response.code) === null || _a === void 0 ? void 0 : _a.toString()) || '200';
|
|
463
|
+
const statusCode = parseInt(code);
|
|
464
|
+
// Only include success responses (2xx) in RETURNS section
|
|
465
|
+
// Error responses go in ERRORS section
|
|
466
|
+
if (isNaN(statusCode) || statusCode < 200 || statusCode >= 300) {
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
469
|
+
// Avoid duplicate response codes
|
|
470
|
+
if (seenCodes.has(code))
|
|
471
|
+
continue;
|
|
472
|
+
seenCodes.add(code);
|
|
473
|
+
// Skip 204 No Content
|
|
474
|
+
if (code === '204')
|
|
475
|
+
continue;
|
|
476
|
+
let returnType = 'ANY';
|
|
477
|
+
if (response.body) {
|
|
478
|
+
const responseData = parseJsonExample(response.body);
|
|
479
|
+
if (responseData) {
|
|
480
|
+
const responseStructName = generateStructName(itemName, method, path, `Response${code}`);
|
|
481
|
+
returnType = `STRUCT(${responseStructName})`;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
// Only add if there's actually a return type
|
|
485
|
+
if (returnType !== constants_1.TYPE_VOID) {
|
|
486
|
+
// Generate descriptive RETURNVAR name based on response code and operation
|
|
487
|
+
// Clean itemName: replace spaces and special chars with underscores, convert to lowercase
|
|
488
|
+
const cleanItemName = itemName
|
|
489
|
+
.replace(/[^a-zA-Z0-9]/g, '_')
|
|
490
|
+
.replace(/_+/g, '_')
|
|
491
|
+
.replace(/^_|_$/g, '')
|
|
492
|
+
.toLowerCase();
|
|
493
|
+
const returnVarName = (0, response_utils_1.generateReturnVarName)(cleanItemName, code);
|
|
494
|
+
returns.push({
|
|
495
|
+
RETURNTYPE: returnType,
|
|
496
|
+
RETURNVAR: returnVarName,
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
return returns;
|
|
502
|
+
}
|
|
503
|
+
function extractErrors(item, itemName, method, path) {
|
|
504
|
+
var _a;
|
|
505
|
+
const errors = [];
|
|
506
|
+
if (item.response) {
|
|
507
|
+
for (const response of item.response) {
|
|
508
|
+
const code = parseInt(((_a = response.code) === null || _a === void 0 ? void 0 : _a.toString()) || '200');
|
|
509
|
+
if (isNaN(code) || code < 400)
|
|
510
|
+
continue;
|
|
511
|
+
let errorType = 'ANY';
|
|
512
|
+
let when = `HTTP ${code}`;
|
|
513
|
+
if (response.body) {
|
|
514
|
+
const responseData = parseJsonExample(response.body);
|
|
515
|
+
if (responseData) {
|
|
516
|
+
const errorStructName = `Error${code}`;
|
|
517
|
+
errorType = `STRUCT(${errorStructName})`;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
when = (0, response_utils_1.generateErrorWhen)(response, code.toString());
|
|
521
|
+
errors.push({
|
|
522
|
+
TYPE: errorType,
|
|
523
|
+
WHEN: when,
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
return errors;
|
|
528
|
+
}
|
|
529
|
+
function extractOperations(collection, variables) {
|
|
530
|
+
const operations = [];
|
|
531
|
+
const operationNameCount = {};
|
|
532
|
+
function getUniqueOperationName(name) {
|
|
533
|
+
if (operationNameCount[name] === undefined) {
|
|
534
|
+
operationNameCount[name] = 1;
|
|
535
|
+
return name;
|
|
536
|
+
}
|
|
537
|
+
else {
|
|
538
|
+
operationNameCount[name] += 1;
|
|
539
|
+
return `${name} ${operationNameCount[name]}`;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
function processItem(item, parentName = null) {
|
|
543
|
+
if (item.request) {
|
|
544
|
+
const method = item.request.method || 'GET';
|
|
545
|
+
const url = item.request.url;
|
|
546
|
+
const path = extractPathFromUrl(url, variables);
|
|
547
|
+
const itemName = item.name || 'unknown';
|
|
548
|
+
const immediateParentName = parentName || null;
|
|
549
|
+
// Generate operation ID (alias)
|
|
550
|
+
let operationId = itemName.toLowerCase().replace(/[^a-z0-9]/g, '-');
|
|
551
|
+
operationId = getUniqueOperationName(operationId);
|
|
552
|
+
const summary = generateSummary(item, method, path);
|
|
553
|
+
const { bodyType } = getContentTypeAndBodyType(item.request);
|
|
554
|
+
const headers = getHeadersForOperation(item.request, variables);
|
|
555
|
+
const inputs = extractParameters(item.request, variables);
|
|
556
|
+
const bodyInputs = extractRequestBody(item.request, itemName, method, path);
|
|
557
|
+
const returns = extractResponses(item, itemName, method, path);
|
|
558
|
+
const errors = extractErrors(item, itemName, method, path);
|
|
559
|
+
const allInputs = [...inputs];
|
|
560
|
+
if (bodyInputs.length > 0) {
|
|
561
|
+
allInputs.push(...bodyInputs);
|
|
562
|
+
}
|
|
563
|
+
// Build method in v2.0.1 format
|
|
564
|
+
const methodDef = {
|
|
565
|
+
SUMMARY: summary,
|
|
566
|
+
};
|
|
567
|
+
// Add DESC if description exists
|
|
568
|
+
const desc = getItemDescription(item);
|
|
569
|
+
if (desc) {
|
|
570
|
+
methodDef.DESC = desc;
|
|
571
|
+
}
|
|
572
|
+
// HTTP section (mandatory for API methods)
|
|
573
|
+
methodDef.HTTP = {
|
|
574
|
+
METHOD: method.toUpperCase(),
|
|
575
|
+
ENDPOINT: `/${path}`,
|
|
576
|
+
HEADERS: headers,
|
|
577
|
+
};
|
|
578
|
+
if (bodyType !== constants_1.BODYTYPE_RAW) {
|
|
579
|
+
methodDef.HTTP.BODYTYPE = bodyType;
|
|
580
|
+
}
|
|
581
|
+
// EXECUTION section (mandatory)
|
|
582
|
+
methodDef.EXECUTION = {
|
|
583
|
+
MODE: constants_1.EXECUTION_MODE_ASYNC, // HTTP methods default to async
|
|
584
|
+
};
|
|
585
|
+
// ASYNC section (required when MODE = async)
|
|
586
|
+
const resultType = returns.length > 0 ? returns[0].RETURNTYPE : constants_1.TYPE_VOID;
|
|
587
|
+
methodDef.ASYNC = {
|
|
588
|
+
RETURNS: constants_1.ASYNC_RETURNS_RESULT,
|
|
589
|
+
RESULT: {
|
|
590
|
+
TYPE: resultType,
|
|
591
|
+
},
|
|
592
|
+
};
|
|
593
|
+
// INPUTS section (optional)
|
|
594
|
+
if (allInputs.length > 0) {
|
|
595
|
+
methodDef.INPUTS = allInputs;
|
|
596
|
+
}
|
|
597
|
+
// RETURNS section (optional - omit for void)
|
|
598
|
+
if (returns.length > 0) {
|
|
599
|
+
methodDef.RETURNS = returns;
|
|
600
|
+
}
|
|
601
|
+
// ERRORS section (optional)
|
|
602
|
+
if (errors.length > 0) {
|
|
603
|
+
methodDef.ERRORS = errors;
|
|
604
|
+
}
|
|
605
|
+
operations.push(Object.assign({ name: operationId }, methodDef));
|
|
606
|
+
}
|
|
607
|
+
if (item.item) {
|
|
608
|
+
for (const subItem of item.item) {
|
|
609
|
+
processItem(subItem, item.name || parentName || null);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
for (const item of collection.item) {
|
|
614
|
+
processItem(item, null);
|
|
615
|
+
}
|
|
616
|
+
return operations;
|
|
617
|
+
}
|
|
618
|
+
function extractBaseUrl(collection, variables) {
|
|
619
|
+
// First, try to get from collection variables (merged with passed variables)
|
|
620
|
+
const collectionVars = extractCollectionVariables(collection);
|
|
621
|
+
const allVariables = Object.assign(Object.assign({}, collectionVars), variables);
|
|
622
|
+
// Check for common base URL variable names
|
|
623
|
+
for (const varName of constants_1.BASE_URL_VARIABLE_NAMES) {
|
|
624
|
+
if (allVariables[varName]) {
|
|
625
|
+
let baseUrl = allVariables[varName];
|
|
626
|
+
// Remove trailing slash
|
|
627
|
+
baseUrl = baseUrl.replace(/\/$/, '');
|
|
628
|
+
// If it's a variable placeholder, skip it
|
|
629
|
+
if (!baseUrl.startsWith('{{')) {
|
|
630
|
+
return baseUrl;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
// Try to extract from first request URL
|
|
635
|
+
function findFirstRequestUrl(item) {
|
|
636
|
+
var _a;
|
|
637
|
+
if ((_a = item.request) === null || _a === void 0 ? void 0 : _a.url) {
|
|
638
|
+
const url = item.request.url;
|
|
639
|
+
if (url.raw) {
|
|
640
|
+
// Extract base URL from raw URL
|
|
641
|
+
// Handle cases like "{{url}}/api/v1/endpoint" or "https://api.example.com/api/v1/endpoint"
|
|
642
|
+
let rawUrl = url.raw;
|
|
643
|
+
// Try to resolve variables first
|
|
644
|
+
rawUrl = resolveVariables(rawUrl, allVariables);
|
|
645
|
+
// Extract base URL (protocol + host)
|
|
646
|
+
const match = rawUrl.match(/^(https?:\/\/[^\/\s]+)/);
|
|
647
|
+
if (match) {
|
|
648
|
+
return match[1];
|
|
649
|
+
}
|
|
650
|
+
// If still has variables, try to extract from host array
|
|
651
|
+
if (url.host && Array.isArray(url.host) && url.host.length > 0) {
|
|
652
|
+
const host = url.host[0];
|
|
653
|
+
const resolvedHost = resolveVariables(host, allVariables);
|
|
654
|
+
// If host is resolved and not a variable placeholder
|
|
655
|
+
if (resolvedHost && !resolvedHost.startsWith('{{') && !resolvedHost.includes('{{')) {
|
|
656
|
+
const protocol = (url.protocol && !url.protocol.startsWith('{{'))
|
|
657
|
+
? url.protocol.replace(':', '')
|
|
658
|
+
: 'https';
|
|
659
|
+
return `${protocol}://${resolvedHost}`;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
if (item.item && Array.isArray(item.item)) {
|
|
665
|
+
for (const subItem of item.item) {
|
|
666
|
+
const found = findFirstRequestUrl(subItem);
|
|
667
|
+
if (found)
|
|
668
|
+
return found;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
return null;
|
|
672
|
+
}
|
|
673
|
+
if (collection.item && Array.isArray(collection.item)) {
|
|
674
|
+
for (const item of collection.item) {
|
|
675
|
+
const baseUrl = findFirstRequestUrl(item);
|
|
676
|
+
if (baseUrl) {
|
|
677
|
+
return baseUrl.replace(/\/$/, '');
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
// Default fallback
|
|
682
|
+
return constants_1.DEFAULT_BASE_URL;
|
|
683
|
+
}
|
|
684
|
+
function generateWrekenfile(collection, variables) {
|
|
685
|
+
if (!collection || typeof collection !== 'object') {
|
|
686
|
+
throw new Error("Argument 'collection' is required and must be an object");
|
|
687
|
+
}
|
|
688
|
+
if (!variables || typeof variables !== 'object') {
|
|
689
|
+
throw new Error("Argument 'variables' is required and must be an object");
|
|
690
|
+
}
|
|
691
|
+
// Extract base URL from collection
|
|
692
|
+
const baseUrl = extractBaseUrl(collection, variables);
|
|
693
|
+
// Merge collection variables with passed variables
|
|
694
|
+
const collectionVars = extractCollectionVariables(collection);
|
|
695
|
+
const allVariables = Object.assign(Object.assign({}, collectionVars), variables);
|
|
696
|
+
const structs = extractStructs(collection, allVariables);
|
|
697
|
+
const operations = extractOperations(collection, allVariables);
|
|
698
|
+
const wrekenfile = {
|
|
699
|
+
VERSION: constants_1.WREKENFILE_VERSION,
|
|
700
|
+
};
|
|
701
|
+
// Add DEFAULTS if we have any
|
|
702
|
+
const defaults = {};
|
|
703
|
+
// Add base URL first
|
|
704
|
+
defaults.w_base_url = baseUrl;
|
|
705
|
+
// Add other variables
|
|
706
|
+
if (Object.keys(allVariables).length > 0) {
|
|
707
|
+
for (const [key, value] of Object.entries(allVariables)) {
|
|
708
|
+
// Skip base URL variables as we've already added w_base_url
|
|
709
|
+
if (constants_1.BASE_URL_VARIABLE_NAMES.includes(key)) {
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
const isSensitive = constants_1.SENSITIVE_KEYS.some(sensitiveKey => key.toLowerCase().includes(sensitiveKey));
|
|
713
|
+
if (isSensitive) {
|
|
714
|
+
defaults[key] = `{{${key}}}`;
|
|
715
|
+
}
|
|
716
|
+
else {
|
|
717
|
+
defaults[key] = value;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
if (Object.keys(defaults).length > 0) {
|
|
722
|
+
wrekenfile.DEFAULTS = defaults;
|
|
723
|
+
}
|
|
724
|
+
// Add METHODS (mandatory)
|
|
725
|
+
const methods = {};
|
|
726
|
+
for (const operation of operations) {
|
|
727
|
+
const { name } = operation, methodDef = __rest(operation, ["name"]);
|
|
728
|
+
methods[name] = methodDef;
|
|
729
|
+
}
|
|
730
|
+
wrekenfile.METHODS = methods;
|
|
731
|
+
// Add STRUCTS if we have any
|
|
732
|
+
if (Object.keys(structs).length > 0) {
|
|
733
|
+
wrekenfile.STRUCTS = structs;
|
|
734
|
+
}
|
|
735
|
+
let yamlString = (0, js_yaml_1.dump)(wrekenfile, constants_1.YAML_DUMP_OPTIONS);
|
|
736
|
+
// Post-process to remove quotes from type strings
|
|
737
|
+
yamlString = (0, yaml_utils_1.removeTypeQuotes)(yamlString);
|
|
738
|
+
yamlString = (0, yaml_utils_1.cleanYaml)(yamlString);
|
|
739
|
+
(0, yaml_utils_1.checkYamlForHiddenChars)(yamlString);
|
|
740
|
+
(0, yaml_utils_1.validateYaml)(yamlString);
|
|
741
|
+
return yamlString;
|
|
742
|
+
}
|