cmte
Version:
Design by Committee™ except it's just you and LLMs
111 lines (94 loc) • 3.09 kB
text/typescript
import { PathResolver, PathSegment } from './PathResolver';
import { ValidationError } from './errors';
import { IterationContext, Workflow } from './types';
export class VariableResolver {
private outputs: Map<string, any>;
private globals: Record<string, any>;
private objectsForIteration: Record<string, any>;
constructor(workflow: Workflow) {
this.outputs = new Map();
this.globals = workflow.variables || {};
this.objectsForIteration = workflow.objectsForIteration || {};
}
/**
* Resolve a variable reference, handling both output paths and globals
*/
resolve(reference: string, context: IterationContext = {}): any {
if (reference.startsWith('$')) {
return this.resolveOutputPath(reference, context);
}
return this.resolveGlobalReference(reference);
}
/**
* Register an output value at a specific path
*/
registerOutput(path: string, value: any, context: IterationContext = {}): void {
const segments = PathResolver.parsePath(path);
const phase = segments[0].name;
if (!this.outputs.has(phase)) {
this.outputs.set(phase, {});
}
let current = this.outputs.get(phase);
// Walk through segments except the last one
for (let i = 1; i < segments.length - 1; i++) {
const segment = segments[i];
const resolvedChoice = segment.choice ?
PathResolver.resolveChoice(segment, context, this.objectsForIteration) :
null;
if (!current[segment.name]) {
current[segment.name] = {};
}
current = current[segment.name];
if (resolvedChoice) {
if (!current[resolvedChoice]) {
current[resolvedChoice] = {};
}
current = current[resolvedChoice];
}
}
// Handle the last segment
const lastSegment = segments[segments.length - 1];
const resolvedChoice = lastSegment.choice ?
PathResolver.resolveChoice(lastSegment, context, this.objectsForIteration) :
null;
if (resolvedChoice) {
if (!current[lastSegment.name]) {
current[lastSegment.name] = {};
}
current[lastSegment.name][resolvedChoice] = value;
} else {
current[lastSegment.name] = value;
}
}
private resolveOutputPath(path: string, context: IterationContext): any {
const segments = PathResolver.parsePath(path);
const phase = segments[0].name;
if (!this.outputs.has(phase)) {
throw new ValidationError(`Phase '${phase}' not found`);
}
return PathResolver.walkPath(
this.outputs.get(phase),
segments.slice(1),
context,
this.objectsForIteration
);
}
private resolveGlobalReference(reference: string): any {
if (!(reference in this.globals)) {
throw new ValidationError(`Global variable '${reference}' not found`);
}
return this.globals[reference];
}
/**
* Get all outputs for a phase
*/
getPhaseOutputs(phase: string): any {
return this.outputs.get(phase);
}
/**
* Clear all outputs (useful for testing)
*/
clearOutputs(): void {
this.outputs.clear();
}
}