UNPKG

@macroforge/mcp-server

Version:

MCP server for Macroforge documentation and code analysis

71 lines (49 loc) • 2.31 kB
# Cycles & Forward References `JSON.stringify` throws on a cycle. Macroforge's generated encoders handle cyclic graphs by assigning identities and emitting back-references. ```typescript /** @derive(Encode, Decode) */ class Node { name: string; parent?: Node; children: Node[]; } const root = new Node(); const child = new Node(); root.children = [child]; child.parent = root; // cycle const json = Node.encode(root, true); // keepMetadata ``` ## Metadata Cycle detection works by tagging objects with `__type` and `__id` as they're visited; a repeat visit emits a reference to the existing id instead of recursing. Those fields are **stripped from the output by default**. Pass `keepMetadata: true` to retain them: which you must do if you intend to decode a cyclic graph back, since the ids are what reconnect it. ```typescript Node.encode(root); // clean payload, cycles flattened Node.encode(root, true); // round-trippable, includes __type/__id ``` For API responses you usually want the default. For persistence or transferring an object graph, keep the metadata. ## The context object `encodeWithContext` / `decodeWithContext` take a context that carries the identity map. The top-level `encode` / `decode` create one for you; you only need the context form when composing encoders by hand. ## Forward references While decoding, a reference may point at an object that hasn't been constructed yet. Rather than failing, the decoder records a `PendingRef` placeholder and patches it once the target exists. Two consequences worth knowing: - The field is temporarily `null` between the placeholder being recorded and patches being applied. If you read the graph mid-decoding you may observe that. - `ctx.applyPatches()` performs the fix-up. The top-level `decode` calls it for you. ## `freeze` Pass `freeze: true` to deep-freeze every object produced: ```typescript const r = Node.decode(input, { freeze: true }); ``` Freezing happens after patches are applied, so cyclic graphs freeze correctly rather than being frozen mid-construction. ## Errors Structural failures inside `decodeWithContext` throw `DecodeError`. The top-level `decode` catches these and returns `{ success: false, errors }` instead: so the entry point never throws, while the internal context form does.