# wity-rkf -- API Quick Reference Pure API surface. No prose. Signatures, types, constants, and schema attributes. Version: rkf-core v2.0.0 --- ## IMPORT PATHS ```js // Core -- parse/evaluate/serialize/validate import { parse, setXmlParser, evaluate, initState, applyUpdate, applyUpdates, serialize, validate, evalExpr, evalGate, setGraphProvider, rkfToKnowledge, RkfEdit, splitBlocksByHeading, BLOCK_KINDS, SCHEMA_VERSION, ELEMENT_TAGS, ENV_TAGS, STRUCTURE_TAGS, STATE_TAGS, LAYOUT_TAGS, MARK_TAGS, PORT_TAGS, PORT_TYPES, PORT_DIRECTIONS, ENV_TYPES, TEXT_ANCHOR_VALUES, SCALE_TYPE_VALUES, AXIS_ORIENT_VALUES, MATH_DISPLAY_VALUES, } from '@wity-ai/rkf-core'; ``` --- ## FUNCTIONS ### parse(xml: string) -> RkfDocument ``` Input: a document (the stored form of an RKF document) Output: { version, id?, title?, vars: RkfVar[], sections: RkfSection[] } Throws: if the graph module was not injected, or the root element is not ``` ### setGraphProvider(graph: { GraphAbstract, serialize, parse, setXmlParser }) -> void Inject @wity/graph-headless. Required for parse() / serialize(). ### rkfToKnowledge(rkfXml: string) -> string Convert a document in the retired format to . ### setXmlParser(parserInstance: { parseFromString(xml, mime) -> Document }) -> void Inject DOMParser for Node.js (forwarded to graph-headless). Browser uses native DOMParser. ### initState(doc: RkfDocument) -> Object ``` Collects all rkf-var declarations (document + section + machine level). Returns plain object: { [varId]: initialValue } ``` ### evaluate(doc: RkfDocument, state: Object) -> ComputedFrame ``` Input: parsed document + current state Output: { state: Object, // resolved state including derives marks: ComputedMark[], // all visible marks (recursively collected) controls: Object[], // all visible dir="in" ports with current value envs: Object[], // all visible environments (prose, math, image, etc.) gates: Object, // named gate values { [gateId]: boolean } outputs: Object[], // all dir="out" ports with current value elastics: Object[], // elastic variable reset info { var, resetTo, delayMs } sections: Object[], // section tree with resolved visibility } Pure function. No side effects. ``` ### applyUpdate(update: RkfTransform, state: Object, event: string, gateValues?: Object) -> Object ``` Checks update.on === event (if on is set). Evaluates update.trigger (gate id looked up in gateValues, or expression). Applies rkf-set assignments sequentially. Returns new state object (or same ref if no match). ``` ### applyUpdates(updates: RkfTransform[], state: Object, event: string, gateValues?: Object) -> Object Applies all transforms sequentially for the given event. Accepts optional gateValues. ### serialize(doc: RkfDocument) -> string Serializes document to XML string. Round-trips with parse(). ### validate(doc: RkfDocument) -> { valid: boolean, errors: string[], warnings: string[] } Schema validation. Checks required attrs, duplicate var IDs, structure. ### evalExpr(expr: string, state: Object) -> any ``` Safe recursive descent evaluator. Ops: + - * / % == != < > <= >= && || ! ? : () Builtins: Math.abs, Math.floor, Math.ceil, Math.round, Math.min, Math.max, Math.sqrt, Math.sin, Math.cos, Math.tan, Math.PI Dot access: obj.field (including nested machine vars: sub.varName) Literals: number, true, false, 'string' Variables: resolved from state object ``` ### evalGate(gate: string | null, state: Object) -> boolean Returns true if gate is null/undefined. Otherwise evaluates as boolean expression. --- ## TYPES (JSDoc) ### RkfDocument ``` { version: string, id?: string, title?: string, vars: RkfVar[], sections: RkfSection[] } ``` ### RkfSection ``` { tag: 'rkf-section', id: string, label?: string, gate?: string, vars: RkfVar[], children: RkfElement[] } ``` ### RkfEnv ``` { tag: 'rkf-env', type: 'prose'|'math'|'image'|'video'|'model'|'audio', content?: string, src?: string, alt?: string, display?: 'block'|'inline', gate?: string, ports?: RkfPort[], blocks?: RkfBlock[], pos?: { x, y, w, h? } } ``` ### RkfBlock (v2.0 content block) ``` { kind: 'text'|'image'|'math'|'embed'|'machine'|'mermaid'|'row', id: string } id: stable, unique within the card kind='text': { content?, heading?, list?, lineHeight? } kind='image': { src, alt? } kind='math': { content } kind='embed': { id, application: 'workbook'|'system'|'decision-matrix'|'swot'|'eisenhower'|'ooda', snapshot: Object } kind='machine': { id, vars, gates, derives, transforms, ports, machines, children } kind='mermaid': { content } kind='row': { columns, cells: { blocks: RkfBlock[] }[] } a cell holds any kind except row, embed, machine (earlier shape { columns?, blocks } — one block per cell — is read and upgraded) ``` ### RkfMachine ``` { tag: 'rkf-machine', id: string, gate?: string, vars: RkfVar[], gates: RkfGate[], derives: RkfDerive[], transforms: RkfTransform[], ports: RkfPort[], machines: RkfMachine[], children: RkfElement[] } ``` ### RkfVar ``` { tag: 'rkf-var', id: string, init: number|string|boolean|array, type: 'number'|'string'|'boolean'|'array', elasticity?: number, elasticDefault?: any } ``` ### RkfGate ``` { tag: 'rkf-gate', id: string, expr: string } ``` ### RkfDerive ``` { tag: 'rkf-derive', id: string, expr: string } ``` ### RkfTransform ``` { tag: 'rkf-transform', trigger: string, on?: string, sets: RkfSet[] } ``` ### RkfSet ``` { tag: 'rkf-set', var: string, expr: string, when?: string } ``` ### RkfPort ``` { tag: 'rkf-port', dir: 'in'|'out', var: string, type?: string, label?: string, hint?: string, gate?: string } dir="in", type="continuous": { min, max, step? } dir="in", type="choice": { options, labels? } dir="in", type="boolean": {} dir="in", type="clock": { interval? } dir="in", type="increment": { by? } dir="in", type="event": { on, children: RkfElement[] } dir="out": { var } ``` ### RkfCanvas ``` { tag: 'rkf-canvas', width: number, height: number, padding?: number, gate?: string, children: RkfElement[] } ``` ### RkfScale ``` { tag: 'rkf-scale', id: string, domain: string, range: string, type: 'linear'|'log'|'ordinal' } ``` ### RkfAxis ``` { tag: 'rkf-axis', scale: string, orient?: 'bottom'|'left'|'top'|'right', label?: string } ``` ### RkfGroup ``` { tag: 'rkf-group', id?: string, gate?: string, children: RkfElement[] } ``` ### Mark types ``` rkf-scatter: { data, x, y, colorBy?, radius?, gate? } rkf-line: { x1?, y1?, x2?, y2?, stroke?, strokeWidth?, dash?, gate? } rkf-arrow: { x1, y1, x2, y2, color?, width?, label?, gate? } rkf-rect: { x, y, width, height, fill?, stroke?, rx?, gate? } rkf-circle: { cx, cy, r, fill?, stroke?, gate? } rkf-arc: { cx, cy, r, startAngle, endAngle, fill?, stroke?, gate? } rkf-text: { x?, y?, content, textAnchor?, fontSize?, color?, fontWeight?, gate? } rkf-image: { src, x?, y?, width?, height?, gate? } rkf-path: { d, fill?, stroke?, strokeWidth?, gate? } rkf-area: { data, x, y0, y1, fill?, gate? } ``` ### ComputedMark ``` { tag: string, visible: boolean, x?: number, y?: number, x1?: number, y1?: number, x2?: number, y2?: number, r?: number, width?: number, height?: number, fill?: string, stroke?: string, content?: string } ``` ### ComputedFrame ``` { state: Object, marks: ComputedMark[], controls: Object[], envs: Object[], gates: Object, outputs: Object[], elastics: Object[], sections: Object[] } ``` --- ## SCHEMA CONSTANTS ```js SCHEMA_VERSION = '2.0' ELEMENT_TAGS = [...ENV_TAGS, ...STRUCTURE_TAGS, ...STATE_TAGS, ...LAYOUT_TAGS, ...MARK_TAGS, ...PORT_TAGS] ENV_TAGS = ['rkf-env'] STRUCTURE_TAGS = ['rkf-section', 'rkf-machine', 'rkf-canvas', 'rkf-group'] STATE_TAGS = ['rkf-var', 'rkf-derive', 'rkf-gate', 'rkf-transform', 'rkf-set'] LAYOUT_TAGS = ['rkf-scale', 'rkf-axis'] MARK_TAGS = ['rkf-scatter', 'rkf-line', 'rkf-arrow', 'rkf-rect', 'rkf-arc', 'rkf-circle', 'rkf-text', 'rkf-image', 'rkf-path', 'rkf-area'] PORT_TAGS = ['rkf-port'] PORT_TYPES = ['continuous', 'choice', 'boolean', 'clock', 'increment', 'event'] PORT_DIRECTIONS = ['in', 'out'] ENV_TYPES = ['prose', 'math', 'image', 'video', 'model', 'audio'] BLOCK_KINDS = ['text', 'image', 'math', 'embed', 'machine', 'mermaid', 'row'] TEXT_ANCHOR_VALUES = ['start', 'middle', 'end'] SCALE_TYPE_VALUES = ['linear', 'log', 'ordinal'] AXIS_ORIENT_VALUES = ['bottom', 'left', 'top', 'right'] MATH_DISPLAY_VALUES = ['block', 'inline'] ``` --- ## EDIT API -- RkfEdit ```js import { RkfEdit, splitBlocksByHeading } from '@wity-ai/rkf-core'; ``` Headless, pure over the document model. Every function mutates what it is given in place and returns what a caller records/reports. No undo, events, selection or rendering. Addressing: section `id` · child: index in its section or stable `_uid` · connector `_uid` · block `id` (unique within its card) · mark `id` (unique in the document) · machine `id` · var/gate/derive `id` (its name) · transform/port generated `id`. ``` createDoc() -> doc one 'main' section normalizeDoc(doc) -> doc viewports, _uids, pos, blocks, ids on blocks/marks/transforms/ports, row cells; idempotent findSection(doc, sectionId) -> section | null addSection(doc, label = 'New Section') -> section renameSection(doc, sectionId, label) -> section | null throws on an empty name removeSection(doc, sectionId) -> section | null setDecisionSpace(doc, xml) -> doc opaque string; falsy clears getChild(doc, sectionId, childIndex) -> child | null findChildIndex(doc, sectionId, childUid) -> index | -1 nextBlockPos(doc, sectionId) -> { x, y, w } addCard(doc, sectionId, { content?, blocks?, pos? }) -> { child, childIndex } | null addMachine(doc, sectionId) -> { child, childIndex } | null insertChild(doc, sectionId, childIndex, child) -> boolean removeChild(doc, sectionId, childIndex) -> { child, connectors } | null links to/from it removed too moveChild(doc, sectionId, childIndex, { x, y }) -> previous pos | null | undefined resizeChild(doc, sectionId, childIndex, width) -> { before, after } | null min MIN_BLOCK_WIDTH (120) setChildBlocks(doc, sectionId, childIndex, blocks) -> previous blocks | null setChildContent(doc, sectionId, childIndex, content) -> boolean splitCard(doc, sectionId, childUid) -> { removed: { child, childIndex, connectors }, added: [{ child, childIndex }] } | null splitBlocksByHeading(blocks) -> block[][] | null groups at the shallowest heading level occurring 2+ times CARD_STYLE_KEYS = ['borderColor', 'borderWidth', 'backgroundColor'] checkCardStyle(attrs) -> attrs hex colours; borderWidth 1..4; null/'' clears; throws otherwise setChildStyle(doc, sectionId, childUid, attrs) -> child | null getConnectors(doc, sectionId) -> connector[] addConnector(doc, sectionId, { from, fromPort, to, toPort, color?, width?, data? }) -> connector | null updateConnector(doc, sectionId, connectorUid, attrs) -> connector | null removeConnector(doc, sectionId, connectorUid) -> connector | null ``` Blocks (container = a card or a row cell; these throw when the block/row does not exist): ``` newBlock(kind, opts?) -> block opts: heading, list (text) · application (embed) · columns (row) findBlock(container, blockId) -> { list, index, block, row, cellIndex } | null insertBlock(container, block, where?) -> { block, index } updateBlock(container, blockId, patch) -> { block, before } null/undefined removes a field; kind, id, cells, blocks are fixed removeBlock(container, blockId) -> { block, index, row, cellIndex } moveBlock(container, blockId, where?) -> { block, index } splitTextBlock(container, blockId, { before, after }) -> { block, created } mergeIntoPrevious(container, blockId) -> { into, removed, offset } setBlocks(container, blocks) -> previous list walkBlocks(container) -> iterator of { block, row, cellIndex } normalizeBlocks(blocks) -> blocks · normalizeRow(row) -> row setRowColumns(container, rowId, columns) -> { row, columns, cells } shrinking drops only trailing empty cells removeRowCell(container, rowId, cellIndex) -> { row, cell, columns } a row keeps at least one cell MAX_ROW_COLUMNS = 8 where: { index } | { afterId } | { beforeId } | { rowId, cell, index? } | omitted = end ``` Drawing marks (a section's rkf-canvas; every shape also takes opacity 0..1): ``` line x1, y1, x2, y2 · color, width, dash arrow x1, y1, x2, y2 · color, width rect x, y, width, height · stroke, strokeWidth, fill, rx circle cx, cy, r · stroke, strokeWidth, fill path d · stroke, strokeWidth, fill text content, x, y · color, fontSize, fontWeight, anchor MARK_SHAPES · newMark(shape, attrs) -> mark · markShape(mark) -> shape | null findMark(doc, markId) -> { section, index, mark } | null setMarkAttrs(doc, markId, attrs) -> { section, index, mark } | null normalizeMarks(doc) -> doc getDrawingCanvas(doc, sectionId) -> canvas | null · getMarks(doc, sectionId) -> mark[] addMark(doc, sectionId, mark) -> boolean updateMark(doc, sectionId, markIndex, attrs) -> mark | null · removeMark(doc, sectionId, markIndex) -> mark | null ``` Machines (a block of kind 'machine', always top-level in its card): ``` listMachines(doc) -> [{ section, child, machine }] findMachine(doc, machineId) -> { section, child, machine } | null renameMachine(doc, machineId, newId) -> { section, child, machine } | null findMachinePart(machine, part, id) -> { index, item } | null setMachinePart(machine, part, { id, newId?, ...fields }) -> { item, index, created, unknownRefs } removeMachinePart(machine, part, id) -> { item, usedBy } | null normalizeMachine(machine) -> machine part: 'var' { id, type: 'number'|'boolean'|'string', init, elasticity?, elasticDefault? } 'gate' { id, expr } 'derive' { id, expr } 'transform' { id, trigger, on?, sets: [{ var, expr, when? }] } 'port' { id, dir: 'in'|'out', var, label?, type?, ...settings } input port settings: continuous: min, max, step · choice: options, labels · boolean · clock: interval · increment: by ``` setMachinePart: only the fields given change; nothing changes when a rule is broken. Rules: names are identifiers (not true/false/Math) and unique among vars, gates and derives; expressions parse; derives/gates don't depend on each other in a circle; an input port's settings fit its type; an output port takes only var and label. Names not declared in the machine are returned as `unknownRefs`, not refused.