Skip to content

[Feature] A message that works in-process can silently lose its prototype on a worker hop and fail on the wire, and nothing catches either #1386

Description

@pathosDev

Use case

A message in this framework crosses up to three boundaries, and each has different semantics:

Boundary Mechanism What arrives
In-process reference the same object; prototype intact, methods work
Worker hop bare postMessage → structured clone a plain object — prototype gone, methods gone; a non-cloneable field throws DataCloneError
Cluster wire the serializer different rules again, depending on the registered serializer and tag definitions

The worker path is src/runtime/worker/NodeWorkerBackend.ts:96 (this.native.postMessage(value, transfer)) and src/worker/WorkerBroker.ts:102. No serializer is involved; the runtime's structured clone algorithm is.

The test suite exercises the first boundary almost exclusively. So a message type can be green locally, degrade silently across a worker hop, and fail on the wire — and the middle case is the dangerous one, because it produces no error at all. A class instance sent to a worker arrives as a plain object. It does not throw. It fails later, as a match arm that does not fire, a ts-pattern .exhaustive() that throws somewhere unrelated, or a method that is undefined.

There are zero occurrences of structuredClone or DataCloneError anywhere in src/. Nothing in the framework detects, reports, or documents this transition.

Proposed shape

A test-only mode that forces every local delivery through the boundary semantics it would meet in production, so the failure surfaces at development time rather than the first time somebody enables WorkerCluster or joins a second node.

  • Serializer round-trip mode. Every local tell is encoded and decoded before delivery. A message with no serializer binding throws immediately, naming the type and the sending actor.
  • Structured-clone mode. The JavaScript-specific half, and the more valuable one. Apply structuredClone to the payload and report when the round-trip is lossy — specifically when the input had a prototype other than Object.prototype and the output does not. That is the silent case, and it is mechanically detectable.
  • Off by default, on in the testkit. The natural default is enabled for TestKit and disabled in production, since the cost is real. It must be switchable both ways: a user should be able to run their own suite with it off, and to enable it in a staging build.
  • Configuration follows the established precedence, and the key must be reachable from ConfigKeys and read by something in src/ per the dead-key rule.

Worth deciding at implementation time: whether the two modes are one setting with levels or two independent switches. They catch different defects — one a hard failure, the other a silent degradation — which argues for independent.

Acceptance

  • A mode forces local messages through a serializer round-trip and fails loudly on an unserializable message.
  • A mode detects prototype loss across structuredClone and reports the message type.
  • Enabled by default under TestKit, disabled in production, switchable both ways.
  • Running the existing suite with it enabled produces a list of affected message types — that list goes on this issue, whether it is empty or not.
  • EN + DE docs explain the three boundaries and what each mode catches.

Verification status

Confirmed by reading and search. grep -rn "structuredClone\|DataCloneError" over src/ returns nothing. The worker send path was read at src/runtime/worker/NodeWorkerBackend.ts:96 and src/worker/WorkerBroker.ts:102; neither consults a serializer. grep for a serialize-messages-style switch across src/config/Reference.ts and src/config/ConfigKeys.ts returns nothing. The structured-clone prototype behaviour is specified by the HTML structured-clone algorithm and is not framework-specific.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestpriority: highTop priority — high impact, plan nextproduction-goalBlocks or defines the path to production readiness

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions