@fluixi/reactive 1.0.0-alpha.100

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