wrekenfile-converter
Version:
Convert OpenAPI and Postman specs to Wrekenfile format with mini-chunking for vector DB storage
654 lines (653 loc) • 26.6 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;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.validateWrekenfile = validateWrekenfile;
exports.fixWrekenfile = fixWrekenfile;
exports.printValidationResult = printValidationResult;
const fs = __importStar(require("fs"));
const yaml = __importStar(require("js-yaml"));
function fixWrekenfile(data) {
var _a, _b, _c;
// Create a clean, properly formatted YAML structure
const fixedData = {};
// Fix VERSION
if (data.VERSION) {
fixedData.VERSION = data.VERSION;
}
// Fix INIT section
if (data.INIT) {
fixedData.INIT = {};
if (data.INIT.DEFAULTS && Array.isArray(data.INIT.DEFAULTS)) {
fixedData.INIT.DEFAULTS = data.INIT.DEFAULTS.map((defaultValue) => {
if (typeof defaultValue === 'object' && defaultValue !== null) {
const key = Object.keys(defaultValue)[0];
const value = defaultValue[key];
return { [key]: value };
}
return defaultValue;
});
}
}
// Fix INTERFACES section
if (data.INTERFACES) {
fixedData.INTERFACES = {};
for (const [interfaceName, interfaceData] of Object.entries(data.INTERFACES)) {
if (typeof interfaceData === 'object' && interfaceData !== null) {
fixedData.INTERFACES[interfaceName] = {
DESC: interfaceData.DESC || '',
ENDPOINT: interfaceData.ENDPOINT || '',
VISIBILITY: interfaceData.VISIBILITY || 'PUBLIC',
HTTP: {
METHOD: ((_a = interfaceData.HTTP) === null || _a === void 0 ? void 0 : _a.METHOD) || 'GET',
HEADERS: Array.isArray((_b = interfaceData.HTTP) === null || _b === void 0 ? void 0 : _b.HEADERS) ? interfaceData.HTTP.HEADERS : [],
BODYTYPE: ((_c = interfaceData.HTTP) === null || _c === void 0 ? void 0 : _c.BODYTYPE) || 'JSON'
},
INPUTS: Array.isArray(interfaceData.INPUTS) ? interfaceData.INPUTS : [],
RETURNS: Array.isArray(interfaceData.RETURNS) ? interfaceData.RETURNS : []
};
}
}
}
// Fix STRUCTS section
if (data.STRUCTS) {
fixedData.STRUCTS = {};
for (const [structName, structData] of Object.entries(data.STRUCTS)) {
if (Array.isArray(structData)) {
fixedData.STRUCTS[structName] = structData.map((field) => {
if (typeof field === 'object' && field !== null) {
return {
name: field.name || '',
type: field.type || 'ANY',
required: field.required || 'OPTIONAL'
};
}
return field;
});
}
}
}
// Generate clean YAML with proper formatting
return yaml.dump(fixedData, {
indent: 2,
lineWidth: 120,
noRefs: true,
sortKeys: false,
flowLevel: -1
});
}
function fixYamlContent(fileContent) {
// Fix common YAML formatting issues
let fixedContent = fileContent;
// Fix malformed arrays like "INPUTS: []" that should be "INPUTS:"
fixedContent = fixedContent.replace(/^(\s*[A-Z_]+:\s*)\[\s*\]\s*$/gm, '$1[]');
// Fix specific malformed INPUTS section
fixedContent = fixedContent.replace(/^(\s*INPUTS:\s*)\[\s*\]\s*$/gm, '$1[]');
// Fix indentation - normalize to 2 spaces
fixedContent = fixedContent.replace(/^\s+/gm, (match) => {
const level = Math.floor(match.length / 2);
return ' '.repeat(level);
});
// Fix missing quotes around values that contain special characters
// But don't quote keys or section headers
fixedContent = fixedContent.replace(/^(\s*[a-zA-Z_][a-zA-Z0-9_-]*:\s*)([^"'\s][^"\n]*)$/gm, (match, prefix, value) => {
// Don't quote if it's a section header (no indentation) or if it ends with colon
if (prefix.trim().endsWith(':') || value.includes(':')) {
return match;
}
if (value.includes('{') || value.includes('}') || value.includes('[') || value.includes(']') || value.includes('/')) {
return `${prefix}"${value}"`;
}
return match;
});
// Fix empty arrays
fixedContent = fixedContent.replace(/^\s*\[\s*\]\s*$/gm, '[]');
// Fix trailing spaces
fixedContent = fixedContent.replace(/[ \t]+$/gm, '');
return fixedContent;
}
function parseYamlRobust(fileContent) {
try {
// First try normal YAML parsing
return yaml.load(fileContent);
}
catch (error) {
console.log('⚠️ Standard YAML parsing failed, attempting to fix formatting...');
try {
// Try to fix common YAML formatting issues
const fixedContent = fixYamlContent(fileContent);
// Try parsing the fixed content
return yaml.load(fixedContent);
}
catch (secondError) {
console.log('❌ Could not parse YAML even after fixing formatting');
return null;
}
}
}
function validateWrekenfile(filePath) {
var _a;
if (!filePath || typeof filePath !== 'string') {
throw new Error("Argument 'filePath' is required and must be a string");
}
const result = {
isValid: true,
errors: [],
warnings: []
};
try {
// Read and parse the YAML file
const fileContent = fs.readFileSync(filePath, 'utf8');
// Use robust parsing
const data = parseYamlRobust(fileContent);
if (!data) {
result.isValid = false;
result.errors.push('File is empty or could not be parsed');
return result;
}
// Validate VERSION
validateVersion(data, result);
// Validate INIT section
validateInitSection(data, result);
// Validate INTERFACES section
validateInterfacesSection(data, result);
// Validate STRUCTS section
validateStructsSection(data, result);
// Cross-reference validation
validateCrossReferences(data, result);
}
catch (error) {
result.isValid = false;
if (error.name === 'YAMLException') {
result.errors.push(`YAML parsing error at line ${((_a = error.mark) === null || _a === void 0 ? void 0 : _a.line) || 'unknown'}: ${error.message}`);
}
else {
result.errors.push(`Failed to parse YAML file: ${error.message}`);
}
}
return result;
}
function validateVersion(data, result) {
if (!data.VERSION) {
result.isValid = false;
result.errors.push('Missing required VERSION field');
return;
}
if (typeof data.VERSION !== 'string') {
result.isValid = false;
result.errors.push('VERSION must be a string');
return;
}
// Check if version is in expected format (e.g., '1.2')
if (!/^\d+\.\d+$/.test(data.VERSION)) {
result.warnings.push(`VERSION format '${data.VERSION}' may not be standard`);
}
}
function validateInitSection(data, result) {
if (!data.INIT) {
result.warnings.push('Missing INIT section (optional but recommended)');
return;
}
if (typeof data.INIT !== 'object') {
result.isValid = false;
result.errors.push('INIT must be an object');
return;
}
// Validate DEFAULTS if present
if (data.INIT.DEFAULTS) {
if (!Array.isArray(data.INIT.DEFAULTS)) {
result.isValid = false;
result.errors.push('INIT.DEFAULTS must be an array');
return;
}
for (let i = 0; i < data.INIT.DEFAULTS.length; i++) {
const defaultValue = data.INIT.DEFAULTS[i];
if (typeof defaultValue !== 'object' || defaultValue === null) {
result.isValid = false;
result.errors.push(`INIT.DEFAULTS[${i}] must be an object`);
continue;
}
const keys = Object.keys(defaultValue);
if (keys.length !== 1) {
result.isValid = false;
result.errors.push(`INIT.DEFAULTS[${i}] must have exactly one key-value pair`);
continue;
}
const key = keys[0];
const value = defaultValue[key];
if (typeof value !== 'string') {
result.isValid = false;
result.errors.push(`INIT.DEFAULTS[${i}].${key} must be a string`);
}
}
}
}
function validateInterfacesSection(data, result) {
if (!data.INTERFACES) {
result.isValid = false;
result.errors.push('Missing required INTERFACES section');
return;
}
if (typeof data.INTERFACES !== 'object') {
result.isValid = false;
result.errors.push('INTERFACES must be an object');
return;
}
const interfaces = Object.keys(data.INTERFACES);
if (interfaces.length === 0) {
result.warnings.push('INTERFACES section is empty');
return;
}
for (const interfaceName of interfaces) {
validateInterface(data.INTERFACES[interfaceName], interfaceName, result);
}
}
function validateInterface(interfaceData, interfaceName, result) {
if (typeof interfaceData !== 'object' || interfaceData === null) {
result.isValid = false;
result.errors.push(`Interface '${interfaceName}' must be an object`);
return;
}
// Required fields for interfaces
const requiredFields = ['DESC', 'ENDPOINT', 'VISIBILITY', 'HTTP', 'INPUTS', 'RETURNS'];
for (const field of requiredFields) {
if (!(field in interfaceData)) {
result.isValid = false;
result.errors.push(`Interface '${interfaceName}' missing required field: ${field}`);
}
}
// Validate DESC
if (interfaceData.DESC && typeof interfaceData.DESC !== 'string') {
result.isValid = false;
result.errors.push(`Interface '${interfaceName}'.DESC must be a string`);
}
// Validate ENDPOINT
if (interfaceData.ENDPOINT && typeof interfaceData.ENDPOINT !== 'string') {
result.isValid = false;
result.errors.push(`Interface '${interfaceName}'.ENDPOINT must be a string`);
}
// Validate VISIBILITY
if (interfaceData.VISIBILITY) {
const validVisibilities = ['PUBLIC', 'PRIVATE', 'INTERNAL'];
if (!validVisibilities.includes(interfaceData.VISIBILITY)) {
result.isValid = false;
result.errors.push(`Interface '${interfaceName}'.VISIBILITY must be one of: ${validVisibilities.join(', ')}`);
}
}
// Validate HTTP section
if (interfaceData.HTTP) {
validateHttpSection(interfaceData.HTTP, interfaceName, result);
}
// Validate INPUTS
if (interfaceData.INPUTS) {
validateInputs(interfaceData.INPUTS, interfaceName, result);
}
// Validate RETURNS
if (interfaceData.RETURNS) {
validateReturns(interfaceData.RETURNS, interfaceName, result);
}
}
function validateHttpSection(httpData, interfaceName, result) {
if (typeof httpData !== 'object' || httpData === null) {
result.isValid = false;
result.errors.push(`Interface '${interfaceName}'.HTTP must be an object`);
return;
}
// Required HTTP fields
const requiredHttpFields = ['METHOD', 'HEADERS', 'BODYTYPE'];
for (const field of requiredHttpFields) {
if (!(field in httpData)) {
result.isValid = false;
result.errors.push(`Interface '${interfaceName}'.HTTP missing required field: ${field}`);
}
}
// Validate METHOD
if (httpData.METHOD) {
const validMethods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'];
if (!validMethods.includes(httpData.METHOD)) {
result.isValid = false;
result.errors.push(`Interface '${interfaceName}'.HTTP.METHOD must be one of: ${validMethods.join(', ')}`);
}
}
// Validate HEADERS
if (httpData.HEADERS) {
if (!Array.isArray(httpData.HEADERS)) {
result.isValid = false;
result.errors.push(`Interface '${interfaceName}'.HTTP.HEADERS must be an array`);
}
else {
for (let i = 0; i < httpData.HEADERS.length; i++) {
const header = httpData.HEADERS[i];
if (typeof header !== 'object' || header === null) {
result.isValid = false;
result.errors.push(`Interface '${interfaceName}'.HTTP.HEADERS[${i}] must be an object`);
continue;
}
const headerKeys = Object.keys(header);
if (headerKeys.length !== 1) {
result.isValid = false;
result.errors.push(`Interface '${interfaceName}'.HTTP.HEADERS[${i}] must have exactly one key-value pair`);
}
}
}
}
// Validate BODYTYPE
if (httpData.BODYTYPE) {
const validBodyTypes = ['RAW', 'JSON', 'FORM'];
if (!validBodyTypes.includes(httpData.BODYTYPE)) {
result.isValid = false;
result.errors.push(`Interface '${interfaceName}'.HTTP.BODYTYPE must be one of: ${validBodyTypes.join(', ')}`);
}
}
}
function validateInputs(inputs, interfaceName, result) {
if (!Array.isArray(inputs)) {
result.isValid = false;
result.errors.push(`Interface '${interfaceName}'.INPUTS must be an array`);
return;
}
for (let i = 0; i < inputs.length; i++) {
const input = inputs[i];
if (typeof input !== 'object' || input === null) {
result.isValid = false;
result.errors.push(`Interface '${interfaceName}'.INPUTS[${i}] must be an object`);
continue;
}
// Required input fields
const requiredInputFields = ['name', 'type', 'required'];
for (const field of requiredInputFields) {
if (!(field in input)) {
result.isValid = false;
result.errors.push(`Interface '${interfaceName}'.INPUTS[${i}] missing required field: ${field}`);
}
}
// Validate name
if (input.name && typeof input.name !== 'string') {
result.isValid = false;
result.errors.push(`Interface '${interfaceName}'.INPUTS[${i}].name must be a string`);
}
// Validate type
if (input.type && typeof input.type !== 'string') {
result.isValid = false;
result.errors.push(`Interface '${interfaceName}'.INPUTS[${i}].type must be a string`);
}
// Validate required
if (input.required && typeof input.required !== 'string') {
result.isValid = false;
result.errors.push(`Interface '${interfaceName}'.INPUTS[${i}].required must be a string`);
}
else if (input.required && !['TRUE', 'FALSE'].includes(input.required)) {
result.warnings.push(`Interface '${interfaceName}'.INPUTS[${i}].required '${input.required}' should be 'TRUE' or 'FALSE'`);
}
// Validate location if present
if (input.location && typeof input.location !== 'string') {
result.isValid = false;
result.errors.push(`Interface '${interfaceName}'.INPUTS[${i}].location must be a string`);
}
else if (input.location && !['PATH', 'QUERY', 'HEADER', 'BODY'].includes(input.location)) {
result.warnings.push(`Interface '${interfaceName}'.INPUTS[${i}].location '${input.location}' should be one of: PATH, QUERY, HEADER, BODY`);
}
}
}
function validateReturns(returns, interfaceName, result) {
if (!Array.isArray(returns)) {
result.isValid = false;
result.errors.push(`Interface '${interfaceName}'.RETURNS must be an array`);
return;
}
if (returns.length === 0) {
result.warnings.push(`Interface '${interfaceName}'.RETURNS is empty`);
return;
}
for (let i = 0; i < returns.length; i++) {
const ret = returns[i];
if (typeof ret !== 'object' || ret === null) {
result.isValid = false;
result.errors.push(`Interface '${interfaceName}'.RETURNS[${i}] must be an object`);
continue;
}
// Required return fields
const requiredReturnFields = ['RETURNTYPE', 'RETURNNAME', 'CODE'];
for (const field of requiredReturnFields) {
if (!(field in ret)) {
result.isValid = false;
result.errors.push(`Interface '${interfaceName}'.RETURNS[${i}] missing required field: ${field}`);
}
}
// Validate RETURNTYPE
if (ret.RETURNTYPE && typeof ret.RETURNTYPE !== 'string') {
result.isValid = false;
result.errors.push(`Interface '${interfaceName}'.RETURNS[${i}].RETURNTYPE must be a string`);
}
// Validate RETURNNAME
if (ret.RETURNNAME && typeof ret.RETURNNAME !== 'string') {
result.isValid = false;
result.errors.push(`Interface '${interfaceName}'.RETURNS[${i}].RETURNNAME must be a string`);
}
// Validate CODE
if (ret.CODE && typeof ret.CODE !== 'string') {
result.isValid = false;
result.errors.push(`Interface '${interfaceName}'.RETURNS[${i}].CODE must be a string`);
}
else if (ret.CODE && !/^\d{3}$/.test(ret.CODE)) {
result.warnings.push(`Interface '${interfaceName}'.RETURNS[${i}].CODE '${ret.CODE}' may not be a valid HTTP status code`);
}
}
}
function validateStructsSection(data, result) {
if (!data.STRUCTS) {
result.warnings.push('Missing STRUCTS section (optional but recommended)');
return;
}
if (typeof data.STRUCTS !== 'object') {
result.isValid = false;
result.errors.push('STRUCTS must be an object');
return;
}
const structs = Object.keys(data.STRUCTS);
if (structs.length === 0) {
result.warnings.push('STRUCTS section is empty');
return;
}
for (const structName of structs) {
validateStruct(data.STRUCTS[structName], structName, result);
}
}
function validateStruct(structData, structName, result) {
if (!Array.isArray(structData)) {
result.isValid = false;
result.errors.push(`Struct '${structName}' must be an array`);
return;
}
for (let i = 0; i < structData.length; i++) {
const field = structData[i];
if (typeof field !== 'object' || field === null) {
result.isValid = false;
result.errors.push(`Struct '${structName}'[${i}] must be an object`);
continue;
}
// Required struct field properties
const requiredFieldProps = ['name', 'type', 'required'];
for (const prop of requiredFieldProps) {
if (!(prop in field)) {
result.isValid = false;
result.errors.push(`Struct '${structName}'[${i}] missing required property: ${prop}`);
}
}
// Validate name
if (field.name && typeof field.name !== 'string') {
result.isValid = false;
result.errors.push(`Struct '${structName}'[${i}].name must be a string`);
}
// Validate type
if (field.type && typeof field.type !== 'string') {
result.isValid = false;
result.errors.push(`Struct '${structName}'[${i}].type must be a string`);
}
// Validate required - be more flexible with actual values
if (field.required && typeof field.required !== 'string') {
result.isValid = false;
result.errors.push(`Struct '${structName}'[${i}].required must be a string`);
}
else if (field.required && !['TRUE', 'FALSE', 'OPTIONAL'].includes(field.required)) {
result.warnings.push(`Struct '${structName}'[${i}].required '${field.required}' should be 'TRUE', 'FALSE', or 'OPTIONAL'`);
}
}
}
function validateCrossReferences(data, result) {
if (!data.INTERFACES || !data.STRUCTS) {
return; // Skip if either section is missing
}
const availableStructs = Object.keys(data.STRUCTS);
const referencedStructs = new Set();
// Collect all struct references from interfaces
for (const interfaceName of Object.keys(data.INTERFACES)) {
const interfaceData = data.INTERFACES[interfaceName];
// Check INPUTS
if (interfaceData.INPUTS && Array.isArray(interfaceData.INPUTS)) {
for (const input of interfaceData.INPUTS) {
if (input.type && input.type.startsWith('STRUCT(')) {
const structName = input.type.replace('STRUCT(', '').replace(')', '');
referencedStructs.add(structName);
}
}
}
// Check RETURNS
if (interfaceData.RETURNS && Array.isArray(interfaceData.RETURNS)) {
for (const ret of interfaceData.RETURNS) {
if (ret.RETURNTYPE && ret.RETURNTYPE.startsWith('STRUCT(')) {
const structName = ret.RETURNTYPE.replace('STRUCT(', '').replace(')', '');
referencedStructs.add(structName);
}
}
}
}
// Check for undefined structs
for (const referencedStruct of referencedStructs) {
if (!availableStructs.includes(referencedStruct)) {
result.warnings.push(`Referenced struct '${referencedStruct}' is not defined in STRUCTS section`);
}
}
// Check for unused structs
for (const availableStruct of availableStructs) {
if (!referencedStructs.has(availableStruct)) {
result.warnings.push(`Struct '${availableStruct}' is defined but not referenced in any interface`);
}
}
}
function printValidationResult(result) {
console.log('🔍 Wrekenfile Validation Results:');
console.log('=====================================');
if (result.isValid) {
console.log('✅ Wrekenfile is VALID');
}
else {
console.log('❌ Wrekenfile is INVALID');
}
if (result.errors.length > 0) {
console.log('\n🚨 Errors:');
result.errors.forEach((error, index) => {
console.log(` ${index + 1}. ${error}`);
});
}
if (result.warnings.length > 0) {
console.log('\n⚠️ Warnings:');
result.warnings.forEach((warning, index) => {
console.log(` ${index + 1}. ${warning}`);
});
}
if (result.errors.length === 0 && result.warnings.length === 0) {
console.log('\n🎉 No issues found!');
}
}
function main() {
const args = process.argv.slice(2);
if (args.length === 0) {
console.error('Usage: node wrekenfile-validator.js <wrekenfile.yaml> [--fix]');
console.error('');
console.error('Arguments:');
console.error(' wrekenfile.yaml Path to the Wrekenfile to validate');
console.error(' --fix Automatically fix indentation, quotes, and spacing issues');
process.exit(1);
}
// Find the file path (first non-flag argument)
const filePath = args.find(arg => !arg.startsWith('--'));
const shouldFix = args.includes('--fix');
if (!filePath) {
console.error('❌ Error: No Wrekenfile path provided');
process.exit(1);
}
if (!fs.existsSync(filePath)) {
console.error(`❌ Error: File '${filePath}' does not exist`);
process.exit(1);
}
if (shouldFix) {
try {
console.log('🔧 Attempting to fix Wrekenfile...');
// Read the original file
const fileContent = fs.readFileSync(filePath, 'utf8');
// Try to fix the YAML formatting
const fixedContent = fixYamlContent(fileContent);
// Try to parse the fixed content
const data = yaml.load(fixedContent);
if (!data) {
console.error('❌ Error: Could not parse the file even after fixing');
process.exit(1);
}
// Create backup
const backupPath = `${filePath}.backup`;
fs.writeFileSync(backupPath, fileContent);
console.log(`📁 Backup created at: ${backupPath}`);
// Write fixed file
fs.writeFileSync(filePath, fixedContent);
console.log('✅ Wrekenfile has been fixed and saved!');
// Validate the fixed file
console.log('\n🔍 Validating fixed Wrekenfile...');
const result = validateWrekenfile(filePath);
printValidationResult(result);
}
catch (error) {
console.error(`❌ Error fixing Wrekenfile: ${error.message}`);
process.exit(1);
}
}
else {
const result = validateWrekenfile(filePath);
printValidationResult(result);
}
process.exit(0);
}
if (require.main === module) {
main();
}