@fluixi/reactive 1.0.0-alpha.82 → 1.0.0-alpha.84

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.
Files changed (169) hide show
  1. package/README.md +4 -0
  2. package/dist/cdn/reactive.cjs +2673 -1
  3. package/dist/cdn/reactive.global.js +2 -1
  4. package/dist/cdn/reactive.mjs +2657 -1
  5. package/dist/cdn/signal.cjs +299 -1
  6. package/dist/cdn/signal.d.ts +1 -1
  7. package/dist/cdn/signal.global.js +2 -1
  8. package/dist/cdn/signal.js +1 -1
  9. package/dist/cdn/signal.mjs +276 -1
  10. package/dist/index.cjs +2673 -1
  11. package/dist/index.mjs +2657 -1
  12. package/dist/lib/index.cjs +2674 -1
  13. package/dist/lib/index.js +1 -1
  14. package/dist/lib/index.mjs +2659 -1
  15. package/dist/lib/observable/index.cjs +386 -1
  16. package/dist/lib/observable/index.mjs +364 -1
  17. package/dist/lib/observable/observable.cjs +384 -1
  18. package/dist/lib/observable/observable.d.ts +0 -137
  19. package/dist/lib/observable/observable.d.ts.map +1 -1
  20. package/dist/lib/observable/observable.js +0 -71
  21. package/dist/lib/observable/observable.mjs +361 -1
  22. package/dist/lib/observable/subscription.cjs +180 -1
  23. package/dist/lib/observable/subscription.d.ts +0 -12
  24. package/dist/lib/observable/subscription.d.ts.map +1 -1
  25. package/dist/lib/observable/subscription.js +0 -12
  26. package/dist/lib/observable/subscription.mjs +157 -1
  27. package/dist/lib/observable/types.cjs +18 -1
  28. package/dist/lib/observable/types.d.ts.map +1 -1
  29. package/dist/lib/predicates.cjs +44 -1
  30. package/dist/lib/predicates.d.ts +2 -2
  31. package/dist/lib/predicates.js +2 -2
  32. package/dist/lib/predicates.mjs +23 -1
  33. package/dist/lib/signal/api.cjs +571 -1
  34. package/dist/lib/signal/api.d.ts +211 -1
  35. package/dist/lib/signal/api.d.ts.map +1 -1
  36. package/dist/lib/signal/api.js +207 -12
  37. package/dist/lib/signal/api.mjs +548 -1
  38. package/dist/lib/signal/cached-resource.cjs +672 -1
  39. package/dist/lib/signal/cached-resource.d.ts +28 -16
  40. package/dist/lib/signal/cached-resource.d.ts.map +1 -1
  41. package/dist/lib/signal/cached-resource.js +35 -29
  42. package/dist/lib/signal/cached-resource.mjs +650 -1
  43. package/dist/lib/signal/graph/children.cjs +325 -1
  44. package/dist/lib/signal/graph/children.js +1 -1
  45. package/dist/lib/signal/graph/children.mjs +302 -1
  46. package/dist/lib/signal/graph/compat.cjs +578 -1
  47. package/dist/lib/signal/graph/compat.d.ts +13 -13
  48. package/dist/lib/signal/graph/compat.d.ts.map +1 -1
  49. package/dist/lib/signal/graph/compat.js +51 -32
  50. package/dist/lib/signal/graph/compat.mjs +556 -1
  51. package/dist/lib/signal/graph/core.cjs +295 -1
  52. package/dist/lib/signal/graph/core.d.ts +28 -24
  53. package/dist/lib/signal/graph/core.d.ts.map +1 -1
  54. package/dist/lib/signal/graph/core.js +45 -31
  55. package/dist/lib/signal/graph/core.mjs +273 -1
  56. package/dist/lib/signal/graph/debounced.cjs +668 -1
  57. package/dist/lib/signal/graph/debounced.d.ts +5 -5
  58. package/dist/lib/signal/graph/debounced.d.ts.map +1 -1
  59. package/dist/lib/signal/graph/debounced.js +5 -5
  60. package/dist/lib/signal/graph/debounced.mjs +645 -1
  61. package/dist/lib/signal/graph/derived.cjs +496 -1
  62. package/dist/lib/signal/graph/derived.d.ts +1 -1
  63. package/dist/lib/signal/graph/derived.js +1 -1
  64. package/dist/lib/signal/graph/derived.mjs +473 -1
  65. package/dist/lib/signal/graph/detect.cjs +66 -1
  66. package/dist/lib/signal/graph/detect.mjs +45 -1
  67. package/dist/lib/signal/graph/error-boundary.cjs +301 -1
  68. package/dist/lib/signal/graph/error-boundary.js +3 -3
  69. package/dist/lib/signal/graph/error-boundary.mjs +278 -1
  70. package/dist/lib/signal/graph/index.cjs +305 -1
  71. package/dist/lib/signal/graph/index.d.ts +7 -6
  72. package/dist/lib/signal/graph/index.d.ts.map +1 -1
  73. package/dist/lib/signal/graph/index.js +7 -6
  74. package/dist/lib/signal/graph/index.mjs +283 -1
  75. package/dist/lib/signal/graph/observe.cjs +163 -0
  76. package/dist/lib/signal/graph/observe.d.ts +141 -0
  77. package/dist/lib/signal/graph/observe.d.ts.map +1 -0
  78. package/dist/lib/signal/graph/observe.js +149 -0
  79. package/dist/lib/signal/graph/observe.mjs +140 -0
  80. package/dist/lib/signal/graph/resource.cjs +620 -1
  81. package/dist/lib/signal/graph/resource.d.ts +1 -1
  82. package/dist/lib/signal/graph/resource.js +7 -8
  83. package/dist/lib/signal/graph/resource.mjs +597 -1
  84. package/dist/lib/signal/graph/runtime.cjs +501 -1
  85. package/dist/lib/signal/graph/runtime.d.ts +9 -9
  86. package/dist/lib/signal/graph/runtime.d.ts.map +1 -1
  87. package/dist/lib/signal/graph/runtime.js +56 -37
  88. package/dist/lib/signal/graph/runtime.mjs +478 -1
  89. package/dist/lib/signal/graph/signal-next.cjs +1208 -1
  90. package/dist/lib/signal/graph/signal-next.d.ts +5 -5
  91. package/dist/lib/signal/graph/signal-next.d.ts.map +1 -1
  92. package/dist/lib/signal/graph/signal-next.js +14 -12
  93. package/dist/lib/signal/graph/signal-next.mjs +1186 -1
  94. package/dist/lib/signal/graph/ssr-hooks.cjs +60 -1
  95. package/dist/lib/signal/graph/ssr-hooks.d.ts +6 -7
  96. package/dist/lib/signal/graph/ssr-hooks.d.ts.map +1 -1
  97. package/dist/lib/signal/graph/ssr-hooks.js +6 -7
  98. package/dist/lib/signal/graph/ssr-hooks.mjs +39 -1
  99. package/dist/lib/signal/graph/state.cjs +72 -1
  100. package/dist/lib/signal/graph/state.d.ts +43 -10
  101. package/dist/lib/signal/graph/state.d.ts.map +1 -1
  102. package/dist/lib/signal/graph/state.js +83 -21
  103. package/dist/lib/signal/graph/state.mjs +49 -1
  104. package/dist/lib/signal/graph/suspense.cjs +301 -1
  105. package/dist/lib/signal/graph/suspense.js +4 -4
  106. package/dist/lib/signal/graph/suspense.mjs +278 -1
  107. package/dist/lib/signal/index-legacy.cjs +2540 -1
  108. package/dist/lib/signal/index-legacy.d.ts +1 -1
  109. package/dist/lib/signal/index-legacy.js +1 -1
  110. package/dist/lib/signal/index-legacy.mjs +2518 -1
  111. package/dist/lib/signal/index.cjs +1275 -1
  112. package/dist/lib/signal/index.d.ts +5 -2
  113. package/dist/lib/signal/index.d.ts.map +1 -1
  114. package/dist/lib/signal/index.js +8 -3
  115. package/dist/lib/signal/index.mjs +1253 -1
  116. package/dist/lib/signal/list.cjs +549 -1
  117. package/dist/lib/signal/list.d.ts +5 -5
  118. package/dist/lib/signal/list.js +10 -10
  119. package/dist/lib/signal/list.mjs +526 -1
  120. package/dist/lib/signal/resource-api.cjs +626 -1
  121. package/dist/lib/signal/resource-api.d.ts +5 -5
  122. package/dist/lib/signal/resource-api.js +5 -5
  123. package/dist/lib/signal/resource-api.mjs +603 -1
  124. package/dist/lib/signal/signal.cjs +2023 -1
  125. package/dist/lib/signal/signal.d.ts +4 -4
  126. package/dist/lib/signal/signal.d.ts.map +1 -1
  127. package/dist/lib/signal/signal.js +22 -23
  128. package/dist/lib/signal/signal.mjs +2000 -1
  129. package/dist/lib/signal/types.cjs +38 -1
  130. package/dist/lib/signal/types.d.ts.map +1 -1
  131. package/dist/lib/signal/types.js +2 -2
  132. package/dist/lib/signal/types.mjs +17 -1
  133. package/dist/lib/signal/utilities.cjs +1012 -1
  134. package/dist/lib/signal/utilities.d.ts +0 -108
  135. package/dist/lib/signal/utilities.d.ts.map +1 -1
  136. package/dist/lib/signal/utilities.js +0 -158
  137. package/dist/lib/signal/utilities.mjs +990 -1
  138. package/dist/lib/store/api.cjs +612 -1
  139. package/dist/lib/store/api.d.ts +33 -0
  140. package/dist/lib/store/api.d.ts.map +1 -1
  141. package/dist/lib/store/api.js +41 -8
  142. package/dist/lib/store/api.mjs +596 -1
  143. package/dist/lib/store/index.cjs +691 -1
  144. package/dist/lib/store/index.js +1 -1
  145. package/dist/lib/store/index.mjs +676 -1
  146. package/dist/lib/store/setter.types.cjs +18 -1
  147. package/dist/lib/store/setter.types.d.ts +43 -3
  148. package/dist/lib/store/setter.types.d.ts.map +1 -1
  149. package/dist/lib/store/store.cjs +658 -1
  150. package/dist/lib/store/store.d.ts +72 -0
  151. package/dist/lib/store/store.d.ts.map +1 -1
  152. package/dist/lib/store/store.js +294 -11
  153. package/dist/lib/store/store.mjs +644 -1
  154. package/dist/lib/store/types.cjs +18 -1
  155. package/dist/lib/store/types.d.ts +1 -1
  156. package/dist/lib/store/types.d.ts.map +1 -1
  157. package/dist/lib/store/utils.cjs +43 -1
  158. package/dist/lib/store/utils.d.ts +1 -1
  159. package/dist/lib/store/utils.js +1 -1
  160. package/dist/lib/store/utils.mjs +22 -1
  161. package/dist/lib/subject.cjs +450 -1
  162. package/dist/lib/subject.js +3 -91
  163. package/dist/lib/subject.mjs +427 -1
  164. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  165. package/dist/version.generated.cjs +27 -1
  166. package/dist/version.generated.d.ts +1 -1
  167. package/dist/version.generated.js +2 -2
  168. package/dist/version.generated.mjs +7 -1
  169. package/package.json +5 -4
