Skip to content

js/deltic: websocket.ts fails type-checking under a dom-lib config (WebSocket.send vs Uint8Array<ArrayBufferLike>) #43

Description

@lann

The deltic host module is browser-first JS, but it does not type-check in a compilation that includes the dom lib — which is exactly the configuration of a consumer's browser-facing module graph. Found while porting polymorph-iroh's upstream-iroh experiments onto deltic (polymorph-components/polymorph-iroh#41): the experiments' browser entries need dom for document etc., deno check type-checks local imports (the .deps sibling checkouts included), and adding dom to the shared config broke the graph on this module — so those browser entries now sit outside the type-check gate, with the bundle + Playwright suites carrying them instead (noted in that PR's experiments/iroh-relay-ws/host/deno.json comment).

Repro

Against current main (6632473) or the polymorph-iroh pin (2000f54), with the module's own js/deltic/deno.json plus one compilerOptions addition:

{
  "imports": { "@deltic/runtime/embedder": "…as in js/deltic/deno.json…" },
  "compilerOptions": { "lib": ["dom", "dom.iterable", "dom.asynciterable", "deno.ns"] }
}
$ deno check --config that.json js/deltic/websocket.ts
TS2345 [ERROR]: Argument of type 'string | Uint8Array<ArrayBufferLike>' is not assignable to parameter of type 'string | BufferSource | Blob'.
  Type 'Uint8Array<ArrayBufferLike>' is not assignable to type 'ArrayBufferView<ArrayBuffer>'.
      this.#ws.send(message.val as string | Uint8Array);
                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    at js/deltic/websocket.ts:460:21

One error; the rest of the file is clean under dom. Deno 2.9.5 / TypeScript 6.0.3.

Why it only shows up downstream

  • TypeScript's generic typed arrays (5.7+): bare Uint8Array means Uint8Array<ArrayBufferLike>, i.e. "possibly SharedArrayBuffer-backed".
  • The dom lib's WebSocket.send(data: string | BufferSource | Blob) excludes SAB-backed views (BufferSource = ArrayBufferView<ArrayBuffer> | ArrayBuffer), tracking the platform rule that shared buffers don't cross these APIs.
  • Deno's own WebSocket.send declaration is more permissive, so the repo's deno task check (default lib, no dom) stays green.

Suggested fix

The cast at the send site is the whole problem; tightening it is sound here: binary payloads reaching send are either lifted by the deltic embedder (list<u8> → always a fresh copy, per its value-mapping contract) or supplied by host-side callers, and a SAB-backed view would be refused by the platform API at runtime anyway — typing it away is strictly more honest:

this.#ws.send(message.val as string | Uint8Array<ArrayBuffer>);

(Alternative, if you'd rather push the constraint to the public surface: type Message's binary case as Uint8Array<ArrayBuffer> — but that ripples into every consumer's Message literals, so the local cast seems proportionate.)

Worth adding dom to a check lane (or a second deno check task with a dom-lib config) so the browser-consumer configuration is covered by the gate that this repo owns, rather than each consumer rediscovering it.

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