@fluixi/reactive 1.0.0-alpha.53

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 (176) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +343 -0
  3. package/dist/cdn/reactive.cjs +1 -0
  4. package/dist/cdn/reactive.d.ts +11 -0
  5. package/dist/cdn/reactive.d.ts.map +1 -0
  6. package/dist/cdn/reactive.global.js +1 -0
  7. package/dist/cdn/reactive.js +10 -0
  8. package/dist/cdn/reactive.mjs +1 -0
  9. package/dist/cdn/signal.cjs +1 -0
  10. package/dist/cdn/signal.d.ts +19 -0
  11. package/dist/cdn/signal.d.ts.map +1 -0
  12. package/dist/cdn/signal.global.js +1 -0
  13. package/dist/cdn/signal.js +18 -0
  14. package/dist/cdn/signal.mjs +1 -0
  15. package/dist/index.cjs +1 -0
  16. package/dist/index.d.ts +2 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.js +1 -0
  19. package/dist/index.mjs +1 -0
  20. package/dist/lib/index.cjs +1 -0
  21. package/dist/lib/index.d.ts +5 -0
  22. package/dist/lib/index.d.ts.map +1 -0
  23. package/dist/lib/index.js +4 -0
  24. package/dist/lib/index.mjs +1 -0
  25. package/dist/lib/observable/index.cjs +1 -0
  26. package/dist/lib/observable/index.d.ts +4 -0
  27. package/dist/lib/observable/index.d.ts.map +1 -0
  28. package/dist/lib/observable/index.js +3 -0
  29. package/dist/lib/observable/index.mjs +1 -0
  30. package/dist/lib/observable/observable.cjs +1 -0
  31. package/dist/lib/observable/observable.d.ts +383 -0
  32. package/dist/lib/observable/observable.d.ts.map +1 -0
  33. package/dist/lib/observable/observable.js +339 -0
  34. package/dist/lib/observable/observable.mjs +1 -0
  35. package/dist/lib/observable/subscription.cjs +1 -0
  36. package/dist/lib/observable/subscription.d.ts +70 -0
  37. package/dist/lib/observable/subscription.d.ts.map +1 -0
  38. package/dist/lib/observable/subscription.js +190 -0
  39. package/dist/lib/observable/subscription.mjs +1 -0
  40. package/dist/lib/observable/types.cjs +1 -0
  41. package/dist/lib/observable/types.d.ts +44 -0
  42. package/dist/lib/observable/types.d.ts.map +1 -0
  43. package/dist/lib/observable/types.js +1 -0
  44. package/dist/lib/observable/types.mjs +0 -0
  45. package/dist/lib/signal/cached-resource.cjs +1 -0
  46. package/dist/lib/signal/cached-resource.d.ts +94 -0
  47. package/dist/lib/signal/cached-resource.d.ts.map +1 -0
  48. package/dist/lib/signal/cached-resource.js +257 -0
  49. package/dist/lib/signal/cached-resource.mjs +1 -0
  50. package/dist/lib/signal/graph/children.cjs +1 -0
  51. package/dist/lib/signal/graph/children.d.ts +2 -0
  52. package/dist/lib/signal/graph/children.d.ts.map +1 -0
  53. package/dist/lib/signal/graph/children.js +30 -0
  54. package/dist/lib/signal/graph/children.mjs +1 -0
  55. package/dist/lib/signal/graph/compat.cjs +1 -0
  56. package/dist/lib/signal/graph/compat.d.ts +64 -0
  57. package/dist/lib/signal/graph/compat.d.ts.map +1 -0
  58. package/dist/lib/signal/graph/compat.js +178 -0
  59. package/dist/lib/signal/graph/compat.mjs +1 -0
  60. package/dist/lib/signal/graph/core.cjs +1 -0
  61. package/dist/lib/signal/graph/core.d.ts +95 -0
  62. package/dist/lib/signal/graph/core.d.ts.map +1 -0
  63. package/dist/lib/signal/graph/core.js +254 -0
  64. package/dist/lib/signal/graph/core.mjs +1 -0
  65. package/dist/lib/signal/graph/derived.cjs +1 -0
  66. package/dist/lib/signal/graph/derived.d.ts +14 -0
  67. package/dist/lib/signal/graph/derived.d.ts.map +1 -0
  68. package/dist/lib/signal/graph/derived.js +81 -0
  69. package/dist/lib/signal/graph/derived.mjs +1 -0
  70. package/dist/lib/signal/graph/detect.cjs +1 -0
  71. package/dist/lib/signal/graph/detect.d.ts +16 -0
  72. package/dist/lib/signal/graph/detect.d.ts.map +1 -0
  73. package/dist/lib/signal/graph/detect.js +36 -0
  74. package/dist/lib/signal/graph/detect.mjs +1 -0
  75. package/dist/lib/signal/graph/error-boundary.cjs +1 -0
  76. package/dist/lib/signal/graph/error-boundary.d.ts +16 -0
  77. package/dist/lib/signal/graph/error-boundary.d.ts.map +1 -0
  78. package/dist/lib/signal/graph/error-boundary.js +34 -0
  79. package/dist/lib/signal/graph/error-boundary.mjs +1 -0
  80. package/dist/lib/signal/graph/index.cjs +1 -0
  81. package/dist/lib/signal/graph/index.d.ts +21 -0
  82. package/dist/lib/signal/graph/index.d.ts.map +1 -0
  83. package/dist/lib/signal/graph/index.js +20 -0
  84. package/dist/lib/signal/graph/index.mjs +1 -0
  85. package/dist/lib/signal/graph/resource.cjs +1 -0
  86. package/dist/lib/signal/graph/resource.d.ts +36 -0
  87. package/dist/lib/signal/graph/resource.d.ts.map +1 -0
  88. package/dist/lib/signal/graph/resource.js +156 -0
  89. package/dist/lib/signal/graph/resource.mjs +1 -0
  90. package/dist/lib/signal/graph/runtime.cjs +1 -0
  91. package/dist/lib/signal/graph/runtime.d.ts +56 -0
  92. package/dist/lib/signal/graph/runtime.d.ts.map +1 -0
  93. package/dist/lib/signal/graph/runtime.js +277 -0
  94. package/dist/lib/signal/graph/runtime.mjs +1 -0
  95. package/dist/lib/signal/graph/signal-next.cjs +1 -0
  96. package/dist/lib/signal/graph/signal-next.d.ts +37 -0
  97. package/dist/lib/signal/graph/signal-next.d.ts.map +1 -0
  98. package/dist/lib/signal/graph/signal-next.js +41 -0
  99. package/dist/lib/signal/graph/signal-next.mjs +1 -0
  100. package/dist/lib/signal/graph/ssr-hooks.cjs +1 -0
  101. package/dist/lib/signal/graph/ssr-hooks.d.ts +28 -0
  102. package/dist/lib/signal/graph/ssr-hooks.d.ts.map +1 -0
  103. package/dist/lib/signal/graph/ssr-hooks.js +45 -0
  104. package/dist/lib/signal/graph/ssr-hooks.mjs +1 -0
  105. package/dist/lib/signal/graph/state.cjs +1 -0
  106. package/dist/lib/signal/graph/state.d.ts +43 -0
  107. package/dist/lib/signal/graph/state.d.ts.map +1 -0
  108. package/dist/lib/signal/graph/state.js +37 -0
  109. package/dist/lib/signal/graph/state.mjs +1 -0
  110. package/dist/lib/signal/graph/suspense.cjs +1 -0
  111. package/dist/lib/signal/graph/suspense.d.ts +13 -0
  112. package/dist/lib/signal/graph/suspense.d.ts.map +1 -0
  113. package/dist/lib/signal/graph/suspense.js +36 -0
  114. package/dist/lib/signal/graph/suspense.mjs +1 -0
  115. package/dist/lib/signal/index-legacy.cjs +1 -0
  116. package/dist/lib/signal/index-legacy.d.ts +12 -0
  117. package/dist/lib/signal/index-legacy.d.ts.map +1 -0
  118. package/dist/lib/signal/index-legacy.js +11 -0
  119. package/dist/lib/signal/index-legacy.mjs +1 -0
  120. package/dist/lib/signal/index.cjs +1 -0
  121. package/dist/lib/signal/index.d.ts +12 -0
  122. package/dist/lib/signal/index.d.ts.map +1 -0
  123. package/dist/lib/signal/index.js +11 -0
  124. package/dist/lib/signal/index.mjs +1 -0
  125. package/dist/lib/signal/list.cjs +1 -0
  126. package/dist/lib/signal/list.d.ts +39 -0
  127. package/dist/lib/signal/list.d.ts.map +1 -0
  128. package/dist/lib/signal/list.js +172 -0
  129. package/dist/lib/signal/list.mjs +1 -0
  130. package/dist/lib/signal/signal.cjs +1 -0
  131. package/dist/lib/signal/signal.d.ts +295 -0
  132. package/dist/lib/signal/signal.d.ts.map +1 -0
  133. package/dist/lib/signal/signal.js +1219 -0
  134. package/dist/lib/signal/signal.mjs +1 -0
  135. package/dist/lib/signal/types.cjs +1 -0
  136. package/dist/lib/signal/types.d.ts +133 -0
  137. package/dist/lib/signal/types.d.ts.map +1 -0
  138. package/dist/lib/signal/types.js +38 -0
  139. package/dist/lib/signal/types.mjs +1 -0
  140. package/dist/lib/signal/utilities.cjs +1 -0
  141. package/dist/lib/signal/utilities.d.ts +355 -0
  142. package/dist/lib/signal/utilities.d.ts.map +1 -0
  143. package/dist/lib/signal/utilities.js +504 -0
  144. package/dist/lib/signal/utilities.mjs +1 -0
  145. package/dist/lib/store/index.cjs +1 -0
  146. package/dist/lib/store/index.d.ts +14 -0
  147. package/dist/lib/store/index.d.ts.map +1 -0
  148. package/dist/lib/store/index.js +11 -0
  149. package/dist/lib/store/index.mjs +1 -0
  150. package/dist/lib/store/setter.types.cjs +1 -0
  151. package/dist/lib/store/setter.types.d.ts +79 -0
  152. package/dist/lib/store/setter.types.d.ts.map +1 -0
  153. package/dist/lib/store/setter.types.js +65 -0
  154. package/dist/lib/store/setter.types.mjs +0 -0
  155. package/dist/lib/store/store.cjs +1 -0
  156. package/dist/lib/store/store.d.ts +86 -0
  157. package/dist/lib/store/store.d.ts.map +1 -0
  158. package/dist/lib/store/store.js +442 -0
  159. package/dist/lib/store/store.mjs +1 -0
  160. package/dist/lib/store/types.cjs +1 -0
  161. package/dist/lib/store/types.d.ts +57 -0
  162. package/dist/lib/store/types.d.ts.map +1 -0
  163. package/dist/lib/store/types.js +1 -0
  164. package/dist/lib/store/types.mjs +0 -0
  165. package/dist/lib/store/utils.cjs +1 -0
  166. package/dist/lib/store/utils.d.ts +13 -0
  167. package/dist/lib/store/utils.d.ts.map +1 -0
  168. package/dist/lib/store/utils.js +29 -0
  169. package/dist/lib/store/utils.mjs +1 -0
  170. package/dist/lib/subject.cjs +1 -0
  171. package/dist/lib/subject.d.ts +14 -0
  172. package/dist/lib/subject.d.ts.map +1 -0
  173. package/dist/lib/subject.js +169 -0
  174. package/dist/lib/subject.mjs +1 -0
  175. package/dist/tsconfig.lib.tsbuildinfo +1 -0
  176. package/package.json +114 -0