@@ -1,27 +1,32 @@
1
1
  /**
2
- * L1 — the runtime layer the TC39 proposal deliberately OMITS, rebuilt on top of
3
- * the L0 graph core (./core.ts). TC39 ships only State/Computed/Watcher; everything
4
- * a framework needs — ownership/disposal, a scheduler, batching, and effects — is
5
- * left to userland. This module is that userland, expressed once, cleanly:
2
+ * The runtime layer, on top of the graph core in `core.ts`.
6
3
  *
7
- * - Owner tree: createRoot / onCleanup / getOwner / runWithOwner (no graph entanglement)
8
- * - Scheduler: a microtask-batched effect queue + flushSync / batch
9
- * - effect(): re-running side effect built on a Computed + a Watcher + an owner
4
+ * TC39 ships `State`, `Computed` and `Watcher` and stops there. Everything else a
5
+ * framework needs is left to userland, and this module is that userland:
10
6
  *
11
- * It is intentionally NOT wired into the package's public exports yet (those names
12
- * clash with the legacy createEffect/createRoot/onCleanup/batch). It is the staged
13
- * replacement: once proven, the legacy public API re-points onto this. Effects are
14
- * built on `Signal.subtle.Watcher` exactly as the proposal intends the watcher's
15
- * notify only schedules; the actual re-run (a tracked recompute) happens on flush.
7
+ * - Owner tree: createRoot, onCleanup, getOwner, runWithOwner. No graph edges;
8
+ * ownership is about disposal, not dependency.
9
+ * - Scheduler: an effect queue with flushSync and batch.
10
+ * - effect(): a re-running side effect, built from a Computed, a Watcher and
11
+ * an owner.
12
+ *
13
+ * This is the live implementation. `@fluixi/reactive/signal` re-exports it through
14
+ * `signal-next.ts`, and `compat.ts` puts the `create*` names over the top, so the
15
+ * long-standing public API lands here.
16
+ *
17
+ * Effects are built on `Signal.subtle.Watcher` the way the proposal intends: the
18
+ * watcher's notify only schedules, and the re-run, a tracked recompute, happens on
19
+ * flush.
16
20
  */
