firsthand

ADR-0008: A logical owner tree, independent of the DOM

Status: accepted (Phase 1)

Problem

Context resolution, disposal, error ownership and portal semantics all need a notion of “parent”. The physical DOM parent is the wrong answer for portals, and walking the DOM is the wrong answer for performance.

Constraints

Options considered

  1. DOM traversal for context (the composedPath/parentNode walk used by many web-component libraries, or a context-request event). Portals break it by construction, shadow boundaries complicate it, and every lookup is O(depth) of the DOM.
  2. context-request DOM event protocol. Good for interop with foreign components, but allocates an event per lookup, is bound to the physical tree, and cannot express “resolve once, then subscribe”.
  3. A logical owner tree maintained by the framework. Components, branches, list rows and portals push an owner; the current owner is a module-level variable during setup.

Chosen design

Option 3. An Owner is a plain object with a parent pointer, an intrusive child list, a lazily created disposal array and a lazily created, prototype-chained context record.

Option 2 remains available as an interop adapter for consuming third-party context-request providers; it is not the internal mechanism.

Performance implications

Memory implications

One owner per scope, with arrays allocated only when something is actually registered. A disposed subtree drops its owners, its context records and its links in one pass.

DX implications

The rule “ownership is where you wrote it, not where the DOM ended up” is simple and matches intuition. The one sharp edge: creating reactive scopes outside any owner (e.g. at module top level) leaves them un-owned; createRoot makes that explicit and returns a dispose.

Rejected alternatives

DOM traversal (breaks portals, O(depth), shadow complications), event-based context (allocation per lookup, physical-tree bound).