[docs-infra] Use length-prefixed binary framing for SocketServer IPC#1309
Draft
[docs-infra] Use length-prefixed binary framing for SocketServer IPC#1309
Conversation
The `SocketServer` ↔ `SocketClient` protocol used newline-delimited
JSON: each message was `JSON.stringify(message) + '\n'`, and the
receiver buffered incoming bytes as a JS string and split on '\n'
before `JSON.parse`ing. Two linked failure modes on large consumer
projects:
1. `JSON.stringify` threw `RangeError: Invalid string length` once a
payload's UTF-8 representation exceeded Node's ~500 MB string cap.
Reproducible on mui-x `DataGridProps` (131 direct props with a
fully expanded generic chain) — the worker pool crashed with
RangeError: Invalid string length
at JSON.stringify (<anonymous>)
at SocketServer.sendResponse (.../socketServer.mjs:168:26)
and the validate CLI terminated reporting "0 files updated" after
the workers died.
2. The client-side decoder concatenated each chunk via
`this.buffer += data.toString()`. That is O(n) UTF-8 decoding + O(n)
string re-allocation per chunk, which adds up to O(n²) on payloads
delivered in many small TCP segments, and shares the same string
length ceiling as the encoder.
Fix: replace NDJSON with length-prefixed binary frames using
`v8.serialize` / `v8.deserialize`. That's the same structured-clone
algorithm `worker_threads.postMessage` uses internally; it has no
UTF-8 string-length ceiling (max frame body is 2^32 − 1 bytes ≈ 4 GB),
preserves binary structures faithfully, and is faster than JSON on
deeply nested objects.
Wire format:
[ 4-byte big-endian uint32 body length ][ body bytes ]
Changes:
* New `socketFraming.ts` module exposing `encodeFrame(message): Buffer`
and a stateful `FrameDecoder` that handles partial frames, multiple
frames delivered in one chunk, and a single frame split across many
chunks (including one byte at a time). The decoder compacts its
internal buffer whenever a complete frame is emitted — no O(n²)
re-concat on large payloads.
* `socketServer.ts`: data handler routes chunks through `FrameDecoder`;
`sendResponse` encodes via `encodeFrame`. The previous band-aid
try/catch around `JSON.stringify` is dropped — `v8.serialize` only
fails on structured-clone-incompatible shapes (functions, host
objects), which would be a coding bug not a size issue, and those
are still caught and reported as a recoverable per-request error.
* `socketClient.ts`: symmetric changes on the client side. The
`buffer = ''` string state is replaced with a `FrameDecoder`
instance.
* `socketFraming.test.ts`: round-trip tests for primitives, non-JSON
types (`Map`, `BigInt`, `Uint8Array`), multiple frames per chunk,
partial frames split byte-by-byte, a trailing partial frame
buffered across two pushes, and a 200 MB payload (far above any
realistic JSON.stringify ceiling).
Verified end-to-end on mui-x: the `docs-infra validate --types`
command processes 290+ generated `types.*.ts` entries (including
`DataGrid`, `DataGridPro`, `DataGridPremium`, and all of the main
`@mui/x-*` component exports) without a single
`RangeError: Invalid string length`. `pnpm test` for
`@mui/internal-docs-infra` stays green; 8 new tests added.
Deploy previewhttps://deploy-preview-1309--mui-internal.netlify.app/ PerformanceTotal duration: 15.77 ms -0.37 ms(-2.3%) | Renders: 4 (+0) | Paint: 71.04 ms +2.05 ms(+3.0%)
Bundle size
Check out the code infra dashboard for more information about this PR. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces the NDJSON framing between
SocketServerandSocketClientwith a length-prefixed binary format usingv8.serialize/v8.deserialize.Why:
JSON.stringifycrashed withRangeError: Invalid string lengthon payloads whose UTF-8 size exceeds Node's ~500 MB string cap. mui-xDataGridProps(131 props, fully-expanded generic chain) reliably reproduced this and took down the worker pool. The client-side decoder also paid O(n) UTF-8 cost per chunk viabuffer += chunk.What:
socketFraming.tswithencodeFrame/FrameDecoder(4-byte BE length prefix +v8.serializebody).SocketServer.handleConnection/sendResponseandSocketClient.handleData/sendRequestswitched to the new codec.v8.serializeis the same structured-clone used internally byworker_threads.postMessage, so payload shape guarantees are unchanged.