@adobe/data-lit 0.10.13 → 0.10.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,22 +1,40 @@
1
1
  import type { Component } from "./component.js";
2
2
  /**
3
- * Give a hook host a real disconnect edge.
3
+ * Give a hook host a correct lifecycle by separating two concerns that the DOM
4
+ * connect/disconnect edge conflates.
4
5
  *
5
- * The hook model stores every effect's cleanup in `host.hooks[i].dispose`,
6
- * but {@link useEffect} only invokes it when dependencies change — never when
7
- * the element leaves the DOM. Left alone, every `useObservable` /
8
- * `useObservableValues` / `useEffect` subscription leaks on unmount and keeps
9
- * firing `requestUpdate` on a detached element. `useConnected` is likewise
10
- * inert because nothing dispatches the `"connected"` / `"disconnected"`
11
- * events it listens for.
6
+ * A hook host has two kinds of slot:
12
7
  *
13
- * This installs a single Lit {@link ReactiveController} — the one hook-friendly
14
- * path to a genuine `hostDisconnected` — that:
15
- * - on connect, dispatches `"connected"`, and on every *re*-connect forces a
16
- * re-render so the (previously torn-down) hooks re-initialize and
17
- * re-subscribe;
18
- * - on disconnect, dispatches `"disconnected"` then disposes and clears all
19
- * hook slots (full unmount semantics).
8
+ * - **Value / subscription slots** — {@link useState}, {@link useRef},
9
+ * {@link useMemo}, and effect-backed subscriptions ({@link useEffect},
10
+ * {@link useObservable}, {@link useObservableValues}). These are *stateful*
11
+ * and *expensive to rebuild*. They must SURVIVE a DOM move and be finalized
12
+ * only when the element is genuinely gone.
13
+ * - **Connectedness edges** — {@link useConnected}, the primitive for window /
14
+ * document listeners, portals, drag sessions: things that track raw DOM
15
+ * connectedness and must attach/detach *synchronously* on every edge.
16
+ *
17
+ * The trap: the DOM fires `disconnectedCallback` then `connectedCallback`
18
+ * **synchronously, in the same task,** whenever a connected element is merely
19
+ * *moved* (a Lit `repeat` reorder, a list re-parenting, a drag-reorder). There
20
+ * is no synchronous signal that distinguishes a move from a genuine unmount, so
21
+ * this controller does not try to classify the edge. Instead it handles each
22
+ * concern with the policy that concern actually wants:
23
+ *
24
+ * - **Edges fire synchronously, every time.** `hostConnected` /
25
+ * `hostDisconnected` dispatch the `"connected"` / `"disconnected"` events
26
+ * inline on every raw edge, so {@link useConnected} tears down and re-attaches
27
+ * deterministically — including across a move (cheap, and exactly what a
28
+ * listener/portal wants).
29
+ * - **Slot finalization is deferred behind an `isConnected` re-check.** A
30
+ * disconnect schedules a microtask; when it runs it disposes the slots only
31
+ * if the host is STILL disconnected. A move has reconnected by then, so its
32
+ * slots survive untouched — no re-subscribe, no state reset, no forced
33
+ * re-render. A genuine unmount is still disconnected, so it finalizes,
34
+ * preserving the unmount-leak fix.
35
+ *
36
+ * On a reconnect after a genuine finalization, the slots are empty, so the
37
+ * controller forces one `requestUpdate` to re-initialize them.
20
38
  *
21
39
  * It is installed lazily from inside the wrapped `render` (see {@link withHooks}),
22
40
  * which runs after `connectedCallback`, so `addController` fires `hostConnected`
@@ -14,14 +14,18 @@ import { useObservable } from "../use-observable.js";
14
14
  * is torn down when the element leaves the DOM (instead of leaking and firing
15
15
  * `requestUpdate` on a detached element).
16
16
  */
