@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,1219 @@
1
+ import { Observable } from '../observable/observable.js';
2
+ import { isPromise } from '@fluixi/utils';
3
+ // Detection guards + helpers now live in the TC39-owned ./graph/detect.js
4
+ // (graph-agnostic). Imported for internal use, re-exported below for the surface.
5
+ import { __DEV__, isSignal, isMemo, isResource, isComponent, isSuspense, isProvider, makeArrayFlat, } from './graph/detect.js';
6
+ export { __DEV__, isSignal, isMemo, isResource, isComponent, isSuspense, isProvider, makeArrayFlat, };
7
+ // Derived utilities ported onto the TC39 graph — live in ./graph/derived.js.
8
+ import { createSelector, createDeferred, createDebounce } from './graph/derived.js';
9
+ export { createSelector, createDeferred, createDebounce };
10
+ // Reactive children resolver (was the mis-named `childrens`) — ./graph/children.js.
11
+ import { readChildren } from './graph/children.js';
12
+ export { readChildren };
13
+ export const $SIGNAL = Symbol.for('signal');
14
+ export const $MEMO = Symbol.for('memo');
15
+ export const $RESOURCE = Symbol.for('resource');
16
+ export const $PROXY = Symbol('fluixi-proxy');
17
+ const $NODE = Symbol.for('signal-node');
18
+ const CLEAN = 0;
19
+ const STALE = 1;
20
+ const DIRTY = 2;
21
+ // RUNNING is a sentinel state set on a computation just before its fn executes.
22
+ // Any propagateDirty() call that fires DURING the fn (e.g. a signal write inside
23
+ // a route component's setup code) will override it with DIRTY. After the fn,
24
+ // RUNNING → CLEAN (nothing re-dirtied it) or DIRTY → stay DIRTY (needs re-run).
25
+ // This correctly distinguishes "was dirty going in" from "re-dirtied during fn."
26
+ const RUNNING = 3;
27
+ let Owner = null;
28
+ let Listener = null;
29
+ let Updates = null;
30
+ let Effects = null;
31
+ let ExecCount = 0;
32
+ // When true, a signal write eagerly schedules a stale effect reached through a
33
+ // Legacy propagateDirty flag. Never enabled anymore — SSR runs on the TC39 graph,
34
+ // which schedules its own re-runs, so this is always off (dead branch below).
35
+ const _eagerStaleEffects = false;
36
+ let Pending = false;
37
+ /** Depth of user-initiated batch() calls. Incremented on batch entry, decremented on exit. */
38
+ let BatchDepth = 0;
39
+ const MAX_ITERATIONS = 1000000;
40
+ const signalObservers = new WeakMap();
41
+ /** Active transition — non-null while startTransition's fn is running or effects are deferred. */
42
+ let Transition = null;
43
+ const errorHandlers = [];
44
+ let Scheduler = null;
45
+ const nodeRegistry = new Map();
46
+ let nodeIdCounter = 0;
47
+ export const sharedConfig = {
48
+ context: null,
49
+ owner: null,
50
+ routeData: new Map(),
51
+ matchedRoute: new Map(),
52
+ };
53
+ /**
54
+ * Set current reactive owner (for internal use)
55
+ * @internal
56
+ */
57
+ export function setOwner(owner) {
58
+ Owner = owner;
59
+ sharedConfig.owner = owner;
60
+ sharedConfig.context = owner;
61
+ }
62
+ /**
63
+ * Get shared config (for compatibility)
64
+ * @internal
65
+ */
66
+ export function getSharedConfig() {
67
+ return sharedConfig;
68
+ }
69
+ // SSR resource injectables moved to the TC39-owned ./graph/ssr-hooks.js
70
+ // (graph-agnostic). Imported for the legacy createResource below, re-exported for
71
+ // the public surface.
72
+ import { setResourceTracker, trackResourcePromise, setServerDataGetter, getServerData, setResourceIdSource, nextResourceId, setResourceDataSink, reportResourceData, } from './graph/ssr-hooks.js';
73
+ export { setResourceTracker, trackResourcePromise, setServerDataGetter, getServerData, setResourceIdSource, nextResourceId, setResourceDataSink, reportResourceData, };
74
+ function createNodeId() {
75
+ return `node_${++nodeIdCounter}_${Date.now()}`;
76
+ }
77
+ function registerNode(node) {
78
+ if (__DEV__)
79
+ nodeRegistry.set(node.id, new WeakRef(node));
80
+ }
81
+ function unregisterNode(node) {
82
+ if (__DEV__)
83
+ nodeRegistry.delete(node.id);
84
+ }
85
+ function createReactiveNode(type, initialValue, options) {
86
+ const node = {
87
+ id: createNodeId(),
88
+ type,
89
+ value: initialValue,
90
+ version: 0,
91
+ equals: options?.equals,
92
+ name: options?.name,
93
+ created: Date.now(),
94
+ updated: Date.now(),
95
+ };
96
+ registerNode(node);
97
+ return node;
98
+ }
99
+ function defaultEquals(a, b) {
100
+ return a === b;
101
+ }
102
+ function handleError(err) {
103
+ if (typeof console !== 'undefined')
104
+ console.error('Reactive error:', err);
105
+ throw err;
106
+ }
107
+ // Initialized lazily so it doesn't run before createSignal is defined.
108
+ let _transPending = null;
109
+ let _setTransPending = null;
110
+ function ensureTransPending() {
111
+ if (!_transPending) {
112
+ const [get, set] = createSignal(false);
113
+ _transPending = get;
114
+ _setTransPending = set;
115
+ }
116
+ }
117
+ /**
118
+ * Central computation scheduling that respects batch mode and active transitions.
119
+ *
120
+ * - In batch → always queue in Updates (flushed by flushUpdates).
121
+ * - In transition (non-pure) → defer to Transition.effects (flush async).
122
+ * - Computed (pure=true, computed=true) → run eagerly.
123
+ * - Effect (pure=false) → run eagerly (unless deferred by transition).
124
+ * - Memo (pure=true, computed=false) → skip here; lazy, runs on next read.
125
+ */
126
+ function scheduleComputation(c) {
127
+ if (Updates) {
128
+ if (Updates.indexOf(c) === -1)
129
+ Updates.push(c);
130
+ }
131
+ else if (Transition && Transition.running && !c.pure) {
132
+ if (Transition.effects.indexOf(c) === -1)
133
+ Transition.effects.push(c);
134
+ }
135
+ else if (!c.pure || c.computed) {
136
+ updateComputation(c);
137
+ }
138
+ else if (c.pure && !c.computed && c.accessor) {
139
+ // Pure memo outside batch: evaluate now so its equality check gates whether
140
+ // downstream effects are scheduled. If value unchanged, observers not notified.
141
+ //
142
+ // Set Updates=[] so any signal writes during the memo's fn (e.g. setOutlet
143
+ // inside routeStates.fn) are deferred instead of running effects immediately
144
+ // before the DOM structure is ready. Process them via runCascadeAndEffects
145
+ // afterwards — same cascade that batch uses.
146
+ const prevListener = Listener;
147
+ const prevUpdatesM = Updates;
148
+ Listener = null;
149
+ Updates = [];
150
+ c.accessor();
151
+ Listener = prevListener;
152
+ const memoDeferred = Updates;
153
+ Updates = prevUpdatesM;
154
+ if (memoDeferred.length > 0) {
155
+ const savedU = Updates;
156
+ runCascadeAndEffects(memoDeferred);
157
+ Updates = savedU;
158
+ }
159
+ }
160
+ }
161
+ function propagate(observerList, propagator) {
162
+ for (let i = 0; i < observerList.length; i++)
163
+ propagator(observerList[i]);
164
+ }
165
+ export function createSignal(_value, options) {
166
+ let value = _value;
167
+ if (typeof value === 'object' && value && $PROXY in value)
168
+ delete value[$PROXY];
169
+ const node = createReactiveNode('signal', value, {
170
+ equals: options?.equals === false ? undefined : options?.equals,
171
+ name: options?.name,
172
+ });
173
+ const s = [read, write];
174
+ signalObservers.set(s, new Set());
175
+ let lastObs;
176
+ function read() {
177
+ let observer = Listener;
178
+ if (observer) {
179
+ if (!observer.sources)
180
+ observer.sources = [s];
181
+ else if (observer.sources.indexOf(s) === -1)
182
+ observer.sources.push(s);
183
+ const observers = signalObservers.get(s);
184
+ if (observers && !observers.has(observer))
185
+ observers.add(observer);
186
+ }
187
+ return value;
188
+ }
189
+ function write(nextValue) {
190
+ const v = typeof nextValue === 'function'
191
+ ? nextValue(value)
192
+ : nextValue;
193
+ const equals = node.equals || defaultEquals;
194
+ if (options?.equals === false || !equals(value, v)) {
195
+ value = v;
196
+ node.value = v;
197
+ node.version++;
198
+ node.updated = Date.now();
199
+ const observers = signalObservers.get(s);
200
+ if (observers && observers.size > 0) {
201
+ const observerList = Array.from(observers);
202
+ const toUpdate = [];
203
+ const visited = new Set();
204
+ function propagateDirty(comp, viaPureMemo = false) {
205
+ const prevState = comp.state;
206
+ if (prevState === RUNNING && comp === Listener)
207
+ return;
208
+ if (viaPureMemo && !comp.pure) {
209
+ if (prevState === CLEAN) {
210
+ comp.state = STALE;
211
+ // Eagerly SCHEDULE the stale effect — but ONLY when enabled (server SSR
212
+ // drain). There is no scheduler/read loop on the server to re-run a
213
+ // stranded STALE effect, so the <Suspense> output insert effect wouldn't
214
+ // re-render on the inFallback flip and the resolved children never
215
+ // replaced the fallback in the serialized tree. On the CLIENT this stays
216
+ // OFF: eager scheduling here removes a loop-breaker and can spin a
217
+ // memo↔effect cycle forever (froze the storefront after hydration). The
218
+ // client keeps the lazy behavior — the effect re-runs when next read.
219
+ if (_eagerStaleEffects) {
220
+ if (Updates !== null && Updates.indexOf(comp) === -1)
221
+ Updates.push(comp);
222
+ else
223
+ toUpdate.push(comp);
224
+ }
225
+ }
226
+ return;
227
+ }
228
+ if (visited.has(comp))
229
+ return;
230
+ visited.add(comp);
231
+ comp.state = DIRTY;
232
+ if (prevState === CLEAN || prevState === STALE)
233
+ toUpdate.push(comp);
234
+ else if (!comp.pure &&
235
+ Updates !== null &&
236
+ Updates.indexOf(comp) === -1)
237
+ Updates.push(comp);
238
+ if (comp.pure && comp.accessor) {
239
+ const memoObservers = signalObservers.get(comp.accessor);
240
+ if (memoObservers)
241
+ propagate(Array.from(memoObservers), (c) => propagateDirty(c, true));
242
+ }
243
+ }
244
+ propagate(observerList, propagateDirty);
245
+ for (const comp of toUpdate)
246
+ scheduleComputation(comp);
247
+ }
248
+ }
249
+ }
250
+ read.toObservable = () => new Observable((observer) => {
251
+ let first = true;
252
+ const dispose = createEffect(() => {
253
+ const val = read();
254
+ if (first)
255
+ first = false;
256
+ else
257
+ observer.next(val);
258
+ });
259
+ return dispose;
260
+ });
261
+ Object.defineProperty(read, $SIGNAL, { value: true, enumerable: true });
262
+ Object.defineProperty(read, $NODE, { value: node, enumerable: false });
263
+ return s;
264
+ }
265
+ export function createEffect(fn, value, options) {
266
+ const c = {
267
+ fn,
268
+ state: DIRTY,
269
+ sources: null,
270
+ sourceSlots: null,
271
+ owned: null,
272
+ cleanups: null,
273
+ owner: Owner,
274
+ contexts: null,
275
+ pure: false,
276
+ updatedAt: null,
277
+ };
278
+ if (Owner) {
279
+ if (!Owner.owned)
280
+ Owner.owned = [c];
281
+ else
282
+ Owner.owned.push(c);
283
+ }
284
+ const SC = _SuspenseContext && useContext(_SuspenseContext);
285
+ if (SC)
286
+ c.suspense = SC;
287
+ runComputation(c, value);
288
+ return () => dispose(c);
289
+ }
290
+ function runComputation(c, value) {
291
+ if (c.sources) {
292
+ for (const source of c.sources) {
293
+ const observers = signalObservers.get(source);
294
+ if (observers)
295
+ observers.delete(c);
296
+ }
297
+ c.sources = null;
298
+ c.sourceSlots = null;
299
+ }
300
+ if (c.owned) {
301
+ for (let i = 0; i < c.owned.length; i++)
302
+ dispose(c.owned[i]);
303
+ c.owned = null;
304
+ }
305
+ const prevListener = Listener;
306
+ const prevOwner = Owner;
307
+ Listener = c;
308
+ Owner = c;
309
+ ExecCount++;
310
+ if (ExecCount > MAX_ITERATIONS) {
311
+ ExecCount = 0;
312
+ Listener = prevListener;
313
+ Owner = prevOwner;
314
+ throw new Error('Potential infinite loop detected. Exceeded maximum iterations.');
315
+ }
316
+ try {
317
+ c.state = RUNNING;
318
+ const result = c.fn(value);
319
+ if (typeof result === 'function') {
320
+ if (!c.cleanups)
321
+ c.cleanups = [result];
322
+ else
323
+ c.cleanups.push(result);
324
+ }
325
+ if (c.state === RUNNING)
326
+ c.state = CLEAN;
327
+ }
328
+ catch (err) {
329
+ if (isPromise(err)) {
330
+ const ctx = _SuspenseContext && useContext(_SuspenseContext);
331
+ if (ctx) {
332
+ ctx.increment();
333
+ err.finally(() => ctx.decrement());
334
+ if (c.suspense)
335
+ ctx.effects.push(c);
336
+ }
337
+ return;
338
+ }
339
+ handleError(err);
340
+ }
341
+ finally {
342
+ Listener = prevListener;
343
+ Owner = prevOwner;
344
+ }
345
+ }
346
+ function updateComputation(c) {
347
+ if (c.disposed || !c.fn || c.state === CLEAN)
348
+ return;
349
+ if (c.state === DIRTY || c.state === STALE || c.state === RUNNING) {
350
+ if (c.cleanups) {
351
+ for (let i = 0; i < c.cleanups.length; i++)
352
+ c.cleanups[i]();
353
+ c.cleanups = null;
354
+ }
355
+ if (c.owned) {
356
+ for (let i = 0; i < c.owned.length; i++)
357
+ dispose(c.owned[i]);
358
+ c.owned = null;
359
+ }
360
+ if (c.renderEffect)
361
+ runRenderComputation(c);
362
+ else
363
+ runComputation(c, c.value);
364
+ }
365
+ }
366
+ function dispose(c) {
367
+ // Mark first so a resume triggered during teardown (e.g. a Suspense boundary
368
+ // decrementing as a now-resolved promise settles) can't re-run this node.
369
+ c.disposed = true;
370
+ if (c.sources) {
371
+ for (const source of c.sources) {
372
+ const observers = signalObservers.get(source);
373
+ if (observers)
374
+ observers.delete(c);
375
+ }
376
+ }
377
+ if (c.cleanups)
378
+ for (let i = 0; i < c.cleanups.length; i++)
379
+ c.cleanups[i]();
380
+ if (c.owned)
381
+ for (let i = 0; i < c.owned.length; i++)
382
+ dispose(c.owned[i]);
383
+ if (c.owner && c.owner.owned) {
384
+ const index = c.owner.owned.indexOf(c);
385
+ if (index !== -1)
386
+ c.owner.owned.splice(index, 1);
387
+ }
388
+ }
389
+ export function createMemo(fn, value, options) {
390
+ const node = createReactiveNode('memo', value, {
391
+ equals: options?.equals === false ? undefined : options?.equals,
392
+ name: options?.name,
393
+ });
394
+ const c = {
395
+ fn,
396
+ state: DIRTY,
397
+ sources: null,
398
+ sourceSlots: null,
399
+ value,
400
+ owned: null,
401
+ cleanups: null,
402
+ owner: Owner,
403
+ contexts: null,
404
+ pure: true,
405
+ updatedAt: null,
406
+ node,
407
+ accessor: undefined,
408
+ };
409
+ if (Owner) {
410
+ if (!Owner.owned)
411
+ Owner.owned = [c];
412
+ else
413
+ Owner.owned.push(c);
414
+ }
415
+ const read = () => {
416
+ if (Listener && Listener !== c) {
417
+ if (!Listener.sources)
418
+ Listener.sources = [read];
419
+ else if (Listener.sources.indexOf(read) === -1)
420
+ Listener.sources.push(read);
421
+ const memoAsSignal = read;
422
+ if (!signalObservers.has(memoAsSignal))
423
+ signalObservers.set(memoAsSignal, new Set());
424
+ const observers = signalObservers.get(memoAsSignal);
425
+ if (observers)
426
+ observers.add(Listener);
427
+ }
428
+ if (c.state !== CLEAN) {
429
+ if (c.sources) {
430
+ for (const source of c.sources) {
431
+ const observers = signalObservers.get(source);
432
+ if (observers)
433
+ observers.delete(c);
434
+ }
435
+ c.sources = null;
436
+ c.sourceSlots = null;
437
+ }
438
+ if (c.cleanups) {
439
+ for (let i = 0; i < c.cleanups.length; i++)
440
+ c.cleanups[i]();
441
+ c.cleanups = null;
442
+ }
443
+ if (c.owned) {
444
+ for (let i = 0; i < c.owned.length; i++)
445
+ dispose(c.owned[i]);
446
+ c.owned = null;
447
+ }
448
+ const prevListener = Listener;
449
+ const prevOwner = Owner;
450
+ Listener = c;
451
+ Owner = c;
452
+ c.state = RUNNING;
453
+ try {
454
+ const nextValue = c.fn(c.value);
455
+ const equals = node.equals || defaultEquals;
456
+ const hasChanged = options?.equals === false || !equals(c.value, nextValue);
457
+ if (hasChanged) {
458
+ c.value = nextValue;
459
+ node.value = nextValue;
460
+ node.version++;
461
+ node.updated = Date.now();
462
+ const memoAsSignal = read;
463
+ const observers = signalObservers.get(memoAsSignal);
464
+ if (observers && observers.size > 0) {
465
+ const observerList = Array.from(observers);
466
+ const toUpdate = [];
467
+ const visited = new Set();
468
+ // Mirror the signal-write propagateDirty (see createSignal.write): when an
469
+ // observer is itself a pure memo, recurse through it to its observers so
470
+ // they are marked stale too. Without this, a memo M observed by another
471
+ // memo N (e.g. a child route content/outlet memo gating on a parent content
472
+ // memo) updates M but never notifies N's downstream effects, so a deferred
473
+ // <Outlet/> deep in a nested layout never re-renders after its parent
474
+ // settles. viaPureMemo marks downstream non-pure (effects) STALE.
475
+ function propagateDirty(comp, viaPureMemo = false) {
476
+ if (comp === c)
477
+ return;
478
+ const prevState = comp.state;
479
+ if (prevState === RUNNING)
480
+ return;
481
+ if (viaPureMemo && !comp.pure) {
482
+ if (prevState === CLEAN) {
483
+ comp.state = STALE;
484
+ if (Updates !== null && Updates.indexOf(comp) === -1)
485
+ Updates.push(comp);
486
+ else
487
+ toUpdate.push(comp);
488
+ }
489
+ return;
490
+ }
491
+ if (visited.has(comp))
492
+ return;
493
+ visited.add(comp);
494
+ comp.state = DIRTY;
495
+ if (prevState === CLEAN || prevState === STALE)
496
+ toUpdate.push(comp);
497
+ else if (!comp.pure &&
498
+ Updates !== null &&
499
+ Updates.indexOf(comp) === -1)
500
+ Updates.push(comp);
501
+ if (comp.pure && comp.accessor) {
502
+ const memoObservers = signalObservers.get(comp.accessor);
503
+ if (memoObservers && memoObservers.size > 0)
504
+ for (const o of Array.from(memoObservers))
505
+ propagateDirty(o, true);
506
+ }
507
+ }
508
+ for (let i = 0; i < observerList.length; i++)
509
+ propagateDirty(observerList[i]);
510
+ for (const comp of toUpdate)
511
+ scheduleComputation(comp);
512
+ }
513
+ }
514
+ if (c.state === RUNNING)
515
+ c.state = CLEAN;
516
+ }
517
+ catch (err) {
518
+ if (isPromise(err)) {
519
+ let ctx = _SuspenseContext && useContext(_SuspenseContext);
520
+ if (!ctx && _SuspenseContext) {
521
+ let owner = prevOwner;
522
+ while (owner) {
523
+ if (owner.contexts?.has(_SuspenseContext.id)) {
524
+ ctx = owner.contexts.get(_SuspenseContext.id);
525
+ break;
526
+ }
527
+ owner = owner.owner;
528
+ }
529
+ }
530
+ if (ctx) {
531
+ ctx.increment();
532
+ err.finally(() => ctx.decrement());
533
+ }
534
+ return c.value;
535
+ }
536
+ handleError(err);
537
+ }
538
+ finally {
539
+ Listener = prevListener;
540
+ Owner = prevOwner;
541
+ }
542
+ }
543
+ return c.value;
544
+ };
545
+ c.accessor = read;
546
+ signalObservers.set(read, new Set());
547
+ read.toObservable = () => new Observable((observer) => {
548
+ let first = true;
549
+ const dispose = createEffect(() => {
550
+ const val = read();
551
+ if (first)
552
+ first = false;
553
+ else
554
+ observer.next(val);
555
+ });
556
+ return dispose;
557
+ });
558
+ Object.defineProperty(read, $SIGNAL, { value: true, enumerable: true });
559
+ Object.defineProperty(read, $MEMO, { value: true, enumerable: true });
560
+ Object.defineProperty(read, $NODE, { value: node, enumerable: false });
561
+ return read;
562
+ }
563
+ /**
564
+ * Cascade-evaluates pure memos from `initial`, transitively following any
565
+ * newly-triggered pure memos, then runs all collected non-pure effects.
566
+ *
567
+ * Pure memos with no downstream observers are skipped (lazy — they'll
568
+ * re-evaluate on next read without side effects).
569
+ */
570
+ function runCascadeAndEffects(initial) {
571
+ const cascadeQueue = [];
572
+ const effectsToRun = [];
573
+ for (const comp of initial) {
574
+ if (comp.pure && !comp.computed && comp.accessor) {
575
+ if (cascadeQueue.indexOf(comp) === -1)
576
+ cascadeQueue.push(comp);
577
+ }
578
+ else if ((comp.computed || !comp.pure) &&
579
+ effectsToRun.indexOf(comp) === -1)
580
+ effectsToRun.push(comp);
581
+ }
582
+ Updates = [];
583
+ let ci = 0;
584
+ while (ci < cascadeQueue.length) {
585
+ const comp = cascadeQueue[ci++];
586
+ const observers = signalObservers.get(comp.accessor);
587
+ if (observers && observers.size > 0) {
588
+ const prevListener = Listener;
589
+ Listener = null;
590
+ comp.accessor();
591
+ Listener = prevListener;
592
+ }
593
+ const fresh = Updates.splice(0);
594
+ for (const c of fresh) {
595
+ if (c.pure && !c.computed && c.accessor) {
596
+ if (cascadeQueue.indexOf(c) === -1)
597
+ cascadeQueue.push(c);
598
+ }
599
+ else if ((c.computed || !c.pure) && effectsToRun.indexOf(c) === -1)
600
+ effectsToRun.push(c);
601
+ }
602
+ }
603
+ Updates = null;
604
+ for (const comp of effectsToRun)
605
+ if (comp.computed || !comp.pure)
606
+ updateComputation(comp);
607
+ }
608
+ export function batch(fn) {
609
+ if (Updates && BatchDepth > 0)
610
+ return fn();
611
+ if (Updates && BatchDepth === 0) {
612
+ const prev = Updates;
613
+ const nested = [];
614
+ Updates = nested;
615
+ BatchDepth++;
616
+ let result;
617
+ try {
618
+ result = fn();
619
+ }
620
+ finally {
621
+ BatchDepth--;
622
+ Updates = prev;
623
+ }
624
+ const savedUpdates = Updates;
625
+ runCascadeAndEffects(nested);
626
+ Updates = savedUpdates;
627
+ return result;
628
+ }
629
+ BatchDepth++;
630
+ Updates = [];
631
+ Effects = [];
632
+ try {
633
+ return fn();
634
+ }
635
+ finally {
636
+ BatchDepth--;
637
+ flushUpdates();
638
+ }
639
+ }
640
+ function flushUpdates() {
641
+ if (Pending)
642
+ return;
643
+ Pending = true;
644
+ ExecCount = 0;
645
+ try {
646
+ while (Updates && Updates.length > 0) {
647
+ const updates = Updates;
648
+ Updates = null;
649
+ runCascadeAndEffects(updates);
650
+ }
651
+ Updates = null;
652
+ if (Effects) {
653
+ const effects = Effects;
654
+ Effects = null;
655
+ for (let i = 0; i < effects.length; i++)
656
+ updateComputation(effects[i]);
657
+ }
658
+ }
659
+ finally {
660
+ Pending = false;
661
+ }
662
+ }
663
+ export function createRoot(fn) {
664
+ const owner = {
665
+ owned: null,
666
+ cleanups: null,
667
+ contexts: null,
668
+ owner: Owner,
669
+ };
670
+ const prevListener = Listener;
671
+ Listener = null;
672
+ const prevOwner = Owner;
673
+ Owner = owner;
674
+ try {
675
+ return fn(() => {
676
+ if (owner.owned)
677
+ for (let i = 0; i < owner.owned.length; i++)
678
+ dispose(owner.owned[i]);
679
+ if (owner.cleanups)
680
+ for (let i = 0; i < owner.cleanups.length; i++)
681
+ owner.cleanups[i]();
682
+ });
683
+ }
684
+ finally {
685
+ Owner = prevOwner;
686
+ Listener = prevListener;
687
+ }
688
+ }
689
+ export function untrack(fn) {
690
+ const prevListener = Listener;
691
+ Listener = null;
692
+ try {
693
+ return fn();
694
+ }
695
+ finally {
696
+ Listener = prevListener;
697
+ }
698
+ }
699
+ export function onCleanup(fn) {
700
+ if (Owner) {
701
+ if (!Owner.cleanups)
702
+ Owner.cleanups = [fn];
703
+ else
704
+ Owner.cleanups.push(fn);
705
+ }
706
+ }
707
+ const cache = new Map();
708
+ function fetchCached(key) {
709
+ if (!cache.has(key))
710
+ cache.set(key, fetch(key));
711
+ return cache.get(key);
712
+ }
713
+ export function createResource(source, fetcherFn, options = {}) {
714
+ let s;
715
+ let fetcher;
716
+ if (typeof fetcherFn === 'function') {
717
+ s = typeof source === 'function' ? source : () => source;
718
+ fetcher = fetcherFn;
719
+ }
720
+ else {
721
+ s = () => true;
722
+ fetcher = source;
723
+ if (fetcherFn && typeof fetcherFn === 'object')
724
+ options = fetcherFn;
725
+ }
726
+ const { initialValue, name } = options;
727
+ // SSR data transport: a stable per-render id; on the client, seed from the
728
+ // server-serialized value so hydration doesn't refetch (and doesn't flash).
729
+ const __ssrId = nextResourceId();
730
+ const __seed = __ssrId !== undefined ? getServerData(__ssrId) : undefined;
731
+ let skipFirstFetch = __seed !== undefined;
732
+ const [value, setValue] = createSignal(__seed !== undefined ? __seed.value : options.initialValue, { equals: false });
733
+ const [error, setError] = createSignal(undefined, { equals: false });
734
+ const [state, setState] = createSignal(__seed !== undefined || options.initialValue !== undefined ? 'ready' : 'unresolved', { equals: false });
735
+ const [promise, setPromise] = createSignal(undefined, { equals: false });
736
+ const [track, trigger] = createSignal(undefined, { equals: false });
737
+ const UNSET = Symbol();
738
+ let prevKey = UNSET;
739
+ let manualRefetch = false;
740
+ const saveToCache = (data) => {
741
+ // No localStorage on the server (SSR) — skip silently.
742
+ if (typeof localStorage === 'undefined')
743
+ return;
744
+ try {
745
+ if (data)
746
+ localStorage.setItem('storage', JSON.stringify({ data, timestamp: Date.now() }));
747
+ }
748
+ catch (error) {
749
+ console.error(`Failed to save resource to cache (${name}):`, error);
750
+ }
751
+ };
752
+ const dispose = createEffect(() => {
753
+ const val = s ? s() : source;
754
+ track();
755
+ if (val === false || val === null || val === undefined) {
756
+ prevKey = UNSET;
757
+ batch(() => {
758
+ setState('unresolved');
759
+ setPromise(undefined);
760
+ setError(undefined);
761
+ });
762
+ return;
763
+ }
764
+ const isManual = manualRefetch;
765
+ manualRefetch = false;
766
+ if (prevKey !== UNSET && val === prevKey && !isManual)
767
+ return;
768
+ prevKey = val;
769
+ // Seeded from server data during hydration — value is already set, skip the
770
+ // first fetch (still tracked the source above, so later refetches work).
771
+ if (skipFirstFetch) {
772
+ skipFirstFetch = false;
773
+ return;
774
+ }
775
+ let cancelled = false;
776
+ onCleanup(() => (cancelled = true));
777
+ const currentState = untrack(state);
778
+ const currentValue = untrack(value);
779
+ const isRefreshing = currentState === 'ready' || currentState === 'refreshing';
780
+ batch(() => {
781
+ setState(isRefreshing ? 'refreshing' : 'pending');
782
+ setError(undefined);
783
+ });
784
+ try {
785
+ const res = fetcher(val, {
786
+ value: currentValue,
787
+ refetching: isRefreshing,
788
+ });
789
+ if (isPromise(res)) {
790
+ setPromise(res);
791
+ trackResourcePromise(res); // server async SSR: collect & await
792
+ res
793
+ .then((v) => {
794
+ if (cancelled)
795
+ return;
796
+ if (__ssrId !== undefined)
797
+ reportResourceData(__ssrId, v); // server: collect for serialization
798
+ batch(() => {
799
+ setValue(() => v);
800
+ saveToCache(v);
801
+ setState('ready');
802
+ setPromise(undefined);
803
+ });
804
+ }, (e) => {
805
+ if (cancelled)
806
+ return;
807
+ batch(() => {
808
+ setError(e);
809
+ setState('errored');
810
+ setPromise(undefined);
811
+ });
812
+ })
813
+ .catch((e) => {
814
+ if (cancelled)
815
+ return;
816
+ batch(() => {
817
+ setError(e);
818
+ setState('errored');
819
+ setPromise(undefined);
820
+ });
821
+ });
822
+ const suspenseCtx = _SuspenseContext && useContext(_SuspenseContext);
823
+ if (suspenseCtx) {
824
+ suspenseCtx.increment();
825
+ res.finally(() => suspenseCtx.decrement());
826
+ }
827
+ }
828
+ else {
829
+ if (cancelled)
830
+ return;
831
+ batch(() => {
832
+ setValue(() => res);
833
+ setState('ready');
834
+ setPromise(undefined);
835
+ });
836
+ }
837
+ }
838
+ catch (e) {
839
+ if (cancelled)
840
+ return;
841
+ batch(() => {
842
+ setError(e);
843
+ setState('errored');
844
+ setPromise(undefined);
845
+ });
846
+ }
847
+ });
848
+ onCleanup(dispose);
849
+ const loading = () => {
850
+ const s = state();
851
+ return s === 'pending' || s === 'refreshing';
852
+ };
853
+ const resource = (() => {
854
+ const e = error();
855
+ if (e)
856
+ throw e;
857
+ if (loading()) {
858
+ const p = promise();
859
+ if (p)
860
+ throw p;
861
+ }
862
+ return value();
863
+ });
864
+ Object.defineProperty(resource, $RESOURCE, { value: true, enumerable: true });
865
+ Object.defineProperty(resource, $SIGNAL, { value: true, enumerable: true });
866
+ Object.defineProperties(resource, {
867
+ state: { get: state },
868
+ loading: { get: () => loading() },
869
+ error: { get: error },
870
+ latest: { get: value },
871
+ });
872
+ return [
873
+ resource,
874
+ {
875
+ mutate: setValue,
876
+ refetch: () => {
877
+ manualRefetch = true;
878
+ trigger(undefined);
879
+ },
880
+ },
881
+ ];
882
+ }
883
+ export function getOwner() {
884
+ return Owner;
885
+ }
886
+ export function runWithOwner(owner, fn) {
887
+ const prevOwner = Owner;
888
+ const prevListener = Listener;
889
+ Owner = owner;
890
+ Listener = null;
891
+ try {
892
+ return fn();
893
+ }
894
+ finally {
895
+ Owner = prevOwner;
896
+ Listener = prevListener;
897
+ }
898
+ }
899
+ export function createContext(defaultValue) {
900
+ const id = Symbol('context');
901
+ const Provider = createProvider(id);
902
+ return { id, Provider, defaultValue };
903
+ }
904
+ export function createProvider(id) {
905
+ return (props) => {
906
+ let _node = Owner;
907
+ while (_node) {
908
+ if (!_node.contexts)
909
+ _node.contexts = new Map();
910
+ if (!_node.contexts.has(id))
911
+ _node.contexts.set(id, props.value);
912
+ _node = _node.owner;
913
+ }
914
+ let provider;
915
+ createRenderEffect(() => {
916
+ provider = untrack(() => {
917
+ const owner = Owner;
918
+ if (owner) {
919
+ if (!owner.contexts)
920
+ owner.contexts = new Map();
921
+ owner.contexts.set(id, props.value);
922
+ }
923
+ return readChildren(() => props.children);
924
+ });
925
+ });
926
+ return provider;
927
+ };
928
+ }
929
+ export function useContext(context) {
930
+ let owner = Owner;
931
+ while (owner) {
932
+ if (owner.contexts && owner.contexts.has(context.id))
933
+ return owner.contexts.get(context.id);
934
+ owner = owner.owner;
935
+ }
936
+ if (!context)
937
+ return;
938
+ return context.defaultValue;
939
+ }
940
+ export function flush() {
941
+ return new Promise((resolve) => {
942
+ queueMicrotask(() => {
943
+ flushUpdates();
944
+ resolve();
945
+ });
946
+ });
947
+ }
948
+ /**
949
+ * startTransition — defer non-critical effect updates.
950
+ *
951
+ * Runs `fn` synchronously, capturing all non-pure effects that would fire as
952
+ * a result into a deferred queue. Pure computations (memos, createComputed)
953
+ * update immediately so signal values are consistent. The deferred effects are
954
+ * then flushed asynchronously (next microtask), keeping the current UI
955
+ * responsive while the new state is prepared.
956
+ */
957
+ export function startTransition(fn) {
958
+ if (Transition && Transition.running) {
959
+ fn();
960
+ return Transition.done;
961
+ }
962
+ ensureTransPending();
963
+ let resolve;
964
+ const done = new Promise((res) => (resolve = res));
965
+ const t = { running: true, effects: [], resolve, done };
966
+ Transition = t;
967
+ try {
968
+ fn();
969
+ }
970
+ finally {
971
+ t.running = false;
972
+ }
973
+ if (!t.effects.length) {
974
+ Transition = null;
975
+ resolve();
976
+ return done;
977
+ }
978
+ _setTransPending(true);
979
+ Promise.resolve().then(() => {
980
+ const effects = t.effects.splice(0);
981
+ Transition = null;
982
+ _setTransPending(false);
983
+ if (effects.length)
984
+ batch(() => {
985
+ for (const e of effects) {
986
+ e.state = DIRTY;
987
+ updateComputation(e);
988
+ }
989
+ });
990
+ resolve();
991
+ });
992
+ return done;
993
+ }
994
+ /**
995
+ * useTransition — reactive hook version of startTransition.
996
+ * Returns `[isPending, start]`
997
+ */
998
+ export function useTransition() {
999
+ ensureTransPending();
1000
+ return [_transPending, startTransition];
1001
+ }
1002
+ export function on(deps, fn, options) {
1003
+ const defer = options?.defer ?? false;
1004
+ let isFirst = true;
1005
+ return (prev) => {
1006
+ const values = deps();
1007
+ if (defer && isFirst) {
1008
+ isFirst = false;
1009
+ return prev;
1010
+ }
1011
+ return untrack(() => fn(values, prev));
1012
+ };
1013
+ }
1014
+ /**
1015
+ * createRenderEffect - runs synchronously during rendering.
1016
+ */
1017
+ export function createRenderEffect(fn, value, options) {
1018
+ const c = {
1019
+ fn: fn,
1020
+ state: DIRTY,
1021
+ sources: null,
1022
+ sourceSlots: null,
1023
+ value,
1024
+ owned: null,
1025
+ cleanups: null,
1026
+ owner: Owner,
1027
+ contexts: null,
1028
+ pure: false,
1029
+ updatedAt: null,
1030
+ renderEffect: true,
1031
+ };
1032
+ if (Owner) {
1033
+ if (!Owner.owned)
1034
+ Owner.owned = [c];
1035
+ else
1036
+ Owner.owned.push(c);
1037
+ }
1038
+ const SC = _SuspenseContext && useContext(_SuspenseContext);
1039
+ if (SC)
1040
+ c.suspense = SC;
1041
+ runRenderComputation(c);
1042
+ return () => dispose(c);
1043
+ }
1044
+ function runRenderComputation(c) {
1045
+ if (c.sources) {
1046
+ for (const source of c.sources) {
1047
+ const observers = signalObservers.get(source);
1048
+ if (observers)
1049
+ observers.delete(c);
1050
+ }
1051
+ c.sources = null;
1052
+ c.sourceSlots = null;
1053
+ }
1054
+ if (c.cleanups) {
1055
+ for (let i = 0; i < c.cleanups.length; i++)
1056
+ c.cleanups[i]();
1057
+ c.cleanups = null;
1058
+ }
1059
+ if (c.owned) {
1060
+ for (let i = 0; i < c.owned.length; i++)
1061
+ dispose(c.owned[i]);
1062
+ c.owned = null;
1063
+ }
1064
+ const prevListener = Listener;
1065
+ const prevOwner = Owner;
1066
+ const prevUpdates = Updates;
1067
+ Listener = c;
1068
+ Owner = c;
1069
+ if (Updates === null)
1070
+ Updates = [];
1071
+ c.state = RUNNING;
1072
+ try {
1073
+ const result = c.fn(c.value);
1074
+ if (typeof result === 'function') {
1075
+ if (!c.cleanups)
1076
+ c.cleanups = [];
1077
+ c.cleanups.push(result);
1078
+ }
1079
+ c.value = result;
1080
+ if (c.state === RUNNING)
1081
+ c.state = CLEAN;
1082
+ }
1083
+ catch (err) {
1084
+ if (isPromise(err)) {
1085
+ const ctx = _SuspenseContext && useContext(_SuspenseContext);
1086
+ if (ctx) {
1087
+ ctx.increment();
1088
+ err.finally(() => ctx.decrement());
1089
+ if (c.suspense)
1090
+ ctx.effects.push(c);
1091
+ }
1092
+ return;
1093
+ }
1094
+ handleError(err);
1095
+ }
1096
+ finally {
1097
+ Listener = prevListener;
1098
+ Owner = prevOwner;
1099
+ const deferred = Updates;
1100
+ Updates = prevUpdates;
1101
+ // Flush computations scheduled during this render effect via the full cascade,
1102
+ // which re-evaluates PURE MEMOS (and propagates to their observers) as well as
1103
+ // running effects. The old loop processed only effects, so a pure memo scheduled
1104
+ // here was left dirty and never notified its own downstream effects — e.g. a
1105
+ // child route content/outlet memo that settled while a parent layout rendered
1106
+ // would never re-render the child (deeply nested <Outlet/> stayed empty).
1107
+ if (deferred !== prevUpdates && deferred && deferred.length > 0) {
1108
+ const saved = Updates;
1109
+ runCascadeAndEffects(deferred);
1110
+ Updates = saved;
1111
+ }
1112
+ }
1113
+ }
1114
+ /**
1115
+ * createComputed — synchronous pre-render computation.
1116
+ */
1117
+ export function createComputed(fn, value, options) {
1118
+ const c = {
1119
+ fn,
1120
+ state: DIRTY,
1121
+ sources: null,
1122
+ sourceSlots: null,
1123
+ value,
1124
+ owned: null,
1125
+ cleanups: null,
1126
+ owner: Owner,
1127
+ contexts: null,
1128
+ pure: true,
1129
+ computed: true,
1130
+ updatedAt: null,
1131
+ };
1132
+ if (Owner) {
1133
+ if (!Owner.owned)
1134
+ Owner.owned = [c];
1135
+ else
1136
+ Owner.owned.push(c);
1137
+ }
1138
+ if (Transition && Transition.running) {
1139
+ if (!Updates)
1140
+ Updates = [];
1141
+ if (Updates.indexOf(c) === -1)
1142
+ Updates.push(c);
1143
+ }
1144
+ else if (Updates) {
1145
+ if (Updates.indexOf(c) === -1)
1146
+ Updates.push(c);
1147
+ }
1148
+ else
1149
+ runComputation(c, value);
1150
+ }
1151
+ let _SuspenseContext;
1152
+ export function getSuspenseContext() {
1153
+ return (_SuspenseContext ||
1154
+ (_SuspenseContext = createContext()));
1155
+ }
1156
+ let _SuspenseListContext;
1157
+ export function getSuspenseListContext() {
1158
+ return (_SuspenseListContext ||
1159
+ (_SuspenseListContext = createContext()));
1160
+ }
1161
+ export function resumeEffects(effects) {
1162
+ if (!effects.length)
1163
+ return;
1164
+ const queued = effects.slice();
1165
+ effects.length = 0;
1166
+ for (const c of queued) {
1167
+ c.state = DIRTY;
1168
+ updateComputation(c);
1169
+ }
1170
+ }
1171
+ export function onResolve(fn) {
1172
+ const ctx = _SuspenseContext && useContext(_SuspenseContext);
1173
+ if (ctx && ctx.inFallback())
1174
+ createEffect(() => {
1175
+ if (!ctx.inFallback())
1176
+ fn();
1177
+ });
1178
+ else
1179
+ fn();
1180
+ }
1181
+ export function setScheduler(scheduler) {
1182
+ Scheduler = scheduler;
1183
+ }
1184
+ export function trackPromise(promise) {
1185
+ const ctx = _SuspenseContext && useContext(_SuspenseContext);
1186
+ if (!ctx)
1187
+ return promise;
1188
+ ctx.increment();
1189
+ promise.finally(() => ctx.decrement());
1190
+ return promise;
1191
+ }
1192
+ export default {
1193
+ createSignal,
1194
+ createEffect,
1195
+ createMemo,
1196
+ createComputed,
1197
+ createRoot,
1198
+ createSelector,
1199
+ createResource,
1200
+ batch,
1201
+ untrack,
1202
+ onCleanup,
1203
+ getOwner,
1204
+ runWithOwner,
1205
+ createContext,
1206
+ useContext,
1207
+ flush,
1208
+ startTransition,
1209
+ useTransition,
1210
+ isSignal,
1211
+ on,
1212
+ createRenderEffect,
1213
+ getSuspenseContext,
1214
+ getSuspenseListContext,
1215
+ resumeEffects,
1216
+ onResolve,
1217
+ setScheduler,
1218
+ trackPromise,
1219
+ };