UNPKG

cmte

Version:

Design by Committee™ except it's just you and LLMs

109 lines (94 loc) 2.75 kB
import { ValidationError } from './errors'; import { IterationContext } from './types'; export interface PathSegment { name: string; choice?: string | null; isAll: boolean; } export class PathResolver { /** * Parse a path into segments, handling phase names, choices, and 'all' keyword */ static parsePath(path: string): PathSegment[] { // Remove $ prefix if present const cleanPath = path.startsWith('$') ? path.slice(1) : path; const segments = cleanPath.split('.'); return segments.map(segment => { // Handle 'all' keyword if (segment === 'all') { return { name: segment, choice: null, isAll: true }; } // Check for choice syntax [...] const choiceMatch = segment.match(/^([^[]+)(?:\[([^\]]+)\])?$/); if (!choiceMatch) { throw new ValidationError(`Invalid path segment: ${segment}`); } const [, name, choice] = choiceMatch; return { name, choice: choice || null, isAll: false }; }); } /** * Resolve a path segment's choice, handling $this references */ static resolveChoice( segment: PathSegment, context: IterationContext, objectsForIteration: Record<string, any> ): string { if (!segment.choice) { return ''; } if (segment.choice === '$this') { if (!context.currentIteration) { throw new ValidationError('$this used outside iteration context'); } return context.currentIteration; } // Validate choice exists in objectsForIteration const iterationObject = objectsForIteration[segment.name]; if (!iterationObject?.[segment.choice]) { throw new ValidationError( `Invalid choice '${segment.choice}' for iteration '${segment.name}'` ); } return segment.choice; } /** * Walk through a path to get or set a value */ static walkPath( root: any, segments: PathSegment[], context: IterationContext, objectsForIteration: Record<string, any> ): any { let current = root; for (const segment of segments) { if (segment.isAll) { return Object.values(current); } if (!current[segment.name]) { throw new ValidationError(`Path segment '${segment.name}' not found`); } current = current[segment.name]; if (segment.choice) { const resolvedChoice = this.resolveChoice( segment, context, objectsForIteration ); if (!current[resolvedChoice]) { throw new ValidationError( `Choice '${resolvedChoice}' not found in '${segment.name}'` ); } current = current[resolvedChoice]; } } return current; } }