Commits
Add an options arg to createChild so a fresh child can be spliced before an
existing sibling: createChild(name, { before }). Rebuilds the children Set to
splice in position. Throws when before is not a current child. options is left
open for future positioning hints (after, at).
Spec: C14/C15, N7, N22.
- focus.ts: sync functions taking a node; useFocus installs a remove-interceptor
via node.scope.around(NodeApi)
- NodeApi gains sync remove; Node.remove() returns the teardown Promise
- delete freedom.ts (FreedomApi/append/operations) and tree.ts (useTree)
- drop Component/Tree/Node.eval from types; mod.ts exports the new surface
- focus.test.ts rewritten imperatively (synchronous)
Each node captures its task scope (useScope); eval repoints the current
routine's scope at the node's scope, runs the op inline, and restores —
immediate, non-serial, re-entrant by nesting. Removes the channel/eval-loop,
CallEval, box, and EvalOwner. Focus stays synchronous and no longer deadlocks
on cross-node setFocused during dispatch.
GA3 now waits a turn (sleep 0) for concurrent component init, like its siblings —
inline eval no longer grants the incidental scheduler turn the channel did.
This reverts commit 0758092febafa422116437242178cef0cc78b21d.
This reverts commit 087d137b7b8ead1cce8736de17d64eb6ef290de5.
Self-eval deadlock avoidance used NodeContext to detect re-entrant calls,
but NodeContext spans the whole node scope — including the root event loop
task — so the event loop's dispatch wrongly short-circuited into its own
scope, where middleware (registered in the eval-loop scope) was invisible.
Use a dedicated EvalOwner context set inside spawnEvalLoop, which is present
only while executing within that loop. Genuine re-entrant self-evals still
run inline; the event loop's dispatch routes through the channel as before.
Adds a regression test for re-entrant self-eval (previously uncovered).
- focus.ts: sync functions taking a node; useFocus installs a remove-interceptor
via node.scope.around(NodeApi)
- NodeApi gains sync remove; Node.remove() returns the teardown Promise
- delete freedom.ts (FreedomApi/append/operations) and tree.ts (useTree)
- drop Component/Tree/Node.eval from types; mod.ts exports the new surface
- focus.test.ts rewritten imperatively (synchronous)
Each node captures its task scope (useScope); eval repoints the current
routine's scope at the node's scope, runs the op inline, and restores —
immediate, non-serial, re-entrant by nesting. Removes the channel/eval-loop,
CallEval, box, and EvalOwner. Focus stays synchronous and no longer deadlocks
on cross-node setFocused during dispatch.
GA3 now waits a turn (sleep 0) for concurrent component init, like its siblings —
inline eval no longer grants the incidental scheduler turn the channel did.
Self-eval deadlock avoidance used NodeContext to detect re-entrant calls,
but NodeContext spans the whole node scope — including the root event loop
task — so the event loop's dispatch wrongly short-circuited into its own
scope, where middleware (registered in the eval-loop scope) was invisible.
Use a dedicated EvalOwner context set inside spawnEvalLoop, which is present
only while executing within that loop. Genuine re-entrant self-evals still
run inline; the event loop's dispatch routes through the channel as before.
Adds a regression test for re-entrant self-eval (previously uncovered).