You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
perf/hardening: request context, middleware context and handle store sit in V8 dictionary mode; request-derived keys on plain objects #1109
A read-only performance review measured three per-request objects that sit in V8's slow "dictionary mode" for their whole lifetime, and found a few places where request-derived keys are looked up on plain objects. These are server-only changes with no public API, Flight or client bundle impact. Measured on Node 24.12 (V8 13).
Performance: object literals with get/set members
A get/set member inside an object literal puts the object in dictionary mode, so every property read on it is a hash lookup instead of a fast inline-cached load. Adding the accessor with Object.defineProperty keeps the object fast. request-context.ts already does that for theme.
Location
Object
Measured / estimated cost today
server/request-context.ts (the request context literal, get res() / set res())
62 members, once per request
~690 ns and ~3.3 KB to create, against ~20 ns and ~520 B as a data-only literal; slower reads for the whole request
router/middleware.ts (middleware context getters)
~21 members, per middleware call
est. ~300 ns and ~1.2 KB per call
server/handle-store.ts (store getters)
~20 members, once per request
est. ~300 ns and ~1 KB per request
Possible lazy step: the request context builds a handle snapshot eagerly on every non-streaming request, but only ctx.get(handle) reads it, and only after rendered(). Building it on first read is estimated to save ~310 ns and ~1.5 KB per request, if the store is final at that point.
Hardening: request-derived keys on plain objects
None of these pollutes Object.prototype. They can make lookups hit inherited properties, or carry odd keys into user code:
Route matching looks up static children in a plain object keyed by raw URL segments, so segments such as __proto__, constructor or toString hit inherited properties. A null prototype on those maps fixes it, keeps lookups fast (measured), and makes reads slightly faster.
Cookie parsing builds a plain object keyed by cookie names, so cookies().has("toString") and cookies().get("constructor") see inherited values.
The loader fetch endpoint spreads request JSON into ctx.params. Keys and values should be validated: strings only, with reserved keys rejected.
Code that copies loader-returned objects may carry an own __proto__ key from user JSON.parse data, which then fails in Flight. To verify.
Not worth doing (measured)
Null-prototype objects for params, segments, loader data or handle data: they are slower and larger, React Flight rejects them in production, and user code calling params.hasOwnProperty(...) would break.
Map for params or handle buckets: slower and larger for 1-3 entries.
A shared frozen empty params object: handlers may mutate params.
Client-side swaps: each costs client bundle bytes for well under a microsecond per navigation.
Activity
docs(router): trim render-pass.ts comments to the invariants