17
- /** An Observe whose live subscriber count the test can inspect. */
17
+ /** An Observe whose live subscriber count and cumulative churn the test can inspect. */
18
18
  function createTrackedObservable() {
19
19
  const subscribers = new Set();
20
20
  let current = 0;
21
+ let subscribes = 0;
22
+ let unsubscribes = 0;
21
23
  const observable = observer => {
24
+ subscribes++;
22
25
  subscribers.add(observer);
23
26
  observer(current);
24
27
  return () => {
28
+ unsubscribes++;
25
29
  subscribers.delete(observer);
26
30
  };
27
31
  };
@@ -33,8 +37,12 @@ function createTrackedObservable() {
33
37
  observer(value);
34
38
  },
35
39
  subscriberCount: () => subscribers.size,
40
+ totalSubscribes: () => subscribes,
41
+ totalUnsubscribes: () => unsubscribes,
36
42
  };
37
43
  }
44
+ /** Resolves after the current microtask checkpoint, so deferred teardown can run. */
45
+ const flushMicrotasks = () => new Promise(resolve => queueMicrotask(resolve));
38
46
  class HookProbeElement extends LitElement {
39
47
  constructor() {
40
48
  super();
@@ -63,6 +71,9 @@ describe("hook disposal on real DOM disconnect", () => {
63
71
  originalRequestUpdate(...args);
64
72
  };
65
73
  el.remove();
74
+ // Teardown is deferred one microtask (so a re-parent MOVE can cancel it —
75
+ // see the move test below); a genuine unmount has no reconnect, so it runs.
76
+ await flushMicrotasks();
66
77
  // Disconnect ran the effect cleanup: the subscription is gone.
67
78
  expect(subscriberCount()).toBe(0);
68
79
  // A later emit reaches no leaked subscriber, so the detached element is
@@ -70,5 +81,39 @@ describe("hook disposal on real DOM disconnect", () => {
70
81
  emit(99);
71
82
  expect(updatesAfterRemove).toBe(0);
72
83
  });
84
+ it("does NOT tear down and rebuild subscriptions when the element is merely MOVED", async () => {
85
+ const { observable, subscriberCount, totalSubscribes, totalUnsubscribes } = createTrackedObservable();
86
+ const parentA = document.createElement("div");
87
+ const parentB = document.createElement("div");
88
+ document.body.appendChild(parentA);
89
+ document.body.appendChild(parentB);
90
+ const el = document.createElement("hook-probe-element");
91
+ el.observable = observable;
92
+ parentA.appendChild(el);
93
+ await el.updateComplete;
94
+ expect(subscriberCount()).toBe(1);
95
+ expect(totalSubscribes()).toBe(1);
96
+ let updatesDuringMove = 0;
97
+ const originalRequestUpdate = el.requestUpdate.bind(el);
98
+ el.requestUpdate = (...args) => {
99
+ updatesDuringMove++;
100
+ originalRequestUpdate(...args);
101
+ };
102
+ // MOVE: appendChild to another already-connected parent fires
103
+ // disconnectedCallback then connectedCallback synchronously, in one task.
104
+ parentB.appendChild(el);
105
+ await flushMicrotasks();
106
+ await el.updateComplete;
107
+ // The element is still mounted and its subscription is intact...
108
+ expect(el.isConnected).toBe(true);
109
+ expect(subscriberCount()).toBe(1);
110
+ // ...and, crucially, the move neither disposed nor re-created it. Before the
111
+ // deferred-disposal fix, a move disposed and re-subscribed (2 subs / 1 unsub)
112
+ // and forced an extra re-render — the source of the studio-page lag, churn,
113
+ // and transient empty lists on project switch (FFP-111900).
114
+ expect(totalUnsubscribes()).toBe(0);
115
+ expect(totalSubscribes()).toBe(1);
116
+ expect(updatesDuringMove).toBe(0);
117
+ });
73
118
  });
74
119
  //# sourceMappingURL=hooks-controller.integration.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"hooks-controller.integration.test.js","sourceRoot":"","sources":["../../../src/hooks/component/hooks-controller.integration.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hooks-controller.integration.test.js","sourceRoot":"","sources":["../../../src/hooks/component/hooks-controller.integration.test.ts"],"names":[],"mappings":"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"}
@@ -9,44 +9,57 @@ function isReactiveHost(host) {
9
9
  return "addController" in host && typeof host.addController === "function";
10
10
  }
11
11
  /**
12
- * Dispose every effect-style hook slot and reset the hook cursor so the next
13
- * render re-initializes hooks from scratch. Only slots that expose a
14
- * `dispose` function (effects, subscriptions) are torn down; value slots
15
- * (state, memo, ref) are simply dropped.
12
+ * Finalize a host: dispose every hook slot's cleanup and clear the slots so the
13
+ * next render re-initializes from scratch. Only slots that expose a `dispose`
14
+ * function (effects, subscriptions) are torn down; value slots (state, memo,
15
+ * ref) are simply dropped.
16
16
  */
17
- function disposeHooks(host) {
17
+ function finalizeHooks(host) {
18
18
  const { hooks } = host;
19
19
  if (hooks) {
20
20
  for (const hook of hooks) {
21
21
  hook?.dispose?.();
22
22
  }
23
23
  }
24
- // Invariant: every live subscription (useEffect / useObservable / ...) exposes a
25
- // `dispose` and is torn down in the loop above, so nothing should fire after this.
26
- // A useState setter closure that still runs post-disconnect would write into the
27
- // emptied array and requestUpdate a detached host. That signals an un-torn-down
28
- // subscription (a bug at the subscription site), not something to guard here.
29
24
  host.hooks = [];
30
25
  host.hookIndex = 0;
31
26
  }
32
27
  /**
33
- * Give a hook host a real disconnect edge.
28
+ * Give a hook host a correct lifecycle by separating two concerns that the DOM
29
+ * connect/disconnect edge conflates.
34
30
  *
35
- * The hook model stores every effect's cleanup in `host.hooks[i].dispose`,
36
- * but {@link useEffect} only invokes it when dependencies change — never when
37
- * the element leaves the DOM. Left alone, every `useObservable` /
38
- * `useObservableValues` / `useEffect` subscription leaks on unmount and keeps
39
- * firing `requestUpdate` on a detached element. `useConnected` is likewise
40
- * inert because nothing dispatches the `"connected"` / `"disconnected"`
41
- * events it listens for.
31
+ * A hook host has two kinds of slot:
42
32
  *
43
- * This installs a single Lit {@link ReactiveController} — the one hook-friendly
44
- * path to a genuine `hostDisconnected` — that:
45
- * - on connect, dispatches `"connected"`, and on every *re*-connect forces a
46
- * re-render so the (previously torn-down) hooks re-initialize and
47
- * re-subscribe;
48
- * - on disconnect, dispatches `"disconnected"` then disposes and clears all
49
- * hook slots (full unmount semantics).
33
+ * - **Value / subscription slots** — {@link useState}, {@link useRef},
34
+ * {@link useMemo}, and effect-backed subscriptions ({@link useEffect},
35
+ * {@link useObservable}, {@link useObservableValues}). These are *stateful*
36
+ * and *expensive to rebuild*. They must SURVIVE a DOM move and be finalized
37
+ * only when the element is genuinely gone.
38
+ * - **Connectedness edges** — {@link useConnected}, the primitive for window /
39
+ * document listeners, portals, drag sessions: things that track raw DOM
40
+ * connectedness and must attach/detach *synchronously* on every edge.
41
+ *
42
+ * The trap: the DOM fires `disconnectedCallback` then `connectedCallback`
43
+ * **synchronously, in the same task,** whenever a connected element is merely
44
+ * *moved* (a Lit `repeat` reorder, a list re-parenting, a drag-reorder). There
45
+ * is no synchronous signal that distinguishes a move from a genuine unmount, so
46
+ * this controller does not try to classify the edge. Instead it handles each
47
+ * concern with the policy that concern actually wants:
48
+ *
49
+ * - **Edges fire synchronously, every time.** `hostConnected` /
50
+ * `hostDisconnected` dispatch the `"connected"` / `"disconnected"` events
51
+ * inline on every raw edge, so {@link useConnected} tears down and re-attaches
52
+ * deterministically — including across a move (cheap, and exactly what a
53
+ * listener/portal wants).
54
+ * - **Slot finalization is deferred behind an `isConnected` re-check.** A
55
+ * disconnect schedules a microtask; when it runs it disposes the slots only
56
+ * if the host is STILL disconnected. A move has reconnected by then, so its
57
+ * slots survive untouched — no re-subscribe, no state reset, no forced
58
+ * re-render. A genuine unmount is still disconnected, so it finalizes,
59
+ * preserving the unmount-leak fix.
60
+ *
61
+ * On a reconnect after a genuine finalization, the slots are empty, so the
62
+ * controller forces one `requestUpdate` to re-initialize them.
50
63
  *
51
64
  * It is installed lazily from inside the wrapped `render` (see {@link withHooks}),
52
65
  * which runs after `connectedCallback`, so `addController` fires `hostConnected`
@@ -63,24 +76,45 @@ export function installHooksController(host) {
63
76
  // addController fires hostConnected synchronously, and setting the flag first
64
77
  // guards against a re-entrant render re-installing a duplicate controller.
65
78
  host[HOOKS_CONTROLLER] = true;
66
- let connectedOnce = false;
79
+ // The slots were finalized (disposed + cleared) while the host was detached,
80
+ // so the next connect must re-render to re-initialize and re-subscribe them.
81
+ let finalized = false;
82
+ // A finalization microtask is already queued; don't queue a second.
83
+ let finalizationQueued = false;
67
84
  const controller = {
68
85
  hostConnected() {
69
- // The "connected" / "disconnected" events are for EXTERNAL listeners only.
70
- // The internal useConnected does NOT depend on them: this fires before the
71
- // render body attaches any listener, so useConnected is driven by its direct
72
- // isConnected check plus useEffect cleanup instead.
86
+ // Fire the connect edge synchronously on EVERY connect (mount / move /
87
+ // remount) so edge hooks (useConnected) run their setup deterministically.
73
88
  host.dispatchEvent(new Event("connected"));
74
- if (connectedOnce) {
75
- // Reconnect: the previous disconnect disposed and cleared every
76
- // hook slot, so re-render to re-run hooks and re-subscribe.
89
+ if (finalized) {
90
+ // The host was fully finalized while detached (a genuine unmount): its
91
+ // slots are empty, so re-render to re-initialize and re-subscribe them.
92
+ finalized = false;
77
93
  host.requestUpdate();
78
94
  }
79
- connectedOnce = true;
80
95
  },
81
96
  hostDisconnected() {
97
+ // Fire the disconnect edge synchronously on EVERY disconnect (unmount /
98
+ // move) so edge hooks tear down synchronously.
82
99
  host.dispatchEvent(new Event("disconnected"));
83
- disposeHooks(host);
100
+ // Defer slot finalization behind an isConnected re-check: a DOM move has
101
+ // reconnected by the time the microtask runs, so its value/subscription
102
+ // slots survive untouched; only a genuine unmount (still detached) is
103
+ // finalized. This is what keeps a move from churning subscriptions while
104
+ // still disposing them on a real unmount.
105
+ if (finalizationQueued) {
106
+ return;
107
+ }
108
+ finalizationQueued = true;
109
+ queueMicrotask(() => {
110
+ finalizationQueued = false;
111
+ if (host.isConnected) {
112
+ // Moved / reconnected within the task — keep every slot alive.
113
+ return;
114
+ }
115
+ finalizeHooks(host);
116
+ finalized = true;
117
+ });
84
118
  },
85
119
  };
86
120
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"hooks-controller.js","sourceRoot":"","sources":["../../../src/hooks/component/hooks-controller.ts"],"names":[],"mappings":"AAAA,uDAAuD;AAKvD;;;;GAIG;AACH,MAAM,gBAAgB,GAAG,MAAM,CAAC,0BAA0B,CAAC,CAAC;AAI5D,SAAS,cAAc,CAAC,IAAe;IACnC,OAAO,eAAe,IAAI,IAAI,IAAI,OAAO,IAAI,CAAC,aAAa,KAAK,UAAU,CAAC;AAC/E,CAAC;AAED;;;;;GAKG;AACH,SAAS,YAAY,CAAC,IAAe;IACjC,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;IACvB,IAAI,KAAK,EAAE,CAAC;QACR,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACtB,IAA6C,EAAE,OAAO,EAAE,EAAE,CAAC;QAChE,CAAC;IACL,CAAC;IACD,iFAAiF;IACjF,mFAAmF;IACnF,iFAAiF;IACjF,gFAAgF;IAChF,8EAA8E;IAC9E,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;IAChB,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC;AACvB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAe;IAClD,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC;QAClD,OAAO;IACX,CAAC;IACD,0EAA0E;IAC1E,8EAA8E;IAC9E,2EAA2E;IAC3E,IAAI,CAAC,gBAAgB,CAAC,GAAG,IAAI,CAAC;IAE9B,IAAI,aAAa,GAAG,KAAK,CAAC;IAC1B,MAAM,UAAU,GAAuB;QACnC,aAAa;YACT,2EAA2E;YAC3E,2EAA2E;YAC3E,6EAA6E;YAC7E,oDAAoD;YACpD,IAAI,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC;YAC3C,IAAI,aAAa,EAAE,CAAC;gBAChB,gEAAgE;gBAChE,4DAA4D;gBAC5D,IAAI,CAAC,aAAa,EAAE,CAAC;YACzB,CAAC;YACD,aAAa,GAAG,IAAI,CAAC;QACzB,CAAC;QACD,gBAAgB;YACZ,IAAI,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC;YAC9C,YAAY,CAAC,IAAI,CAAC,CAAC;QACvB,CAAC;KACJ,CAAC;IACF,IAAI,CAAC;QACD,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;IACnC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,4EAA4E;QAC5E,2EAA2E;QAC3E,IAAI,CAAC,gBAAgB,CAAC,GAAG,SAAS,CAAC;QACnC,MAAM,KAAK,CAAC;IAChB,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"hooks-controller.js","sourceRoot":"","sources":["../../../src/hooks/component/hooks-controller.ts"],"names":[],"mappings":"AAAA,uDAAuD;AAKvD;;;;GAIG;AACH,MAAM,gBAAgB,GAAG,MAAM,CAAC,0BAA0B,CAAC,CAAC;AAI5D,SAAS,cAAc,CAAC,IAAe;IACnC,OAAO,eAAe,IAAI,IAAI,IAAI,OAAO,IAAI,CAAC,aAAa,KAAK,UAAU,CAAC;AAC/E,CAAC;AAED;;;;;GAKG;AACH,SAAS,aAAa,CAAC,IAAe;IAClC,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;IACvB,IAAI,KAAK,EAAE,CAAC;QACR,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACtB,IAA6C,EAAE,OAAO,EAAE,EAAE,CAAC;QAChE,CAAC;IACL,CAAC;IACD,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;IAChB,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC;AACvB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAe;IAClD,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC;QAClD,OAAO;IACX,CAAC;IACD,0EAA0E;IAC1E,8EAA8E;IAC9E,2EAA2E;IAC3E,IAAI,CAAC,gBAAgB,CAAC,GAAG,IAAI,CAAC;IAE9B,6EAA6E;IAC7E,6EAA6E;IAC7E,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,oEAAoE;IACpE,IAAI,kBAAkB,GAAG,KAAK,CAAC;IAE/B,MAAM,UAAU,GAAuB;QACnC,aAAa;YACT,uEAAuE;YACvE,2EAA2E;YAC3E,IAAI,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC;YAC3C,IAAI,SAAS,EAAE,CAAC;gBACZ,uEAAuE;gBACvE,wEAAwE;gBACxE,SAAS,GAAG,KAAK,CAAC;gBAClB,IAAI,CAAC,aAAa,EAAE,CAAC;YACzB,CAAC;QACL,CAAC;QACD,gBAAgB;YACZ,wEAAwE;YACxE,+CAA+C;YAC/C,IAAI,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC;YAC9C,yEAAyE;YACzE,wEAAwE;YACxE,sEAAsE;YACtE,yEAAyE;YACzE,0CAA0C;YAC1C,IAAI,kBAAkB,EAAE,CAAC;gBACrB,OAAO;YACX,CAAC;YACD,kBAAkB,GAAG,IAAI,CAAC;YAC1B,cAAc,CAAC,GAAG,EAAE;gBAChB,kBAAkB,GAAG,KAAK,CAAC;gBAC3B,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;oBACnB,+DAA+D;oBAC/D,OAAO;gBACX,CAAC;gBACD,aAAa,CAAC,IAAI,CAAC,CAAC;gBACpB,SAAS,GAAG,IAAI,CAAC;YACrB,CAAC,CAAC,CAAC;QACP,CAAC;KACJ,CAAC;IACF,IAAI,CAAC;QACD,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;IACnC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,4EAA4E;QAC5E,2EAA2E;QAC3E,IAAI,CAAC,gBAAgB,CAAC,GAAG,SAAS,CAAC;QACnC,MAAM,KAAK,CAAC;IAChB,CAAC;AACL,CAAC"}
@@ -4,8 +4,10 @@ import { installHooksController } from "./hooks-controller.js";
4
4
  /**
5
5
  * Minimal stand-in for a Lit host: an EventTarget that captures the controller
6
6
  * installed on it so the test can drive `hostConnected` / `hostDisconnected`
7
- * directly (a real Lit element would need a DOM). `addController` mirrors Lit's
8
- * contract of firing `hostConnected` synchronously when already connected.
7
+ * directly (a real Lit element would need a DOM). `connect()` / `disconnect()`
8
+ * flip `isConnected` BEFORE dispatching, mirroring the DOM (the callback runs
9
+ * after connectedness has already changed) — the controller's deferred
10
+ * finalization reads `isConnected` to tell a move from a genuine unmount.
9
11
  */
10
12
  class FakeHost extends EventTarget {
11
13
  constructor() {
@@ -20,18 +22,21 @@ class FakeHost extends EventTarget {
20
22
  addController(controller) {
21
23
  this.controllers.push(controller);
22
24
  }
23
- // Simulate Lit dispatching lifecycle to every registered controller.
24
25
  connect() {
26
+ this.isConnected = true;
25
27
  for (const c of this.controllers)
26
28
  c.hostConnected?.();
27
29
  }
28
30
  disconnect() {
31
+ this.isConnected = false;
29
32
  for (const c of this.controllers)
30
33
  c.hostDisconnected?.();
31
34
  }
32
35
  }
36
+ /** Resolves after the current microtask checkpoint, so deferred finalization can run. */
37
+ const flushMicrotasks = () => new Promise(resolve => queueMicrotask(resolve));
33
38
  describe("installHooksController", () => {
34
- it("disposes every effect hook and resets the cursor on disconnect", () => {
39
+ it("finalizes every effect hook and resets the cursor on a genuine disconnect", async () => {
35
40
  const host = new FakeHost();
36
41
  const disposeA = vi.fn();
37
42
  const disposeB = vi.fn();
@@ -44,12 +49,15 @@ describe("installHooksController", () => {
44
49
  host.hookIndex = 4;
45
50
  installHooksController(host);
46
51
  host.disconnect();
52
+ // Finalization is deferred one microtask (so a move can cancel it); with no
53
+ // reconnect the host is still disconnected, so it runs on the next checkpoint.
54
+ await flushMicrotasks();
47
55
  expect(disposeA).toHaveBeenCalledTimes(1);
48
56
  expect(disposeB).toHaveBeenCalledTimes(1);
49
57
  expect(host.hooks).toEqual([]);
50
58
  expect(host.hookIndex).toBe(0);
51
59
  });
52
- it("dispatches connected / disconnected events across the lifecycle", () => {
60
+ it("fires the connect / disconnect EDGE synchronously — no microtask needed", () => {
53
61
  const host = new FakeHost();
54
62
  const onConnected = vi.fn();
55
63
  const onDisconnected = vi.fn();
@@ -59,15 +67,64 @@ describe("installHooksController", () => {
59
67
  host.connect();
60
68
  expect(onConnected).toHaveBeenCalledTimes(1);
61
69
  host.disconnect();
70
+ // No flush: the edge is synchronous so connectedness hooks (useConnected)
71
+ // tear down inline. Under the old defer-everything controller this event
72
+ // only fired on a later microtask.
62
73
  expect(onDisconnected).toHaveBeenCalledTimes(1);
63
74
  });
64
- it("forces a re-render only on RE-connect, not the first connect", () => {
75
+ it("fires disconnect THEN connect synchronously across a MOVE, and does NOT finalize", async () => {
76
+ const host = new FakeHost();
77
+ const dispose = vi.fn();
78
+ host.hooks = [{ dispose, dependencies: [] }];
79
+ host.hookIndex = 1;
80
+ const edges = [];
81
+ host.addEventListener("disconnected", () => edges.push("disconnect"));
82
+ host.addEventListener("connected", () => edges.push("connect"));
83
+ installHooksController(host);
84
+ host.requestUpdate.mockClear();
85
+ // Move: disconnect immediately followed by reconnect, same task.
86
+ host.disconnect();
87
+ host.connect();
88
+ expect(edges).toEqual(["disconnect", "connect"]); // both edges, synchronous, in order
89
+ await flushMicrotasks();
90
+ // The slot survived: not disposed, and no re-render was forced.
91
+ expect(dispose).not.toHaveBeenCalled();
92
+ expect(host.hooks).toHaveLength(1);
93
+ expect(host.requestUpdate).not.toHaveBeenCalled();
94
+ });
95
+ it("a rapid disconnect→connect→disconnect bounce finalizes exactly once (net state wins)", async () => {
96
+ const host = new FakeHost();
97
+ const dispose = vi.fn();
98
+ host.hooks = [{ dispose, dependencies: [] }];
99
+ installHooksController(host);
100
+ // Bounce within one task: only one finalization microtask is queued, and it
101
+ // reads the final isConnected (false) → finalizes once, not per disconnect.
102
+ host.disconnect();
103
+ host.connect();
104
+ host.disconnect();
105
+ await flushMicrotasks();
106
+ expect(dispose).toHaveBeenCalledTimes(1);
107
+ expect(host.hooks).toEqual([]);
108
+ });
109
+ it("gates finalization on isConnected — a slot that reconnects before the microtask survives", async () => {
110
+ const host = new FakeHost();
111
+ const dispose = vi.fn();
112
+ host.hooks = [{ dispose, dependencies: [] }];
113
+ installHooksController(host);
114
+ host.disconnect(); // schedules finalization; isConnected now false
115
+ host.connect(); // reconnects in the same task; isConnected back to true
116
+ await flushMicrotasks();
117
+ expect(dispose).not.toHaveBeenCalled();
118
+ expect(host.hooks).toHaveLength(1);
119
+ });
120
+ it("forces a re-render only on a genuine RE-connect, not the first connect or a move", async () => {
65
121
  const host = new FakeHost();
66
122
  installHooksController(host);
67
123
  host.connect(); // first mount
68
124
  expect(host.requestUpdate).not.toHaveBeenCalled();
69
125
  host.disconnect();
70
- host.connect(); // reconnect
126
+ await flushMicrotasks(); // genuine unmount: finalization runs
127
+ host.connect(); // reconnect must re-initialize the finalized hooks
71
128
  expect(host.requestUpdate).toHaveBeenCalledTimes(1);
72
129
  });
73
130
  it("is idempotent — repeated installs register a single controller", () => {
@@ -1 +1 @@
1
- {"version":3,"file":"hooks-controller.test.js","sourceRoot":"","sources":["../../../src/hooks/component/hooks-controller.test.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAGlD,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAE/D;;;;;GAKG;AACH,MAAM,QAAS,SAAQ,WAAW;IAAlC;;QACI,gBAAW,GAAG,IAAI,CAAC;QACnB,cAAS,GAAG,CAAC,CAAC;QACd,UAAK,GAAU,EAAE,CAAC;QAClB,qBAAgB,GAAG,IAAI,GAAG,EAAc,CAAC;QACzC,kBAAa,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QACxB,gBAAW,GAAyB,EAAE,CAAC;IAY3C,CAAC;IAVG,aAAa,CAAC,UAA8B;QACxC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACtC,CAAC;IACD,qEAAqE;IACrE,OAAO;QACH,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,WAAW;YAAE,CAAC,CAAC,aAAa,EAAE,EAAE,CAAC;IAC1D,CAAC;IACD,UAAU;QACN,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,WAAW;YAAE,CAAC,CAAC,gBAAgB,EAAE,EAAE,CAAC;IAC7D,CAAC;CACJ;AAED,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,gEAAgE,EAAE,GAAG,EAAE;QACtE,MAAM,IAAI,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC5B,MAAM,QAAQ,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QACzB,MAAM,QAAQ,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QACzB,IAAI,CAAC,KAAK,GAAG;YACT,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,EAAE;YACvC,EAAE,EAAE,uDAAuD;YAC3D,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE,EAAE;YACvC,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,0BAA0B;SAChD,CAAC;QACF,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC;QAEnB,sBAAsB,CAAC,IAAI,CAAC,CAAC;QAC7B,IAAI,CAAC,UAAU,EAAE,CAAC;QAElB,MAAM,CAAC,QAAQ,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QAC1C,MAAM,CAAC,QAAQ,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QAC1C,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC/B,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;QACvE,MAAM,IAAI,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC5B,MAAM,WAAW,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5B,MAAM,cAAc,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QAC/B,IAAI,CAAC,gBAAgB,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;QAChD,IAAI,CAAC,gBAAgB,CAAC,cAAc,EAAE,cAAc,CAAC,CAAC;QAEtD,sBAAsB,CAAC,IAAI,CAAC,CAAC;QAC7B,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,MAAM,CAAC,WAAW,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QAE7C,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,MAAM,CAAC,cAAc,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8DAA8D,EAAE,GAAG,EAAE;QACpE,MAAM,IAAI,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC5B,sBAAsB,CAAC,IAAI,CAAC,CAAC;QAE7B,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,cAAc;QAC9B,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QAElD,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,YAAY;QAC5B,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gEAAgE,EAAE,GAAG,EAAE;QACtE,MAAM,IAAI,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC5B,sBAAsB,CAAC,IAAI,CAAC,CAAC;QAC7B,sBAAsB,CAAC,IAAI,CAAC,CAAC;QAC7B,sBAAsB,CAAC,IAAI,CAAC,CAAC;QAC7B,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC1D,MAAM,IAAI,GAAG,IAAI,WAAW,EAA0B,CAAC;QACvD,MAAM,CAAC,GAAG,EAAE,CAAC,sBAAsB,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IAC7D,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"hooks-controller.test.js","sourceRoot":"","sources":["../../../src/hooks/component/hooks-controller.test.ts"],"names":[],"mappings":"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"}