UNPKG

mcard-js

Version:

MCard - Content-addressable storage with cryptographic hashing, handle resolution, and vector search for Node.js and browsers

217 lines 8.35 kB
/** * CLMLoader - Load and parse Cubical Logic Model files * * Supports YAML CLM specifications from chapters/ */ import * as fs from 'fs'; import * as path from 'path'; import * as yaml from 'yaml'; /** * Map balanced.test_cases to examples, preserving original input content. */ function mapTestCasesToExamples(testCases, treatEmptyWhenAsGiven) { return testCases.map((tc) => { const givenRaw = tc.given; const givenLabel = (() => { if (typeof givenRaw === 'string') return givenRaw; if (givenRaw && typeof givenRaw === 'object') { if (typeof givenRaw.description === 'string') return givenRaw.description; if (typeof givenRaw.name === 'string') return givenRaw.name; try { return JSON.stringify(givenRaw); } catch { return String(givenRaw); } } return String(givenRaw); })(); const givenContext = (givenRaw && typeof givenRaw === 'object') ? (givenRaw.context || {}) : {}; const givenCondition = (givenRaw && typeof givenRaw === 'object') ? (givenRaw.condition || {}) : {}; const givenPreconditions = (givenContext && typeof givenContext === 'object' && givenCondition && typeof givenCondition === 'object') ? { ...givenContext, ...givenCondition } : (givenCondition && typeof givenCondition === 'object') ? givenCondition : (givenContext && typeof givenContext === 'object') ? givenContext : {}; let input = tc.given; if (tc.when) { const params = tc.when.params || {}; const context = tc.when.context || {}; const args = tc.when.arguments || {}; const hasParams = Object.keys(params).length > 0; const hasContext = Object.keys(context).length > 0; const hasArgs = Object.keys(args).length > 0; const hasWhenKeys = typeof tc.when === 'object' && Object.keys(tc.when).length > 0; if (hasParams || hasContext || hasArgs) { input = { ...tc.when, ...givenPreconditions, ...context, ...params, ...args }; // Also ensure params are nested if present if (hasParams) input.params = params; if (input.__input_content__ === undefined) { input.__input_content__ = givenLabel; } } else if (tc.when && typeof tc.when === 'object' && (!treatEmptyWhenAsGiven || hasWhenKeys)) { input = { ...tc.when, ...givenPreconditions }; if (input.__input_content__ === undefined) { input.__input_content__ = givenLabel; } } } else if (givenPreconditions && typeof givenPreconditions === 'object' && Object.keys(givenPreconditions).length > 0) { input = { ...givenPreconditions }; if (input.__input_content__ === undefined) { input.__input_content__ = givenLabel; } } return { name: tc.name || `Test Case: ${givenLabel}`, input, expected_output: tc.then?.result, result_contains: tc.then?.result_contains, }; }); } export class CLMLoader { basePath; constructor(basePath = process.cwd()) { this.basePath = basePath; } /** * Load a CLM file from path. */ load(clmPath) { const fullPath = path.isAbsolute(clmPath) ? clmPath : path.resolve(this.basePath, clmPath); const content = fs.readFileSync(fullPath, 'utf-8'); const parsed = yaml.parse(content); // Normalize format (aliases, defaults, legacy compatibility) const spec = this.normalizeFormat(parsed, fullPath); // Standard format: check for test_cases/examples this.normalizeExamples(spec); return spec; } /** * Load all CLM files from a directory. */ loadDirectory(dirPath) { const fullPath = path.resolve(this.basePath, dirPath); const files = fs.readdirSync(fullPath); const clmFiles = new Map(); for (const file of files) { if (file.endsWith('.yaml') || file.endsWith('.clm')) { const filePath = path.join(fullPath, file); try { clmFiles.set(file, this.load(filePath)); } catch (e) { console.warn(`Failed to load ${file}:`, e); } } } return clmFiles; } /** * Load logic file content for a CLM. */ loadLogicFile(clm, chapterDir) { const config = clm.clm?.concrete; if (!config) return null; // Check for inline code if (config.code) { return config.code; } // Try runtimes_config for JavaScript if (config.runtimes_config) { const jsConfig = config.runtimes_config.find(r => r.name === 'javascript'); if (jsConfig?.file) { const logicPath = path.resolve(this.basePath, chapterDir, jsConfig.file); return fs.readFileSync(logicPath, 'utf-8'); } } // Try code_file if (config.code_file) { const logicPath = path.resolve(this.basePath, chapterDir, config.code_file); if (fs.existsSync(logicPath)) { return fs.readFileSync(logicPath, 'utf-8'); } } return null; } normalizeFormat(parsed, fullPath) { // Ensure version exists if (!parsed.version) { parsed.version = '1.0'; } // If clm block is missing but abstract is at root, it's legacy if (!parsed.clm && parsed.abstract) { const basename = path.basename(fullPath); const title = parsed.chapter?.title || parsed.metadata?.name || basename; parsed.clm = { abstract: parsed.abstract, concrete: parsed.concrete, balanced: parsed.balanced }; if (!parsed.chapter) { parsed.chapter = { id: 0, title }; } } // Ensure clm block exists if (!parsed.clm) parsed.clm = {}; // Support aliases within clm block const abstract = parsed.clm.abstract_spec || parsed.clm.abstract || {}; const concrete = parsed.clm.concrete_impl || parsed.clm.concrete || {}; const balanced = parsed.clm.balanced_exp || parsed.clm.balanced || {}; // Default to lambda runtime if (!concrete.runtime) { concrete.runtime = 'lambda'; } // Default builtin to true for lambda and network if (concrete.builtin === undefined) { if (concrete.runtime === 'lambda' || concrete.runtime === 'network') { concrete.builtin = true; } } // Apply normalized dimensions back parsed.clm.abstract = abstract; parsed.clm.concrete = concrete; parsed.clm.balanced = balanced; return parsed; } normalizeExamples(parsed) { const balanced = parsed.clm?.balanced; if (!balanced) return; // Map test_cases to examples if (balanced.test_cases) { const mapped = mapTestCasesToExamples(balanced.test_cases, false); if (!parsed.examples) { parsed.examples = mapped; } else { // Prepend test cases to examples to ensure verification runs parsed.examples = [...mapped, ...parsed.examples]; } } // Map balanced.examples (used in some chapters) if (balanced.examples && !parsed.examples) { parsed.examples = balanced.examples.map((ex, i) => ({ name: `Example ${i + 1}`, input: ex, expected_output: undefined })); } } } //# sourceMappingURL=loader.js.map