Repository navigation
Conversation
🦋 Changeset detectedLatest commit: 43ed281 The changes in this PR will be included in the next version bump. This PR includes changesets to release 23 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: true
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
View your CI Pipeline Execution ↗ for commit 43ed281
☁️ Nx Cloud last updated this comment at |
🚀 Changeset Version Preview1 package(s) bumped directly, 22 bumped as dependents. 🟩 Patch bumps
|
Bundle Size Benchmarks
The following scenarios have bundle-size changes compared with the baseline:
Current gzip tracks all emitted client JS chunks. Initial gzip tracks only the entry/import graph. Trend sparkline is historical current gzip ending with this PR measurement; lower is better. |
Merging this PR will improve performance by 17.51%
|
…l is read A reasonless native abort() eagerly creates its AbortError, and Chromium and Node format a stack for it (1.6-5 us per call). Every client navigation aborts at least one router controller, and almost none of their signals are ever read. RouterAbortController records the abort internally and performs the native abort only once the signal has been read: the signal getter flushes a deferred abort, so readers still see an aborted signal with the engine's AbortError and the first reason wins. Internal cancellation checks use the internal record. Internal waits registered while the signal is unread settle without native listeners, before any public listener could have been registered, so listener ordering is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5e413ff to
43ed281
Compare
🎯 Changes
Every client navigation aborts at least one router controller (the previous transaction's, plus released loader flights). A reasonless native
abort()eagerly creates its AbortError, and Chromium and Node format a stack for it: 1.6–5 µs per call in Chromium, 2.4–3.5 µs in Node, 0.5 µs in WebKit. Almost none of these signals are ever read.RouterAbortController(inload-client.ts) defers the native abort until.signalis read:_ainstead ofsignal.aborted.waitFortakes the controller and rejects with it._wand settle before the native abort. No public listener can exist before the first read, so listener ordering is unchanged. Once the signal is read, internal waits use native listeners again.aborted === truewith the engine's AbortError. The first reason wins, including falsy custom reasons. Listeners added before the abort fire synchronously, as before.Perf: client-nav medians in µs/step, 5 alternating rounds in fresh processes, with an A/A control:
WebKit was neutral in earlier runs. Firefox was not measured.
Bundle size (gzip vs main). Raw size shrinks in every scenario (−7 to −79 B). The gzip growth is the class plus the parked-wait branch: their tokens are new, while the removed
.signal.abortedreads compressed well.Deferring only the abort, without parking internal waits, is +12 B on react-router.minimal but gets only about −6% in Chromium on loaders and async-pipeline. Always parking internal waits, even after the signal is read, would be +5 B, but it changes ordering in a way users can observe, so I rejected it. A test pins that ordering.
Semantics review: six adversarial reviews compared PR and main differentially, using public APIs only. The areas were self-abort, reasons, listener/microtask timing, lanes and shared flights, identity/environment, and memory. None found a behaviour difference. Remaining caveats:
.signalread, not at theabort()call._a,_oand_w. They are visible inObject.keys,JSON.stringifyand the devtools Explorer. Symbol keys would hide them, at +39 B gzip.Object.freeze(abortController)) now breaks it.abortController.constructor !== AbortController.instanceof AbortControllerstill holds.AbortControllermust exist when router-core is imported. All supported runtimes provide it.Tests:
tests/deferred-abort-signal.test.tspins:clearCache/invalidate, with the signal unread or read late;AbortController.prototype.abortthat observes the optimization. This is the only test that fails on main.Mutants caught: missing flush, dropping parked waits on read, always parking, flushing without the reason, a scalar
_a.Passing:
test:typesandtest:eslintfor all four;react-router/basic-file-based(206),react-start/basicvite-ssr (139),react-start/selective-ssr(11).✅ Checklist
🚀 Release Impact
🤖 Generated with Claude Code