toWire

tcmts.cli.codec.toWire · #gbf47mon · term · runs anywhere

export function toWire(v: unknown, path: string, seen: Set<object>): unknown {
  if (v === undefined) return { $u: 1 };
  if (v === null || typeof v === "boolean" || typeof v === "string") return v;
  if (typeof v === "number") return Number.isFinite(v) && !Object.is(v, -0) ? v : { $n: Object.is(v, -0) ? "-0" : String(v) };
  if (typeof v === "bigint") return { $b: v.toString() };
  if (typeof v !== "object") throw new TypeError(`can't send ${typeof v} at ${path} over the network (only plain data crosses)`);
  if (seen.has(v)) throw new TypeError(`can't send a circular structure at ${path}`);
  seen.add(v);
  try {
    if (v instanceof Date) return { $d: v.toISOString() };
    if (Array.isArray(v)) return v.map((x, i) => toWire(x, `${path}[${i}]`, seen));
    if (v instanceof Map) return { $m: [...v].map(([k, x], i) => [toWire(k, `${path}<key ${i}>`, seen), toWire(x, `${path}.get(…)`, seen)]) };
    if (v instanceof Set) return { $s: [...v].map((x, i) => toWire(x, `${path}<item ${i}>`, seen)) };
    const proto = Object.getPrototypeOf(v);
    if (proto !== Object.prototype && proto !== null) {
      throw new TypeError(`can't send a ${proto?.constructor?.name ?? "class"} instance at ${path} over the network (only plain data crosses)`);
    }
    const out: Record<string, unknown> = {};
    for (const [k, x] of Object.entries(v)) out[k] = toWire(x, `${path}.${k}`, seen);
    // an object that looks like one of our tags is wrapped so it decodes as itself
    const keys = Object.keys(out);
    return keys.length === 1 && keys[0]!.startsWith("$") ? { $o: out } : out;
  } finally {
    seen.delete(v);
  }
}

used by