17
21
  import { Computed, Watcher } from './core.js';
18
22
  import { G } from './state.js';
23
+ import { reportReactiveContext, reportReactiveContextCreated, reportReactiveDispose, reportReactiveNode, } from './observe.js';
19
24
  /**
20
25
  * A child scope of `parent`, attached so the parent's teardown disposes it too.
21
26
  *
22
27
  * For a derivation that re-runs: context still resolves up the chain from the
23
28
  * creation-time owner, while anything registered during a run belongs to this
24
- * scope and can be torn down before the next one instead of accumulating on
29
+ * scope and can be torn down before the next one: instead of accumulating on
25
30
  * the enclosing scope and only running when that finally dies.
26
31
  */
27
32
  export function createChildOwner(parent) {
@@ -33,7 +38,7 @@ export function createChildOwner(parent) {
33
38
  /**
34
39
  * Tear down a scope's children and cleanups.
35
40
  *
36
- * `final: false` keeps the scope itself alive and reusable what a re-running
41
+ * `final: false` keeps the scope itself alive and reusable, what a re-running
37
42
  * derivation needs between runs.
38
43
  */
39
44
  export function disposeScope(owner, final = true) {
@@ -48,16 +53,19 @@ export function isOwnerDisposed(owner) {
48
53
  function makeOwner(parent) {
49
54
  return { cleanups: null, owned: null, parent, contexts: null };
50
55
  }
51
- export function createContext(defaultValue) {
52
- const id = Symbol('context');
56
+ export function createContext(defaultValue, name) {
57
+ // Named so a watcher can tell two of them apart. Nothing else reads the description.
58
+ const id = Symbol(name ?? 'context');
59
+ reportReactiveContextCreated(id);
53
60
  const Provider = (props) => {
54
- // Stamp the value on a DEDICATED child owner, then read `children` under it never
61
+ // Stamp the value on a DEDICATED child owner, then read `children` under it: never
55
62
  // on the current owner directly. Two sibling <Ctx.Provider> elements used inline (no
56
- // wrapping component) run under the SAME ambient owner, so stamping there lets the
57
- // second clobber the first's entry every descendant then resolves the last value.
63
+ // wrapping component) run under the same ambient owner, so stamping there lets the
64
+ // second clobber the first's entry, every descendant then resolves the last value.
58
65
  // A child scope isolates each Provider; because createMemo captures the owner at
59
66
  // creation, the children memos built while reading `props.children` here capture this
60
67
  // scope and resolve the right value even when rendered lazily (Portal/Show/For).
68
+ reportReactiveContext(id, 'provide', props.value);
61
69
  const parent = G.owner;
62
70
  const owner = makeOwner(parent);
63
71
  (owner.contexts = new Map()).set(id, props.value);
@@ -77,10 +85,14 @@ export function createContext(defaultValue) {
77
85
  export function useContext(context) {
78
86
  let o = G.owner;
79
87
  while (o) {
80
- if (o.contexts && o.contexts.has(context.id))
81
- return o.contexts.get(context.id);
88
+ if (o.contexts && o.contexts.has(context.id)) {
89
+ const found = o.contexts.get(context.id);
90
+ reportReactiveContext(context.id, 'read', found);
91
+ return found;
92
+ }
82
93
  o = o.parent;
83
94
  }
95
+ reportReactiveContext(context.id, 'read', context.defaultValue);
84
96
  return context.defaultValue;
85
97
  }
86
98
  /** Read `context` starting from a specific owner chain (not the current scope). */
@@ -108,7 +120,7 @@ export function provide(context, value, fn) {
108
120
  G.owner = prev;
109
121
  }
110
122
  }
111
- // ── Suspend / error hooks (injected by the boundary layers; L1 stays decoupled) ──
123
+ // ── Suspend / error hooks, injected by the boundary layers so this file stays clear of them ──
112
124
  /** Register how a thrown promise (a suspended read) is reported to a boundary. */
113
125
  export function setSuspendHandler(fn) {
114
126
  G.onSuspend = fn;
@@ -147,9 +159,9 @@ export function runWithOwner(owner, fn) {
147
159
  /** Register a teardown for the current owner scope. */
148
160
  export function onCleanup(fn) {
149
161
  if (!G.owner)
150
- return; // no owner not retained, runs never (matches Solid)
151
- // A disposed owner is dead: pushing here would recreate its (nulled) cleanups
152
- // array and never run a leak. Run the teardown immediately instead, so a
162
+ return; // no owner -> not retained, runs never (matches Solid)
163
+ // A disposed owner is dead, pushing here would recreate its (nulled) cleanups
164
+ // array and never run, a leak. Run the teardown immediately instead, so a
153
165
  // teardown registered from deferred work (a queued onMount) still fires.
154
166
  if (G.owner.disposed) {
155
167
  fn();
@@ -157,10 +169,10 @@ export function onCleanup(fn) {
157
169
  }
158
170
  (G.owner.cleanups ??= []).push(fn);
159
171
  }
160
- // `final` distinguishes a permanent teardown (the scope is gone mark it disposed so
172
+ // `final` distinguishes a permanent teardown (the scope is gone: mark it disposed so
161
173
  // deferred work bails) from an effect's between-runs cleanup (the owner is REUSED for
162
- // the next run, so it must NOT be marked disposed). Children are always torn down
163
- // permanently only the effect's own owner survives a re-run.
174
+ // the next run, so it must not be marked disposed). Children are always torn down
175
+ // permanently, only the effect's own owner survives a re-run.
164
176
  function disposeOwner(owner, final = true) {
165
177
  if (owner.disposed)
166
178
  return;
@@ -191,7 +203,7 @@ export function createRoot(fn) {
191
203
  G.owner = prev;
192
204
  }
193
205
  }
194
- // ── Scheduler (TC39 has none) state on G (see ./state.ts) ───────────────────
206
+ // ── Scheduler (TC39 has none): state on G (see ./state.ts) ───────────────────
195
207
  export function setScheduler(fn) {
196
208
  G.hostSchedule = fn ?? ((run) => void Promise.resolve().then(run));
197
209
  }
@@ -233,8 +245,8 @@ export function flushSync() {
233
245
  }
234
246
  }
235
247
  /**
236
- * Run pending effects after a write completes but only at the top level (not
237
- * inside a batch). A single write thus marks the WHOLE graph dirty first, then
248
+ * Run pending effects after a write completes, but only at the top level (not
249
+ * inside a batch). A single write thus marks the whole graph dirty first, then
238
250
  * flushes once (legacy-equivalent synchronous effects, glitch-free). The `G.flushing`
239
251
  * guard makes a write inside an effect a no-op here (the running loop drains it).
240
252
  */
@@ -253,16 +265,16 @@ export function batch(fn) {
253
265
  flushSync();
254
266
  }
255
267
  }
256
- // ── effect() built on Computed + Watcher + Owner ───────────────────────────
268
+ // ── effect(): built on Computed + Watcher + Owner ───────────────────────────
257
269
  class EffectNode {
258
270
  computed;
259
271
  watcher;
260
272
  owner;
261
273
  queued = false;
262
274
  disposed = false;
263
- constructor(fn) {
275
+ constructor(fn, report = false) {
264
276
  this.owner = makeOwner(G.owner);
265
- // Tie disposal to the creating scope: when the parent owner tears down it must
277
+ // Tie disposal to the creating scope, when the parent owner tears down it must
266
278
  // fully dispose this effect (unwatch + body cleanups), not just run the body
267
279
  // cleanups. Register dispose() as a parent cleanup rather than as an owned tree.
268
280
  if (G.owner)
@@ -270,7 +282,7 @@ class EffectNode {
270
282
  // The Computed runs the user fn inside a tracking scope + this effect's owner,
271
283
  // so reads subscribe and onCleanup/nested roots attach here.
272
284
  this.computed = new Computed(() => {
273
- // teardown previous run's cleanups before re-running NOT final: the owner is
285
+ // teardown previous run's cleanups before re-running. not final: the owner is
274
286
  // reused for this run, so it must stay alive (only its children are disposed).
275
287
  disposeOwner(this.owner, false);
276
288
  runWithOwner(this.owner, () => {
@@ -279,6 +291,10 @@ class EffectNode {
279
291
  (this.owner.cleanups ??= []).push(r);
280
292
  });
281
293
  });
294
+ // Before the first run, so a watcher counts that run like every later one. A render
295
+ // effect is reported too, marked as plumbing: it is not worth drawing, but a node made
296
+ // inside one has to be able to find its way back to the component that caused it.
297
+ reportReactiveNode({ kind: 'effect', node: this.computed, handle: this, internal: !report });
282
298
  // The watcher only schedules; the recompute happens on flush.
283
299
  this.watcher = new Watcher(() => enqueue(this));
284
300
  this.run(); // initial synchronous run (Solid semantics)
@@ -309,12 +325,15 @@ class EffectNode {
309
325
  if (this.disposed)
310
326
  return;
311
327
  this.disposed = true;
328
+ reportReactiveDispose(this.computed);
312
329
  this.watcher.unwatch(this.computed);
313
330
  disposeOwner(this.owner);
314
331
  }
315
332
  }
316
333
  /** A re-running side effect. Runs once now, then again (G.scheduled) when deps change. */
317
- export function effect(fn) {
318
- const node = new EffectNode(fn);
334
+ export function effect(fn, report = false) {
335
+ // Reported only when the caller says so. Every DOM binding is a render effect through this
336
+ // same factory, and a graph holding one node per text node is not a graph anyone reads.
337
+ const node = new EffectNode(fn, report);
319
338
  return () => node.dispose();
320
339
  }
@@ -1 +1,478 @@
1
- var x="__fluixi_signal_next_state__",m=e=>{typeof queueMicrotask=="function"?queueMicrotask(e):Promise.resolve().then(e)},t=globalThis[x]??={consumer:null,untracked:!1,owner:null,queue:[],scheduled:!1,flushing:!1,batchDepth:0,hostSchedule:m,onSuspend:null,onError:null};var y=0,T=1,d=2,p=class{constructor(){this.observers=null;this.watchers=null;this.version=0}updateIfNecessary(){}track(){if(t.untracked)return;let n=t.consumer;n&&((this.observers??=new Set).add(n),(n.sources??=new Set).add(this))}};var c=class extends p{constructor(r,o){super();this._threw=!1;this._initialized=!1;this.state=d;this.sources=null;this._fn=r,this._equals=o?.equals===!1?()=>!1:o?.equals??Object.is}get(){if(t.consumer===this)throw new Error("Signal.Computed cannot read itself");if(this.track(),this.updateIfNecessary(),this._threw)throw this._error;return this._value}updateIfNecessary(){if(this.state===T&&this.sources){for(let r of this.sources)if(r.updateIfNecessary(),this.state===d)break}(this.state===d||!this._initialized)&&this.recompute(),this.state=y}recompute(){if(this.sources){for(let u of this.sources)u.observers?.delete(this);this.sources.clear()}let r=t.consumer,o=t.untracked;t.consumer=this,t.untracked=!1;let s,i=!1,f;try{s=this._fn()}catch(u){i=!0,f=u,s=this._value}finally{t.consumer=r,t.untracked=o}let b=!this._initialized||this._threw!==i||(i?f!==this._error:!this._equals(this._value,s));if(this._initialized=!0,this._threw=i,this._error=f,this._value=s,b){if(this.version++,this.observers)for(let u of[...this.observers])u.markDirty(d);_(this)}}markDirty(r){if(this.state>=r)return;let o=this.state===y;if(this.state=r,o){if(this.observers)for(let s of[...this.observers])s.markDirty(T);_(this)}}peek(){if(this.updateIfNecessary(),this._threw)throw this._error;return this._value}};function _(e){if(e.watchers)for(let n of[...e.watchers])n._notify(e)}var h=class{constructor(n){this._watched=new Set;this._pending=new Set;this._notifyFn=n}watch(...n){for(let r of n)this._watched.add(r),(r.watchers??=new Set).add(this)}unwatch(...n){for(let r of n)this._watched.delete(r),r.watchers?.delete(this),this._pending.delete(r)}getPending(){let n=[...this._pending];return this._pending.clear(),n}_notify(n){if(!this._watched.has(n)||this._pending.has(n))return;let r=this._pending.size===0;this._pending.add(n),r&&this._notifyFn()}};function D(e){let n=l(e);return e&&(e.owned??=[]).push(n),n}function N(e,n=!0){a(e,n)}function P(e){return!!e&&e.disposed===!0}function l(e){return{cleanups:null,owned:null,parent:e,contexts:null}}function z(e){let n=Symbol("context");return{id:n,defaultValue:e,Provider:o=>{let s=t.owner,i=l(s);(i.contexts=new Map).set(n,o.value),s&&(s.owned??=[]).push(i),t.owner=i;try{return o.children}finally{t.owner=s}}}}function I(e){let n=t.owner;for(;n;){if(n.contexts&&n.contexts.has(e.id))return n.contexts.get(e.id);n=n.parent}return e.defaultValue}function G(e,n){let r=e;for(;r;){if(r.contexts&&r.contexts.has(n.id))return r.contexts.get(n.id);r=r.parent}return n.defaultValue}function R(e,n,r){let o=l(t.owner);(o.contexts=new Map).set(e.id,n),t.owner&&(t.owner.owned??=[]).push(o);let s=t.owner;t.owner=o;try{return r()}finally{t.owner=s}}function M(e){t.onSuspend=e}function W(e){return t.onSuspend?(t.onSuspend(e,t.owner),!0):!1}function A(e){t.onError=e}function F(){return t.owner}function H(e){let n=t.owner;return t.owner=e,n}function S(e,n){let r=t.owner;t.owner=e;try{return n()}finally{t.owner=r}}function V(e){if(t.owner){if(t.owner.disposed){e();return}(t.owner.cleanups??=[]).push(e)}}function a(e,n=!0){if(!e.disposed){if(e.owned){for(let r=e.owned.length-1;r>=0;r--)a(e.owned[r]);e.owned=null}if(e.cleanups){for(let r=e.cleanups.length-1;r>=0;r--)e.cleanups[r]();e.cleanups=null}n&&(e.disposed=!0)}}function K(e){let n=l(t.owner);t.owner&&(t.owner.owned??=[]).push(n);let r=t.owner;t.owner=n;try{return e(()=>a(n))}finally{t.owner=r}}function Y(e){t.hostSchedule=e??(n=>{Promise.resolve().then(n)})}function k(e){e.queued||(e.queued=!0,t.queue.push(e),!t.scheduled&&t.batchDepth===0&&(t.scheduled=!0,t.hostSchedule(v)))}function v(){if(t.scheduled=!1,t.flushing)return;t.flushing=!0;let e=0;try{for(;t.queue.length;){if(++e>1e5)throw new Error("[fluixi] effect flush did not settle (cycle?)");let n=t.queue;t.queue=[];for(let r of n)r.queued=!1,r.disposed||r.run()}}finally{t.flushing=!1}}function j(){t.batchDepth===0&&v()}function L(e){t.batchDepth++;try{return e()}finally{--t.batchDepth===0&&v()}}var w=class{constructor(n){this.queued=!1;this.disposed=!1;this.owner=l(t.owner),t.owner&&(t.owner.cleanups??=[]).push(()=>this.dispose()),this.computed=new c(()=>{a(this.owner,!1),S(this.owner,()=>{let r=n();typeof r=="function"&&(this.owner.cleanups??=[]).push(r)})}),this.watcher=new h(()=>k(this)),this.run(),this.watcher.watch(this.computed)}run(){if(!this.disposed){this.watcher.getPending();try{this.computed.get()}catch(n){if(n&&typeof n.then=="function"&&t.onSuspend){t.onSuspend(n,this.owner);return}if(t.onError&&t.onError(n,this.owner))return;throw n}}}dispose(){this.disposed||(this.disposed=!0,this.watcher.unwatch(this.computed),a(this.owner))}};function U(e){let n=new w(e);return()=>n.dispose()}export{j as afterWrite,L as batch,D as createChildOwner,z as createContext,K as createRoot,N as disposeScope,U as effect,v as flushSync,F as getOwner,P as isOwnerDisposed,V as onCleanup,R as provide,G as readContextFrom,W as reportSuspend,S as runWithOwner,A as setErrorHandler,H as setOwner,Y as setScheduler,M as setSuspendHandler,I as useContext};
1
+ // src/version.generated.ts
2
+ var VERSION = "1.0.0-alpha.84";
3
+
4
+ // src/lib/signal/graph/state.ts
5
+ var MAJOR = VERSION.split(".")[0];
6
+ var KEY = `__fluixi_signal_state_v${MAJOR}__`;
7
+ var VERSIONS_KEY = `__fluixi_signal_builds_v${MAJOR}__`;
8
+ var defaultSchedule = (run) => {
9
+ if (typeof queueMicrotask === "function") queueMicrotask(run);
10
+ else void Promise.resolve().then(run);
11
+ };
12
+ function defaults() {
13
+ return {
14
+ consumer: null,
15
+ untracked: false,
16
+ owner: null,
17
+ queue: [],
18
+ scheduled: false,
19
+ flushing: false,
20
+ batchDepth: 0,
21
+ hostSchedule: defaultSchedule,
22
+ onSuspend: null,
23
+ onError: null,
24
+ observer: null
25
+ };
26
+ }
27
+ var realm = globalThis;
28
+ function backfillState(existing) {
29
+ const fresh = defaults();
30
+ const target = existing;
31
+ for (const field of Object.keys(fresh)) {
32
+ if (!(field in existing)) target[field] = fresh[field];
33
+ }
34
+ return existing;
35
+ }
36
+ function claim() {
37
+ const existing = realm[KEY];
38
+ return existing ? backfillState(existing) : realm[KEY] = defaults();
39
+ }
40
+ var G = claim();
41
+ (realm[VERSIONS_KEY] ??= []).push(VERSION);
42
+
43
+ // src/lib/signal/graph/observe.ts
44
+ function observeReactiveNodes(next) {
45
+ G.observer = next;
46
+ return () => {
47
+ if (G.observer === next) G.observer = null;
48
+ };
49
+ }
50
+ var running = [];
51
+ var origin;
52
+ function reportReactiveNode(report) {
53
+ const observer = G.observer;
54
+ if (observer) {
55
+ observer.created(
56
+ report.parents === void 0 ? { ...report, parents: running, origin: report.origin ?? origin } : report
57
+ );
58
+ }
59
+ }
60
+ function enterReactiveRun(node) {
61
+ if (!G.observer) return false;
62
+ running.push(node);
63
+ return true;
64
+ }
65
+ function exitReactiveRun() {
66
+ running.pop();
67
+ }
68
+ function reportReactiveRun(node) {
69
+ G.observer?.ran?.(node);
70
+ }
71
+ function reportReactiveContextCreated(id) {
72
+ G.observer?.contextCreated?.(id);
73
+ }
74
+ function reportReactiveContext(id, kind, value) {
75
+ const observer = G.observer;
76
+ if (!observer?.context || !running.length) return;
77
+ observer.context(running[running.length - 1], id, kind, value);
78
+ }
79
+ function reportReactiveDispose(node) {
80
+ G.observer?.disposed?.(node);
81
+ }
82
+ var HOOK = "__FLUIXI_DEVTOOLS_HOOK__";
83
+ var hook = globalThis[HOOK];
84
+ if (typeof hook?.inject === "function") {
85
+ try {
86
+ hook.inject({ observeReactiveNodes, version: VERSION });
87
+ } catch {
88
+ }
89
+ }
90
+
91
+ // src/lib/signal/graph/core.ts
92
+ var CLEAN = 0;
93
+ var CHECK = 1;
94
+ var DIRTY = 2;
95
+ var Source = class {
96
+ constructor() {
97
+ /** Computeds, and watched leaves, that read this node. */
98
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
99
+ this.observers = null;
100
+ /** Watchers eagerly observing this node, which is how `runtime.ts` schedules effects. */
101
+ this.watchers = null;
102
+ /** Bumped whenever this node's value actually changes (equality-gated). */
103
+ this.version = 0;
104
+ }
105
+ /** Settle this node so a reader sees a current value/version. Overridden by Computed. */
106
+ updateIfNecessary() {
107
+ }
108
+ /** Link the current consumer (if any) as an observer of this node. */
109
+ track() {
110
+ if (G.untracked) return;
111
+ const consumer = G.consumer;
112
+ if (!consumer) return;
113
+ (this.observers ??= /* @__PURE__ */ new Set()).add(consumer);
114
+ (consumer.sources ??= /* @__PURE__ */ new Set()).add(this);
115
+ }
116
+ };
117
+ var Computed = class extends Source {
118
+ constructor(fn, options) {
119
+ super();
120
+ this._threw = false;
121
+ this._initialized = false;
122
+ this.state = DIRTY;
123
+ /** Nodes this computation read on its last run. Rebuilt on every run. */
124
+ this.sources = null;
125
+ this._fn = fn;
126
+ this._equals = options?.equals === false ? () => false : options?.equals ?? Object.is;
127
+ }
128
+ get() {
129
+ if (G.consumer === this) {
130
+ throw new Error("Signal.Computed cannot read itself");
131
+ }
132
+ this.track();
133
+ this.updateIfNecessary();
134
+ if (this._threw) throw this._error;
135
+ return this._value;
136
+ }
137
+ /** Settle, if CHECK, verify sources; if DIRTY (or never run), recompute. */
138
+ updateIfNecessary() {
139
+ if (this.state === CHECK) {
140
+ if (this.sources) {
141
+ for (const s of this.sources) {
142
+ s.updateIfNecessary();
143
+ if (this.state === DIRTY) break;
144
+ }
145
+ }
146
+ }
147
+ if (this.state === DIRTY || !this._initialized) this.recompute();
148
+ this.state = CLEAN;
149
+ }
150
+ recompute() {
151
+ if (this.sources) {
152
+ for (const s of this.sources) s.observers?.delete(this);
153
+ this.sources.clear();
154
+ }
155
+ const prevConsumer = G.consumer;
156
+ const prevUntracked = G.untracked;
157
+ const watched = enterReactiveRun(this);
158
+ G.consumer = this;
159
+ G.untracked = false;
160
+ let next;
161
+ let threw = false;
162
+ let err;
163
+ try {
164
+ next = this._fn();
165
+ } catch (e) {
166
+ threw = true;
167
+ err = e;
168
+ next = this._value;
169
+ } finally {
170
+ G.consumer = prevConsumer;
171
+ G.untracked = prevUntracked;
172
+ if (watched) exitReactiveRun();
173
+ }
174
+ const changed = !this._initialized || this._threw !== threw || (threw ? err !== this._error : !this._equals(this._value, next));
175
+ this._initialized = true;
176
+ this._threw = threw;
177
+ this._error = err;
178
+ this._value = next;
179
+ reportReactiveRun(this);
180
+ if (changed) {
181
+ this.version++;
182
+ if (this.observers) for (const o of [...this.observers]) o.markDirty(DIRTY);
183
+ notifyWatchers(this);
184
+ }
185
+ }
186
+ /**
187
+ * Push a maybe-dirty mark. A direct observer of a changed node gets DIRTY; its
188
+ * own observers get CHECK (recurse), stopping where already marked, so the
189
+ * push is O(newly-dirtied) and never recomputes.
190
+ */
191
+ markDirty(level) {
192
+ if (this.state >= level) return;
193
+ const wasClean = this.state === CLEAN;
194
+ this.state = level;
195
+ if (wasClean) {
196
+ if (this.observers) for (const o of [...this.observers]) o.markDirty(CHECK);
197
+ notifyWatchers(this);
198
+ }
199
+ }
200
+ peek() {
201
+ this.updateIfNecessary();
202
+ if (this._threw) throw this._error;
203
+ return this._value;
204
+ }
205
+ };
206
+ function notifyWatchers(node) {
207
+ if (!node.watchers) return;
208
+ for (const w of [...node.watchers]) w._notify(node);
209
+ }
210
+ var Watcher = class {
211
+ constructor(notify) {
212
+ this._watched = /* @__PURE__ */ new Set();
213
+ this._pending = /* @__PURE__ */ new Set();
214
+ this._notifyFn = notify;
215
+ }
216
+ watch(...nodes) {
217
+ for (const n of nodes) {
218
+ this._watched.add(n);
219
+ (n.watchers ??= /* @__PURE__ */ new Set()).add(this);
220
+ }
221
+ }
222
+ unwatch(...nodes) {
223
+ for (const n of nodes) {
224
+ this._watched.delete(n);
225
+ n.watchers?.delete(this);
226
+ this._pending.delete(n);
227
+ }
228
+ }
229
+ /** Watched nodes that may have changed since the last getPending(). */
230
+ getPending() {
231
+ const out = [...this._pending];
232
+ this._pending.clear();
233
+ return out;
234
+ }
235
+ /** @internal, a watched node became non-clean. Edge-triggered: notify once. */
236
+ _notify(node) {
237
+ if (!this._watched.has(node) || this._pending.has(node)) return;
238
+ const wasEmpty = this._pending.size === 0;
239
+ this._pending.add(node);
240
+ if (wasEmpty) this._notifyFn();
241
+ }
242
+ };
243
+
244
+ // src/lib/signal/graph/runtime.ts
245
+ function createChildOwner(parent) {
246
+ const owner = makeOwner(parent);
247
+ if (parent) (parent.owned ??= []).push(owner);
248
+ return owner;
249
+ }
250
+ function disposeScope(owner, final = true) {
251
+ disposeOwner(owner, final);
252
+ }
253
+ function isOwnerDisposed(owner) {
254
+ return !!owner && owner.disposed === true;
255
+ }
256
+ function makeOwner(parent) {
257
+ return { cleanups: null, owned: null, parent, contexts: null };
258
+ }
259
+ function createContext(defaultValue, name) {
260
+ const id = Symbol(name ?? "context");
261
+ reportReactiveContextCreated(id);
262
+ const Provider = (props) => {
263
+ reportReactiveContext(id, "provide", props.value);
264
+ const parent = G.owner;
265
+ const owner = makeOwner(parent);
266
+ (owner.contexts = /* @__PURE__ */ new Map()).set(id, props.value);
267
+ if (parent) (parent.owned ??= []).push(owner);
268
+ G.owner = owner;
269
+ try {
270
+ return props.children;
271
+ } finally {
272
+ G.owner = parent;
273
+ }
274
+ };
275
+ return { id, defaultValue, Provider };
276
+ }
277
+ function useContext(context) {
278
+ let o = G.owner;
279
+ while (o) {
280
+ if (o.contexts && o.contexts.has(context.id)) {
281
+ const found = o.contexts.get(context.id);
282
+ reportReactiveContext(context.id, "read", found);
283
+ return found;
284
+ }
285
+ o = o.parent;
286
+ }
287
+ reportReactiveContext(context.id, "read", context.defaultValue);
288
+ return context.defaultValue;
289
+ }
290
+ function readContextFrom(owner, context) {
291
+ let o = owner;
292
+ while (o) {
293
+ if (o.contexts && o.contexts.has(context.id)) return o.contexts.get(context.id);
294
+ o = o.parent;
295
+ }
296
+ return context.defaultValue;
297
+ }
298
+ function provide(context, value, fn) {
299
+ const owner = makeOwner(G.owner);
300
+ (owner.contexts = /* @__PURE__ */ new Map()).set(context.id, value);
301
+ if (G.owner) (G.owner.owned ??= []).push(owner);
302
+ const prev = G.owner;
303
+ G.owner = owner;
304
+ try {
305
+ return fn();
306
+ } finally {
307
+ G.owner = prev;
308
+ }
309
+ }
310
+ function setSuspendHandler(fn) {
311
+ G.onSuspend = fn;
312
+ }
313
+ function reportSuspend(promise) {
314
+ if (G.onSuspend) {
315
+ G.onSuspend(promise, G.owner);
316
+ return true;
317
+ }
318
+ return false;
319
+ }
320
+ function setErrorHandler(fn) {
321
+ G.onError = fn;
322
+ }
323
+ function getOwner() {
324
+ return G.owner;
325
+ }
326
+ function setOwner(owner) {
327
+ const prev = G.owner;
328
+ G.owner = owner;
329
+ return prev;
330
+ }
331
+ function runWithOwner(owner, fn) {
332
+ const prev = G.owner;
333
+ G.owner = owner;
334
+ try {
335
+ return fn();
336
+ } finally {
337
+ G.owner = prev;
338
+ }
339
+ }
340
+ function onCleanup(fn) {
341
+ if (!G.owner) return;
342
+ if (G.owner.disposed) {
343
+ fn();
344
+ return;
345
+ }
346
+ (G.owner.cleanups ??= []).push(fn);
347
+ }
348
+ function disposeOwner(owner, final = true) {
349
+ if (owner.disposed) return;
350
+ if (owner.owned) {
351
+ for (let i = owner.owned.length - 1; i >= 0; i--) disposeOwner(owner.owned[i]);
352
+ owner.owned = null;
353
+ }
354
+ if (owner.cleanups) {
355
+ for (let i = owner.cleanups.length - 1; i >= 0; i--) owner.cleanups[i]();
356
+ owner.cleanups = null;
357
+ }
358
+ if (final) owner.disposed = true;
359
+ }
360
+ function createRoot(fn) {
361
+ const owner = makeOwner(G.owner);
362
+ if (G.owner) (G.owner.owned ??= []).push(owner);
363
+ const prev = G.owner;
364
+ G.owner = owner;
365
+ try {
366
+ return fn(() => disposeOwner(owner));
367
+ } finally {
368
+ G.owner = prev;
369
+ }
370
+ }
371
+ function setScheduler(fn) {
372
+ G.hostSchedule = fn ?? ((run) => void Promise.resolve().then(run));
373
+ }
374
+ function enqueue(e) {
375
+ if (e.queued) return;
376
+ e.queued = true;
377
+ G.queue.push(e);
378
+ if (!G.scheduled && G.batchDepth === 0) {
379
+ G.scheduled = true;
380
+ G.hostSchedule(flushSync);
381
+ }
382
+ }
383
+ function flushSync() {
384
+ G.scheduled = false;
385
+ if (G.flushing) return;
386
+ G.flushing = true;
387
+ let guard = 0;
388
+ try {
389
+ while (G.queue.length) {
390
+ if (++guard > 1e5) throw new Error("[fluixi] effect flush did not settle (cycle?)");
391
+ const batchOf = G.queue;
392
+ G.queue = [];
393
+ for (const e of batchOf) {
394
+ e.queued = false;
395
+ if (!e.disposed) e.run();
396
+ }
397
+ }
398
+ } finally {
399
+ G.flushing = false;
400
+ }
401
+ }
402
+ function afterWrite() {
403
+ if (G.batchDepth === 0) flushSync();
404
+ }
405
+ function batch(fn) {
406
+ G.batchDepth++;
407
+ try {
408
+ return fn();
409
+ } finally {
410
+ if (--G.batchDepth === 0) flushSync();
411
+ }
412
+ }
413
+ var EffectNode = class {
414
+ constructor(fn, report = false) {
415
+ this.queued = false;
416
+ this.disposed = false;
417
+ this.owner = makeOwner(G.owner);
418
+ if (G.owner) (G.owner.cleanups ??= []).push(() => this.dispose());
419
+ this.computed = new Computed(() => {
420
+ disposeOwner(this.owner, false);
421
+ runWithOwner(this.owner, () => {
422
+ const r = fn();
423
+ if (typeof r === "function") (this.owner.cleanups ??= []).push(r);
424
+ });
425
+ });
426
+ reportReactiveNode({ kind: "effect", node: this.computed, handle: this, internal: !report });
427
+ this.watcher = new Watcher(() => enqueue(this));
428
+ this.run();
429
+ this.watcher.watch(this.computed);
430
+ }
431
+ run() {
432
+ if (this.disposed) return;
433
+ this.watcher.getPending();
434
+ try {
435
+ this.computed.get();
436
+ } catch (e) {
437
+ if (e && typeof e.then === "function" && G.onSuspend) {
438
+ G.onSuspend(e, this.owner);
439
+ return;
440
+ }
441
+ if (G.onError && G.onError(e, this.owner)) return;
442
+ throw e;
443
+ }
444
+ }
445
+ dispose() {
446
+ if (this.disposed) return;
447
+ this.disposed = true;
448
+ reportReactiveDispose(this.computed);
449
+ this.watcher.unwatch(this.computed);
450
+ disposeOwner(this.owner);
451
+ }
452
+ };
453
+ function effect(fn, report = false) {
454
+ const node = new EffectNode(fn, report);
455
+ return () => node.dispose();
456
+ }
457
+ export {
458
+ afterWrite,
459
+ batch,
460
+ createChildOwner,
461
+ createContext,
462
+ createRoot,
463
+ disposeScope,
464
+ effect,
465
+ flushSync,
466
+ getOwner,
467
+ isOwnerDisposed,
468
+ onCleanup,
469
+ provide,
470
+ readContextFrom,
471
+ reportSuspend,
472
+ runWithOwner,
473
+ setErrorHandler,
474
+ setOwner,
475
+ setScheduler,
476
+ setSuspendHandler,
477
+ useContext
478
+ };