wrekenfile-converter
Version:
Convert OpenAPI and Postman specs into Wrekenfiles, with chunking for vector database storage
891 lines • 37.8 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateWrekenfile = generateWrekenfile;
exports.extractStructs = extractStructs;
exports.extractOperations = extractOperations;
exports.mapType = mapType;
exports.parseJsonExample = parseJsonExample;
exports.extractFieldsFromObject = extractFieldsFromObject;
exports.loadEnvironmentFile = loadEnvironmentFile;
exports.extractCollectionVariables = extractCollectionVariables;
exports.resolveVariables = resolveVariables;
// postman-to-wrekenfile.ts
// Converts Postman collections to Wrekenfile v2.0.1 format
const fs = __importStar(require("fs"));
const yaml_utils_1 = require("./utils/yaml-utils");
const constants_1 = require("./utils/constants");
const response_utils_1 = require("./utils/response-utils");
const error_utils_1 = require("./utils/error-utils");
const canonical_id_1 = require("./utils/canonical-id");
const struct_utils_1 = require("./utils/struct-utils");
function mapType(value) {
if (typeof value === 'string') {
// Check for common patterns
if (/^\d{4}-\d{2}-\d{2}/.test(value))
return 'DATE';
if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value))
return 'STRING'; // UUID as string
if (/^\d+$/.test(value))
return 'INT';
if (/^\d+\.\d+$/.test(value))
return 'FLOAT';
if (value === 'true' || value === 'false')
return 'BOOL';
return 'STRING';
}
if (typeof value === 'number') {
return Number.isInteger(value) ? 'INT' : 'FLOAT';
}
if (typeof value === 'boolean')
return 'BOOL';
if (Array.isArray(value))
return 'ANY'; // Arrays will be handled specially
if (value === null || value === undefined)
return 'ANY';
return 'ANY';
}
function getItemDescription(item) {
var _a, _b, _c, _d, _e;
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;
if (!description)
return '';
// Postman can store description as an object with { content: string }
if (typeof description === 'object' && typeof description.content === 'string') {
description = description.content;
}
if (typeof description !== 'string')
return '';
return description.replace(/<[^>]*>/g, '').replace(/\n+/g, ' ').trim();
}
function generateSummary(item, method, path) {
const cleaned = getItemDescription(item);
if (cleaned) {
// Use first sentence as summary
const firstSentence = cleaned.split(/[.!?]\s/)[0];
return firstSentence || cleaned.substring(0, 100);
}
const verb = constants_1.SUMMARY_VERBS[method.toLowerCase()] || 'Call';
const entity = path.split('/').filter(p => p && !p.startsWith('{')).pop() || 'resource';
return `${verb} ${entity}`;
}
function generateStructName(_itemName, method, path, suffix) {
// Use canonical ID as the semantic base for Postman-derived structs
// (Request, ResponseXXX). This keeps struct names aligned with methods.
const canonicalId = (0, canonical_id_1.computeCanonicalId)('api', method.toUpperCase(), path);
return `${canonicalId}${suffix}`;
}
function parseJsonExample(jsonStr) {
try {
return JSON.parse(jsonStr);
}
catch (_a) {
return null;
}
}
function extractFieldsFromObject(obj, depth = 0, prefix = '') {
if (depth > 3)
return [];
if (!obj)
return [];
// If the root object is an array, extract fields from the first object
if (Array.isArray(obj) && obj.length > 0 && typeof obj[0] === 'object' && obj[0] !== null) {
return extractFieldsFromObject(obj[0], depth + 1, prefix);
}
if (Array.isArray(obj))
return [];
if (typeof obj !== 'object')
return [];
const fields = [];
const keyCount = {};
for (const [key, value] of Object.entries(obj)) {
let type = 'ANY';
let required = false;
// Handle duplicate keys
let fieldName = key;
if (keyCount[key] === undefined) {
keyCount[key] = 1;
}
else {
keyCount[key] += 1;
fieldName = `${key} ${keyCount[key]}`;
}
if (Array.isArray(value)) {
if (value.length > 0) {
const firstItem = value[0];
if (typeof firstItem === 'object' && firstItem !== null) {
type = `[]STRUCT(${prefix}${fieldName}Item)`;
}
else {
type = `[]${mapType(firstItem)}`;
}
}
else {
type = '[]ANY';
}
}
else if (typeof value === 'object' && value !== null) {
type = `STRUCT(${prefix}${fieldName})`;
}
else {
type = mapType(value);
}
fields.push({
name: fieldName,
type,
REQUIRED: required,
});
}
return fields;
}
function loadEnvironmentFile(envPath) {
try {
const envData = fs.readFileSync(envPath, 'utf8');
const env = JSON.parse(envData);
const variables = {};
if (env.values) {
for (const variable of env.values) {
if (variable.key && variable.value) {
variables[variable.key] = variable.value;
}
}
}
return variables;
}
catch (error) {
return {};
}
}
function extractCollectionVariables(collection) {
const variables = {};
// Extract collection-level variables
if (collection.variable) {
for (const variable of collection.variable) {
if (variable.key && variable.value) {
variables[variable.key] = variable.value;
}
}
}
return variables;
}
function resolveVariables(value, variables) {
if (typeof value !== 'string')
return value;
// Replace {{variable}} patterns with actual values
return value.replace(/\{\{([^}]+)\}\}/g, (match, varName) => {
return variables[varName] || match;
});
}
function extractStructs(collection, variables) {
const structs = {};
const structNameCount = {};
function getUniqueStructName(name) {
if (structs[name] === undefined) {
structNameCount[name] = 1;
return name;
}
else {
structNameCount[name] = (structNameCount[name] || 1) + 1;
return `${name} ${structNameCount[name]}`;
}
}
function processItem(item) {
var _a;
if (item.request) {
const method = item.request.method || constants_1.HTTP_METHOD_GET;
const url = item.request.url;
const path = extractPathFromUrl(url, variables);
const itemName = item.name || 'unknown';
// Extract request body structs
if (((_a = item.request.body) === null || _a === void 0 ? void 0 : _a.mode) === 'raw' && item.request.body.raw) {
const bodyData = parseJsonExample(item.request.body.raw);
if (bodyData) {
let requestStructName = generateStructName(itemName, method, path, 'Request');
requestStructName = getUniqueStructName(requestStructName);
const fields = extractFieldsFromObject(bodyData, 0, requestStructName);
if (fields.length > 0) {
structs[requestStructName] = fields;
}
extractNestedStructs(bodyData, structs, requestStructName);
}
}
// Extract response structs from examples
if (item.response) {
for (const response of item.response) {
let responseStructName = generateStructName(itemName, method, path, `Response${response.code || '200'}`);
responseStructName = getUniqueStructName(responseStructName);
if (response.body) {
const responseData = parseJsonExample(response.body);
if (responseData) {
const fields = extractFieldsFromObject(responseData, 0, responseStructName);
if (fields.length > 0) {
structs[responseStructName] = fields;
}
extractNestedStructs(responseData, structs, responseStructName);
}
}
}
}
}
if (item.item) {
for (const subItem of item.item) {
processItem(subItem);
}
}
}
for (const item of collection.item) {
processItem(item);
}
return structs;
}
function extractNestedStructs(obj, structs, basePrefix = '') {
if (!obj || typeof obj !== 'object' || Array.isArray(obj))
return;
for (const [key, value] of Object.entries(obj)) {
if (Array.isArray(value) && value.length > 0) {
const firstItem = value[0];
if (typeof firstItem === 'object' && firstItem !== null) {
const structName = `${basePrefix}${key}Item`;
const fields = extractFieldsFromObject(firstItem, 0, structName);
if (fields.length > 0) {
structs[structName] = fields;
}
// For deeper levels, keep using the same base prefix to avoid runaway name growth
extractNestedStructs(firstItem, structs, basePrefix);
}
}
else if (typeof value === 'object' && value !== null) {
const structName = `${basePrefix}${key}`;
const fields = extractFieldsFromObject(value, 0, structName);
if (fields.length > 0) {
structs[structName] = fields;
}
// For deeper levels, keep using the same base prefix
extractNestedStructs(value, structs, basePrefix);
}
}
}
function extractPathFromUrl(url, _variables) {
if (url === null || url === void 0 ? void 0 : url.raw) {
// Remove base URL and protocol
let path = url.raw;
path = path.replace(/^https?:\/\/[^\/]+/, ''); // Remove protocol and host
// Remove base URL variables like {{url}} from path (they belong in base URL)
path = path.replace(/\{\{url\}\}/gi, '');
// Strip query string if present
path = path.split('?')[0];
// Convert Postman path variables {{var}} to OpenAPI-style {var}
path = path.replace(/\{\{([^}]+)\}\}/g, '{$1}');
path = path.replace(/\/+/g, '/'); // Normalize slashes
path = path.replace(/^\/|\/$/g, ''); // Remove leading/trailing slashes
return path;
}
if (url === null || url === void 0 ? void 0 : url.path) {
const pathSegments = url.path
.filter((segment) => {
// Filter out base URL variables like {{url}}
return !segment.match(/^\{\{url\}\}$/i);
})
.map((segment) => {
// Convert Postman variables {{var}} to OpenAPI-style {var}
return segment.replace(/\{\{([^}]+)\}\}/g, '{$1}');
});
return pathSegments.join('/');
}
return '';
}
function getContentTypeAndBodyType(request) {
const headers = request.header || [];
const contentTypeHeader = headers.find((h) => { var _a; return ((_a = h.key) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === constants_1.HEADER_CONTENT_TYPE.toLowerCase(); });
let contentType = constants_1.CONTENT_TYPE_JSON;
if (contentTypeHeader) {
contentType = contentTypeHeader.value || constants_1.CONTENT_TYPE_JSON;
}
let bodyType = constants_1.BODYTYPE_RAW;
if (contentType === constants_1.CONTENT_TYPE_FORM_DATA) {
bodyType = 'form-data';
}
else if (contentType === constants_1.CONTENT_TYPE_URLENCODED) {
bodyType = 'x-www-form-urlencoded';
}
return { contentType, bodyType };
}
function getAcceptContentType(item) {
var _a;
// Get the first content type from the first success response (2xx)
if (item.response) {
for (const response of item.response) {
const code = parseInt(((_a = response.code) === null || _a === void 0 ? void 0 : _a.toString()) || '200');
if (code >= 200 && code < 300) {
// Check Content-Type header in response
if (response.header) {
const acceptHeader = response.header.find((h) => { var _a; return ((_a = h.key) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === 'content-type'; });
if (acceptHeader === null || acceptHeader === void 0 ? void 0 : acceptHeader.value) {
return acceptHeader.value;
}
}
}
}
}
// Default to JSON if no response content type found
return constants_1.CONTENT_TYPE_JSON;
}
function getHeadersForOperation(request, variables) {
var _a;
const { contentType } = getContentTypeAndBodyType(request);
const headerMap = new Map();
// Add Content-Type header for POST/PUT/PATCH requests
if (constants_1.HTTP_METHODS_WITH_BODY.includes(((_a = request.method) === null || _a === void 0 ? void 0 : _a.toLowerCase()) || '')) {
headerMap.set(constants_1.HEADER_CONTENT_TYPE, contentType);
}
// Add authentication headers
const authHeaders = request.header || [];
for (const header of authHeaders) {
if (header.key && header.value) {
const key = header.key.toLowerCase();
if (key === constants_1.AUTH_HEADER_X_API_KEY || key === constants_1.AUTH_HEADER_AUTHORIZATION || key === constants_1.AUTH_HEADER_X_SIGNATURE) {
let value = resolveVariables(header.value, variables);
if (key === constants_1.AUTH_HEADER_X_API_KEY)
value = constants_1.AUTH_API_KEY;
else if (key === constants_1.AUTH_HEADER_AUTHORIZATION)
value = constants_1.AUTH_BEARER_TOKEN;
else if (key === constants_1.AUTH_HEADER_X_SIGNATURE)
value = constants_1.AUTH_SIGNATURE;
headerMap.set(header.key, value);
}
}
}
// Convert Map to object
const headers = {};
for (const [key, value] of headerMap.entries()) {
headers[key] = value;
}
return headers;
}
function extractParameters(request, _variables) {
var _a;
const inputParams = [];
// v2.0.2: All parameters (path, query, header) must be in INPUTS with LOCATION
// Path variables are also in ENDPOINT (e.g., /users/{userId})
// Header parameters are also in HTTP.HEADERS
const url = request.url;
// Extract path variables from URL
if ((url === null || url === void 0 ? void 0 : url.raw) || (url === null || url === void 0 ? void 0 : url.path)) {
const urlStr = url.raw || (Array.isArray(url.path) ? url.path.join('/') : url.path || '');
const pathMatches = urlStr.match(/\{\{(\w+)\}\}/g) || [];
for (const match of pathMatches) {
const varName = match.replace(/\{\{|\}\}/g, '');
const inputParam = {};
inputParam[varName] = {
TYPE: 'STRING',
LOCATION: 'path',
};
inputParams.push(inputParam);
}
}
// Extract query parameters
if (url === null || url === void 0 ? void 0 : url.query) {
for (const query of url.query) {
// Skip disabled query parameters
if (query.disabled) {
continue;
}
const isRequired = !query.disabled;
const inputParam = {};
// v2.0.2: All INPUTS must have LOCATION field
if (isRequired) {
// Simple form with LOCATION
inputParam[query.key] = {
TYPE: 'STRING',
LOCATION: 'query',
};
}
else {
// Extended form with LOCATION
inputParam[query.key] = {
TYPE: 'STRING',
REQUIRED: false,
LOCATION: 'query',
};
}
inputParams.push(inputParam);
}
}
// Extract header parameters (non-auth headers that should be in INPUTS)
if (request.header) {
for (const header of request.header) {
// Skip disabled headers
if (header.disabled) {
continue;
}
// Skip Content-Type and Authorization - they're in HTTP.HEADERS
const headerKey = (_a = header.key) === null || _a === void 0 ? void 0 : _a.toLowerCase();
if (headerKey === 'content-type' || headerKey === 'authorization') {
continue;
}
const isRequired = !header.disabled;
const inputParam = {};
// v2.0.2: All INPUTS must have LOCATION field
if (isRequired) {
inputParam[header.key] = {
TYPE: 'STRING',
LOCATION: 'header',
};
}
else {
inputParam[header.key] = {
TYPE: 'STRING',
REQUIRED: false,
LOCATION: 'header',
};
}
inputParams.push(inputParam);
}
}
return inputParams;
}
function extractRequestBody(request, itemName, method, path) {
var _a, _b, _c;
const inputParams = [];
if (((_a = request.body) === null || _a === void 0 ? void 0 : _a.mode) === 'raw' && request.body.raw) {
const bodyData = parseJsonExample(request.body.raw);
if (bodyData) {
const requestStructName = generateStructName(itemName, method, path, 'Request');
const inputParam = {};
// v2.0.2: All INPUTS must have LOCATION field
inputParam.body = {
TYPE: `STRUCT(${requestStructName})`,
REQUIRED: true,
LOCATION: 'body',
};
inputParams.push(inputParam);
}
}
// Handle form-data and urlencoded
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') {
const formData = request.body.formdata || request.body.urlencoded || [];
for (const field of formData) {
const type = field.type === 'file' ? 'STRING' : 'STRING';
const isRequired = !field.disabled;
const inputParam = {};
// v2.0.2: All INPUTS must have LOCATION field
if (isRequired) {
inputParam[field.key] = {
TYPE: type,
LOCATION: 'body',
};
}
else {
inputParam[field.key] = {
TYPE: type,
REQUIRED: false,
LOCATION: 'body',
};
}
inputParams.push(inputParam);
}
}
return inputParams;
}
function extractResponses(item, itemName, method, path) {
var _a;
const returns = [];
const seenCodes = new Set();
if (item.response) {
for (const response of item.response) {
const code = ((_a = response.code) === null || _a === void 0 ? void 0 : _a.toString()) || '200';
const statusCode = parseInt(code);
// Only include success responses (2xx) in RETURNS section
// Error responses go in ERRORS section
if (isNaN(statusCode) || statusCode < 200 || statusCode >= 300) {
continue;
}
// Avoid duplicate response codes
if (seenCodes.has(code))
continue;
seenCodes.add(code);
// Skip 204 No Content
if (code === '204')
continue;
let returnType = 'ANY';
if (response.body) {
const responseData = parseJsonExample(response.body);
if (responseData) {
const responseStructName = generateStructName(itemName, method, path, `Response${code}`);
returnType = `STRUCT(${responseStructName})`;
}
}
// Only add if there's actually a return type
if (returnType !== constants_1.TYPE_VOID) {
// Generate descriptive RETURNVAR name based on response code and operation
// Clean itemName: replace spaces and special chars with underscores, convert to lowercase
const cleanItemName = itemName
.replace(/[^a-zA-Z0-9]/g, '_')
.replace(/_+/g, '_')
.replace(/^_|_$/g, '')
.toLowerCase();
const returnVarName = (0, response_utils_1.generateReturnVarName)(cleanItemName, code);
// v2.0.2: STATUS code is required in RETURNS
returns.push({
RETURNTYPE: returnType,
RETURNVAR: returnVarName,
STATUS: statusCode,
});
}
}
}
return returns;
}
function extractErrors(item, _itemName, _method, _path) {
var _a;
const errors = [];
if (item.response) {
for (const response of item.response) {
const code = parseInt(((_a = response.code) === null || _a === void 0 ? void 0 : _a.toString()) || '200');
if (isNaN(code) || code < 400)
continue;
let errorType = 'ANY';
let when = `HTTP ${code}`;
if (response.body) {
const responseData = parseJsonExample(response.body);
if (responseData) {
const errorStructName = `Error${code}`;
errorType = `STRUCT(${errorStructName})`;
}
}
when = (0, response_utils_1.generateErrorWhen)(response, code.toString());
// v2.0.2: STATUS code is required in ERRORS
errors.push({
TYPE: errorType,
STATUS: code,
WHEN: when,
});
}
}
return errors;
}
function extractOperations(collection, variables) {
const operations = [];
const operationNameCount = {};
function getUniqueOperationName(name) {
if (operationNameCount[name] === undefined) {
operationNameCount[name] = 1;
return name;
}
else {
operationNameCount[name] += 1;
return `${name} ${operationNameCount[name]}`;
}
}
function processItem(item, parentName = null) {
if (item.request) {
const method = item.request.method || constants_1.HTTP_METHOD_GET;
const url = item.request.url;
const path = extractPathFromUrl(url, variables);
const itemName = item.name || 'unknown';
// Generate operation ID (alias)
let operationId = itemName.toLowerCase().replace(/[^a-z0-9]/g, '-');
operationId = getUniqueOperationName(operationId);
const summary = generateSummary(item, method, path);
const { contentType, bodyType } = getContentTypeAndBodyType(item.request);
const headers = getHeadersForOperation(item.request, variables);
const inputs = extractParameters(item.request, variables);
const bodyInputs = extractRequestBody(item.request, itemName, method, path);
const returns = extractResponses(item, itemName, method, path);
const errors = extractErrors(item, itemName, method, path);
const allInputs = [...inputs];
if (bodyInputs.length > 0) {
allInputs.push(...bodyInputs);
}
// Build method in v2.0.2 format
const methodDef = {
SUMMARY: summary,
};
// Add DESC if description exists
const desc = getItemDescription(item);
if (desc) {
methodDef.DESC = desc;
}
// Get accept content type from responses
const acceptContentType = getAcceptContentType(item);
// HTTP section (mandatory for API methods)
methodDef.HTTP = {
METHOD: method.toUpperCase(),
ENDPOINT: `/${path}`,
HEADERS: headers,
CONTENT_TYPE: contentType,
ACCEPT: acceptContentType,
};
// v2.0.2: BODY.TYPE should be STRUCT(...) format
if (bodyInputs.length > 0 && bodyInputs[0].body) {
const bodyTypeValue = bodyInputs[0].body.TYPE || bodyInputs[0].body;
methodDef.HTTP.BODY = {
TYPE: bodyTypeValue,
};
}
if (bodyType !== constants_1.BODYTYPE_RAW) {
methodDef.HTTP.BODYTYPE = bodyType;
}
// EXECUTION section (mandatory) - v2.0.2 requires KIND
methodDef.EXECUTION = {
KIND: 'http',
MODE: constants_1.EXECUTION_MODE_SYNC, // REST APIs are synchronous request/response
};
// INPUTS section (optional)
if (allInputs.length > 0) {
methodDef.INPUTS = allInputs;
}
// RETURNS section (optional - omit for void)
if (returns.length > 0) {
methodDef.RETURNS = returns;
}
// ERRORS section (optional)
if (errors.length > 0) {
methodDef.ERRORS = errors;
}
operations.push(Object.assign({ name: operationId }, methodDef));
}
if (item.item) {
for (const subItem of item.item) {
processItem(subItem, item.name || parentName || null);
}
}
}
for (const item of collection.item) {
processItem(item, null);
}
return operations;
}
function extractBaseUrl(collection, variables) {
// First, try to get from collection variables (merged with passed variables)
const collectionVars = extractCollectionVariables(collection);
const allVariables = Object.assign(Object.assign({}, collectionVars), variables);
// Check for common base URL variable names
for (const varName of constants_1.BASE_URL_VARIABLE_NAMES) {
if (allVariables[varName]) {
let baseUrl = allVariables[varName];
// Remove trailing slash
baseUrl = baseUrl.replace(/\/$/, '');
// If it's a variable placeholder, skip it
if (!baseUrl.startsWith('{{')) {
return baseUrl;
}
}
}
// Try to extract from first request URL
function findFirstRequestUrl(item) {
var _a;
if ((_a = item.request) === null || _a === void 0 ? void 0 : _a.url) {
const url = item.request.url;
if (url.raw) {
// Extract base URL from raw URL
// Handle cases like "{{url}}/api/v1/endpoint" or "https://api.example.com/api/v1/endpoint"
let rawUrl = url.raw;
// Try to resolve variables first
rawUrl = resolveVariables(rawUrl, allVariables);
// Extract base URL (protocol + host)
const match = rawUrl.match(/^(https?:\/\/[^\/\s]+)/);
if (match) {
return match[1];
}
// If still has variables, try to extract from host array
if (url.host && Array.isArray(url.host) && url.host.length > 0) {
const host = url.host[0];
const resolvedHost = resolveVariables(host, allVariables);
// If host is resolved and not a variable placeholder
if (resolvedHost && !resolvedHost.startsWith('{{') && !resolvedHost.includes('{{')) {
const protocol = (url.protocol && !url.protocol.startsWith('{{'))
? url.protocol.replace(':', '')
: 'https';
return `${protocol}://${resolvedHost}`;
}
}
}
}
if (item.item && Array.isArray(item.item)) {
for (const subItem of item.item) {
const found = findFirstRequestUrl(subItem);
if (found)
return found;
}
}
return null;
}
if (collection.item && Array.isArray(collection.item)) {
for (const item of collection.item) {
const baseUrl = findFirstRequestUrl(item);
if (baseUrl) {
return baseUrl.replace(/\/$/, '');
}
}
}
// Default fallback
return constants_1.DEFAULT_BASE_URL;
}
function generateWrekenfile(collection, variables) {
var _a, _b, _c, _d, _e;
try {
// Validate inputs
(0, error_utils_1.validatePostmanCollection)(collection);
if (!variables || typeof variables !== 'object') {
throw (0, error_utils_1.createConverterError)("Argument 'variables' is required and must be an object", "INVALID_VARIABLES", { variablesType: typeof variables });
}
// Extract base URL from collection
const baseUrl = extractBaseUrl(collection, variables);
// Merge collection variables with passed variables
const collectionVars = extractCollectionVariables(collection);
const allVariables = Object.assign(Object.assign({}, collectionVars), variables);
const structs = extractStructs(collection, allVariables);
const operations = extractOperations(collection, allVariables);
const wrekenfile = {
VERSION: constants_1.WREKENFILE_VERSION,
};
// Add DEFAULTS if we have any
const defaults = {};
// Add base URL first
defaults.w_base_url = baseUrl;
// Add other variables
if (Object.keys(allVariables).length > 0) {
for (const [key, value] of Object.entries(allVariables)) {
// Skip base URL variables as we've already added w_base_url
if (constants_1.BASE_URL_VARIABLE_NAMES.includes(key)) {
continue;
}
const isSensitive = constants_1.SENSITIVE_KEYS.some(sensitiveKey => key.toLowerCase().includes(sensitiveKey));
if (isSensitive) {
defaults[key] = `{{${key}}}`;
}
else {
// Ensure value is a string for DEFAULTS section
if (value !== undefined && value !== null) {
defaults[key] = String(value);
}
}
}
}
if (Object.keys(defaults).length > 0) {
wrekenfile.DEFAULTS = defaults;
}
// Add METHODS (mandatory)
const methods = {};
for (const operation of operations) {
const { name } = operation, methodDef = __rest(operation, ["name"]);
methods[name] = methodDef;
}
// Resolve canonical IDs for all methods
const canonicalInputs = Object.entries(methods).map(([methodId, methodData]) => {
var _a, _b;
return ({
methodId,
httpMethod: (_a = methodData.HTTP) === null || _a === void 0 ? void 0 : _a.METHOD,
endpoint: (_b = methodData.HTTP) === null || _b === void 0 ? void 0 : _b.ENDPOINT,
existingCanonicalId: methodData.CANONICAL_ID,
});
});
const libraryName = ((_a = collection === null || collection === void 0 ? void 0 : collection.info) === null || _a === void 0 ? void 0 : _a.name) || 'unknown';
const canonicalIdMap = (0, canonical_id_1.resolveCanonicalIds)(canonicalInputs, libraryName);
// Add CANONICAL_ID to each method
for (const [methodId, methodData] of Object.entries(methods)) {
const canonicalId = canonicalIdMap.get(methodId);
if (canonicalId) {
methodData.CANONICAL_ID = canonicalId;
}
}
// Update RETURNVARs to be derived from CANONICAL_ID
for (const methodData of Object.values(methods)) {
const canonicalId = methodData.CANONICAL_ID;
if (!canonicalId || !Array.isArray(methodData.RETURNS))
continue;
const baseVar = canonicalId.replace(/\./g, '_');
for (const ret of methodData.RETURNS) {
const status = ret.STATUS;
if (status === 200 || status === '200') {
ret.RETURNVAR = baseVar;
}
else if (status !== undefined && status !== null) {
ret.RETURNVAR = `${baseVar}_${status}`;
}
else {
ret.RETURNVAR = baseVar;
}
}
}
// Use CANONICAL_ID as key when available
const renamedMethods = {};
for (const [oldId, methodData] of Object.entries(methods)) {
const canonicalId = methodData.CANONICAL_ID;
const key = canonicalId || oldId;
renamedMethods[key] = methodData;
}
wrekenfile.METHODS = renamedMethods;
// Add STRUCTS if we have any
if (Object.keys(structs).length > 0) {
wrekenfile.STRUCTS = structs;
}
// Remove unused STRUCTS (keep only those referenced by METHODS)
(0, struct_utils_1.filterStructsByUsage)(wrekenfile);
// Generate YAML string using the standard pipeline
return (0, yaml_utils_1.generateYamlString)(wrekenfile);
}
catch (err) {
// Log error with context
(0, error_utils_1.logError)(err, {
converter: 'postman-to-wrekenfile',
collectionName: ((_b = collection === null || collection === void 0 ? void 0 : collection.info) === null || _b === void 0 ? void 0 : _b.name) || 'unknown',
collectionSchema: ((_c = collection === null || collection === void 0 ? void 0 : collection.info) === null || _c === void 0 ? void 0 : _c.schema) || 'unknown'
});
// Re-throw with additional context if it's not already a ConverterError
if (err.code && (err.code.startsWith('INVALID_') || err.code.startsWith('MISSING_') || err.code.startsWith('EMPTY_'))) {
throw err;
}
throw (0, error_utils_1.createConverterError)(`Failed to generate Wrekenfile from Postman collection: ${err.message}`, "GENERATION_FAILED", {
converter: 'postman-to-wrekenfile',
collectionName: ((_d = collection === null || collection === void 0 ? void 0 : collection.info) === null || _d === void 0 ? void 0 : _d.name) || 'unknown',
collectionSchema: ((_e = collection === null || collection === void 0 ? void 0 : collection.info) === null || _e === void 0 ? void 0 : _e.schema) || 'unknown'
}, err);
}
}
//# sourceMappingURL=postman-to-wrekenfile.js.map