UNPKG

meld

Version:

Meld: A template language for LLM prompts

185 lines (161 loc) 4.77 kB
import type { IStateService } from './IStateService.js'; import type { StateNode } from './types.js'; import { StateFactory } from './StateFactory.js'; import { stateLogger as logger } from '@core/utils/logger.js'; /** * Options for migrating state */ export interface MigrationOptions { /** * Whether to preserve immutability status * @default true */ preserveImmutability?: boolean; /** * Whether to validate the migrated state * @default true */ validate?: boolean; /** * Whether to throw on validation errors * @default false */ strict?: boolean; } /** * Result of state migration */ export interface MigrationResult { /** * The migrated state node */ state: StateNode; /** * Any validation warnings that occurred during migration */ warnings: string[]; /** * Whether the migration was successful */ success: boolean; } /** * Migrates an old state service instance to a new immutable state node */ export function migrateState(oldState: IStateService, options: MigrationOptions = {}): MigrationResult { const { preserveImmutability = true, validate = true, strict = false } = options; const warnings: string[] = []; const factory = new StateFactory(); try { // Create base state const state = factory.createState({ source: 'migration', filePath: oldState.getCurrentFilePath() ?? undefined }); // Migrate variables const text = new Map(oldState.getAllTextVars()); const data = new Map(oldState.getAllDataVars()); const path = new Map(oldState.getAllPathVars()); // Migrate commands const commands = new Map(oldState.getAllCommands()); // Migrate imports const imports = oldState.getImports(); // Migrate nodes const nodes = oldState.getNodes(); // Create migrated state const migrated = factory.updateState(state, { variables: { text, data, path }, commands, imports, nodes }); // Validate migrated state if (validate) { validateMigration(oldState, migrated, warnings); if (strict && warnings.length > 0) { throw new Error('Migration validation failed:\n' + warnings.join('\n')); } } logger.debug('Migrated state', { textVars: text.size, dataVars: data.size, pathVars: path.size, commands: commands.size, imports: imports.size, nodes: nodes.length, warnings: warnings.length }); return { state: migrated, warnings, success: true }; } catch (error) { logger.error('State migration failed', { error }); return { state: factory.createState(), warnings: [...warnings, String(error)], success: false }; } } /** * Validates that the migrated state matches the original */ export function validateMigration(oldState: IStateService, newState: StateNode, warnings: string[]): void { // Validate text variables for (const [key, value] of oldState.getAllTextVars()) { const newValue = newState.variables.text.get(key); if (newValue !== value) { warnings.push(`Text variable mismatch: ${key}`); } } // Validate data variables for (const [key, value] of oldState.getAllDataVars()) { const newValue = newState.variables.data.get(key); if (JSON.stringify(newValue) !== JSON.stringify(value)) { warnings.push(`Data variable mismatch: ${key}`); } } // Validate path variables for (const [key, value] of oldState.getAllPathVars()) { const newValue = newState.variables.path.get(key); if (newValue !== value) { warnings.push(`Path variable mismatch: ${key}`); } } // Validate commands for (const [key, value] of oldState.getAllCommands()) { const newValue = newState.commands.get(key); if (!newValue || JSON.stringify(newValue) !== JSON.stringify(value)) { warnings.push(`Command mismatch: ${key}`); } } // Validate imports for (const importPath of oldState.getImports()) { if (!newState.imports.has(importPath)) { warnings.push(`Missing import: ${importPath}`); } } // Validate nodes const oldNodes = oldState.getNodes(); if (oldNodes.length !== newState.nodes.length) { warnings.push(`Node count mismatch: ${oldNodes.length} vs ${newState.nodes.length}`); } else { for (let i = 0; i < oldNodes.length; i++) { if (JSON.stringify(oldNodes[i]) !== JSON.stringify(newState.nodes[i])) { warnings.push(`Node mismatch at index ${i}`); } } } // Validate file path const oldPath = oldState.getCurrentFilePath(); const newPath = newState.filePath; if (oldPath !== (newPath ?? null)) { warnings.push(`File path mismatch: ${oldPath} vs ${newPath}`); } }