UNPKG

@lifi/compose-spec

Version:

Public wire-format types and schemas for Compose flows

67 lines (57 loc) 2.15 kB
import type { Ref } from './flowSchema.js'; export type ParsedRef = | { readonly scope: 'input'; readonly port: string } | { readonly scope: 'output'; readonly node: string; readonly port: string } | { readonly scope: 'context'; readonly key: ContextKey }; export type RefScope = ParsedRef['scope']; export type ContextKey = 'sender' | 'executionAddress'; export const isContextKey = (key: string): key is ContextKey => key === 'sender' || key === 'executionAddress'; /** * Ref prefixes that parseRef intercepts before the fallback `output` scope. * A node whose id matches one of these would produce ambiguous refs * (e.g. `context.sender` could be a context ref or node-output ref). * Validation must reject these as node ids. */ export const RESERVED_REF_SCOPES: ReadonlySet<string> = new Set([ 'input', 'context', 'literal', ]); export const isRef = (v: unknown): v is Ref => typeof v === 'object' && v !== null && '$ref' in v && typeof v.$ref === 'string'; export const parseRef = (ref: Ref): ParsedRef => { const dot = ref.$ref.indexOf('.'); if (dot === -1) throw new Error(`Invalid ref: "${ref.$ref}" (must contain a dot)`); const prefix = ref.$ref.slice(0, dot); const suffix = ref.$ref.slice(dot + 1); if (prefix === 'input') return { scope: 'input', port: suffix }; if (prefix === 'context') { if (!isContextKey(suffix)) throw new Error(`Unknown context key: "${suffix}"`); return { scope: 'context', key: suffix }; } return { scope: 'output', node: prefix, port: suffix }; }; export const foldRef = <R>( ref: ParsedRef, cases: { readonly input: (port: string) => R; readonly context: (key: ContextKey) => R; readonly output: (node: string, port: string) => R; }, ): R => { if (ref.scope === 'input') return cases.input(ref.port); if (ref.scope === 'context') return cases.context(ref.key); return cases.output(ref.node, ref.port); }; export const refKey = (ref: Ref): string => foldRef(parseRef(ref), { input: (port) => `input:${port}`, context: (key) => `context:${key}`, output: (node, port) => `output:${node}:${port}`, });