{{ message }}
Repository navigation
fix(muya): #4747 deep clone editor state without recursing - #5259
Merged
Merged
Conversation
structuredClone recurses once per nesting level and throws RangeError on a bullet list a few hundred levels deep, so JSONState.getState() failed before the document could render. deepClone now walks arrays and plain objects with an explicit stack, keeps shared references shared, and still hands any other object type to structuredClone.
Jocs
approved these changes
Sep 7, 2026
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.

Closes #4747
Summary
JSONState.getState()copies the document withstructuredClone, which recurses once per nesting level. A bullet list nested about 600 levels deep is roughly 2400 nested containers in the state tree (bullet-list→children→list-item→childrenper level), which exceeds the engine stack, soEditor.init()andEditor.setContent()throwRangeErrorbefore the document renders.deepClonenow walks arrays and plain objects with an explicit stack instead of recursing. Shared references inside the input stay shared in the copy (a source → copy map, which also rules out looping on cycles). Values that are neither arrays nor plain objects (Date,Map, typed arrays, ...) still go throughstructuredClone, so that behaviour is unchanged for callers that relied on it. Functions are the one inputstructuredClonerejected that the new walk does not: they are copied by reference. No caller passes them.Muya playground (
packages/muya/examples, Vite dev server) in Chrome 148 on Windows 11,muya.setContent()with the nested list from the issue's generator, thenmuya.getMarkdown():develop(89d6988)RangeErroratdeepClone←JSONState.getState←Editor.setContent.mu-bullet-listrenderedRangeError, same stack.mu-bullet-listrendered, deepestliat depth 1000develop, 600 levels:Type of change
Test plan
packages/muya/src/utils/__tests__/deepClone.spec.ts. The 600-level case fails ondevelopwith theRangeErrorabove; the other cases pin fresh containers in the copy, shared references staying shared, and thestructuredClonefallback for non-plain values.cedhas no prebuilt binary and itsnode-gypbuild needs Visual Studio Build Tools. The desktop path in the issue's stack (Muya.setContent→Editor.setContent→JSONState.getState→deepClone) is the same code the playground exercises.Notes for reviewers
deepClonechanges.getState()still returns a defensive copy, so the publicmuya.getState()contract is untouched. The alternatives the issue lists (a non-cloning accessor for editor-internal paths, a nesting-depth guard inmarkdownToState) are not part of this change.JSON.stringify, whichbuildReplaceOpuses to diff top-level blocks, also recurses and starts throwing at roughly 2000 list levels in V8, so lists that deep still fail further down the pipeline. The 600 to 1000 range the issue reports now loads.By submitting this pull request, I confirm that my contribution is made under the terms of the MIT license.