/** * References in value space. * * A reference is a pointer to another value object, by its id. It is encoded as * a one-key object `{ "$ref": "" }` (see `spec.md`): * * - Single-key rule: a reference is *exactly* one key named `$ref` whose value * is a string. An object with a `$ref` key plus anything else is data. * - The `$` prefix is system-owned, so data keys beginning with `$` are escaped * by doubling the leading `$` (`$ref` -> `$$ref`); the reader reverses it. * This makes a raw single-key `{ "$ref": ... }` always a real pointer. */ export interface Ref { $ref: string; } /** Build a reference to the value object with the given id. */ export function makeRef(id: string): Ref { return { $ref: id }; } /** True if `value` is a reference: a single-key object `{ $ref: }`. */ export function isRef(value: unknown): value is Ref { if (typeof value !== "object" || value === null || Array.isArray(value)) { return false; } const keys = Object.keys(value); return ( keys.length === 1 && keys[0] === "$ref" && typeof (value as Record).$ref === "string" ); } /** * A positional reference into an RGA sequence. Like a `{ $ref }`, but it resolves * to a *position* rather than a value: the offset of element `.` within * the fully-materialized `target` sequence — a byte offset for a string sequence, * an item index for an array sequence. Unlike `$ref`, an `$idx` may appear at any * depth of inline data (e.g. a facet's `byteStart`). * * `after: true` resolves to the position just *past* the element instead — the * exclusive boundary, used for an end offset like a facet's `byteEnd`. */ export interface IdxRef { $idx: { target: string; id: string; idx: number; after?: boolean }; } /** True if `value` is a positional reference: a single-key object `{ $idx: {…} }`. */ export function isIdxRef(value: unknown): value is IdxRef { if (typeof value !== "object" || value === null || Array.isArray(value)) { return false; } const keys = Object.keys(value); if (keys.length !== 1 || keys[0] !== "$idx") return false; const idx = (value as Record).$idx; return typeof idx === "object" && idx !== null && !Array.isArray(idx); } /** Escape a data key for storage: a leading `$` is doubled. */ export function escapeKey(key: string): string { return key.startsWith("$") ? "$" + key : key; } /** * Reverse `escapeKey`: un-double a doubled leading `$` (`$$ref` -> `$ref`). * * Only escaped data keys carry a doubled `$`. A single leading `$` marks a system * key (`$type`, `$link`, `$idx`, …) that was never escaped, so it passes through * verbatim — `$type` stays `$type`. */ export function unescapeKey(key: string): string { return key.startsWith("$$") ? key.slice(1) : key; }