mirror of
https://github.com/anomalyco/opencode.git
synced 2026-05-22 20:05:23 +00:00
fix: defer terminal cleanup state write to stop cleanNode reentry crash
Terminal onCleanup ran persistTerminal synchronously during a dispose cascade, which flowed through props.onCleanup -> ops.update -> update() in context/terminal.tsx and fired setStore on the terminal store. That store write reentered the reactive graph mid cleanNode iteration; solid then nulled an ancestors owned while an outer cleanNode recursion was still iterating it, crashing with Cannot read properties of null reading 1 at node.owned[i]. Wrapping finalize in queueMicrotask pushes the store write past the current synchronous cleanup cascade so the teardown cannot race with cleanNodes owned walk.
This commit is contained in:
@@ -613,17 +613,30 @@ export const Terminal = (props: TerminalProps) => {
|
||||
drop?.()
|
||||
if (ws && ws.readyState !== WebSocket.CLOSED && ws.readyState !== WebSocket.CLOSING) ws.close(1000)
|
||||
|
||||
// Defer finalize (persistTerminal + local cleanup()) to a microtask so
|
||||
// that its synchronous store write inside `persistTerminal` — which
|
||||
// flows through `props.onCleanup` -> `ops.update` -> `update()` in
|
||||
// `context/terminal.tsx` and calls `setStore("all", i, ...)` — does
|
||||
// NOT run inside the outer solid cleanNode cascade. Running it
|
||||
// synchronously mid-cascade races with solid's recursive owned
|
||||
// iteration (readSignal on a stale memo re-enters updateComputation,
|
||||
// which nulls an ancestor's owned while the outer loop is still
|
||||
// iterating it) and crashes with "Cannot read properties of null
|
||||
// (reading '1')" at node.owned[i] inside chunk-EZWYHVNM.js cleanNode.
|
||||
// queueMicrotask runs after the current sync reactive flush, so the
|
||||
// store write lands in a fresh tick.
|
||||
const finalize = () => {
|
||||
persistTerminal({ term, addon: serializeAddon, cursor, id, onCleanup: props.onCleanup })
|
||||
cleanup()
|
||||
}
|
||||
const schedule = () => queueMicrotask(finalize)
|
||||
|
||||
if (!output) {
|
||||
finalize()
|
||||
schedule()
|
||||
return
|
||||
}
|
||||
|
||||
output.flush(finalize)
|
||||
output.flush(schedule)
|
||||
})
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user