/** * Marker for a computed capability field embedded in stored capability data. * * At registration time, each `computed:` entry is folded into the capability's * `data` JSON as `{ [name]: { [MARKER_KEY]: "" } }`. At read * time the resolver detects the marker and evaluates the expression in the * PROVIDER's context (see resolver.ts capability case). This mirrors how * static `$self:` refs already live verbatim in stored data and resolve lazily. */ /** Property key that flags a value as a computed-field marker. */ export const COMPUTED_MARKER_KEY = '__celilo_computed__'; export interface ComputedMarker { [COMPUTED_MARKER_KEY]: string; } /** Build a marker object for storage. */ export function computedMarker(expression: string): ComputedMarker { return { [COMPUTED_MARKER_KEY]: expression }; } /** Narrow an arbitrary value to a computed marker, returning its expression. */ export function asComputedExpression(value: unknown): string | null { if ( value !== null && typeof value === 'object' && !Array.isArray(value) && COMPUTED_MARKER_KEY in (value as Record) ) { const expr = (value as Record)[COMPUTED_MARKER_KEY]; return typeof expr === 'string' ? expr : null; } return null; } /** * Cheap recursive scan: does this value (or anything nested) contain a * computed marker? Used to skip the (relatively expensive) provider-lookup * build when a capability has no computed fields — the common case. */ export function containsComputedMarker(value: unknown): boolean { if (asComputedExpression(value) !== null) return true; if (Array.isArray(value)) { return value.some(containsComputedMarker); } if (value !== null && typeof value === 'object') { for (const v of Object.values(value as Record)) { if (containsComputedMarker(v)) return true; } } return false; }