meld
Version:
Meld: A template language for LLM prompts
136 lines (121 loc) • 3.89 kB
text/typescript
import { MeldNode, DirectiveNode, TextNode } from 'meld-spec';
import { IStateService } from '@services/state/StateService/IStateService.js';
import { ResolutionContext, ResolutionErrorCode } from '@services/resolution/ResolutionService/IResolutionService.js';
import { ResolutionError } from '@services/resolution/ResolutionService/errors/ResolutionError.js';
import { MeldResolutionError } from '@core/errors/MeldResolutionError.js';
import { ErrorSeverity } from '@core/errors/MeldError.js';
/**
* Handles resolution of data variables ($data)
*/
export class DataResolver {
constructor(private stateService: IStateService) {}
/**
* Resolve data variables in a node
*/
async resolve(node: MeldNode, context: ResolutionContext): Promise<string> {
// Handle text nodes by returning content unchanged
if (node.type === 'Text') {
return (node as TextNode).content;
}
// Validate node type
if (node.type !== 'Directive' || (node as DirectiveNode).directive.kind !== 'data') {
throw new MeldResolutionError(
'Invalid node type for data resolution',
{
code: ResolutionErrorCode.INVALID_NODE_TYPE,
severity: ErrorSeverity.Fatal,
details: {
value: node.type,
context: JSON.stringify(context)
}
}
);
}
const directiveNode = node as DirectiveNode;
if (!context.allowedVariableTypes.data) {
throw new MeldResolutionError(
'Data variables are not allowed in this context',
{
code: ResolutionErrorCode.INVALID_VARIABLE_TYPE,
severity: ErrorSeverity.Fatal,
details: {
value: directiveNode.directive.value,
context: JSON.stringify(context)
}
}
);
}
const identifier = directiveNode.directive.identifier;
if (!identifier) {
throw new MeldResolutionError(
'Data variable identifier is required',
{
code: ResolutionErrorCode.SYNTAX_ERROR,
severity: ErrorSeverity.Fatal,
details: {
value: JSON.stringify(directiveNode)
}
}
);
}
const value = await this.stateService.getDataVar(identifier);
if (value === undefined) {
throw new MeldResolutionError(
`Data variable '${identifier}' not found`,
{
code: ResolutionErrorCode.UNDEFINED_VARIABLE,
severity: ErrorSeverity.Recoverable,
details: {
variableName: identifier,
variableType: 'data'
}
}
);
}
// Handle field access
if (directiveNode.directive.field) {
const field = directiveNode.directive.field;
const fieldValue = value[field];
if (fieldValue === undefined) {
throw new MeldResolutionError(
`Field '${field}' not found in data variable '${identifier}'`,
{
code: ResolutionErrorCode.UNDEFINED_FIELD,
severity: ErrorSeverity.Recoverable,
details: {
variableName: identifier,
variableType: 'data',
fieldPath: field
}
}
);
}
return this.stringifyValue(fieldValue);
}
return this.stringifyValue(value);
}
/**
* Extract references from a node
*/
extractReferences(node: MeldNode): string[] {
if (node.type !== 'Directive' || (node as DirectiveNode).directive.kind !== 'data') {
return [];
}
return [(node as DirectiveNode).directive.identifier];
}
/**
* Convert a value to string format
*/
private stringifyValue(value: any): string {
if (value === undefined) {
return '';
}
if (value === null) {
return 'null';
}
if (typeof value === 'object') {
return JSON.stringify(value);
}
return String(value);
}
}