Skip to content

TypeScript tdbin codec: no CLI command, no List<T>, no scalar Option<T>, number not bigint #62

Description

@MelbourneDeveloper

Problem

tdbin is only half a wire format if only one language can speak it. A binary IPC channel needs at least two ends, and for us the second end is TypeScript — the VS Code extension and its webview UI consume every wire type in live-ipc.td.

Three separate blockers stop that today.

1. No CLI path

$ typediagram --help
…
TDBIN encode emits a generated Rust ADT+codec module; decode emits codec impls
for generated Rust ADTs; verify validates schema support.

docs/tdbin.html confirms it:

"Generation requires using the Node.js API programmatically since the current CLI has TDBIN commands for Rust only."

Our codegen is a single script that shells out to the typediagram binary once per target (--to rust, --to typescript) and post-processes the output. Requiring a bespoke Node API integration for the TS half — while the Rust half is a CLI call — means two different generation mechanisms for one schema. Please expose TS codec generation through the same CLI surface, e.g. typediagram encode --lang typescript.

2. Lists are unsupported, and our payloads are almost entirely lists

docs/tdbin.html, TypeScript support boundary:

Not yet supported: "lists, scalar Option<T>, semantic scalars (DateTime, Uuid, Decimal), generics, inline enum-unions"

Every top-level payload we send is list-shaped:

type Report {
  action_hints: List<ActionHint>
  boilerplate_hints: List<ReportBoilerplateHint>
  clusters: List<ReportCluster>          # 712 of these in our own repo
  …
}
type ReportCluster {
  occurrences: List<ReportOccurrence>    # 1688 total
  …
}
type ReportPage    { clusters: List<ClusterSummary> }
type ReportDelta   { clusters_added: List<ReportCluster>, clusters_removed: List<String>, … }
type MergePlan     { parameters: List<MergeParameter> }
type MergeParameter{ per_site_arguments: List<String> }

Scalar Option<T> is likewise everywhere (min_score: Option<Float>, dimensions: Option<Int>, since_generation: Option<Int>). The Rust emitter already handles both — w.child_list / r.child_list::<T> and presence bits — so this is parity work, not new design.

3. number instead of bigint for Int

Per docs/tdbin-future-typescript.html, the TS value model still uses number for 64-bit integers. Our Int fields are byte offsets, line numbers, and generation counters — currently safe, but a format whose whole premise is exactness should not silently truncate at 2^53 on one end of the wire and not the other. Round-tripping Rust i64 → TS number → Rust i64 must be lossless or the schema hash guarantees mean nothing.

Ask

  1. typediagram encode/decode accept a language flag; TypeScript is a first-class target.
  2. TS codec parity with Rust for: List<T> (scalar, string, and composite element kinds), scalar Option<T> presence bits, and inline enum-unions.
  3. bigint for Int, with the migration documented.
  4. Publish the TS runtime as a resolvable entry point (typediagram-core/tdbin) with the same version-pinning story as the Rust crate, and state its bundle-size cost — a VS Code webview pays for every byte.
  5. Golden-vector conformance tests that run Rust-encode → TS-decode and TS-encode → Rust-decode over a schema containing lists and optional scalars, not just the Person/Contact fixtures.

Context

Deslop ships a VS Code extension, a JetBrains plugin, and two Rust servers, all generated from one .td file. Rust-only tdbin would let the servers talk to each other but force the editor clients to stay on JSON — meaning we would maintain two wire encodings for one schema forever. That is worse than staying on JSON.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions