tree-result.js (2628B)
1 import { exportTree } from './tree-export.js'; 2 export const RESULT_NODE_LIMIT = 100_000; 3 export function isTreeResult(value) { 4 return ['tricu', 'triangle', 'dag', 'ternary'].includes(value); 5 } 6 // Backend snapshots describe physical sharing, not structural hash-consing. 7 // Reject incomplete/cyclic observations instead of exporting a plausible prefix. 8 export function snapshotBundle(snapshot) { 9 if (snapshot.truncated) 10 throw new Error('Result snapshot exceeds 100,000 nodes; refusing a partial export'); 11 const aliases = new Map(snapshot.aliases.map(a => [a.from, a.to])); 12 const byId = new Map(snapshot.nodes.map(n => [n.id, n])); 13 const resolve = (raw) => { 14 let id = raw; 15 const seen = new Set(); 16 while (aliases.has(id)) { 17 if (seen.has(id)) 18 throw new Error('Snapshot alias cycle'); 19 seen.add(id); 20 id = aliases.get(id); 21 } 22 return id; 23 }; 24 const nodes = [], mapped = new Map(), active = new Set(); 25 const root = resolve(snapshot.root), stack = [[root, false]]; 26 while (stack.length) { 27 const [raw, leave] = stack.pop(), id = resolve(raw); 28 if (mapped.has(id)) 29 continue; 30 const node = byId.get(id); 31 if (!node) 32 throw new Error('Incomplete result snapshot'); 33 const arity = { leaf: 0, stem: 1, fork: 2 }[node.kind]; 34 if (arity === undefined || node.children.length !== arity) 35 throw new Error('Invalid snapshot constructor'); 36 if (!leave) { 37 if (active.has(id)) 38 throw new Error('Cyclic result cannot be exported as a DAG'); 39 active.add(id); 40 stack.push([id, true]); 41 for (const child of [...node.children].reverse()) 42 stack.push([child, false]); 43 } 44 else { 45 active.delete(id); 46 const cs = node.children.map(c => mapped.get(resolve(c))); 47 if (cs.some(c => c === undefined)) 48 throw new Error('Invalid result snapshot order'); 49 mapped.set(id, nodes.length); 50 nodes.push(node.kind === 'leaf' ? { kind: 'leaf' } : node.kind === 'stem' ? { kind: 'stem', child: cs[0] } : { kind: 'fork', left: cs[0], right: cs[1] }); 51 } 52 } 53 return { nodes, roots: [mapped.get(root)], exports: [{ name: 'tree', root: mapped.get(root), kind: 'term', abi: 'arboricx.abi.tree.v1' }], metadata: {} }; 54 } 55 export function treeResult(snapshot, format) { 56 const file = exportTree(snapshotBundle(snapshot), 'tree', format); 57 return { type: format, value: file.text, file }; 58 }