@@ -0,0 +1,178 @@
1
+ /**
2
+ * L2 (core) — the legacy public reactive API re-expressed on top of L0 (graph) +
3
+ * L1 (runtime), with byte-identical signatures so it is a drop-in for the existing
4
+ * `createSignal`/`createMemo`/`createEffect`/… surface. This is the "re-point": the
5
+ * old API delegates to the TC39-shaped core instead of the bespoke Computation graph.
6
+ *
7
+ * Scope: the CORE primitives (signals, memos, effects, roots, batch, untrack, on).
8
+ * The wider surface (resources, stores, context, suspense, transitions, the SSR
9
+ * eager-stale/drain hooks) is ported separately before the production export flips
10
+ * — until then this lives behind its own module + spec, validated for behavioural
11
+ * equivalence against the legacy oracle.
12
+ *
13
+ * Timing note: legacy effects flush SYNCHRONOUSLY on a write (outside batch), so we
14
+ * install a synchronous L1 scheduler here; `batch` still coalesces to one flush.
15
+ */
16
+ import { State, Computed, untrack as l0untrack } from './core.js';
17
+ import { effect as l1effect, batch as l1batch, setScheduler, afterWrite, flushSync, reportSuspend, getOwner, runWithOwner, } from './runtime.js';
18
+ function isThenable(v) {
19
+ return !!v && typeof v.then === 'function';
20
+ }
21
+ export { createRoot, onCleanup, getOwner, setOwner, runWithOwner } from './runtime.js';
22
+ // Compat owns all flush points (afterWrite / batch), so the host scheduler is a
23
+ // no-op: enqueue only QUEUES; a write marks the whole graph then flushes once via
24
+ // afterWrite. This separates marking from running (glitch-free, legacy-equivalent
25
+ // synchronous effects) — a synchronous scheduler that flushed mid-propagation would
26
+ // run effects before all observers were marked.
27
+ setScheduler(() => { });
28
+ const $SIGNAL = Symbol.for('signal');
29
+ const $MEMO = Symbol.for('memo');
30
+ // The legacy createSignal attaches a ReactiveNode (with a live `.value`/`.version`)
31
+ // under this symbol; consumers like the store read `signal[$NODE].value` to gate
32
+ // updates. compat exposes a matching node-like view so those consumers work unchanged.
33
+ const $NODE = Symbol.for('signal-node');
34
+ /** createSignal — a writable cell, returned as the legacy `[get, set]` tuple. */
35
+ export function createSignal(value, options) {
36
+ const state = new State(value, { equals: options?.equals });
37
+ const read = (() => state.get());
38
+ const write = ((next) => {
39
+ const v = typeof next === 'function' ? next(state.peek()) : next;
40
+ state.set(v); // marks the graph dirty
41
+ afterWrite(); // then flush effects once (no-op inside a batch)
42
+ return v;
43
+ });
44
+ Object.defineProperty(read, $SIGNAL, { value: true, enumerable: true });
45
+ // Node-like view for consumers (e.g. the store) that read `signal[$NODE].value`.
46
+ Object.defineProperty(read, $NODE, {
47
+ value: {
48
+ get value() {
49
+ return state.peek();
50
+ },
51
+ get version() {
52
+ return state.version;
53
+ },
54
+ },
55
+ enumerable: false,
56
+ });
57
+ return [read, write];
58
+ }
59
+ /** createMemo — a lazy, cached derived value (legacy accessor). */
60
+ export function createMemo(fn, value, options) {
61
+ let prev = value;
62
+ // Capture the owner at creation so the memo's fn (and any component it renders, e.g.
63
+ // a lazy route) runs under THIS scope's owner — preserving context up the owner chain
64
+ // even when the memo recomputes later (async SSR drain). Matches legacy memo semantics.
65
+ const owner = getOwner();
66
+ const c = new Computed(() => runWithOwner(owner, () => {
67
+ try {
68
+ return (prev = fn(prev));
69
+ }
70
+ catch (e) {
71
+ // A suspended read (resource/lazy while pending) throws its promise. Legacy
72
+ // memos keep their previous value and let a Suspense boundary handle it,
73
+ // rather than rethrowing the promise as an error.
74
+ if (isThenable(e)) {
75
+ reportSuspend(e);
76
+ return prev;
77
+ }
78
+ throw e;
79
+ }
80
+ }), { equals: options?.equals });
81
+ const read = (() => c.get());
82
+ // A memo IS a readable reactive accessor — stamp $SIGNAL (matching the legacy
83
+ // createMemo) so `isSignal(memo)` is true. Without this, the DOM runtime's
84
+ // insert() fails to recognise a control-flow memo (For/Switch/Index/Dynamic)
85
+ // as reactive, caches it as a component, and freezes it → updates never apply.
86
+ Object.defineProperty(read, $SIGNAL, { value: true, enumerable: true });
87
+ Object.defineProperty(read, $MEMO, { value: true, enumerable: true });
88
+ return read;
89
+ }
90
+ /**
91
+ * createEffect — a re-running side effect. The fn receives the previous return
92
+ * value and may return a new one (legacy signature) or a cleanup function.
93
+ */
94
+ export function createEffect(fn, value) {
95
+ let prev = value;
96
+ return l1effect(() => {
97
+ const r = fn(prev);
98
+ // A function result is treated as the next "prev" (legacy) — unless the fn
99
+ // never returns one, in which case onCleanup is the cleanup path. We keep the
100
+ // legacy carry-the-value behaviour; cleanups use onCleanup.
101
+ prev = r;
102
+ });
103
+ }
104
+ /**
105
+ * createComputed — an EAGER, owner-managed reactive computation (returns void).
106
+ * Unlike a lazy memo it runs immediately and re-runs eagerly when a dep changes;
107
+ * used for derivations with side effects (e.g. store internals). On L1 that is an
108
+ * eager effect whose dispose is owned by the current scope.
109
+ */
110
+ export function createComputed(fn, value) {
111
+ let prev = value;
112
+ l1effect(() => {
113
+ prev = fn(prev);
114
+ });
115
+ }
116
+ /**
117
+ * createRenderEffect — an effect that runs in the render phase. For the reactive
118
+ * core this is an eager effect (the render-vs-commit timing distinction is a DOM-
119
+ * layer concern); returns a dispose like createEffect.
120
+ */
121
+ export function createRenderEffect(fn, value) {
122
+ let prev = value;
123
+ return l1effect(() => {
124
+ prev = fn(prev);
125
+ });
126
+ }
127
+ /** batch — coalesce writes so dependent effects flush once. */
128
+ export function batch(fn) {
129
+ return l1batch(fn);
130
+ }
131
+ /** untrack — read without subscribing. */
132
+ export function untrack(fn) {
133
+ return l0untrack(fn);
134
+ }
135
+ /**
136
+ * startTransition — run `fn`'s updates as one batch. (Minimal port: the concurrent
137
+ * time-slicing/deferral of the legacy implementation is a later refinement; the
138
+ * batched-commit semantics that callers rely on are preserved.)
139
+ */
140
+ export function startTransition(fn) {
141
+ l1batch(fn);
142
+ return Promise.resolve();
143
+ }
144
+ /** useTransition — `[isPending, startTransition]`. isPending is synchronous here (no deferral). */
145
+ export function useTransition() {
146
+ return [() => false, startTransition];
147
+ }
148
+ /** Force any pending effects to run now. */
149
+ export { flushSync };
150
+ /**
151
+ * flush — drain pending effects, returning a Promise (legacy `flush()` is async; many
152
+ * callers do `flush().then(...)`). Effects already flush synchronously on each write via
153
+ * afterWrite, so this just settles any remainder and resolves.
154
+ */
155
+ export function flush() {
156
+ flushSync();
157
+ return Promise.resolve();
158
+ }
159
+ /**
160
+ * on(deps, fn, { defer }) — an explicit-dependency helper. Reads `deps` (tracked),
161
+ * then runs `fn(value, prevValue, prevResult)` untracked. `defer` skips the first run.
162
+ */
163
+ export function on(deps, fn, options) {
164
+ const isArray = Array.isArray(deps);
165
+ let prevValue;
166
+ let defer = options?.defer ?? false;
167
+ return (prevResult) => {
168
+ const value = (isArray ? deps.map((d) => d()) : deps());
169
+ if (defer) {
170
+ defer = false;
171
+ prevValue = value;
172
+ return undefined;
173
+ }
174
+ const result = l0untrack(() => fn(value, prevValue, prevResult));
175
+ prevValue = value;
176
+ return result;
177
+ };
178
+ }
@@ -0,0 +1 @@
1
+ var E="__fluixi_signal_next_state__",R=e=>{typeof queueMicrotask=="function"?queueMicrotask(e):Promise.resolve().then(e)},n=globalThis[E]??={consumer:null,untracked:!1,owner:null,queue:[],scheduled:!1,flushing:!1,batchDepth:0,hostSchedule:R,onSuspend:null,onError:null};var g=0,k=1,d=2,h=class{constructor(){this.observers=null;this.watchers=null;this.version=0}updateIfNecessary(){}track(){if(n.untracked)return;let t=n.consumer;t&&((this.observers??=new Set).add(t),(t.sources??=new Set).add(this))}},p=class extends h{constructor(t,r){super(),this._value=t,this._equals=r?.equals===!1?()=>!1:r?.equals??Object.is}get(){return this.track(),this._value}set(t){if(!this._equals(this._value,t)){if(this._value=t,this.version++,this.observers)for(let r of[...this.observers])r.markDirty(d);m(this)}}peek(){return this._value}},c=class extends h{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(n.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===k&&this.sources){for(let r of this.sources)if(r.updateIfNecessary(),this.state===d)break}(this.state===d||!this._initialized)&&this.recompute(),this.state=g}recompute(){if(this.sources){for(let l of this.sources)l.observers?.delete(this);this.sources.clear()}let r=n.consumer,o=n.untracked;n.consumer=this,n.untracked=!1;let s,i=!1,u;try{s=this._fn()}catch(l){i=!0,u=l,s=this._value}finally{n.consumer=r,n.untracked=o}let a=!this._initialized||this._threw!==i||(i?u!==this._error:!this._equals(this._value,s));if(this._initialized=!0,this._threw=i,this._error=u,this._value=s,a){if(this.version++,this.observers)for(let l of[...this.observers])l.markDirty(d);m(this)}}markDirty(r){if(this.state>=r)return;let o=this.state===g;if(this.state=r,o){if(this.observers)for(let s of[...this.observers])s.markDirty(k);m(this)}}peek(){if(this.updateIfNecessary(),this._threw)throw this._error;return this._value}};function m(e){if(e.watchers)for(let t of[...e.watchers])t._notify(e)}var w=class{constructor(t){this._watched=new Set;this._pending=new Set;this._notifyFn=t}watch(...t){for(let r of t)this._watched.add(r),(r.watchers??=new Set).add(this)}unwatch(...t){for(let r of t)this._watched.delete(r),r.watchers?.delete(this),this._pending.delete(r)}getPending(){let t=[...this._pending];return this._pending.clear(),t}_notify(t){if(!this._watched.has(t)||this._pending.has(t))return;let r=this._pending.size===0;this._pending.add(t),r&&this._notifyFn()}};function b(e){if(n.untracked)return e();n.untracked=!0;try{return e()}finally{n.untracked=!1}}function O(e){return{cleanups:null,owned:null,parent:e,contexts:null}}function q(e){return n.onSuspend?(n.onSuspend(e,n.owner),!0):!1}function S(){return n.owner}function N(e){let t=n.owner;return n.owner=e,t}function T(e,t){let r=n.owner;n.owner=e;try{return t()}finally{n.owner=r}}function M(e){n.owner&&(n.owner.cleanups??=[]).push(e)}function v(e){if(e.owned){for(let t=e.owned.length-1;t>=0;t--)v(e.owned[t]);e.owned=null}if(e.cleanups){for(let t=e.cleanups.length-1;t>=0;t--)e.cleanups[t]();e.cleanups=null}}function I(e){let t=O(n.owner);n.owner&&(n.owner.owned??=[]).push(t);let r=n.owner;n.owner=t;try{return e(()=>v(t))}finally{n.owner=r}}function C(e){n.hostSchedule=e??(t=>{Promise.resolve().then(t)})}function W(e){e.queued||(e.queued=!0,n.queue.push(e),!n.scheduled&&n.batchDepth===0&&(n.scheduled=!0,n.hostSchedule(f)))}function f(){if(n.scheduled=!1,n.flushing)return;n.flushing=!0;let e=0;try{for(;n.queue.length;){if(++e>1e5)throw new Error("[fluixi] effect flush did not settle (cycle?)");let t=n.queue;n.queue=[];for(let r of t)r.queued=!1,r.disposed||r.run()}}finally{n.flushing=!1}}function P(){n.batchDepth===0&&f()}function _(e){n.batchDepth++;try{return e()}finally{--n.batchDepth===0&&f()}}var x=class{constructor(t){this.queued=!1;this.disposed=!1;this.owner=O(n.owner),n.owner&&(n.owner.cleanups??=[]).push(()=>this.dispose()),this.computed=new c(()=>{v(this.owner),T(this.owner,()=>{let r=t();typeof r=="function"&&(this.owner.cleanups??=[]).push(r)})}),this.watcher=new w(()=>W(this)),this.run(),this.watcher.watch(this.computed)}run(){if(!this.disposed){this.watcher.getPending();try{this.computed.get()}catch(t){if(t&&typeof t.then=="function"&&n.onSuspend){n.onSuspend(t,this.owner);return}if(n.onError&&n.onError(t,this.owner))return;throw t}}}dispose(){this.disposed||(this.disposed=!0,this.watcher.unwatch(this.computed),v(this.owner))}};function y(e){let t=new x(e);return()=>t.dispose()}function z(e){return!!e&&typeof e.then=="function"}C(()=>{});var D=Symbol.for("signal"),G=Symbol.for("memo"),j=Symbol.for("signal-node");function J(e,t){let r=new p(e,{equals:t?.equals}),o=(()=>r.get()),s=(i=>{let u=typeof i=="function"?i(r.peek()):i;return r.set(u),P(),u});return Object.defineProperty(o,D,{value:!0,enumerable:!0}),Object.defineProperty(o,j,{value:{get value(){return r.peek()},get version(){return r.version}},enumerable:!1}),[o,s]}function Q(e,t,r){let o=t,s=S(),i=new c(()=>T(s,()=>{try{return o=e(o)}catch(a){if(z(a))return q(a),o;throw a}}),{equals:r?.equals}),u=(()=>i.get());return Object.defineProperty(u,D,{value:!0,enumerable:!0}),Object.defineProperty(u,G,{value:!0,enumerable:!0}),u}function X(e,t){let r=t;return y(()=>{r=e(r)})}function Z(e,t){let r=t;y(()=>{r=e(r)})}function ee(e,t){let r=t;return y(()=>{r=e(r)})}function te(e){return _(e)}function ne(e){return b(e)}function V(e){return _(e),Promise.resolve()}function re(){return[()=>!1,V]}function oe(){return f(),Promise.resolve()}function se(e,t,r){let o=Array.isArray(e),s,i=r?.defer??!1;return u=>{let a=o?e.map(A=>A()):e();if(i){i=!1,s=a;return}let l=b(()=>t(a,s,u));return s=a,l}}export{te as batch,Z as createComputed,X as createEffect,Q as createMemo,ee as createRenderEffect,I as createRoot,J as createSignal,oe as flush,f as flushSync,S as getOwner,se as on,M as onCleanup,T as runWithOwner,N as setOwner,V as startTransition,ne as untrack,re as useTransition};
@@ -0,0 +1 @@
1
+ "use strict";var l=Object.defineProperty;var b=Object.getOwnPropertyDescriptor;var T=Object.getOwnPropertyNames;var k=Object.prototype.hasOwnProperty;var S=(s,e)=>{for(var t in e)l(s,t,{get:e[t],enumerable:!0})},m=(s,e,t,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of T(e))!k.call(s,i)&&i!==t&&l(s,i,{get:()=>e[i],enumerable:!(o=b(e,i))||o.enumerable});return s};var g=s=>m(l({},"__esModule",{value:!0}),s);var D={};S(D,{Computed:()=>f,State:()=>d,Watcher:()=>p,currentComputed:()=>N,hasSinks:()=>E,untrack:()=>C});module.exports=g(D);var q="__fluixi_signal_next_state__",x=s=>{typeof queueMicrotask=="function"?queueMicrotask(s):Promise.resolve().then(s)},r=globalThis[q]??={consumer:null,untracked:!1,owner:null,queue:[],scheduled:!1,flushing:!1,batchDepth:0,hostSchedule:x,onSuspend:null,onError:null};var _=0,w=1,a=2,h=class{constructor(){this.observers=null;this.watchers=null;this.version=0}updateIfNecessary(){}track(){if(r.untracked)return;let e=r.consumer;e&&((this.observers??=new Set).add(e),(e.sources??=new Set).add(this))}},d=class extends h{constructor(e,t){super(),this._value=e,this._equals=t?.equals===!1?()=>!1:t?.equals??Object.is}get(){return this.track(),this._value}set(e){if(!this._equals(this._value,e)){if(this._value=e,this.version++,this.observers)for(let t of[...this.observers])t.markDirty(a);v(this)}}peek(){return this._value}},f=class extends h{constructor(t,o){super();this._threw=!1;this._initialized=!1;this.state=a;this.sources=null;this._fn=t,this._equals=o?.equals===!1?()=>!1:o?.equals??Object.is}get(){if(r.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===w&&this.sources){for(let t of this.sources)if(t.updateIfNecessary(),this.state===a)break}(this.state===a||!this._initialized)&&this.recompute(),this.state=_}recompute(){if(this.sources){for(let n of this.sources)n.observers?.delete(this);this.sources.clear()}let t=r.consumer,o=r.untracked;r.consumer=this,r.untracked=!1;let i,u=!1,c;try{i=this._fn()}catch(n){u=!0,c=n,i=this._value}finally{r.consumer=t,r.untracked=o}let y=!this._initialized||this._threw!==u||(u?c!==this._error:!this._equals(this._value,i));if(this._initialized=!0,this._threw=u,this._error=c,this._value=i,y){if(this.version++,this.observers)for(let n of[...this.observers])n.markDirty(a);v(this)}}markDirty(t){if(this.state>=t)return;let o=this.state===_;if(this.state=t,o){if(this.observers)for(let i of[...this.observers])i.markDirty(w);v(this)}}peek(){if(this.updateIfNecessary(),this._threw)throw this._error;return this._value}};function v(s){if(s.watchers)for(let e of[...s.watchers])e._notify(s)}var p=class{constructor(e){this._watched=new Set;this._pending=new Set;this._notifyFn=e}watch(...e){for(let t of e)this._watched.add(t),(t.watchers??=new Set).add(this)}unwatch(...e){for(let t of e)this._watched.delete(t),t.watchers?.delete(this),this._pending.delete(t)}getPending(){let e=[...this._pending];return this._pending.clear(),e}_notify(e){if(!this._watched.has(e)||this._pending.has(e))return;let t=this._pending.size===0;this._pending.add(e),t&&this._notifyFn()}};function C(s){if(r.untracked)return s();r.untracked=!0;try{return s()}finally{r.untracked=!1}}function N(){return r.consumer}function E(s){return!!(s.observers&&s.observers.size)||!!(s.watchers&&s.watchers.size)}
@@ -0,0 +1,95 @@
1
+ /**
2
+ * L0 — the TC39-shaped reactive graph core.
3
+ *
4
+ * Class-based primitives (`State`, `Computed`, `Watcher`) that own ONLY the
5
+ * dependency graph: values, versions, source/observer edges, the lazy push-pull
6
+ * state machine, and glitch-free pull evaluation. They know nothing about
7
+ * ownership, scheduling, effects, suspense, or resources — those live in the L1
8
+ * runtime that sits on top (see ../signal.ts). This is a re-shaping of the exact
9
+ * algorithm the existing graph already runs (CLEAN/CHECK/DIRTY + version + the
10
+ * `Listener`/observers edges), so behaviour is preserved; it's just relocated
11
+ * onto classes whose shape matches the TC39 Signals proposal.
12
+ *
13
+ * The model is the "push the dirt, pull the value" scheme (Reactively / SolidJS /
14
+ * the TC39 polyfill): a write PUSHES a maybe-dirty mark down to observers without
15
+ * recomputing; a read PULLS, settling upstream lazily and recomputing only what
16
+ * actually changed (equality-gated). Reads outside a tracking scope don't subscribe.
17
+ */
18
+ declare const CLEAN = 0;
19
+ declare const CHECK = 1;
20
+ declare const DIRTY = 2;
21
+ type NodeState = typeof CLEAN | typeof CHECK | typeof DIRTY;
22
+ /** A thing that can be depended upon: holds observers + a monotonic version. */
23
+ declare abstract class Source {
24
+ /** Computeds (and watched leaves) that read this node. (was the signalObservers entry) */
25
+ observers: Set<Computed<any>> | null;
26
+ /** Watchers eagerly observing this node, for effect scheduling (L1). */
27
+ watchers: Set<Watcher> | null;
28
+ /** Bumped whenever this node's value actually changes (equality-gated). */
29
+ version: number;
30
+ /** Settle this node so a reader sees a current value/version. Overridden by Computed. */
31
+ updateIfNecessary(): void;
32
+ /** Link the current consumer (if any) as an observer of this node. */
33
+ protected track(): void;
34
+ }
35
+ export declare class State<T> extends Source {
36
+ private _value;
37
+ private readonly _equals;
38
+ constructor(value: T, options?: {
39
+ equals?: ((a: T, b: T) => boolean) | false;
40
+ });
41
+ get(): T;
42
+ set(next: T): void;
43
+ /** Read without subscribing (peek). */
44
+ peek(): T;
45
+ }
46
+ export declare class Computed<T> extends Source {
47
+ private _value;
48
+ private _error;
49
+ private _threw;
50
+ private _initialized;
51
+ state: NodeState;
52
+ /** Nodes this computation read on its last run. (was Computation.sources) */
53
+ sources: Set<Source> | null;
54
+ private readonly _fn;
55
+ private readonly _equals;
56
+ constructor(fn: () => T, options?: {
57
+ equals?: ((a: T, b: T) => boolean) | false;
58
+ });
59
+ get(): T;
60
+ /** Settle: if CHECK, verify sources; if DIRTY (or never run), recompute. */
61
+ updateIfNecessary(): void;
62
+ private recompute;
63
+ /**
64
+ * Push a maybe-dirty mark. A direct observer of a changed node gets DIRTY; its
65
+ * own observers get CHECK (recurse) — stopping where already marked, so the
66
+ * push is O(newly-dirtied) and never recomputes.
67
+ */
68
+ markDirty(level: NodeState): void;
69
+ peek(): T;
70
+ }
71
+ /**
72
+ * Signal.subtle.Watcher — the only eager observation primitive. Its callback fires
73
+ * when a watched signal MAY have changed; the callback MUST NOT read or write
74
+ * signals. Effects (L1) are built on this + a scheduler.
75
+ */
76
+ export declare class Watcher {
77
+ private readonly _notifyFn;
78
+ private readonly _watched;
79
+ private _pending;
80
+ constructor(notify: () => void);
81
+ watch(...nodes: Source[]): void;
82
+ unwatch(...nodes: Source[]): void;
83
+ /** Watched nodes that may have changed since the last getPending(). */
84
+ getPending(): Source[];
85
+ /** @internal — a watched node became non-clean. Edge-triggered: notify once. */
86
+ _notify(node: Source): void;
87
+ }
88
+ /** Signal.subtle.untrack — run `fn` without subscribing the current consumer. */
89
+ export declare function untrack<T>(fn: () => T): T;
90
+ /** Signal.subtle.currentComputed — the computed currently evaluating, or null. */
91
+ export declare function currentComputed(): Computed<unknown> | null;
92
+ /** Whether a node currently has observers/watchers (Signal.subtle.hasSinks). */
93
+ export declare function hasSinks(node: Source): boolean;
94
+ export {};
95
+ //# sourceMappingURL=core.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../../../src/lib/signal/graph/core.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,QAAA,MAAM,KAAK,IAAI,CAAC;AAChB,QAAA,MAAM,KAAK,IAAI,CAAC;AAChB,QAAA,MAAM,KAAK,IAAI,CAAC;AAChB,KAAK,SAAS,GAAG,OAAO,KAAK,GAAG,OAAO,KAAK,GAAG,OAAO,KAAK,CAAC;AAK5D,gFAAgF;AAChF,uBAAe,MAAM;IACnB,0FAA0F;IAE1F,SAAS,EAAE,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAQ;IAC5C,wEAAwE;IACxE,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,IAAI,CAAQ;IACrC,2EAA2E;IAC3E,OAAO,SAAK;IAEZ,yFAAyF;IACzF,iBAAiB,IAAI,IAAI;IAEzB,sEAAsE;IACtE,SAAS,CAAC,KAAK,IAAI,IAAI;CAOxB;AAED,qBAAa,KAAK,CAAC,CAAC,CAAE,SAAQ,MAAM;IAClC,OAAO,CAAC,MAAM,CAAI;IAClB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0B;gBAEtC,KAAK,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC,GAAG,KAAK,CAAA;KAAE;IAM9E,GAAG,IAAI,CAAC;IAKR,GAAG,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI;IAYlB,uCAAuC;IACvC,IAAI,IAAI,CAAC;CAGV;AAED,qBAAa,QAAQ,CAAC,CAAC,CAAE,SAAQ,MAAM;IACrC,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,YAAY,CAAS;IAC7B,KAAK,EAAE,SAAS,CAAS;IACzB,6EAA6E;IAC7E,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAAQ;IACnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAU;IAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0B;gBAEtC,EAAE,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC,GAAG,KAAK,CAAA;KAAE;IAMjF,GAAG,IAAI,CAAC;IAWR,4EAA4E;IACnE,iBAAiB,IAAI,IAAI;IAelC,OAAO,CAAC,SAAS;IA0CjB;;;;OAIG;IACH,SAAS,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAUjC,IAAI,IAAI,CAAC;CAKV;AAOD;;;;GAIG;AACH,qBAAa,OAAO;IAClB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAa;IACvC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAC9C,OAAO,CAAC,QAAQ,CAAqB;gBAEzB,MAAM,EAAE,MAAM,IAAI;IAI9B,KAAK,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI;IAO/B,OAAO,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI;IAQjC,uEAAuE;IACvE,UAAU,IAAI,MAAM,EAAE;IAMtB,gFAAgF;IAChF,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;CAM5B;AAED,iFAAiF;AACjF,wBAAgB,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAQzC;AAED,kFAAkF;AAClF,wBAAgB,eAAe,IAAI,QAAQ,CAAC,OAAO,CAAC,GAAG,IAAI,CAE1D;AAED,gFAAgF;AAChF,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE9C"}
@@ -0,0 +1,254 @@
1
+ /**
2
+ * L0 — the TC39-shaped reactive graph core.
3
+ *
4
+ * Class-based primitives (`State`, `Computed`, `Watcher`) that own ONLY the
5
+ * dependency graph: values, versions, source/observer edges, the lazy push-pull
6
+ * state machine, and glitch-free pull evaluation. They know nothing about
7
+ * ownership, scheduling, effects, suspense, or resources — those live in the L1
8
+ * runtime that sits on top (see ../signal.ts). This is a re-shaping of the exact
9
+ * algorithm the existing graph already runs (CLEAN/CHECK/DIRTY + version + the
10
+ * `Listener`/observers edges), so behaviour is preserved; it's just relocated
11
+ * onto classes whose shape matches the TC39 Signals proposal.
12
+ *
13
+ * The model is the "push the dirt, pull the value" scheme (Reactively / SolidJS /
14
+ * the TC39 polyfill): a write PUSHES a maybe-dirty mark down to observers without
15
+ * recomputing; a read PULLS, settling upstream lazily and recomputing only what
16
+ * actually changed (equality-gated). Reads outside a tracking scope don't subscribe.
17
+ */
18
+ import { G } from './state.js';
19
+ // Node states. CHECK = "a source may have changed — verify on read" (your STALE);
20
+ // DIRTY = "definitely recompute"; CLEAN = "cached value is current".
21
+ const CLEAN = 0;
22
+ const CHECK = 1;
23
+ const DIRTY = 2;
24
+ // The current consumer + untrack flag live on the shared globalThis-backed state (G)
25
+ // so duplicate module copies share one graph. `consumer`/`untracked` proxy them.
26
+ /** A thing that can be depended upon: holds observers + a monotonic version. */
27
+ class Source {
28
+ /** Computeds (and watched leaves) that read this node. (was the signalObservers entry) */
29
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
+ observers = null;
31
+ /** Watchers eagerly observing this node, for effect scheduling (L1). */
32
+ watchers = null;
33
+ /** Bumped whenever this node's value actually changes (equality-gated). */
34
+ version = 0;
35
+ /** Settle this node so a reader sees a current value/version. Overridden by Computed. */
36
+ updateIfNecessary() { }
37
+ /** Link the current consumer (if any) as an observer of this node. */
38
+ track() {
39
+ if (G.untracked)
40
+ return;
41
+ const consumer = G.consumer;
42
+ if (!consumer)
43
+ return;
44
+ (this.observers ??= new Set()).add(consumer);
45
+ (consumer.sources ??= new Set()).add(this);
46
+ }
47
+ }
48
+ export class State extends Source {
49
+ _value;
50
+ _equals;
51
+ constructor(value, options) {
52
+ super();
53
+ this._value = value;
54
+ this._equals = options?.equals === false ? () => false : options?.equals ?? Object.is;
55
+ }
56
+ get() {
57
+ this.track();
58
+ return this._value;
59
+ }
60
+ set(next) {
61
+ if (this._equals(this._value, next))
62
+ return;
63
+ this._value = next;
64
+ this.version++;
65
+ // Push: direct observers are DIRTY, their transitive observers are CHECK.
66
+ // Snapshot first — a synchronous scheduler can re-run an effect mid-propagation,
67
+ // which re-subscribes (mutates `observers`); iterating the live Set would then
68
+ // re-visit the re-added node forever. (Matches the legacy Array.from(observers).)
69
+ if (this.observers)
70
+ for (const o of [...this.observers])
71
+ o.markDirty(DIRTY);
72
+ notifyWatchers(this);
73
+ }
74
+ /** Read without subscribing (peek). */
75
+ peek() {
76
+ return this._value;
77
+ }
78
+ }
79
+ export class Computed extends Source {
80
+ _value;
81
+ _error;
82
+ _threw = false;
83
+ _initialized = false;
84
+ state = DIRTY;
85
+ /** Nodes this computation read on its last run. (was Computation.sources) */
86
+ sources = null;
87
+ _fn;
88
+ _equals;
89
+ constructor(fn, options) {
90
+ super();
91
+ this._fn = fn;
92
+ this._equals = options?.equals === false ? () => false : options?.equals ?? Object.is;
93
+ }
94
+ get() {
95
+ // Cycle guard: a computed reading itself.
96
+ if (G.consumer === this) {
97
+ throw new Error('Signal.Computed cannot read itself');
98
+ }
99
+ this.track();
100
+ this.updateIfNecessary();
101
+ if (this._threw)
102
+ throw this._error;
103
+ return this._value;
104
+ }
105
+ /** Settle: if CHECK, verify sources; if DIRTY (or never run), recompute. */
106
+ updateIfNecessary() {
107
+ if (this.state === CHECK) {
108
+ // Pull each source; if one recomputes to a new value it will have flipped
109
+ // us to DIRTY (markDirty), so stop early.
110
+ if (this.sources) {
111
+ for (const s of this.sources) {
112
+ s.updateIfNecessary();
113
+ if (this.state === DIRTY)
114
+ break;
115
+ }
116
+ }
117
+ }
118
+ if (this.state === DIRTY || !this._initialized)
119
+ this.recompute();
120
+ this.state = CLEAN;
121
+ }
122
+ recompute() {
123
+ // Drop old edges (we re-collect during the run).
124
+ if (this.sources) {
125
+ for (const s of this.sources)
126
+ s.observers?.delete(this);
127
+ this.sources.clear();
128
+ }
129
+ const prevConsumer = G.consumer;
130
+ const prevUntracked = G.untracked;
131
+ G.consumer = this;
132
+ G.untracked = false;
133
+ let next;
134
+ let threw = false;
135
+ let err;
136
+ try {
137
+ next = this._fn();
138
+ }
139
+ catch (e) {
140
+ threw = true;
141
+ err = e;
142
+ next = this._value;
143
+ }
144
+ finally {
145
+ G.consumer = prevConsumer;
146
+ G.untracked = prevUntracked;
147
+ }
148
+ const changed = !this._initialized ||
149
+ this._threw !== threw ||
150
+ (threw ? err !== this._error : !this._equals(this._value, next));
151
+ this._initialized = true;
152
+ this._threw = threw;
153
+ this._error = err;
154
+ this._value = next;
155
+ if (changed) {
156
+ this.version++;
157
+ // Our value moved → observers that were merely CHECK must now recompute.
158
+ if (this.observers)
159
+ for (const o of [...this.observers])
160
+ o.markDirty(DIRTY);
161
+ notifyWatchers(this);
162
+ }
163
+ }
164
+ /**
165
+ * Push a maybe-dirty mark. A direct observer of a changed node gets DIRTY; its
166
+ * own observers get CHECK (recurse) — stopping where already marked, so the
167
+ * push is O(newly-dirtied) and never recomputes.
168
+ */
169
+ markDirty(level) {
170
+ if (this.state >= level)
171
+ return;
172
+ const wasClean = this.state === CLEAN;
173
+ this.state = level;
174
+ if (wasClean) {
175
+ if (this.observers)
176
+ for (const o of [...this.observers])
177
+ o.markDirty(CHECK);
178
+ notifyWatchers(this);
179
+ }
180
+ }
181
+ peek() {
182
+ this.updateIfNecessary();
183
+ if (this._threw)
184
+ throw this._error;
185
+ return this._value;
186
+ }
187
+ }
188
+ function notifyWatchers(node) {
189
+ if (!node.watchers)
190
+ return;
191
+ for (const w of [...node.watchers])
192
+ w._notify(node);
193
+ }
194
+ /**
195
+ * Signal.subtle.Watcher — the only eager observation primitive. Its callback fires
196
+ * when a watched signal MAY have changed; the callback MUST NOT read or write
197
+ * signals. Effects (L1) are built on this + a scheduler.
198
+ */
199
+ export class Watcher {
200
+ _notifyFn;
201
+ _watched = new Set();
202
+ _pending = new Set();
203
+ constructor(notify) {
204
+ this._notifyFn = notify;
205
+ }
206
+ watch(...nodes) {
207
+ for (const n of nodes) {
208
+ this._watched.add(n);
209
+ (n.watchers ??= new Set()).add(this);
210
+ }
211
+ }
212
+ unwatch(...nodes) {
213
+ for (const n of nodes) {
214
+ this._watched.delete(n);
215
+ n.watchers?.delete(this);
216
+ this._pending.delete(n);
217
+ }
218
+ }
219
+ /** Watched nodes that may have changed since the last getPending(). */
220
+ getPending() {
221
+ const out = [...this._pending];
222
+ this._pending.clear();
223
+ return out;
224
+ }
225
+ /** @internal — a watched node became non-clean. Edge-triggered: notify once. */
226
+ _notify(node) {
227
+ if (!this._watched.has(node) || this._pending.has(node))
228
+ return;
229
+ const wasEmpty = this._pending.size === 0;
230
+ this._pending.add(node);
231
+ if (wasEmpty)
232
+ this._notifyFn();
233
+ }
234
+ }
235
+ /** Signal.subtle.untrack — run `fn` without subscribing the current consumer. */
236
+ export function untrack(fn) {
237
+ if (G.untracked)
238
+ return fn();
239
+ G.untracked = true;
240
+ try {
241
+ return fn();
242
+ }
243
+ finally {
244
+ G.untracked = false;
245
+ }
246
+ }
247
+ /** Signal.subtle.currentComputed — the computed currently evaluating, or null. */
248
+ export function currentComputed() {
249
+ return G.consumer;
250
+ }
251
+ /** Whether a node currently has observers/watchers (Signal.subtle.hasSinks). */
252
+ export function hasSinks(node) {
253
+ return !!(node.observers && node.observers.size) || !!(node.watchers && node.watchers.size);
254
+ }
@@ -0,0 +1 @@
1
+ var y="__fluixi_signal_next_state__",b=s=>{typeof queueMicrotask=="function"?queueMicrotask(s):Promise.resolve().then(s)},r=globalThis[y]??={consumer:null,untracked:!1,owner:null,queue:[],scheduled:!1,flushing:!1,batchDepth:0,hostSchedule:b,onSuspend:null,onError:null};var d=0,f=1,a=2,h=class{constructor(){this.observers=null;this.watchers=null;this.version=0}updateIfNecessary(){}track(){if(r.untracked)return;let e=r.consumer;e&&((this.observers??=new Set).add(e),(e.sources??=new Set).add(this))}},v=class extends h{constructor(e,t){super(),this._value=e,this._equals=t?.equals===!1?()=>!1:t?.equals??Object.is}get(){return this.track(),this._value}set(e){if(!this._equals(this._value,e)){if(this._value=e,this.version++,this.observers)for(let t of[...this.observers])t.markDirty(a);l(this)}}peek(){return this._value}},p=class extends h{constructor(t,i){super();this._threw=!1;this._initialized=!1;this.state=a;this.sources=null;this._fn=t,this._equals=i?.equals===!1?()=>!1:i?.equals??Object.is}get(){if(r.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===f&&this.sources){for(let t of this.sources)if(t.updateIfNecessary(),this.state===a)break}(this.state===a||!this._initialized)&&this.recompute(),this.state=d}recompute(){if(this.sources){for(let n of this.sources)n.observers?.delete(this);this.sources.clear()}let t=r.consumer,i=r.untracked;r.consumer=this,r.untracked=!1;let o,u=!1,c;try{o=this._fn()}catch(n){u=!0,c=n,o=this._value}finally{r.consumer=t,r.untracked=i}let w=!this._initialized||this._threw!==u||(u?c!==this._error:!this._equals(this._value,o));if(this._initialized=!0,this._threw=u,this._error=c,this._value=o,w){if(this.version++,this.observers)for(let n of[...this.observers])n.markDirty(a);l(this)}}markDirty(t){if(this.state>=t)return;let i=this.state===d;if(this.state=t,i){if(this.observers)for(let o of[...this.observers])o.markDirty(f);l(this)}}peek(){if(this.updateIfNecessary(),this._threw)throw this._error;return this._value}};function l(s){if(s.watchers)for(let e of[...s.watchers])e._notify(s)}var _=class{constructor(e){this._watched=new Set;this._pending=new Set;this._notifyFn=e}watch(...e){for(let t of e)this._watched.add(t),(t.watchers??=new Set).add(this)}unwatch(...e){for(let t of e)this._watched.delete(t),t.watchers?.delete(this),this._pending.delete(t)}getPending(){let e=[...this._pending];return this._pending.clear(),e}_notify(e){if(!this._watched.has(e)||this._pending.has(e))return;let t=this._pending.size===0;this._pending.add(e),t&&this._notifyFn()}};function S(s){if(r.untracked)return s();r.untracked=!0;try{return s()}finally{r.untracked=!1}}function m(){return r.consumer}function g(s){return!!(s.observers&&s.observers.size)||!!(s.watchers&&s.watchers.size)}export{p as Computed,v as State,_ as Watcher,m as currentComputed,g as hasSinks,S as untrack};
@@ -0,0 +1 @@
1
+ "use strict";var w=Object.defineProperty;var D=Object.getOwnPropertyDescriptor;var P=Object.getOwnPropertyNames;var A=Object.prototype.hasOwnProperty;var E=(n,e)=>{for(var t in e)w(n,t,{get:e[t],enumerable:!0})},N=(n,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of P(e))!A.call(n,o)&&o!==t&&w(n,o,{get:()=>e[o],enumerable:!(s=D(e,o))||s.enumerable});return n};var R=n=>N(w({},"__esModule",{value:!0}),n);var H={};E(H,{createDebounce:()=>V,createDeferred:()=>L,createSelector:()=>U});module.exports=R(H);var M="__fluixi_signal_next_state__",I=n=>{typeof queueMicrotask=="function"?queueMicrotask(n):Promise.resolve().then(n)},r=globalThis[M]??={consumer:null,untracked:!1,owner:null,queue:[],scheduled:!1,flushing:!1,batchDepth:0,hostSchedule:I,onSuspend:null,onError:null};var _=0,k=1,c=2,f=class{constructor(){this.observers=null;this.watchers=null;this.version=0}updateIfNecessary(){}track(){if(r.untracked)return;let e=r.consumer;e&&((this.observers??=new Set).add(e),(e.sources??=new Set).add(this))}},p=class extends f{constructor(e,t){super(),this._value=e,this._equals=t?.equals===!1?()=>!1:t?.equals??Object.is}get(){return this.track(),this._value}set(e){if(!this._equals(this._value,e)){if(this._value=e,this.version++,this.observers)for(let t of[...this.observers])t.markDirty(c);v(this)}}peek(){return this._value}},d=class extends f{constructor(t,s){super();this._threw=!1;this._initialized=!1;this.state=c;this.sources=null;this._fn=t,this._equals=s?.equals===!1?()=>!1:s?.equals??Object.is}get(){if(r.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===k&&this.sources){for(let t of this.sources)if(t.updateIfNecessary(),this.state===c)break}(this.state===c||!this._initialized)&&this.recompute(),this.state=_}recompute(){if(this.sources){for(let a of this.sources)a.observers?.delete(this);this.sources.clear()}let t=r.consumer,s=r.untracked;r.consumer=this,r.untracked=!1;let o,i=!1,u;try{o=this._fn()}catch(a){i=!0,u=a,o=this._value}finally{r.consumer=t,r.untracked=s}let l=!this._initialized||this._threw!==i||(i?u!==this._error:!this._equals(this._value,o));if(this._initialized=!0,this._threw=i,this._error=u,this._value=o,l){if(this.version++,this.observers)for(let a of[...this.observers])a.markDirty(c);v(this)}}markDirty(t){if(this.state>=t)return;let s=this.state===_;if(this.state=t,s){if(this.observers)for(let o of[...this.observers])o.markDirty(k);v(this)}}peek(){if(this.updateIfNecessary(),this._threw)throw this._error;return this._value}};function v(n){if(n.watchers)for(let e of[...n.watchers])e._notify(n)}var h=class{constructor(e){this._watched=new Set;this._pending=new Set;this._notifyFn=e}watch(...e){for(let t of e)this._watched.add(t),(t.watchers??=new Set).add(this)}unwatch(...e){for(let t of e)this._watched.delete(t),t.watchers?.delete(this),this._pending.delete(t)}getPending(){let e=[...this._pending];return this._pending.clear(),e}_notify(e){if(!this._watched.has(e)||this._pending.has(e))return;let t=this._pending.size===0;this._pending.add(e),t&&this._notifyFn()}};function j(n){return{cleanups:null,owned:null,parent:n,contexts:null}}function y(n,e){let t=r.owner;r.owner=n;try{return e()}finally{r.owner=t}}function b(n){r.owner&&(r.owner.cleanups??=[]).push(n)}function T(n){if(n.owned){for(let e=n.owned.length-1;e>=0;e--)T(n.owned[e]);n.owned=null}if(n.cleanups){for(let e=n.cleanups.length-1;e>=0;e--)n.cleanups[e]();n.cleanups=null}}function O(n){r.hostSchedule=n??(e=>{Promise.resolve().then(e)})}function W(n){n.queued||(n.queued=!0,r.queue.push(n),!r.scheduled&&r.batchDepth===0&&(r.scheduled=!0,r.hostSchedule(g)))}function g(){if(r.scheduled=!1,r.flushing)return;r.flushing=!0;let n=0;try{for(;r.queue.length;){if(++n>1e5)throw new Error("[fluixi] effect flush did not settle (cycle?)");let e=r.queue;r.queue=[];for(let t of e)t.queued=!1,t.disposed||t.run()}}finally{r.flushing=!1}}function q(){r.batchDepth===0&&g()}var m=class{constructor(e){this.queued=!1;this.disposed=!1;this.owner=j(r.owner),r.owner&&(r.owner.cleanups??=[]).push(()=>this.dispose()),this.computed=new d(()=>{T(this.owner),y(this.owner,()=>{let t=e();typeof t=="function"&&(this.owner.cleanups??=[]).push(t)})}),this.watcher=new h(()=>W(this)),this.run(),this.watcher.watch(this.computed)}run(){if(!this.disposed){this.watcher.getPending();try{this.computed.get()}catch(e){if(e&&typeof e.then=="function"&&r.onSuspend){r.onSuspend(e,this.owner);return}if(r.onError&&r.onError(e,this.owner))return;throw e}}}dispose(){this.disposed||(this.disposed=!0,this.watcher.unwatch(this.computed),T(this.owner))}};function C(n){let e=new m(n);return()=>e.dispose()}O(()=>{});var G=Symbol.for("signal");var F=Symbol.for("signal-node");function x(n,e){let t=new p(n,{equals:e?.equals}),s=(()=>t.get()),o=(i=>{let u=typeof i=="function"?i(t.peek()):i;return t.set(u),q(),u});return Object.defineProperty(s,G,{value:!0,enumerable:!0}),Object.defineProperty(s,F,{value:{get value(){return t.peek()},get version(){return t.version}},enumerable:!1}),[s,o]}function S(n,e){let t=e;return C(()=>{t=n(t)})}function U(n,e=(t,s)=>t===s){let t=new Map;return S(()=>{let s=n();for(let[o,i]of t){let[u,l]=i,a=e(o,s);u()!==a&&l(a)}}),s=>{let o=t.get(s);if(!o){let[i,u]=x(!1);o=[i,u],t.set(s,o);let l=n();u(e(s,l))}return o[0]()}}function L(n,e){let[t,s]=x(n(),e);return S(()=>{let o=n();e?.timeoutMs?setTimeout(()=>s(()=>o),e.timeoutMs):typeof requestIdleCallback=="function"?requestIdleCallback(()=>s(()=>o)):Promise.resolve().then(()=>s(()=>o))}),t}function V(n,e){let t,s=(...o)=>{t!==void 0&&clearTimeout(t),t=setTimeout(()=>{if(t=void 0,o.length>0){let i=o[o.length-1];typeof i=="object"&&i&&!i.tagName&&i.target&&i.target.tagName&&(i=i.target),i&&typeof i=="object"&&i.tagName&&(i.tagName.toLowerCase()==="input"||i.tagName.toLowerCase()==="textarea")&&i.focus()}n(...o)},e)};return b(()=>{t!==void 0&&clearTimeout(t)}),s}
@@ -0,0 +1,14 @@
1
+ import type { Accessor, EqualityFn } from '../signal.js';
2
+ /** Keyed selector: `selector(key)` is a fine-grained boolean that flips only when its own key's match changes. */
3
+ export declare function createSelector<T, U = T>(source: () => T, fn?: (key: U, value: T) => boolean): (key: U) => boolean;
4
+ /** A signal that tracks `source` but applies updates on idle/timeout, keeping the current UI responsive. */
5
+ export declare function createDeferred<T>(source: Accessor<T>, options?: {
6
+ timeoutMs?: number;
7
+ equals?: false | EqualityFn<T>;
8
+ }): Accessor<T>;
9
+ /**
10
+ * Debounce wrapper — delays invoking `fn` until `delay` ms after the last call.
11
+ * Pending timers are cancelled when the owning scope disposes (via onCleanup).
12
+ */
13
+ export declare function createDebounce<T extends (...args: any[]) => any>(fn: T, delay: number): T;
14
+ //# sourceMappingURL=derived.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"derived.d.ts","sourceRoot":"","sources":["../../../../src/lib/signal/graph/derived.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAA8B,MAAM,cAAc,CAAC;AAErF,kHAAkH;AAClH,wBAAgB,cAAc,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,EACrC,MAAM,EAAE,MAAM,CAAC,EACf,EAAE,GAAE,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,KAAK,OAA6C,GACtE,CAAC,GAAG,EAAE,CAAC,KAAK,OAAO,CAqBrB;AAED,4GAA4G;AAC5G,wBAAgB,cAAc,CAAC,CAAC,EAC9B,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,EACnB,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,CAAA;CAAE,GAC/D,QAAQ,CAAC,CAAC,CAAC,CAUb;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC,CAiCzF"}