Repository navigation
Conversation
emileber
marked this pull request as ready for review
October 23, 2026 22:46
The plain-object branches of createBasicEncoder's encode and decode spread the accumulator on every key, which is quadratic in the number of keys: an object with a few thousand keys takes seconds to cross an endpoint. Build the result with Object.fromEntries instead. Like the spread, it defines own data properties, so keys such as __proto__ and constructor stay ordinary keys, and neither a defined Object.prototype.get or .set nor an inherited setter can change the result. It's also faster than the spread for ordinary payloads. The encoded format is unchanged. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
emileber
force-pushed
the
emileber/linear-object-encoding
branch
from
October 27, 2026 05:52
f656ae1 to
50229dc
Compare
This branch has not been deployed
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
createBasicEncoderencodes and decodes plain objects in time quadratic in their number of keys. This change builds objects withObject.fromEntriesinstead, making both linear without changing the encoded format. It's also faster than the current spread for ordinary payloads.Context
In both
encodeanddecode, the plain-object branch rebuilds the accumulator on every key:Arrays and primitives are linear, so payload size in bytes isn't the problem; key count per object is. An object with a few thousand keys (for example, a snapshot of a key–value store) takes seconds to cross an endpoint.
One object with
nkeys and ~60-byte string values (Node 22, laptop):Ordinary payloads get faster too. A full call from a page to a Web Worker endpoint, carrying a nested payload of about 150 keys, 4 levels deep (Playwright, native spread for the "before" column):
Approach
Object.fromEntries(Object.keys(value).map(…))in bothencodeanddecode. Like the object spread it replaces, it defines own data properties, so:__proto__andconstructorstay ordinary keys instead of changing the result's prototype;Object.prototype.getor.setis defined, or whereObject.prototypehas a setter for a key, doesn't change the result. Plain assignment would let an inherited setter intercept the key.seen), function IDs and retainer propagation are unchanged, so endpoints on 1.4.7 and on this version can talk to each other.Object.fromEntriesis ES2019 (Chrome 73, Firefox 63, Safari 12.1). That is within@shopify/browserslist-config's targets. Consumers supporting older browsers need the usual polyfill.Object.definePropertywith a fresh null-prototype descriptor per key. It was linear but slower than the spread for ordinary payloads (187 µs per call in Chromium), because of the per-key descriptor allocation.One behavior difference worth noting: when the spread is compiled to
Object.assign(older Babel targets), a__proto__key was previously dropped or changed the prototype of the result. It is now an own property, matching native spread.