refactor: make event-chain depth part of PerformEventArgs#2991
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Contributor
📦 Bundle Stats —
|
| Metric | Value | vs main (3296788) |
|---|---|---|
| Internal (raw) | 803.9 KB | -166 B, -0.0% |
| Internal (gzip) | 154.3 KB | -65 B, -0.0% |
| Bundled (raw) | 1.41 MB | -166 B, -0.0% |
| Bundled (gzip) | 318.5 KB | -66 B, -0.0% |
| Import time | 103ms | -3ms, -2.7% |
@portabletext/editor/behaviors
| Metric | Value | vs main (3296788) |
|---|---|---|
| Internal (raw) | 467 B | - |
| Internal (gzip) | 207 B | - |
| Bundled (raw) | 424 B | - |
| Bundled (gzip) | 171 B | - |
| Import time | 2ms | -0ms, -10.0% |
@portabletext/editor/plugins
| Metric | Value | vs main (3296788) |
|---|---|---|
| Internal (raw) | 2.7 KB | - |
| Internal (gzip) | 894 B | - |
| Bundled (raw) | 2.5 KB | - |
| Bundled (gzip) | 827 B | - |
| Import time | 7ms | -0ms, -4.1% |
@portabletext/editor/selectors
| Metric | Value | vs main (3296788) |
|---|---|---|
| Internal (raw) | 81.8 KB | - |
| Internal (gzip) | 15.2 KB | - |
| Bundled (raw) | 77.6 KB | - |
| Bundled (gzip) | 14.1 KB | - |
| Import time | 8ms | -0ms, -1.0% |
@portabletext/editor/traversal
| Metric | Value | vs main (3296788) |
|---|---|---|
| Internal (raw) | 28.1 KB | - |
| Internal (gzip) | 5.6 KB | - |
| Bundled (raw) | 28.1 KB | - |
| Bundled (gzip) | 5.5 KB | - |
| Import time | 6ms | +0ms, +0.6% |
@portabletext/editor/utils
| Metric | Value | vs main (3296788) |
|---|---|---|
| Internal (raw) | 29.7 KB | - |
| Internal (gzip) | 6.2 KB | - |
| Bundled (raw) | 27.4 KB | - |
| Bundled (gzip) | 5.9 KB | - |
| Import time | 6ms | +0ms, +1.8% |
🗺️ . · ./behaviors · ./plugins · ./selectors · ./traversal · ./utils · Artifacts
Details
- Import time regressions over 10% are flagged with
⚠️ - Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.
📦 Bundle Stats — @portabletext/markdown
Compared against main (3296788c)
| Metric | Value | vs main (3296788) |
|---|---|---|
| Internal (raw) | 53.8 KB | - |
| Internal (gzip) | 9.8 KB | - |
| Bundled (raw) | 348.9 KB | - |
| Bundled (gzip) | 96.3 KB | - |
| Import time | 39ms | -2ms, -5.2% |
🗺️ View treemap · Artifacts
Details
- Import time regressions over 10% are flagged with
⚠️ - Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.
christianhg
force-pushed
the
refactor-event-chain-depth-field
branch
from
July 21, 2026 09:21
1017735 to
0de0bce
Compare
performEvent parameter
The depth backstop kept its counter in a module-level `WeakMap<PortableTextEditorEngine, number>` with try/finally unwinding, and `performEvent` was split into a guard wrapper plus `performEventInternal` purely to bolt that on. But the depth is call-stack-shaped state and all recursion is direct: the three `raise`/`forward`/`execute` sites are the only recursive callers and the editor machine is the only external entry. `depth` becomes a required field of `PerformEventArgs`: the machine passes `0`, the recursive sites pass `depth + 1`, the guard is the first statement of the single `performEvent` function, and the wrapper, the WeakMap and the try/finally all disappear. Required rather than defaulted so a future recursive call site cannot omit it and silently reset the chain, disabling the backstop for that path. `sendBack` re-entries go through the actor's event queue (sequential, not nested), so fresh chains correctly start at zero there, exactly as the ambient counter behaved after its `finally`. Also documents the two-sided window the depth limit sits in: it must exceed legitimate raise-within-raise nesting (~10-15 at the pathological end) and stay far below genuine stack exhaustion (dozens of real frames per nesting level, browser limits vary), with the asymmetry spelled out: too low silently drops legitimate events, too high only delays cycle detection by milliseconds. Net behavior unchanged; the cyclic-Behavior pin and the full suites pass without modification.
christianhg
force-pushed
the
refactor-event-chain-depth-field
branch
from
July 21, 2026 09:26
0de0bce to
68432a5
Compare
performEvent parameterPerformEventArgs
christianhg
marked this pull request as ready for review
July 21, 2026 09:28
christianhg
enabled auto-merge (rebase)
July 21, 2026 09:28
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.
Grooming follow-up to the depth backstop that shipped with the annotation-recursion fix (#2985), internal-only.
The depth counter moves from a module-level
WeakMap<PortableTextEditorEngine, number>with try/finally unwinding intoPerformEventArgsitself. The depth is purely call-stack-shaped state and the call graph supports expressing it that way: the threeraise/forward/executesites are the only recursive callers and the editor machine is the only external entry, all direct synchronous calls. The machine passesdepth: 0, the recursive sites passdepth + 1, the guard becomes the first statement ofperformEvent, and the WeakMap, the try/finally, and theperformEventInternalwrapper (which existed only to bolt the guard onto the original body) all disappear.depthis required rather than defaulted, deliberately: a future recursive call site that omits it fails to compile instead of silently resetting the chain and disabling the backstop for that path. The one path worth calling out:sendBackre-entries go through the actor's event queue (sequential, not nested), so a fresh chain correctly starts at zero there, exactly as the ambient counter behaved after itsfinally, and no stack growth is possible through that route.The
maxEventDepth = 100comment now states the two-sided window the value sits in instead of only the lower bound: it must exceed any legitimate raise-within-raise nesting (real Behavior stacks nest ~10-15 deep at the pathological end; the limit measures nesting, not sequential fan-out), and it must stay far below genuine stack exhaustion, since each nesting level costs dozens of real stack frames and browser limits vary. That puts the workable window at roughly 50-500, with asymmetric failure modes: too low silently drops legitimate events, too high only delays cycle detection by milliseconds, so when in doubt, raise it.Net behavior unchanged: the cyclic-Behavior pin and the full unit (861) and browser (1734) suites pass without modification. No changeset, nothing consumer-observable.