mutts 1.0.9 → 1.0.11

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 (206) hide show
  1. package/README.md +60 -35
  2. package/dist/browser.cjs +1333 -1760
  3. package/dist/browser.cjs.map +1 -1
  4. package/dist/browser.d.ts +2 -1392
  5. package/dist/browser.dev.cjs +114 -0
  6. package/dist/browser.dev.cjs.map +1 -0
  7. package/dist/browser.dev.d.ts +2 -0
  8. package/dist/browser.dev.esm.js +5 -0
  9. package/dist/browser.dev.esm.js.map +1 -0
  10. package/dist/browser.esm.js +18 -97
  11. package/dist/browser.esm.js.map +1 -1
  12. package/dist/chunks/{async-browser-lvzLOCgk.cjs → async-browser-Dgr5CreQ.cjs} +16 -95
  13. package/dist/chunks/async-browser-Dgr5CreQ.cjs.map +1 -0
  14. package/dist/chunks/{async-node-C3DeIb0y.cjs → async-node-3PrbVAbB.cjs} +3 -1
  15. package/dist/chunks/async-node-3PrbVAbB.cjs.map +1 -0
  16. package/dist/chunks/index-Sf74wXTV.esm.js +2577 -0
  17. package/dist/chunks/index-Sf74wXTV.esm.js.map +1 -0
  18. package/dist/chunks/node-Bo7WU5S2.esm.js +96 -0
  19. package/dist/chunks/node-Bo7WU5S2.esm.js.map +1 -0
  20. package/dist/chunks/{index-VTO-b2vR.cjs → proxy-Cc79Lrzj.cjs} +2593 -3216
  21. package/dist/chunks/proxy-Cc79Lrzj.cjs.map +1 -0
  22. package/dist/chunks/{index-CtA2AWl3.esm.js → proxy-D2C49sXH.esm.js} +2570 -3174
  23. package/dist/chunks/proxy-D2C49sXH.esm.js.map +1 -0
  24. package/dist/debug.cjs +987 -28
  25. package/dist/debug.cjs.map +1 -1
  26. package/dist/debug.d.ts +37 -38
  27. package/dist/debug.esm.js +963 -1
  28. package/dist/debug.esm.js.map +1 -1
  29. package/dist/devtools/manifest.json +1 -1
  30. package/dist/devtools/panel.html +1 -1
  31. package/dist/devtools/panel.js +107 -94
  32. package/dist/devtools/panel.js.map +1 -1
  33. package/dist/index.d.ts +1322 -1
  34. package/dist/mutts.umd.js +6647 -1
  35. package/dist/mutts.umd.js.map +1 -1
  36. package/dist/mutts.umd.min.js +1 -1
  37. package/dist/mutts.umd.min.js.map +1 -1
  38. package/dist/node.cjs +69 -63
  39. package/dist/node.cjs.map +1 -1
  40. package/dist/node.d.ts +2 -2
  41. package/dist/node.dev.cjs +114 -0
  42. package/dist/node.dev.cjs.map +1 -0
  43. package/dist/node.dev.d.ts +2 -0
  44. package/dist/node.dev.esm.js +6 -0
  45. package/dist/node.dev.esm.js.map +1 -0
  46. package/dist/node.esm.js +4 -97
  47. package/dist/node.esm.js.map +1 -1
  48. package/dist/{types-DaHFfhlN.d.ts → types-Bx2PhORg.d.ts} +134 -88
  49. package/docs/ai/api-reference.md +11 -32
  50. package/docs/ai/manual.md +297 -239
  51. package/docs/reactive/advanced.md +318 -9
  52. package/docs/reactive/attend.md +2 -4
  53. package/docs/reactive/collections.md +22 -187
  54. package/docs/reactive/core.md +223 -131
  55. package/docs/reactive/debugging.md +119 -12
  56. package/docs/reactive/error-handling.md +10 -10
  57. package/docs/reactive/resource.md +125 -0
  58. package/docs/reactive.md +3 -4
  59. package/docs/utils.md +70 -0
  60. package/docs/zone.md +1 -1
  61. package/package.json +76 -38
  62. package/dist/chunks/async-browser-lvzLOCgk.cjs.map +0 -1
  63. package/dist/chunks/async-node-C3DeIb0y.cjs.map +0 -1
  64. package/dist/chunks/index-2vea86wD.esm.js +0 -3011
  65. package/dist/chunks/index-2vea86wD.esm.js.map +0 -1
  66. package/dist/chunks/index-CtA2AWl3.esm.js.map +0 -1
  67. package/dist/chunks/index-VTO-b2vR.cjs.map +0 -1
  68. package/dist/debug/debug.d.ts +0 -122
  69. package/dist/debug/debug.d.ts.map +0 -1
  70. package/dist/debug/index.d.ts +0 -4
  71. package/dist/debug/index.d.ts.map +0 -1
  72. package/dist/debug/lineage-panel.d.ts +0 -5
  73. package/dist/debug/lineage-panel.d.ts.map +0 -1
  74. package/dist/debug/lineage.d.ts +0 -79
  75. package/dist/debug/lineage.d.ts.map +0 -1
  76. package/dist/src/async/browser.d.ts +0 -2
  77. package/dist/src/async/browser.d.ts.map +0 -1
  78. package/dist/src/async/index.d.ts +0 -19
  79. package/dist/src/async/index.d.ts.map +0 -1
  80. package/dist/src/async/node.d.ts +0 -2
  81. package/dist/src/async/node.d.ts.map +0 -1
  82. package/dist/src/decorator.d.ts +0 -106
  83. package/dist/src/decorator.d.ts.map +0 -1
  84. package/dist/src/destroyable.d.ts +0 -87
  85. package/dist/src/destroyable.d.ts.map +0 -1
  86. package/dist/src/entry-browser.d.ts +0 -3
  87. package/dist/src/entry-browser.d.ts.map +0 -1
  88. package/dist/src/entry-node.d.ts +0 -3
  89. package/dist/src/entry-node.d.ts.map +0 -1
  90. package/dist/src/eventful.d.ts +0 -20
  91. package/dist/src/eventful.d.ts.map +0 -1
  92. package/dist/src/flavored.d.ts +0 -33
  93. package/dist/src/flavored.d.ts.map +0 -1
  94. package/dist/src/index.d.ts +0 -14
  95. package/dist/src/index.d.ts.map +0 -1
  96. package/dist/src/indexable.d.ts +0 -243
  97. package/dist/src/indexable.d.ts.map +0 -1
  98. package/dist/src/introspection.d.ts +0 -27
  99. package/dist/src/introspection.d.ts.map +0 -1
  100. package/dist/src/iterableWeak.d.ts +0 -53
  101. package/dist/src/iterableWeak.d.ts.map +0 -1
  102. package/dist/src/mixins.d.ts +0 -25
  103. package/dist/src/mixins.d.ts.map +0 -1
  104. package/dist/src/promiseChain.d.ts +0 -20
  105. package/dist/src/promiseChain.d.ts.map +0 -1
  106. package/dist/src/reactive/array.d.ts +0 -48
  107. package/dist/src/reactive/array.d.ts.map +0 -1
  108. package/dist/src/reactive/buffer.d.ts +0 -120
  109. package/dist/src/reactive/buffer.d.ts.map +0 -1
  110. package/dist/src/reactive/change.d.ts +0 -29
  111. package/dist/src/reactive/change.d.ts.map +0 -1
  112. package/dist/src/reactive/deep-touch.d.ts +0 -28
  113. package/dist/src/reactive/deep-touch.d.ts.map +0 -1
  114. package/dist/src/reactive/deep-watch-state.d.ts +0 -25
  115. package/dist/src/reactive/deep-watch-state.d.ts.map +0 -1
  116. package/dist/src/reactive/deep-watch.d.ts +0 -20
  117. package/dist/src/reactive/deep-watch.d.ts.map +0 -1
  118. package/dist/src/reactive/describe.d.ts +0 -12
  119. package/dist/src/reactive/describe.d.ts.map +0 -1
  120. package/dist/src/reactive/effect-context.d.ts +0 -34
  121. package/dist/src/reactive/effect-context.d.ts.map +0 -1
  122. package/dist/src/reactive/effects.d.ts +0 -164
  123. package/dist/src/reactive/effects.d.ts.map +0 -1
  124. package/dist/src/reactive/index.d.ts +0 -19
  125. package/dist/src/reactive/index.d.ts.map +0 -1
  126. package/dist/src/reactive/map.d.ts +0 -28
  127. package/dist/src/reactive/map.d.ts.map +0 -1
  128. package/dist/src/reactive/memoize.d.ts +0 -28
  129. package/dist/src/reactive/memoize.d.ts.map +0 -1
  130. package/dist/src/reactive/non-reactive-state.d.ts +0 -9
  131. package/dist/src/reactive/non-reactive-state.d.ts.map +0 -1
  132. package/dist/src/reactive/non-reactive.d.ts +0 -11
  133. package/dist/src/reactive/non-reactive.d.ts.map +0 -1
  134. package/dist/src/reactive/project.d.ts +0 -40
  135. package/dist/src/reactive/project.d.ts.map +0 -1
  136. package/dist/src/reactive/proxy-state.d.ts +0 -8
  137. package/dist/src/reactive/proxy-state.d.ts.map +0 -1
  138. package/dist/src/reactive/proxy.d.ts +0 -23
  139. package/dist/src/reactive/proxy.d.ts.map +0 -1
  140. package/dist/src/reactive/record.d.ts +0 -115
  141. package/dist/src/reactive/record.d.ts.map +0 -1
  142. package/dist/src/reactive/register.d.ts +0 -125
  143. package/dist/src/reactive/register.d.ts.map +0 -1
  144. package/dist/src/reactive/registry.d.ts +0 -21
  145. package/dist/src/reactive/registry.d.ts.map +0 -1
  146. package/dist/src/reactive/set.d.ts +0 -26
  147. package/dist/src/reactive/set.d.ts.map +0 -1
  148. package/dist/src/reactive/tracking.d.ts +0 -7
  149. package/dist/src/reactive/tracking.d.ts.map +0 -1
  150. package/dist/src/reactive/types.d.ts +0 -424
  151. package/dist/src/reactive/types.d.ts.map +0 -1
  152. package/dist/src/reactive/watch.d.ts +0 -48
  153. package/dist/src/reactive/watch.d.ts.map +0 -1
  154. package/dist/src/std-decorators.d.ts +0 -45
  155. package/dist/src/std-decorators.d.ts.map +0 -1
  156. package/dist/src/utils.d.ts +0 -49
  157. package/dist/src/utils.d.ts.map +0 -1
  158. package/dist/src/zone.d.ts +0 -40
  159. package/dist/src/zone.d.ts.map +0 -1
  160. package/docs/reactive/describe.md +0 -85
  161. package/docs/reactive/project.md +0 -93
  162. package/docs/reactive/scan.md +0 -293
  163. package/src/async/browser.ts +0 -323
  164. package/src/async/index.ts +0 -27
  165. package/src/async/node.ts +0 -92
  166. package/src/decorator.ts +0 -272
  167. package/src/destroyable.ts +0 -199
  168. package/src/entry-browser.ts +0 -5
  169. package/src/entry-node.ts +0 -5
  170. package/src/eventful.ts +0 -110
  171. package/src/flavored.ts +0 -106
  172. package/src/index.d.ts +0 -12
  173. package/src/index.ts +0 -64
  174. package/src/indexable.ts +0 -526
  175. package/src/introspection.ts +0 -59
  176. package/src/iterableWeak.ts +0 -233
  177. package/src/mixins.ts +0 -123
  178. package/src/promiseChain.ts +0 -110
  179. package/src/reactive/array.ts +0 -500
  180. package/src/reactive/buffer.ts +0 -328
  181. package/src/reactive/change.ts +0 -131
  182. package/src/reactive/deep-touch.ts +0 -273
  183. package/src/reactive/deep-watch-state.ts +0 -82
  184. package/src/reactive/deep-watch.ts +0 -171
  185. package/src/reactive/describe.ts +0 -39
  186. package/src/reactive/effect-context.ts +0 -83
  187. package/src/reactive/effects.ts +0 -1434
  188. package/src/reactive/index.ts +0 -72
  189. package/src/reactive/map.ts +0 -142
  190. package/src/reactive/memoize.ts +0 -186
  191. package/src/reactive/non-reactive-state.ts +0 -49
  192. package/src/reactive/non-reactive.ts +0 -43
  193. package/src/reactive/project.md +0 -107
  194. package/src/reactive/project.ts +0 -430
  195. package/src/reactive/proxy-state.ts +0 -27
  196. package/src/reactive/proxy.ts +0 -282
  197. package/src/reactive/record.ts +0 -181
  198. package/src/reactive/register.ts +0 -538
  199. package/src/reactive/registry.ts +0 -72
  200. package/src/reactive/set.ts +0 -117
  201. package/src/reactive/tracking.ts +0 -41
  202. package/src/reactive/types.ts +0 -520
  203. package/src/reactive/watch.ts +0 -180
  204. package/src/std-decorators.ts +0 -256
  205. package/src/utils.ts +0 -300
  206. package/src/zone.ts +0 -142
@@ -0,0 +1,2577 @@
1
+ import { t as decorator, B as flavored, O as isReactive, $ as reactive, x as effect, a4 as touched, S as link, G as getState, aa as dependant, a5 as touched1, ab as keysOf, ac as markWithRoot, ad as objectsWithDeepWatchers, ae as effectToDeepWatchedObjects, a7 as untracked, af as deepWatchers, a0 as options, L as isNonReactive, a8 as unwrap, ag as registerDeepWatcher, V as named, ah as rootFunctionSymbol, _ as proxyToObject, ai as getRoot, u as deepCompare, aj as optionCall, a2 as root, ak as FoolProof, z as flavorOptions, al as effectHistory, p as captured, am as unreactiveProperties, g as addUnreactiveProps, an as __runInitializers, ao as __esDecorate, n as atomic, ap as batch, aq as contentRef, ar as notifyPropertyChange, as as metaProtos, at as wrapProtos, au as objectParents, av as watchers, aw as effectToReactiveObjects, W as objectToProxy } from './proxy-D2C49sXH.esm.js';
2
+
3
+ // Integrated with `using` statement via Symbol.dispose
4
+ const fr = new FinalizationRegistry((f) => f());
5
+ /**
6
+ * Symbol for marking destructor methods
7
+ */
8
+ const destructor = Symbol('destructor');
9
+ /**
10
+ * Symbol for accessing allocated values in destroyable objects
11
+ */
12
+ const allocatedValues = Symbol('allocated');
13
+ /**
14
+ * Error thrown when attempting to access a destroyed object
15
+ */
16
+ class DestructionError extends Error {
17
+ static throw(msg) {
18
+ return () => {
19
+ throw new DestructionError(msg);
20
+ };
21
+ }
22
+ constructor(msg) {
23
+ super(`Object is destroyed. ${msg}`);
24
+ this.name = 'DestroyedAccessError';
25
+ }
26
+ }
27
+ const destroyedHandler = {
28
+ [Symbol.toStringTag]: 'MutTs Destroyable',
29
+ get: DestructionError.throw('Cannot access destroyed object'),
30
+ set: DestructionError.throw('Cannot access destroyed object'),
31
+ };
32
+ function Destroyable(base, destructorObj) {
33
+ var _a;
34
+ if (base && typeof base !== 'function') {
35
+ destructorObj = base;
36
+ base = undefined;
37
+ }
38
+ if (!base) {
39
+ base = class {
40
+ };
41
+ }
42
+ return _a = class Destroyable extends base {
43
+ static destroy(obj) {
44
+ const destructor = _a.destructors.get(obj);
45
+ if (!destructor)
46
+ return false;
47
+ fr.unregister(obj[allocatedValues]);
48
+ _a.destructors.delete(obj);
49
+ Object.setPrototypeOf(obj, new Proxy({}, destroyedHandler));
50
+ // Clear all own properties
51
+ for (const key of Object.getOwnPropertyNames(obj)) {
52
+ delete obj[key];
53
+ }
54
+ destructor();
55
+ return true;
56
+ }
57
+ static isDestroyable(obj) {
58
+ return _a.destructors.has(obj);
59
+ }
60
+ constructor(...args) {
61
+ super(...args);
62
+ const allocated = {};
63
+ this[allocatedValues] = allocated;
64
+ // @ts-expect-error `this` is an AbstractDestroyable
65
+ const myDestructor = destructorObj?.destructor ?? this[destructor];
66
+ if (!myDestructor) {
67
+ throw new DestructionError('Destructor is not defined');
68
+ }
69
+ function destruction() {
70
+ myDestructor(allocated);
71
+ }
72
+ _a.destructors.set(this, destruction);
73
+ fr.register(this, destruction, allocated);
74
+ }
75
+ },
76
+ _a.destructors = new WeakMap(),
77
+ _a;
78
+ }
79
+ /**
80
+ * Decorator that marks properties to be stored in the allocated object and passed to the destructor
81
+ * Use with accessor properties or explicit get/set pairs
82
+ */
83
+ const allocated = decorator({
84
+ setter(original, _target, propertyKey) {
85
+ return function (value) {
86
+ this[allocatedValues][propertyKey] = value;
87
+ return original.call(this, value);
88
+ };
89
+ },
90
+ });
91
+ /**
92
+ * Registers a callback to be called when an object is garbage collected
93
+ * @param cb - The callback function to execute on garbage collection
94
+ * @returns The object whose reference can be collected
95
+ */
96
+ function callOnGC(cb) {
97
+ let called = false;
98
+ const forward = () => {
99
+ if (called)
100
+ return;
101
+ called = true;
102
+ cb();
103
+ };
104
+ fr.register(forward, cb, cb);
105
+ return forward;
106
+ }
107
+
108
+ /** Max edit distance before bailing out to a single "replace all" patch */
109
+ const BAILOUT_D = 256;
110
+ /**
111
+ * Myers' diff producing grouped patches: `{indexA, indexB, sliceA, sliceB}[]`.
112
+ * - O(N) for identical or prefix/suffix-only differences
113
+ * - O(ND) for small D, with a hard bailout at D=BAILOUT_D → single replacement patch
114
+ */
115
+ function arrayDiff(A, B) {
116
+ let start = 0;
117
+ let endA = A.length;
118
+ let endB = B.length;
119
+ // Trim common prefix
120
+ while (start < endA && start < endB && A[start] === B[start])
121
+ start++;
122
+ // Trim common suffix
123
+ while (endA > start && endB > start && A[endA - 1] === B[endB - 1]) {
124
+ endA--;
125
+ endB--;
126
+ }
127
+ const lenA = endA - start;
128
+ const lenB = endB - start;
129
+ if (lenA === 0 && lenB === 0)
130
+ return [];
131
+ if (lenA === 0)
132
+ return [{ indexA: start, indexB: start, sliceA: [], sliceB: B.slice(start, endB) }];
133
+ if (lenB === 0)
134
+ return [{ indexA: start, indexB: start, sliceA: A.slice(start, endA), sliceB: [] }];
135
+ // Myers with bailout
136
+ const maxD = Math.min(lenA + lenB, BAILOUT_D);
137
+ const vSize = 2 * maxD + 1;
138
+ const vOffset = maxD;
139
+ const V = new Int32Array(vSize);
140
+ V[vOffset + 1] = 0;
141
+ const history = [];
142
+ for (let d = 0; d <= maxD; d++) {
143
+ for (let k = -d; k <= d; k += 2) {
144
+ let x;
145
+ if (k === -d || (k !== d && V[vOffset + k - 1] < V[vOffset + k + 1])) {
146
+ x = V[vOffset + k + 1];
147
+ }
148
+ else {
149
+ x = V[vOffset + k - 1] + 1;
150
+ }
151
+ let y = x - k;
152
+ while (x < lenA && y < lenB && A[start + x] === B[start + y]) {
153
+ x++;
154
+ y++;
155
+ }
156
+ V[vOffset + k] = x;
157
+ if (x >= lenA && y >= lenB)
158
+ return buildPatches(history, A, B, start, x, y, d, k, vOffset);
159
+ }
160
+ history.push(new Int32Array(V));
161
+ }
162
+ // Bailout: too many differences
163
+ return [
164
+ { indexA: start, indexB: start, sliceA: A.slice(start, endA), sliceB: B.slice(start, endB) },
165
+ ];
166
+ }
167
+ function buildPatches(history, A, B, offset, finalX, finalY, finalD, finalK, vOffset) {
168
+ // Backtrack from (finalX, finalY) at step finalD to step 0, collecting ops in reverse
169
+ const ops = []; // 0=eq, 1=ins, 2=del
170
+ let x = finalX;
171
+ let y = finalY;
172
+ let k = finalK;
173
+ for (let d = finalD; d > 0; d--) {
174
+ const prev = history[d - 1];
175
+ let prevK;
176
+ let down;
177
+ if (k === -d) {
178
+ prevK = k + 1;
179
+ down = true;
180
+ }
181
+ else if (k === d) {
182
+ prevK = k - 1;
183
+ down = false;
184
+ }
185
+ else if (prev[vOffset + k - 1] < prev[vOffset + k + 1]) {
186
+ prevK = k + 1;
187
+ down = true;
188
+ }
189
+ else {
190
+ prevK = k - 1;
191
+ down = false;
192
+ }
193
+ const prevXEnd = prev[vOffset + prevK];
194
+ const prevYEnd = prevXEnd - prevK;
195
+ const xStart = down ? prevXEnd : prevXEnd + 1;
196
+ const yStart = down ? prevYEnd + 1 : prevXEnd + 1 - k;
197
+ // Diagonal matches (pushed in reverse)
198
+ while (x > xStart && y > yStart) {
199
+ ops.push(0);
200
+ x--;
201
+ y--;
202
+ }
203
+ // The edit step
204
+ if (down) {
205
+ ops.push(1); // ins
206
+ y--;
207
+ }
208
+ else {
209
+ ops.push(2); // del
210
+ x--;
211
+ }
212
+ k = prevK;
213
+ }
214
+ // Walk ops forward (they were pushed in reverse), grouping contiguous edits
215
+ const patches = [];
216
+ let currA = offset;
217
+ let currB = offset;
218
+ let sliceA = [];
219
+ let sliceB = [];
220
+ let patchA = -1;
221
+ let patchB = -1;
222
+ const flush = () => {
223
+ if (patchA !== -1) {
224
+ patches.push({ indexA: patchA, indexB: patchB, sliceA, sliceB });
225
+ sliceA = [];
226
+ sliceB = [];
227
+ patchA = -1;
228
+ }
229
+ };
230
+ for (let i = ops.length - 1; i >= 0; i--) {
231
+ const op = ops[i];
232
+ if (op === 0) {
233
+ flush();
234
+ currA++;
235
+ currB++;
236
+ }
237
+ else if (op === 1) {
238
+ if (patchA === -1) {
239
+ patchA = currA;
240
+ patchB = currB;
241
+ }
242
+ sliceB.push(B[currB++]);
243
+ }
244
+ else {
245
+ if (patchA === -1) {
246
+ patchA = currA;
247
+ patchB = currB;
248
+ }
249
+ sliceA.push(A[currA++]);
250
+ }
251
+ }
252
+ flush();
253
+ return patches;
254
+ }
255
+
256
+ var _a, _b;
257
+ const events = Symbol('events');
258
+ const hooks = Symbol('hooks');
259
+ const eventBehavior = {
260
+ on(eventOrEvents, cb) {
261
+ if (typeof eventOrEvents === 'object') {
262
+ for (const e of Object.keys(eventOrEvents)) {
263
+ this.on(e, eventOrEvents[e]);
264
+ }
265
+ }
266
+ else if (cb !== undefined) {
267
+ const callbacks = this[events].get(eventOrEvents) ?? new Set();
268
+ if (!callbacks.has(cb))
269
+ callbacks.add(cb);
270
+ this[events].set(eventOrEvents, callbacks);
271
+ }
272
+ return () => this.off(eventOrEvents, cb);
273
+ },
274
+ off(eventOrEvents, cb) {
275
+ if (typeof eventOrEvents === 'object') {
276
+ for (const e of Object.keys(eventOrEvents)) {
277
+ this.off(e, eventOrEvents[e]);
278
+ }
279
+ }
280
+ else if (cb !== null && cb !== undefined) {
281
+ const callbacks = this[events].get(eventOrEvents);
282
+ if (callbacks) {
283
+ callbacks.delete(cb);
284
+ if (!callbacks.size)
285
+ this[events].delete(eventOrEvents);
286
+ }
287
+ }
288
+ else {
289
+ // Remove all listeners for this event
290
+ this[events].delete(eventOrEvents);
291
+ }
292
+ },
293
+ emit(event, ...args) {
294
+ const callbacks = this[events].get(event);
295
+ if (callbacks)
296
+ for (const cb of callbacks)
297
+ cb.apply(this, args);
298
+ for (const cb of this[hooks])
299
+ cb.call(this, event, ...args);
300
+ },
301
+ };
302
+ function perEvent(eventful, fct, use) {
303
+ const cache = new Map();
304
+ return new Proxy(fct, {
305
+ get(target, prop) {
306
+ if (typeof prop !== 'string')
307
+ return target[prop];
308
+ if (use && !eventful[events].has(prop) && !eventful[hooks].size)
309
+ return () => { };
310
+ // Return cached function or create and cache
311
+ let cached = cache.get(prop);
312
+ if (!cached) {
313
+ cached = (...args) => fct.apply(eventful, [prop, ...args]);
314
+ cache.set(prop, cached);
315
+ }
316
+ return cached;
317
+ },
318
+ });
319
+ }
320
+ /**
321
+ * A type-safe event system that provides a clean API for event handling
322
+ * @template Events - The event map defining event names and their handler signatures
323
+ */
324
+ class Eventful {
325
+ constructor() {
326
+ this[_a] = new Map();
327
+ this[_b] = new Set();
328
+ this.on = perEvent(this, eventBehavior.on);
329
+ this.off = perEvent(this, eventBehavior.off);
330
+ this.emit = perEvent(this, eventBehavior.emit, 'use');
331
+ }
332
+ hook(cb) {
333
+ this[hooks].add(cb);
334
+ return () => {
335
+ this[hooks].delete(cb);
336
+ };
337
+ }
338
+ }
339
+ _a = events, _b = hooks;
340
+
341
+ /**
342
+ * Symbol for defining custom getter logic for numeric index access
343
+ */
344
+ const getAt = Symbol('getAt');
345
+ /**
346
+ * Symbol for defining custom setter logic for numeric index access
347
+ */
348
+ const setAt = Symbol('setAt');
349
+ function Indexable(base, accessor) {
350
+ if (base && typeof base !== 'function') {
351
+ accessor = base;
352
+ base = undefined;
353
+ }
354
+ if (!base) {
355
+ //@ts-expect-error
356
+ base = class {
357
+ };
358
+ }
359
+ if (!accessor) {
360
+ accessor = {
361
+ get(index) {
362
+ if (typeof this[getAt] !== 'function') {
363
+ throw new Error('Indexable class must have an [getAt] method');
364
+ }
365
+ return this[getAt](index);
366
+ },
367
+ set(index, value) {
368
+ if (typeof this[setAt] !== 'function') {
369
+ throw new Error('Indexable class has read-only numeric index access');
370
+ }
371
+ this[setAt](index, value);
372
+ },
373
+ };
374
+ }
375
+ class Indexable extends base {
376
+ }
377
+ Object.setPrototypeOf(Indexable.prototype, new Proxy(base.prototype, {
378
+ //@ts-expect-error
379
+ [Symbol.toStringTag]: 'MutTs Indexable',
380
+ get(target, prop, receiver) {
381
+ if (prop in target) {
382
+ const getter = Object.getOwnPropertyDescriptor(target, prop)?.get;
383
+ return getter ? getter.call(receiver) : target[prop];
384
+ }
385
+ if (typeof prop === 'string') {
386
+ if (prop === 'length' && accessor.getLength)
387
+ return accessor.getLength.call(receiver);
388
+ const numProp = Number(prop);
389
+ if (!Number.isNaN(numProp)) {
390
+ return accessor.get.call(receiver, numProp);
391
+ }
392
+ }
393
+ return undefined;
394
+ },
395
+ set(target, prop, value, receiver) {
396
+ if (prop in target) {
397
+ const setter = Object.getOwnPropertyDescriptor(target, prop)?.set;
398
+ if (setter)
399
+ setter.call(receiver, value);
400
+ else
401
+ target[prop] = value;
402
+ return true;
403
+ }
404
+ if (typeof prop === 'string') {
405
+ if (prop === 'length' && accessor.setLength) {
406
+ accessor.setLength.call(receiver, value);
407
+ return true;
408
+ }
409
+ const numProp = Number(prop);
410
+ if (!Number.isNaN(numProp)) {
411
+ if (!accessor.set)
412
+ throw new Error('Indexable class has read-only numeric index access');
413
+ accessor.set.call(receiver, numProp, value);
414
+ return true;
415
+ }
416
+ }
417
+ Object.defineProperty(receiver, prop, {
418
+ value,
419
+ writable: true,
420
+ enumerable: true,
421
+ configurable: true,
422
+ });
423
+ return true;
424
+ },
425
+ has(target, prop) {
426
+ if (prop in target)
427
+ return true;
428
+ if (typeof prop === 'string') {
429
+ if (prop === 'length' && accessor.getLength)
430
+ return true;
431
+ const numProp = Number(prop);
432
+ if (!Number.isNaN(numProp))
433
+ return true;
434
+ }
435
+ return false;
436
+ },
437
+ ownKeys(target) {
438
+ const keys = Reflect.ownKeys(target);
439
+ if (accessor.getLength) {
440
+ keys.push('length');
441
+ const len = accessor.getLength.call(this);
442
+ for (let i = 0; i < len; i++)
443
+ keys.push(String(i));
444
+ }
445
+ return keys;
446
+ },
447
+ getOwnPropertyDescriptor(target, prop) {
448
+ if (prop in target)
449
+ return Object.getOwnPropertyDescriptor(target, prop);
450
+ if (typeof prop === 'string') {
451
+ if (prop === 'length' && accessor.getLength) {
452
+ return {
453
+ enumerable: false,
454
+ configurable: true,
455
+ get: () => accessor.getLength.call(this),
456
+ };
457
+ }
458
+ const numProp = Number(prop);
459
+ if (!Number.isNaN(numProp)) {
460
+ return {
461
+ enumerable: true,
462
+ configurable: true,
463
+ get: () => accessor.get.call(this, numProp),
464
+ set: accessor.set
465
+ ? (v) => accessor.set.call(this, numProp, v)
466
+ : undefined,
467
+ };
468
+ }
469
+ }
470
+ return undefined;
471
+ },
472
+ }));
473
+ return Indexable;
474
+ }
475
+ /**
476
+ * Symbol for accessing the forwarded array in ArrayReadForward
477
+ */
478
+ const forwardArray = Symbol('forwardArray');
479
+ /**
480
+ * A read-only array forwarder that implements all reading/iterating methods of Array
481
+ * but does not implement modification methods.
482
+ *
483
+ * The constructor takes a callback that returns an array, and all methods forward
484
+ * their behavior to the result of that callback.
485
+ */
486
+ class ArrayReadForward {
487
+ get [forwardArray]() {
488
+ throw new Error('ArrayReadForward is not implemented');
489
+ }
490
+ /**
491
+ * Get the length of the array
492
+ */
493
+ get length() {
494
+ return this[forwardArray].length;
495
+ }
496
+ /**
497
+ * Iterator protocol support
498
+ */
499
+ [Symbol.iterator]() {
500
+ return this[forwardArray][Symbol.iterator]();
501
+ }
502
+ // Reading/Iterating methods
503
+ /**
504
+ * Creates a new array with the results of calling a provided function on every element
505
+ */
506
+ map(callbackfn, thisArg) {
507
+ return this[forwardArray].map(callbackfn, thisArg);
508
+ }
509
+ filter(predicate, thisArg) {
510
+ return this[forwardArray].filter(predicate, thisArg);
511
+ }
512
+ reduce(callbackfn, initialValue) {
513
+ return initialValue !== undefined
514
+ ? this[forwardArray].reduce(callbackfn, initialValue)
515
+ : this[forwardArray].reduce(callbackfn);
516
+ }
517
+ reduceRight(callbackfn, initialValue) {
518
+ return initialValue !== undefined
519
+ ? this[forwardArray].reduceRight(callbackfn, initialValue)
520
+ : this[forwardArray].reduceRight(callbackfn);
521
+ }
522
+ /**
523
+ * Executes a provided function once for each array element
524
+ */
525
+ forEach(callbackfn, thisArg) {
526
+ this[forwardArray].forEach(callbackfn, thisArg);
527
+ }
528
+ find(predicate, thisArg) {
529
+ return this[forwardArray].find(predicate, thisArg);
530
+ }
531
+ /**
532
+ * Returns the index of the first element in the array that satisfies the provided testing function
533
+ */
534
+ findIndex(predicate, thisArg) {
535
+ return this[forwardArray].findIndex(predicate, thisArg);
536
+ }
537
+ findLast(predicate, thisArg) {
538
+ return this[forwardArray].findLast(predicate, thisArg);
539
+ }
540
+ /**
541
+ * Returns the index of the last element in the array that satisfies the provided testing function
542
+ */
543
+ findLastIndex(predicate, thisArg) {
544
+ return this[forwardArray].findLastIndex(predicate, thisArg);
545
+ }
546
+ /**
547
+ * Determines whether an array includes a certain value among its entries
548
+ */
549
+ includes(searchElement, fromIndex) {
550
+ return this[forwardArray].includes(searchElement, fromIndex);
551
+ }
552
+ /**
553
+ * Returns the first index at which a given element can be found in the array
554
+ */
555
+ indexOf(searchElement, fromIndex) {
556
+ return this[forwardArray].indexOf(searchElement, fromIndex);
557
+ }
558
+ /**
559
+ * Returns the last index at which a given element can be found in the array
560
+ */
561
+ lastIndexOf(searchElement, fromIndex) {
562
+ return this[forwardArray].lastIndexOf(searchElement, fromIndex);
563
+ }
564
+ /**
565
+ * Returns a shallow copy of a portion of an array into a new array object
566
+ */
567
+ slice(start, end) {
568
+ return this[forwardArray].slice(start, end);
569
+ }
570
+ concat(...items) {
571
+ return this[forwardArray].concat(...items);
572
+ }
573
+ /**
574
+ * Tests whether all elements in the array pass the test implemented by the provided function
575
+ */
576
+ every(predicate, thisArg) {
577
+ return this[forwardArray].every(predicate, thisArg);
578
+ }
579
+ /**
580
+ * Tests whether at least one element in the array passes the test implemented by the provided function
581
+ */
582
+ some(predicate, thisArg) {
583
+ return this[forwardArray].some(predicate, thisArg);
584
+ }
585
+ /**
586
+ * Joins all elements of an array into a string
587
+ */
588
+ join(separator) {
589
+ return this[forwardArray].join(separator);
590
+ }
591
+ /**
592
+ * Returns a new array iterator that contains the keys for each index in the array
593
+ */
594
+ keys() {
595
+ return this[forwardArray].keys();
596
+ }
597
+ /**
598
+ * Returns a new array iterator that contains the values for each index in the array
599
+ */
600
+ values() {
601
+ return this[forwardArray].values();
602
+ }
603
+ /**
604
+ * Returns a new array iterator that contains the key/value pairs for each index in the array
605
+ */
606
+ entries() {
607
+ return this[forwardArray].entries();
608
+ }
609
+ /**
610
+ * Returns a string representation of the array
611
+ */
612
+ toString() {
613
+ return this[forwardArray].toString();
614
+ }
615
+ /**
616
+ * Returns a localized string representing the array
617
+ */
618
+ toLocaleString(locales, options) {
619
+ return this[forwardArray].toLocaleString(locales, options);
620
+ }
621
+ /**
622
+ * Returns the element at the specified index, or undefined if the index is out of bounds
623
+ */
624
+ at(index) {
625
+ return this[forwardArray].at(index);
626
+ }
627
+ /**
628
+ * Returns a new array with all sub-array elements concatenated into it recursively up to the specified depth
629
+ */
630
+ flat(depth) {
631
+ return this[forwardArray].flat(depth);
632
+ }
633
+ /**
634
+ * Returns a new array formed by applying a given callback function to each element of the array,
635
+ * and then flattening the result by one level
636
+ */
637
+ flatMap(callback, thisArg) {
638
+ return this[forwardArray].flatMap(callback, thisArg);
639
+ }
640
+ /**
641
+ * Returns a new array with elements in reversed order (ES2023)
642
+ */
643
+ toReversed() {
644
+ return this[forwardArray].toReversed?.() ?? [...this[forwardArray]].reverse();
645
+ }
646
+ /**
647
+ * Returns a new array with elements sorted (ES2023)
648
+ */
649
+ toSorted(compareFn) {
650
+ return this[forwardArray].toSorted?.(compareFn) ?? [...this[forwardArray]].sort(compareFn);
651
+ }
652
+ /**
653
+ * Returns a new array with some elements removed and/or replaced at a given index (ES2023)
654
+ */
655
+ toSpliced(start, deleteCount, ...items) {
656
+ if (deleteCount === undefined)
657
+ return this[forwardArray].toSpliced(start);
658
+ return this[forwardArray].toSpliced(start, deleteCount, ...items);
659
+ }
660
+ /**
661
+ * Returns a new array with the element at the given index replaced with the given value (ES2023)
662
+ */
663
+ with(index, value) {
664
+ return this[forwardArray].with(index, value);
665
+ }
666
+ get [Symbol.unscopables]() {
667
+ return this[forwardArray][Symbol.unscopables];
668
+ }
669
+ }
670
+
671
+ const forward = (name, target) => (...args) => {
672
+ return target[name](...args);
673
+ };
674
+ const alreadyChained = new WeakMap();
675
+ const originals = new WeakMap();
676
+ function cache$1(target, rv) {
677
+ originals.set(rv, target);
678
+ alreadyChained.set(target, rv);
679
+ }
680
+ const promiseProxyHandler = {
681
+ //@ts-expect-error
682
+ [Symbol.toStringTag]: 'MutTs PromiseChain function',
683
+ get(target, prop) {
684
+ if (prop === Symbol.toStringTag)
685
+ return 'PromiseProxy';
686
+ if (typeof prop === 'string' && ['then', 'catch', 'finally'].includes(prop))
687
+ return target[prop];
688
+ return chainPromise(target.then((r) => r[prop]));
689
+ },
690
+ };
691
+ const promiseForward = (target) => ({
692
+ // biome-ignore lint/suspicious/noThenProperty: This one is the whole point
693
+ then: forward('then', target),
694
+ catch: forward('catch', target),
695
+ finally: forward('finally', target),
696
+ });
697
+ const objectProxyHandler = {
698
+ //@ts-expect-error
699
+ [Symbol.toStringTag]: 'MutTs PromiseChain object',
700
+ get(target, prop, receiver) {
701
+ const getter = Object.getOwnPropertyDescriptor(target, prop)?.get;
702
+ const rv = getter ? getter.call(receiver) : target[prop];
703
+ // Allows fct.call or fct.apply to bypass the chain system
704
+ if (typeof target === 'function')
705
+ return rv;
706
+ return chainPromise(rv);
707
+ },
708
+ apply(target, thisArg, args) {
709
+ return chainPromise(target.apply(thisArg, args));
710
+ },
711
+ };
712
+ function chainObject(given) {
713
+ const rv = new Proxy(given, objectProxyHandler);
714
+ cache$1(given, rv);
715
+ return rv;
716
+ }
717
+ function chainable(x) {
718
+ return x && ['function', 'object'].includes(typeof x);
719
+ }
720
+ /**
721
+ * Transforms a promise or value into a chainable object
722
+ * Allows calling methods directly on promise results without awaiting them first
723
+ * @param given - The promise or value to make chainable
724
+ * @returns A chainable version of the input
725
+ */
726
+ function chainPromise(given) {
727
+ if (!chainable(given))
728
+ return given;
729
+ if (alreadyChained.has(given))
730
+ return alreadyChained.get(given);
731
+ if (!(given instanceof Promise))
732
+ return chainObject(given);
733
+ // @ts-expect-error It's ok as we check if it's an object above
734
+ given = given.then((r) => (chainable(r) ? chainObject(r) : r));
735
+ const target = Object.assign(function (...args) {
736
+ return chainPromise(given.then((r) => {
737
+ return this?.then
738
+ ? this.then((t) => r.apply(t, args))
739
+ : r.apply(this, args);
740
+ }));
741
+ }, promiseForward(given));
742
+ const chained = new Proxy(target, promiseProxyHandler);
743
+ cache$1(given, chained);
744
+ return chained;
745
+ }
746
+
747
+ function attend(source, callback) {
748
+ const enumerate = typeof source === 'function'
749
+ ? source
750
+ : Array.isArray(source)
751
+ ? () => Array.from({ length: source.length }, (_, i) => i)
752
+ : source instanceof Map
753
+ ? () => source.keys()
754
+ : source instanceof Set
755
+ ? () => source.values()
756
+ : () => Object.keys(source);
757
+ const keyEffects = new Map();
758
+ const outer = effect.named('attend')(({ ascend }) => {
759
+ const keys = new Set();
760
+ for (const key of enumerate())
761
+ keys.add(key);
762
+ for (const key of keys) {
763
+ if (keyEffects.has(key))
764
+ continue;
765
+ keyEffects.set(key, ascend(() => effect.named(`attend:${key}`)((access) => callback(key, access))));
766
+ }
767
+ for (const key of Array.from(keyEffects.keys())) {
768
+ if (!keys.has(key)) {
769
+ keyEffects.get(key)();
770
+ keyEffects.delete(key);
771
+ }
772
+ }
773
+ });
774
+ return (reason) => {
775
+ outer(reason);
776
+ for (const stop of keyEffects.values())
777
+ stop(reason);
778
+ keyEffects.clear();
779
+ };
780
+ }
781
+ function lift(cb) {
782
+ let result;
783
+ let rawResult;
784
+ const liftCleanup = effect.named(`lift:${cb.name}`)(markWithRoot((access) => {
785
+ const source = cb(access);
786
+ if (!source || typeof source !== 'object')
787
+ throw new Error('lift callback must return an array or object');
788
+ const sourceProto = Object.getPrototypeOf(source);
789
+ if (!result) {
790
+ rawResult = Array.isArray(source) ? [] : Object.create(sourceProto);
791
+ result = reactive(rawResult);
792
+ }
793
+ if (sourceProto !== Object.getPrototypeOf(result))
794
+ throw new Error('lift callback must return the same type as the previous result');
795
+ if (Array.isArray(source)) {
796
+ const res = result;
797
+ for (const { indexA, sliceA, sliceB } of arrayDiff(res, source).sort((a, b) => a.indexA - b.indexA))
798
+ res.splice(indexA, sliceA.length, ...sliceB);
799
+ }
800
+ else {
801
+ for (const key of Object.keys(source)) {
802
+ const had = key in rawResult;
803
+ const newDesc = Object.getOwnPropertyDescriptor(source, key);
804
+ if (had) {
805
+ const oldDesc = Object.getOwnPropertyDescriptor(rawResult, key);
806
+ const sameAccessor = oldDesc && newDesc.get && oldDesc.get === newDesc.get;
807
+ Object.defineProperty(rawResult, key, newDesc);
808
+ if (!sameAccessor &&
809
+ rawResult[key] !==
810
+ (oldDesc ? (oldDesc.get ? oldDesc.get() : oldDesc.value) : undefined))
811
+ touched1(rawResult, { type: 'set', prop: key }, key);
812
+ }
813
+ else {
814
+ Object.defineProperty(rawResult, key, newDesc);
815
+ touched1(rawResult, { type: 'add', prop: key }, key);
816
+ }
817
+ }
818
+ for (const key of Object.keys(rawResult))
819
+ if (!(key in source)) {
820
+ delete rawResult[key];
821
+ touched1(rawResult, { type: 'del', prop: key }, key);
822
+ }
823
+ }
824
+ }, cb));
825
+ return link(result, liftCleanup);
826
+ }
827
+ /**
828
+ * Reactively maps an array source through `fn`, producing a lazy reactive output array.
829
+ *
830
+ * Each source item gets its own isolated effect (via `root()`) so that changes to one item
831
+ * only recompute that item's projection. Structural changes (push, splice, reorder) are detected
832
+ * via `arrayDiff` and surgically applied to the output cache.
833
+ *
834
+ * The source can be a reactive array or a function returning an array. When a function is provided,
835
+ * the function is re-evaluated inside an effect whenever its dependencies change.
836
+ *
837
+ * Output elements are computed lazily — accessing `result[i]` triggers computation if not yet cached.
838
+ *
839
+ * @param source - A reactive array or a function returning an array
840
+ * @param fn - Mapping function applied to each element
841
+ * @param options - Optional purity hints to skip per-item effects
842
+ * @returns A readonly reactive array with a `[cleanup]` method to dispose all effects
843
+ */
844
+ function morphArray(source, fn, options) {
845
+ if (typeof source !== 'function' && !isReactive(source) && options?.pure === true) {
846
+ return source.map((i) => fn(i));
847
+ }
848
+ let track;
849
+ const itemEffects = new Map();
850
+ const cache = [];
851
+ let input = [];
852
+ function stopItem(key) {
853
+ const entry = itemEffects.get(key);
854
+ if (entry) {
855
+ entry.stop({ type: 'stopped' });
856
+ itemEffects.delete(key);
857
+ }
858
+ }
859
+ function computeItem(key, input) {
860
+ const isPure = options?.pure === true || (typeof options?.pure === 'function' && options.pure(input));
861
+ if (isPure) {
862
+ track(() => {
863
+ cache[key] = fn(input);
864
+ });
865
+ }
866
+ else {
867
+ const indexRef = { value: key };
868
+ const stop = track(() => effect.named(`morph:${fn.name}:${key}`).opaque((access) => {
869
+ cache[indexRef.value] = fn(input, access);
870
+ return (reason) => {
871
+ delete cache[indexRef.value];
872
+ touched1(cache, { type: 'invalidate', prop: 'morph' }, String(key));
873
+ stop?.({ type: 'invalidate', cause: reason });
874
+ };
875
+ }));
876
+ itemEffects.set(key, { stop, index: indexRef });
877
+ }
878
+ }
879
+ const proxy = reactive(cache, {
880
+ get(cache, prop) {
881
+ const n = typeof prop === 'string' ? Number(prop) : NaN;
882
+ if (Number.isNaN(n))
883
+ return cache[prop];
884
+ if (!(n in cache))
885
+ computeItem(n, input[n]);
886
+ return cache[n];
887
+ },
888
+ has(_cache, prop) {
889
+ return Reflect.has(input, prop);
890
+ },
891
+ });
892
+ const stopMain = effect.named(`morph:${fn.name}`)(({ ascend }) => {
893
+ track = ascend;
894
+ const newInput = [...(typeof source === 'function' ? source() : source)];
895
+ const diffs = arrayDiff(input, newInput).toSorted((a, b) => b.indexA - a.indexA);
896
+ if (diffs.length > 0) {
897
+ for (const diff of diffs) {
898
+ // Stop items in removed range
899
+ for (let i = diff.indexA; i < diff.indexA + diff.sliceA.length; i++)
900
+ stopItem(i);
901
+ // Shift existing itemEffects in the Map to match the new indices
902
+ const shift = diff.sliceB.length - diff.sliceA.length;
903
+ if (shift !== 0) {
904
+ // We need to move entries in the Map.
905
+ const entries = Array.from(itemEffects.entries()).sort((a, b) => a[0] - b[0]);
906
+ // Remove entries that will be shifted
907
+ for (const [idx, _entry] of entries) {
908
+ if (idx >= diff.indexA + diff.sliceA.length) {
909
+ itemEffects.delete(idx);
910
+ }
911
+ }
912
+ // Re-add them with shifted indices
913
+ for (const [idx, entry] of entries) {
914
+ if (idx >= diff.indexA + diff.sliceA.length) {
915
+ const newIdx = idx + shift;
916
+ entry.index.value = newIdx;
917
+ itemEffects.set(newIdx, entry);
918
+ }
919
+ }
920
+ }
921
+ // Splice the cache
922
+ cache.splice(diff.indexA, diff.sliceA.length, ...new Array(diff.sliceB.length).fill(undefined));
923
+ // Make holes for lazy computation
924
+ for (let i = diff.indexA; i < diff.indexA + diff.sliceB.length; i++)
925
+ delete cache[i];
926
+ }
927
+ const invalidates = new Set([keysOf]);
928
+ if (input.length !== newInput.length)
929
+ invalidates.add('length');
930
+ for (const diff of diffs) {
931
+ const max = Math.max(diff.sliceA.length, diff.sliceB.length);
932
+ for (let i = 0; i < max; i++)
933
+ invalidates.add(String(diff.indexA + i));
934
+ }
935
+ touched(cache, { type: 'bunch', method: 'morph-input' }, invalidates);
936
+ }
937
+ input = newInput;
938
+ });
939
+ return link(proxy, (reason) => {
940
+ stopMain(reason);
941
+ for (const entry of itemEffects.values())
942
+ entry.stop(reason);
943
+ itemEffects.clear();
944
+ });
945
+ }
946
+ /**
947
+ * Reactively maps a `Map` source through `fn`, producing a reactive output Map.
948
+ *
949
+ * Each key gets its own isolated effect so that value changes for one key only recompute
950
+ * that key's projection. Key additions and removals are tracked via `keysOf` dependency.
951
+ *
952
+ * @param source - A reactive Map
953
+ * @param fn - Mapping function applied to each value
954
+ * @param options - Optional purity hints to skip per-key effects
955
+ * @returns A reactive Map with a `[cleanup]` method to dispose all effects
956
+ */
957
+ function morphMap(source, fn, options) {
958
+ if (!isReactive(source) && options?.pure === true) {
959
+ const res = new Map();
960
+ for (const [k, v] of source)
961
+ res.set(k, fn(v, k));
962
+ return res;
963
+ }
964
+ let track;
965
+ const itemEffects = new Map();
966
+ const cache = new Map();
967
+ Object.defineProperty(cache, 'constructor', { value: Object, enumerable: false });
968
+ function stopItem(key) {
969
+ const stop = itemEffects.get(key);
970
+ if (stop) {
971
+ stop({ type: 'stopped' });
972
+ itemEffects.delete(key);
973
+ }
974
+ }
975
+ function computeItem(key, val) {
976
+ const isPure = options?.pure === true || (typeof options?.pure === 'function' && options.pure(val));
977
+ if (isPure) {
978
+ cache.set(key, track(() => fn(val, key)));
979
+ }
980
+ else {
981
+ const stop = track(() => effect.named(`morph:${fn.name}:${key}`).opaque((access) => {
982
+ cache.set(key, fn(source.get(key), key, access));
983
+ return (reason) => {
984
+ cache.delete(key);
985
+ touched1(cache, { type: 'invalidate', prop: 'morph' }, String(key));
986
+ stop?.({ type: 'invalidate', cause: reason });
987
+ };
988
+ }));
989
+ itemEffects.set(key, stop);
990
+ }
991
+ }
992
+ const proxy = reactive(cache, {
993
+ get(cache, prop) {
994
+ if (prop === 'get')
995
+ return (key) => {
996
+ if (!cache.has(key) && source.has(key))
997
+ computeItem(key, source.get(key));
998
+ return cache.get(key);
999
+ };
1000
+ if (prop === 'has')
1001
+ return (key) => {
1002
+ return source.has(key);
1003
+ };
1004
+ if (prop === 'keys')
1005
+ return () => {
1006
+ return source.keys();
1007
+ };
1008
+ if (prop === 'values')
1009
+ return function* () {
1010
+ for (const key of source.keys()) {
1011
+ yield proxy.get(key);
1012
+ }
1013
+ };
1014
+ if (prop === 'entries')
1015
+ return function* () {
1016
+ for (const key of source.keys()) {
1017
+ yield [key, proxy.get(key)];
1018
+ }
1019
+ };
1020
+ if (prop === Symbol.iterator)
1021
+ return function* () {
1022
+ for (const key of source.keys()) {
1023
+ yield [key, proxy.get(key)];
1024
+ }
1025
+ };
1026
+ return cache[prop];
1027
+ },
1028
+ });
1029
+ let stateSnapshot = getState(source);
1030
+ const stopMain = effect.named(`morph:${fn.name}`)(({ ascend }) => {
1031
+ track = ascend;
1032
+ dependant(source, keysOf);
1033
+ while ('evolution' in stateSnapshot) {
1034
+ const { evolution } = stateSnapshot;
1035
+ stateSnapshot = stateSnapshot.next;
1036
+ if (evolution.type === 'add') {
1037
+ touched1(cache, evolution, evolution.prop);
1038
+ }
1039
+ else if (evolution.type === 'del') {
1040
+ stopItem(evolution.prop);
1041
+ cache.delete(evolution.prop);
1042
+ touched1(cache, evolution, evolution.prop);
1043
+ }
1044
+ }
1045
+ });
1046
+ return link(proxy, (reason) => {
1047
+ stopMain(reason);
1048
+ for (const stop of itemEffects.values())
1049
+ stop(reason);
1050
+ itemEffects.clear();
1051
+ });
1052
+ }
1053
+ /**
1054
+ * Reactively maps a record/object source through `fn`, producing a reactive output record.
1055
+ *
1056
+ * Each key gets its own isolated effect so that value changes for one key only recompute
1057
+ * that key's projection. Key additions and removals are tracked automatically.
1058
+ *
1059
+ * @param source - A reactive record
1060
+ * @param fn - Mapping function applied to each value
1061
+ * @param options - Optional purity hints to skip per-key effects
1062
+ * @returns A reactive record with a `[cleanup]` method to dispose all effects
1063
+ */
1064
+ function morphRecord(source, fn, options) {
1065
+ if (!isReactive(source) && options?.pure === true) {
1066
+ const res = {};
1067
+ for (const k of Object.keys(source))
1068
+ res[k] = fn(source[k], k);
1069
+ return res;
1070
+ }
1071
+ let track;
1072
+ const itemEffects = new Map();
1073
+ const cache = {};
1074
+ function stopItem(key) {
1075
+ const stop = itemEffects.get(key);
1076
+ if (stop) {
1077
+ stop({ type: 'stopped' });
1078
+ itemEffects.delete(key);
1079
+ }
1080
+ }
1081
+ function computeItem(key, val) {
1082
+ const isPure = options?.pure === true || (typeof options?.pure === 'function' && options.pure(val));
1083
+ if (isPure) {
1084
+ cache[key] = track(() => fn(val, key));
1085
+ }
1086
+ else {
1087
+ const stop = track(() => effect.named(`morph:${fn.name}:${key}`).opaque((access) => {
1088
+ cache[key] = fn(source[key], key, access);
1089
+ return (reason) => {
1090
+ delete cache[key];
1091
+ touched1(cache, { type: 'invalidate', prop: 'morph' }, String(key));
1092
+ stop?.({ type: 'invalidate', cause: reason });
1093
+ };
1094
+ }));
1095
+ itemEffects.set(key, stop);
1096
+ }
1097
+ }
1098
+ function get(prop) {
1099
+ if (!(prop in cache) && prop in source)
1100
+ computeItem(prop, source[prop]);
1101
+ return cache[prop];
1102
+ }
1103
+ const proxy = reactive(cache, {
1104
+ get(_, prop) {
1105
+ return get(prop);
1106
+ },
1107
+ has(_, prop) {
1108
+ return prop in source;
1109
+ },
1110
+ ownKeys() {
1111
+ return Reflect.ownKeys(source);
1112
+ },
1113
+ getOwnPropertyDescriptor(_cache, prop) {
1114
+ if (prop in source)
1115
+ return { configurable: true, enumerable: true, get: () => get(prop) };
1116
+ },
1117
+ });
1118
+ let stateSnapshot = getState(source);
1119
+ const stopMain = effect.named(`morph:${fn.name}`)(({ ascend }) => {
1120
+ track = ascend;
1121
+ // Track only structural changes on source
1122
+ dependant(source, keysOf);
1123
+ while ('evolution' in stateSnapshot) {
1124
+ const { evolution } = stateSnapshot;
1125
+ stateSnapshot = stateSnapshot.next;
1126
+ if (evolution.type === 'add') {
1127
+ touched1(cache, evolution, evolution.prop);
1128
+ }
1129
+ else if (evolution.type === 'del') {
1130
+ stopItem(evolution.prop);
1131
+ delete cache[evolution.prop];
1132
+ touched1(cache, evolution, evolution.prop);
1133
+ }
1134
+ }
1135
+ });
1136
+ return link(proxy, (reason) => {
1137
+ stopMain(reason);
1138
+ for (const stop of itemEffects.values())
1139
+ stop(reason);
1140
+ itemEffects.clear();
1141
+ });
1142
+ }
1143
+ /**
1144
+ * Reactively maps a collection (array, Map, or record) through a per-entry function.
1145
+ *
1146
+ * Each entry in the source gets its own reactive context — when only one entry's dependencies
1147
+ * change, only that entry's projection recomputes. Structural changes (additions, removals,
1148
+ * reorders) are detected via diffing and applied surgically.
1149
+ *
1150
+ * Use `morph.pure(source, fn)` when `fn` has no reactive reads (skips per-item effects).
1151
+ *
1152
+ * @example
1153
+ * ```ts
1154
+ * const users = reactive([{ name: 'John' }, { name: 'Jane' }])
1155
+ * const names = morph(users, u => u.name.toUpperCase())
1156
+ * // names[0] = 'JOHN', names[1] = 'JANE'
1157
+ * // Changing users[0].name only recomputes names[0]
1158
+ * ```
1159
+ */
1160
+ const morph = flavored(function morph(source, fn, options) {
1161
+ if (Array.isArray(source) || typeof source === 'function')
1162
+ return morphArray(source, fn, options);
1163
+ if (source instanceof Map)
1164
+ return morphMap(source, fn, options);
1165
+ return morphRecord(source, fn, options);
1166
+ }, {
1167
+ get pure() {
1168
+ return (source, fn, _opt) => this(source, fn, { pure: true });
1169
+ },
1170
+ });
1171
+
1172
+ /**
1173
+ * Deep watch an object and all its nested properties
1174
+ * @param target - The object to watch deeply
1175
+ * @param callback - The callback to call when any nested property changes
1176
+ * @param options - Options for the deep watch
1177
+ * @returns A cleanup function to stop watching
1178
+ */
1179
+ /**
1180
+ * Sets up deep watching for an object, tracking all nested property changes
1181
+ * @param target - The object to watch
1182
+ * @param callback - The callback to call when changes occur
1183
+ * @param options - Options for deep watching
1184
+ * @returns A cleanup function to stop deep watching
1185
+ */
1186
+ function deepWatch(target, callback, { immediate = false } = {}) {
1187
+ if (target === null || target === undefined)
1188
+ return undefined;
1189
+ if (typeof target !== 'object')
1190
+ throw new Error('Target of deep watching must be an object');
1191
+ // Create a wrapper callback that matches EffectTrigger signature
1192
+ const wrappedCallback = markWithRoot((() => callback(target)), callback);
1193
+ registerDeepWatcher();
1194
+ // Use the existing effect system to register dependencies
1195
+ return effect.named('deepWatch')(() => {
1196
+ // Mark the target object as having deep watchers
1197
+ objectsWithDeepWatchers.add(target);
1198
+ // Track which objects this effect is watching for cleanup
1199
+ let effectObjects = effectToDeepWatchedObjects.get(wrappedCallback);
1200
+ if (!effectObjects) {
1201
+ effectObjects = new Set();
1202
+ effectToDeepWatchedObjects.set(wrappedCallback, effectObjects);
1203
+ }
1204
+ effectObjects.add(target);
1205
+ // Traverse the object graph and register dependencies
1206
+ // This will re-run every time the effect runs, ensuring we catch all changes
1207
+ const visited = new WeakSet();
1208
+ function traverseAndTrack(obj, depth = 0) {
1209
+ // Prevent infinite recursion and excessive depth
1210
+ if (!obj || visited.has(obj) || typeof obj !== 'object' || depth > options.maxDeepWatchDepth)
1211
+ return;
1212
+ // Do not traverse into unreactive objects
1213
+ if (isNonReactive(obj))
1214
+ return;
1215
+ visited.add(obj);
1216
+ // Mark this object as having deep watchers
1217
+ objectsWithDeepWatchers.add(obj);
1218
+ effectObjects.add(obj);
1219
+ // Traverse all properties to register dependencies
1220
+ // unwrap to avoid kicking dependency
1221
+ for (const key in unwrap(obj)) {
1222
+ if (Object.hasOwn(obj, key)) {
1223
+ // Access the property to register dependency
1224
+ const value = obj[key];
1225
+ // Make the value reactive if it's an object
1226
+ const reactiveValue = typeof value === 'object' && value !== null ? reactive(value) : value;
1227
+ traverseAndTrack(reactiveValue, depth + 1);
1228
+ }
1229
+ }
1230
+ // Also handle array indices and length
1231
+ // Handle arrays and collections using iterators to ensure proxy tracking is triggered
1232
+ if (typeof obj[Symbol.iterator] === 'function') {
1233
+ // Access the iterator to track additions/removals/collection changes
1234
+ for (const value of obj) {
1235
+ // Make the value reactive if it's an object
1236
+ const reactiveValue = typeof value === 'object' && value !== null ? reactive(value) : value;
1237
+ traverseAndTrack(reactiveValue, depth + 1);
1238
+ }
1239
+ // Explicitly depend on length so array mutations changing count trigger re-evaluation
1240
+ if ('length' in obj) {
1241
+ dependant(obj, 'length');
1242
+ }
1243
+ // For Maps, also ensure we track values explicitly if the iterator yields entries
1244
+ if (obj instanceof Map) {
1245
+ for (const value of obj.values()) {
1246
+ const reactiveValue = typeof value === 'object' && value !== null ? reactive(value) : value;
1247
+ traverseAndTrack(reactiveValue, depth + 1);
1248
+ }
1249
+ }
1250
+ }
1251
+ // Note: WeakSet and WeakMap cannot be iterated, so we can't deep watch their contents
1252
+ // They will only trigger when the collection itself is replaced
1253
+ }
1254
+ // Traverse the target object to register all dependencies
1255
+ // This will register dependencies on all current properties and array elements
1256
+ traverseAndTrack(target);
1257
+ // Only call the callback if immediate is true or if it's not the first run
1258
+ if (immediate) {
1259
+ untracked(() => callback(target));
1260
+ }
1261
+ immediate = true;
1262
+ // Return a cleanup function that properly removes deep watcher tracking
1263
+ return () => {
1264
+ // Get the objects this effect was watching
1265
+ const effectObjects = effectToDeepWatchedObjects.get(wrappedCallback);
1266
+ if (effectObjects) {
1267
+ // Remove deep watcher tracking from all objects this effect was watching
1268
+ for (const obj of effectObjects) {
1269
+ // Check if this object still has other deep watchers
1270
+ const watchers = deepWatchers.get(obj);
1271
+ if (watchers) {
1272
+ // Remove this effect's callback from the watchers
1273
+ watchers.delete(wrappedCallback);
1274
+ // If no more watchers, remove the object from deep watchers tracking
1275
+ if (watchers.size === 0) {
1276
+ deepWatchers.delete(obj);
1277
+ objectsWithDeepWatchers.delete(obj);
1278
+ }
1279
+ }
1280
+ else {
1281
+ // No watchers found, remove from deep watchers tracking
1282
+ objectsWithDeepWatchers.delete(obj);
1283
+ }
1284
+ }
1285
+ // Clean up the tracking data
1286
+ effectToDeepWatchedObjects.delete(wrappedCallback);
1287
+ }
1288
+ };
1289
+ });
1290
+ }
1291
+
1292
+ const memoizedRegistry = new WeakMap();
1293
+ const wrapperRegistry = new WeakMap();
1294
+ function getBranch(tree, key) {
1295
+ tree.branches ?? (tree.branches = new WeakMap());
1296
+ let branch = tree.branches.get(key);
1297
+ if (!branch) {
1298
+ branch = {};
1299
+ tree.branches.set(key, branch);
1300
+ }
1301
+ return branch;
1302
+ }
1303
+ function memoizeFunction(fn, opts) {
1304
+ const fnRoot = getRoot(fn);
1305
+ const existing = memoizedRegistry.get(fnRoot);
1306
+ if (existing)
1307
+ return existing;
1308
+ const cacheRoot = {};
1309
+ const memoized = markWithRoot(function memoized(...args) {
1310
+ if (args.some((arg) => !(arg && ['object', 'symbol', 'function'].includes(typeof arg)))) {
1311
+ if (opts?.lenient)
1312
+ return fn.apply(this, args);
1313
+ throw new Error('memoize expects non-null object arguments');
1314
+ }
1315
+ let node = cacheRoot;
1316
+ // Note: decorators add `this` as first argument
1317
+ for (const arg of args) {
1318
+ node = getBranch(node, arg);
1319
+ }
1320
+ dependant(node, 'memoize');
1321
+ if ('result' in node) {
1322
+ if (options.onMemoizationDiscrepancy) {
1323
+ const wasVerification = options.isVerificationRun;
1324
+ options.isVerificationRun = true;
1325
+ try {
1326
+ const fresh = untracked(() => fn.apply(this, args));
1327
+ if (!deepCompare(node.result, fresh)) {
1328
+ optionCall('onMemoizationDiscrepancy', node.result, fresh, fn, args, 'calculation');
1329
+ }
1330
+ }
1331
+ finally {
1332
+ options.isVerificationRun = wasVerification;
1333
+ }
1334
+ }
1335
+ return node.result;
1336
+ }
1337
+ // Create memoize internal effect to track dependencies and invalidate cache
1338
+ // Use untracked to prevent the effect creation from being affected by parent effects
1339
+ node.cleanup = root(() => effect.named('memoize')(() => {
1340
+ // Execute the function and track its dependencies
1341
+ // The function execution will automatically track dependencies on reactive objects
1342
+ node.result = fn.apply(this, args);
1343
+ return (reason) => {
1344
+ // When dependencies change, clear the cache and notify consumers
1345
+ delete node.result;
1346
+ touched1(node, { type: 'invalidate', prop: args }, 'memoize');
1347
+ // Lazy memoization: stop the effect so it doesn't re-run immediately.
1348
+ // It will be re-created on next access.
1349
+ if (node.cleanup) {
1350
+ node.cleanup({ type: 'invalidate', cause: reason });
1351
+ node.cleanup = undefined;
1352
+ }
1353
+ };
1354
+ }, { opaque: true }));
1355
+ if (options.onMemoizationDiscrepancy) {
1356
+ const wasVerification = options.isVerificationRun;
1357
+ options.isVerificationRun = true;
1358
+ try {
1359
+ const fresh = untracked(() => fn.apply(this, args));
1360
+ if (!deepCompare(node.result, fresh)) {
1361
+ optionCall('onMemoizationDiscrepancy', node.result, fresh, fn, args, 'comparison');
1362
+ }
1363
+ }
1364
+ finally {
1365
+ options.isVerificationRun = wasVerification;
1366
+ }
1367
+ }
1368
+ return node.result;
1369
+ }, fn);
1370
+ memoizedRegistry.set(fnRoot, memoized);
1371
+ memoizedRegistry.set(memoized, memoized);
1372
+ return memoized;
1373
+ }
1374
+ function memoizeObject(target, opts) {
1375
+ const existing = memoizedRegistry.get(target);
1376
+ if (existing)
1377
+ return existing;
1378
+ const proxy = new Proxy(target, {
1379
+ get(source, prop, receiver) {
1380
+ // 1. Walk prototype chain to find descriptor
1381
+ let current = source;
1382
+ let desc;
1383
+ while (current) {
1384
+ desc = Object.getOwnPropertyDescriptor(current, prop);
1385
+ if (desc)
1386
+ break;
1387
+ current = Object.getPrototypeOf(current);
1388
+ }
1389
+ if (!desc)
1390
+ return Reflect.get(source, prop, receiver);
1391
+ // 2. If getter, memoize
1392
+ if (desc.get) {
1393
+ const originalGetter = desc.get;
1394
+ let wrapper = wrapperRegistry.get(originalGetter);
1395
+ if (!wrapper) {
1396
+ wrapper = markWithRoot(named(`${String(source?.constructor?.name ?? 'Object')}.${String(prop)}`, (that) => {
1397
+ return originalGetter.call(that);
1398
+ }), {
1399
+ propertyKey: prop,
1400
+ });
1401
+ const origRoot = originalGetter[rootFunctionSymbol];
1402
+ if (origRoot)
1403
+ wrapper[rootFunctionSymbol] = origRoot;
1404
+ wrapperRegistry.set(originalGetter, wrapper);
1405
+ }
1406
+ const memoized = memoizeFunction(wrapper, opts);
1407
+ return memoized(receiver);
1408
+ }
1409
+ // 3. Otherwise forward
1410
+ return Reflect.get(source, prop, receiver);
1411
+ },
1412
+ // Forward set to the target (source) to ensure it acts as the receiver for reactivity notifications
1413
+ set(source, prop, value, _receiver) {
1414
+ // By strictly passing `source` as receiver, we ensure that if `source` is a reactive proxy,
1415
+ // it recognizes itself and triggers change notifications.
1416
+ return Reflect.set(source, prop, value, source);
1417
+ },
1418
+ });
1419
+ proxyToObject.set(proxy, target);
1420
+ memoizedRegistry.set(target, proxy);
1421
+ return proxy;
1422
+ }
1423
+ /**
1424
+ * Decorator and function wrapper for memoizing computed values based on reactive dependencies.
1425
+ *
1426
+ * When used as a decorator on getters or methods, it caches the result and automatically
1427
+ * invalidates the cache when reactive dependencies change.
1428
+ *
1429
+ * When used as a function wrapper, it memoizes based on object arguments (WeakMap-based cache).
1430
+ *
1431
+ * @example
1432
+ * ```typescript
1433
+ * class User {
1434
+ * @memoize
1435
+ * get fullName() {
1436
+ * return `${this.firstName} ${this.lastName}`
1437
+ * }
1438
+ * }
1439
+ *
1440
+ * // Or as a function wrapper
1441
+ * const expensive = memoize((obj: SomeObject) => {
1442
+ * return heavyComputation(obj)
1443
+ * })
1444
+ * ```
1445
+ */
1446
+ function makeMemoizeDecorator(memoizeOpts) {
1447
+ return decorator({
1448
+ getter(original, target, propertyKey) {
1449
+ return function () {
1450
+ let wrapper = wrapperRegistry.get(original);
1451
+ if (!wrapper) {
1452
+ wrapper = markWithRoot(named(`${String(target?.constructor?.name ?? target?.name ?? 'Object')}.${String(propertyKey)}`, (that) => {
1453
+ return original.call(that);
1454
+ }), {
1455
+ method: original,
1456
+ propertyKey,
1457
+ });
1458
+ const origRoot = original[rootFunctionSymbol];
1459
+ if (origRoot)
1460
+ wrapper[rootFunctionSymbol] = origRoot;
1461
+ wrapperRegistry.set(original, wrapper);
1462
+ }
1463
+ const memoized = memoizeFunction(wrapper, memoizeOpts);
1464
+ return memoized(this);
1465
+ };
1466
+ },
1467
+ method(original, target, name) {
1468
+ return function (...args) {
1469
+ let wrapper = wrapperRegistry.get(original);
1470
+ if (!wrapper) {
1471
+ wrapper = markWithRoot(named(`${String(target?.constructor?.name ?? target?.name ?? 'Object')}.${String(name)}`, (that, ...args) => {
1472
+ return original.call(that, ...args);
1473
+ }), {
1474
+ method: original,
1475
+ propertyKey: name,
1476
+ });
1477
+ const origRoot = original[rootFunctionSymbol];
1478
+ if (origRoot)
1479
+ wrapper[rootFunctionSymbol] = origRoot;
1480
+ wrapperRegistry.set(original, wrapper);
1481
+ }
1482
+ const memoized = memoizeFunction(wrapper, memoizeOpts);
1483
+ return memoized(this, ...args);
1484
+ };
1485
+ },
1486
+ default: (target) => typeof target === 'object'
1487
+ ? memoizeObject(target, memoizeOpts)
1488
+ : memoizeFunction(target, memoizeOpts),
1489
+ });
1490
+ }
1491
+ const memoize = flavored(makeMemoizeDecorator(), {
1492
+ get lenient() {
1493
+ return makeMemoizeDecorator({ lenient: true });
1494
+ },
1495
+ });
1496
+
1497
+ /**
1498
+ * Organizes a source object's properties into a target object using a callback function.
1499
+ * This creates a reactive mapping between source properties and a target object,
1500
+ * automatically handling property additions, updates, and removals.
1501
+ *
1502
+ * @template Source - The type of the source object
1503
+ * @template Target - The type of the target object (defaults to Record<PropertyKey, any>)
1504
+ *
1505
+ * @param {Source} source - The source object to organize
1506
+ * @param {OrganizedCallback<Source, Target>} apply - Callback function that defines how each source property is mapped to the target
1507
+ * @param {Target} [baseTarget={}] - Optional base target object to use (will be made reactive if not already)
1508
+ *
1509
+ * @returns {OrganizedResult<Target>} The target object with cleanup capability
1510
+ *
1511
+ * @example
1512
+ * // Organize user permissions into role-based access
1513
+ * const user = reactive({ isAdmin: true, canEdit: false });
1514
+ * const permissions = organized(
1515
+ * user,
1516
+ * (access, target) => {
1517
+ * if (access.key === 'isAdmin') {
1518
+ * target.hasFullAccess = access.value;
1519
+ * }
1520
+ * target[`can${access.key.charAt(0).toUpperCase() + access.key.slice(1)}`] = access.value;
1521
+ * }
1522
+ * );
1523
+ *
1524
+ * @example
1525
+ * // Transform object structure with cleanup
1526
+ * const source = reactive({ firstName: 'John', lastName: 'Doe' });
1527
+ * const formatted = organized(
1528
+ * source,
1529
+ * (access, target) => {
1530
+ * if (access.key === 'firstName' || access.key === 'lastName') {
1531
+ * target.fullName = `${source.firstName} ${source.lastName}`.trim();
1532
+ * }
1533
+ * }
1534
+ * );
1535
+ *
1536
+ * @example
1537
+ * // Using with cleanup in a component
1538
+ * effect(() => {
1539
+ * const data = fetchData();
1540
+ * const organizedData = organized(data, (access, target) => {
1541
+ * // Transform data
1542
+ * });
1543
+ *
1544
+ * // The cleanup will be called automatically when the effect is disposed
1545
+ * return () => organizedData[cleanup]();
1546
+ * });
1547
+ */
1548
+ function organized(source, apply, baseTarget = {}) {
1549
+ const observedSource = reactive(source);
1550
+ const target = reactive(baseTarget);
1551
+ const stop = attend(() => {
1552
+ const keys = [];
1553
+ for (const key in observedSource)
1554
+ keys.push(key);
1555
+ return keys;
1556
+ }, (key) => {
1557
+ const sourceKey = key;
1558
+ const accessBase = {
1559
+ key: sourceKey,
1560
+ get: () => FoolProof.get(observedSource, sourceKey, observedSource),
1561
+ set: (value) => FoolProof.set(observedSource, sourceKey, value, observedSource),
1562
+ };
1563
+ Object.defineProperty(accessBase, 'value', {
1564
+ get: accessBase.get,
1565
+ set: accessBase.set,
1566
+ configurable: true,
1567
+ enumerable: true,
1568
+ });
1569
+ return apply(accessBase, target);
1570
+ });
1571
+ return link(target, (reason) => stop(reason));
1572
+ }
1573
+ /**
1574
+ * Organizes a property on a target object
1575
+ * Shortcut for defineProperty/delete with touched signal
1576
+ * @param target - The target object
1577
+ * @param property - The property to organize
1578
+ * @param access - The access object
1579
+ * @returns The property descriptor
1580
+ */
1581
+ function organize(target, property, access) {
1582
+ Object.defineProperty(target, property, {
1583
+ get: access.get,
1584
+ set: access.set,
1585
+ configurable: true,
1586
+ enumerable: true,
1587
+ });
1588
+ touched1(target, { type: 'set', prop: property }, property);
1589
+ return () => delete target[property];
1590
+ }
1591
+
1592
+ //#region watch
1593
+ const unsetYet = Symbol('unset-yet');
1594
+ const watch = flavored(function watch(value, //object | ((dep: DependencyAccess) => object),
1595
+ changed, options = {}) {
1596
+ return typeof value === 'function'
1597
+ ? watchCallBack(value, changed, options)
1598
+ : typeof value === 'object' && value !== null
1599
+ ? watchObject(value, changed, options)
1600
+ : (() => {
1601
+ throw new Error('watch: value must be a function or an object');
1602
+ })();
1603
+ }, {
1604
+ get deep() {
1605
+ return flavorOptions(this, { deep: true });
1606
+ },
1607
+ get immediate() {
1608
+ return flavorOptions(this, { immediate: true });
1609
+ },
1610
+ });
1611
+ function watchObject(value, changed, { immediate = false, deep = false } = {}) {
1612
+ if (deep)
1613
+ return deepWatch(value, changed, { immediate });
1614
+ return effect.named('watch:object')(() => {
1615
+ dependant(value);
1616
+ if (immediate)
1617
+ changed(value);
1618
+ immediate = true;
1619
+ });
1620
+ }
1621
+ function watchCallBack(value, changed, { immediate = false, deep = false } = {}) {
1622
+ let oldValue = unsetYet;
1623
+ let deepCleanup;
1624
+ const cbCleanup = effect.named('watch:callback')(markWithRoot((access) => {
1625
+ const newValue = value(access);
1626
+ if (oldValue !== newValue) {
1627
+ const old = oldValue;
1628
+ if (old === unsetYet) {
1629
+ if (immediate)
1630
+ untracked(() => changed(newValue));
1631
+ }
1632
+ else
1633
+ untracked(() => changed(newValue, old));
1634
+ }
1635
+ oldValue = newValue;
1636
+ if (deep) {
1637
+ if (deepCleanup)
1638
+ deepCleanup();
1639
+ deepCleanup = deepWatch(newValue, (value) => changed(value, value));
1640
+ }
1641
+ }, value));
1642
+ return (() => {
1643
+ cbCleanup();
1644
+ if (deepCleanup)
1645
+ deepCleanup();
1646
+ });
1647
+ }
1648
+ //#endregion
1649
+ //#region when
1650
+ /**
1651
+ * Returns a promise that resolves when the predicate returns a truthy value.
1652
+ * The predicate is evaluated reactively — it re-runs whenever its dependencies change.
1653
+ * @param predicate - Reactive function that returns a value; resolves when truthy
1654
+ * @param timeout - Optional timeout in milliseconds — rejects if condition is not met within this duration
1655
+ * @returns Promise that resolves with the first truthy return value
1656
+ */
1657
+ function when(predicate, timeout) {
1658
+ return new Promise((resolve, reject) => {
1659
+ let timer;
1660
+ const stop = effect.named('watch:when')((access) => {
1661
+ try {
1662
+ const value = predicate(access);
1663
+ if (value) {
1664
+ if (timer !== undefined)
1665
+ clearTimeout(timer);
1666
+ timer = undefined;
1667
+ queueMicrotask(() => stop());
1668
+ resolve(value);
1669
+ }
1670
+ }
1671
+ catch (error) {
1672
+ if (timer !== undefined)
1673
+ clearTimeout(timer);
1674
+ timer = undefined;
1675
+ reject(error);
1676
+ }
1677
+ });
1678
+ if (timeout !== undefined) {
1679
+ timer = setTimeout(() => {
1680
+ stop();
1681
+ timer = undefined;
1682
+ reject(new Error(`when: timed out after ${timeout}ms`));
1683
+ }, timeout);
1684
+ }
1685
+ });
1686
+ }
1687
+ //#endregion
1688
+ //#region nonReactive
1689
+ /**
1690
+ * Mark an object as non-reactive. This object and all its properties will never be made reactive.
1691
+ * @param obj - The object to mark as non-reactive
1692
+ */
1693
+ function shallowNonReactive(obj) {
1694
+ obj = unwrap(obj);
1695
+ if (isNonReactive(obj))
1696
+ return obj;
1697
+ obj[unreactiveProperties] = true;
1698
+ return obj;
1699
+ }
1700
+ function unreactiveApplication(arg1, ...args) {
1701
+ return typeof arg1 === 'object'
1702
+ ? shallowNonReactive(arg1)
1703
+ : ((original) => {
1704
+ // Copy the parent's unreactive properties if they exist
1705
+ const parentMarker = original.prototype[unreactiveProperties];
1706
+ // If parent is fully unreactive, child is too
1707
+ if (parentMarker === true) {
1708
+ original.prototype[unreactiveProperties] = true;
1709
+ }
1710
+ else {
1711
+ const set = new Set(parentMarker || []);
1712
+ // Add all arguments (including the first one)
1713
+ set.add(arg1);
1714
+ for (const arg of args)
1715
+ set.add(arg);
1716
+ addUnreactiveProps(original.prototype, set);
1717
+ }
1718
+ return original; // Return the class
1719
+ });
1720
+ }
1721
+ /**
1722
+ * Decorator that marks classes or properties as non-reactive
1723
+ * Prevents objects from being made reactive
1724
+ */
1725
+ const unreactive = decorator({
1726
+ class(original) {
1727
+ original.prototype[unreactiveProperties] = true;
1728
+ },
1729
+ default: unreactiveApplication,
1730
+ });
1731
+ //#endregion
1732
+ //#region resource
1733
+ function lazyInit(resource, load) {
1734
+ const creation = effectHistory.active;
1735
+ let fresh = true;
1736
+ return new Proxy(resource, {
1737
+ [Symbol.toStringTag]: 'LazyInit',
1738
+ get(target, prop) {
1739
+ if (fresh) {
1740
+ captured(creation, load)();
1741
+ fresh = false;
1742
+ }
1743
+ return target[prop];
1744
+ },
1745
+ });
1746
+ }
1747
+ /**
1748
+ * Creates a reactive resource that automatically tracks async state.
1749
+ * @param fetcher - Async function that returns the value. Reactive dependencies are tracked.
1750
+ * @param options - Resource options (initialValue)
1751
+ * @returns Reactive Resource object with value, loading, error, latest properties
1752
+ */
1753
+ function resource(fetcher, options = {}) {
1754
+ const resource = reactive({
1755
+ value: options.initialValue,
1756
+ loading: true,
1757
+ error: undefined,
1758
+ latest: options.initialValue,
1759
+ reload() {
1760
+ reloadSignal.value++;
1761
+ },
1762
+ });
1763
+ const reloadSignal = reactive({ value: 0 });
1764
+ // Solve race conditions: make sure a new fast request is not overloaded by a slow old one
1765
+ let counter = 0;
1766
+ return lazyInit(resource, () => {
1767
+ link(resource, effect.named('watch:resource')((access) => {
1768
+ // Track reload signal to enable manual reloading
1769
+ void reloadSignal.value;
1770
+ const id = ++counter;
1771
+ resource.loading = true;
1772
+ resource.error = undefined;
1773
+ try {
1774
+ const result = fetcher(access);
1775
+ if (result instanceof Promise) {
1776
+ resource.promise = result
1777
+ .then((val) => {
1778
+ if (id === counter) {
1779
+ resource.value = val;
1780
+ resource.latest = val;
1781
+ resource.loading = false;
1782
+ }
1783
+ })
1784
+ .catch((err) => {
1785
+ if (id === counter) {
1786
+ resource.error = err;
1787
+ resource.loading = false;
1788
+ }
1789
+ });
1790
+ }
1791
+ else {
1792
+ resource.promise = Promise.resolve();
1793
+ resource.value = result;
1794
+ resource.latest = result;
1795
+ resource.loading = false;
1796
+ }
1797
+ }
1798
+ catch (err) {
1799
+ resource.promise = Promise.reject(err);
1800
+ resource.error = err;
1801
+ resource.loading = false;
1802
+ }
1803
+ }));
1804
+ });
1805
+ }
1806
+ //#endregion
1807
+
1808
+ /**
1809
+ * Converts an iterator to a generator that yields reactive values
1810
+ */
1811
+ function* makeReactiveIterator(iterator) {
1812
+ let result = iterator.next();
1813
+ while (!result.done) {
1814
+ yield reactive(result.value);
1815
+ result = iterator.next();
1816
+ }
1817
+ }
1818
+ /**
1819
+ * Converts an iterator of key-value pairs to a generator that yields reactive key-value pairs
1820
+ */
1821
+ function* makeReactiveEntriesIterator(iterator) {
1822
+ let result = iterator.next();
1823
+ while (!result.done) {
1824
+ const [key, value] = result.value;
1825
+ yield [reactive(key), reactive(value)];
1826
+ result = iterator.next();
1827
+ }
1828
+ }
1829
+
1830
+ function* index(i, { length = true } = {}) {
1831
+ if (length)
1832
+ yield 'length';
1833
+ yield i;
1834
+ }
1835
+ class Indexer extends Array {
1836
+ get(i) {
1837
+ dependant(this, i);
1838
+ return reactive(this[i]);
1839
+ }
1840
+ // Returns undefined intentionally: signals the proxy handler that notifications
1841
+ // were already dispatched via touched(), preventing double notification
1842
+ set(i, value) {
1843
+ const added = i >= this.length;
1844
+ this[i] = value;
1845
+ touched(this, { type: 'set', prop: i }, index(i, { length: added }));
1846
+ }
1847
+ }
1848
+ const indexLess = { get: FoolProof.get, set: FoolProof.set };
1849
+ // Fast numeric-string check: first char is a digit (0-9)
1850
+ function asIndex(prop) {
1851
+ const c = prop.charCodeAt(0);
1852
+ if (c < 48 || c > 57)
1853
+ return -1; // not 0-9
1854
+ const n = +prop; // coerce — faster than parseInt, handles "0", "12", etc.
1855
+ return n === (n | 0) && n >= 0 ? n : -1;
1856
+ }
1857
+ Object.assign(FoolProof, {
1858
+ get(obj, prop, receiver) {
1859
+ if (Array.isArray(obj) && typeof prop === 'string') {
1860
+ const i = asIndex(prop);
1861
+ if (i >= 0)
1862
+ return Indexer.prototype.get.call(obj, i);
1863
+ }
1864
+ return indexLess.get(obj, prop, receiver);
1865
+ },
1866
+ set(obj, prop, value, receiver) {
1867
+ if (Array.isArray(obj) && typeof prop === 'string') {
1868
+ const i = asIndex(prop);
1869
+ if (i >= 0)
1870
+ return Indexer.prototype.set.call(obj, i, value);
1871
+ }
1872
+ return indexLess.set(obj, prop, value, receiver);
1873
+ },
1874
+ });
1875
+ class ReactiveArray extends Array {
1876
+ toJSON() {
1877
+ return this;
1878
+ }
1879
+ }
1880
+ /**
1881
+ * This is a wrapper class for Array that adds reactive behavior.
1882
+ * It extends Array and overrides methods to add reactive behavior, while making sure that the internal representation is not reactive.
1883
+ */
1884
+ let ReactiveArrayWrapper = (() => {
1885
+ var _a;
1886
+ let _classSuper = Array;
1887
+ let _instanceExtraInitializers = [];
1888
+ let _fill_decorators;
1889
+ let _copyWithin_decorators;
1890
+ let _pop_decorators;
1891
+ let _push_decorators;
1892
+ let _reverse_decorators;
1893
+ let _shift_decorators;
1894
+ let _sort_decorators;
1895
+ let _splice_decorators;
1896
+ let _unshift_decorators;
1897
+ return _a = class ReactiveArrayWrapper extends _classSuper {
1898
+ at(index) {
1899
+ return reactive(super.at(index));
1900
+ }
1901
+ concat(...items) {
1902
+ return reactive(super.concat(...items.map(unwrap)));
1903
+ }
1904
+ entries() {
1905
+ dependant(this, keysOf);
1906
+ return makeReactiveEntriesIterator(super.entries());
1907
+ }
1908
+ every(predicate, thisArg) {
1909
+ return super.every((v, i, a) => predicate.call(thisArg, reactive(v), i, a), thisArg);
1910
+ }
1911
+ fill(value, start, end) {
1912
+ return super.fill(unwrap(value), start, end);
1913
+ }
1914
+ copyWithin(target, start, end) {
1915
+ return super.copyWithin(target, start, end);
1916
+ }
1917
+ filter(predicate, thisArg) {
1918
+ return reactive(super.filter((v, i, a) => predicate.call(thisArg, reactive(v), i, a), thisArg));
1919
+ }
1920
+ find(predicate, thisArg) {
1921
+ return reactive(super.find((v, i, a) => predicate.call(thisArg, reactive(v), i, a), thisArg));
1922
+ }
1923
+ findIndex(predicate, thisArg) {
1924
+ return super.findIndex((v, i, a) => predicate.call(thisArg, reactive(v), i, a), thisArg);
1925
+ }
1926
+ findLast(predicate, thisArg) {
1927
+ return reactive(super.findLast((v, i, a) => predicate.call(thisArg, reactive(v), i, a), thisArg));
1928
+ }
1929
+ findLastIndex(predicate, thisArg) {
1930
+ return super.findLastIndex((v, i, a) => predicate.call(thisArg, reactive(v), i, a), thisArg);
1931
+ }
1932
+ flat(depth) {
1933
+ return reactive(super.flat(depth));
1934
+ }
1935
+ flatMap(callbackfn, thisArg) {
1936
+ return reactive(super.flatMap((v, i, a) => unwrap(callbackfn.call(thisArg, reactive(v), i, a)), thisArg));
1937
+ }
1938
+ forEach(callbackfn, thisArg) {
1939
+ super.forEach((v, i, a) => {
1940
+ callbackfn.call(thisArg, reactive(v), i, a);
1941
+ }, thisArg);
1942
+ }
1943
+ includes(searchElement, fromIndex) {
1944
+ return arguments.length > 1
1945
+ ? super.includes(unwrap(searchElement), fromIndex)
1946
+ : super.includes(unwrap(searchElement));
1947
+ }
1948
+ indexOf(searchElement, fromIndex) {
1949
+ return arguments.length > 1
1950
+ ? super.indexOf(unwrap(searchElement), fromIndex)
1951
+ : super.indexOf(unwrap(searchElement));
1952
+ }
1953
+ join(separator) {
1954
+ return super.join(separator);
1955
+ }
1956
+ keys() {
1957
+ dependant(this, 'length');
1958
+ return super.keys();
1959
+ }
1960
+ lastIndexOf(searchElement, fromIndex) {
1961
+ return arguments.length > 1
1962
+ ? super.lastIndexOf(unwrap(searchElement), fromIndex)
1963
+ : super.lastIndexOf(unwrap(searchElement));
1964
+ }
1965
+ map(callbackfn, thisArg) {
1966
+ return reactive(super.map((v, i, a) => unwrap(callbackfn.call(thisArg, reactive(v), i, a)), thisArg));
1967
+ }
1968
+ pop() {
1969
+ return reactive(super.pop());
1970
+ }
1971
+ push(...items) {
1972
+ return super.push(...items.map(unwrap));
1973
+ }
1974
+ reduce(callbackfn, initialValue) {
1975
+ return reactive(arguments.length > 1
1976
+ ? super.reduce((acc, v, i, a) => unwrap(callbackfn(acc, reactive(v), i, a)), initialValue)
1977
+ : super.reduce((acc, v, i, a) => unwrap(callbackfn(acc, reactive(v), i, a))));
1978
+ }
1979
+ reduceRight(callbackfn, initialValue) {
1980
+ return reactive(arguments.length > 1
1981
+ ? super.reduceRight((acc, v, i, a) => unwrap(callbackfn(acc, reactive(v), i, a)), initialValue)
1982
+ : super.reduceRight((acc, v, i, a) => unwrap(callbackfn(acc, reactive(v), i, a))));
1983
+ }
1984
+ reverse() {
1985
+ return reactive(super.reverse());
1986
+ }
1987
+ shift() {
1988
+ return reactive(super.shift());
1989
+ }
1990
+ slice(start, end) {
1991
+ return reactive(super.slice(start, end));
1992
+ }
1993
+ some(predicate, thisArg) {
1994
+ return super.some((v, i, a) => predicate.call(thisArg, reactive(v), i, a), thisArg);
1995
+ }
1996
+ sort(compareFn) {
1997
+ const wrappedCompare = compareFn
1998
+ ? (a, b) => compareFn(reactive(a), reactive(b))
1999
+ : undefined;
2000
+ return super.sort(wrappedCompare);
2001
+ }
2002
+ splice(start, deleteCount, ...items) {
2003
+ if (arguments.length > 2)
2004
+ return reactive(super.splice(start, deleteCount, ...items.map(unwrap)));
2005
+ if (arguments.length === 2)
2006
+ return reactive(super.splice(start, deleteCount));
2007
+ if (arguments.length === 1)
2008
+ return reactive(super.splice(start));
2009
+ return reactive([]);
2010
+ }
2011
+ unshift(...items) {
2012
+ return super.unshift(...items.map(unwrap));
2013
+ }
2014
+ values() {
2015
+ dependant(this, keysOf);
2016
+ return makeReactiveIterator(super.values());
2017
+ }
2018
+ [(_fill_decorators = [atomic], _copyWithin_decorators = [atomic], _pop_decorators = [atomic], _push_decorators = [atomic], _reverse_decorators = [atomic], _shift_decorators = [atomic], _sort_decorators = [atomic], _splice_decorators = [atomic], _unshift_decorators = [atomic], Symbol.iterator)]() {
2019
+ dependant(this, keysOf);
2020
+ return makeReactiveIterator(super[Symbol.iterator]());
2021
+ }
2022
+ toReversed() {
2023
+ return reactive(super.toReversed());
2024
+ }
2025
+ toSorted(compareFn) {
2026
+ const wrappedCompare = compareFn
2027
+ ? (a, b) => compareFn(reactive(a), reactive(b))
2028
+ : undefined;
2029
+ return reactive(super.toSorted(wrappedCompare));
2030
+ }
2031
+ toSpliced(start, deleteCount, ...items) {
2032
+ if (arguments.length > 2)
2033
+ return reactive(super.toSpliced(start, deleteCount, ...items.map(unwrap)));
2034
+ if (arguments.length === 2)
2035
+ return reactive(super.toSpliced(start, deleteCount));
2036
+ if (arguments.length === 1)
2037
+ return reactive(super.toSpliced(start));
2038
+ return reactive([...this]);
2039
+ }
2040
+ with(index, value) {
2041
+ return reactive(super.with(index, unwrap(value)));
2042
+ }
2043
+ constructor() {
2044
+ super(...arguments);
2045
+ __runInitializers(this, _instanceExtraInitializers);
2046
+ }
2047
+ },
2048
+ (() => {
2049
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
2050
+ __esDecorate(_a, null, _fill_decorators, { kind: "method", name: "fill", static: false, private: false, access: { has: obj => "fill" in obj, get: obj => obj.fill }, metadata: _metadata }, null, _instanceExtraInitializers);
2051
+ __esDecorate(_a, null, _copyWithin_decorators, { kind: "method", name: "copyWithin", static: false, private: false, access: { has: obj => "copyWithin" in obj, get: obj => obj.copyWithin }, metadata: _metadata }, null, _instanceExtraInitializers);
2052
+ __esDecorate(_a, null, _pop_decorators, { kind: "method", name: "pop", static: false, private: false, access: { has: obj => "pop" in obj, get: obj => obj.pop }, metadata: _metadata }, null, _instanceExtraInitializers);
2053
+ __esDecorate(_a, null, _push_decorators, { kind: "method", name: "push", static: false, private: false, access: { has: obj => "push" in obj, get: obj => obj.push }, metadata: _metadata }, null, _instanceExtraInitializers);
2054
+ __esDecorate(_a, null, _reverse_decorators, { kind: "method", name: "reverse", static: false, private: false, access: { has: obj => "reverse" in obj, get: obj => obj.reverse }, metadata: _metadata }, null, _instanceExtraInitializers);
2055
+ __esDecorate(_a, null, _shift_decorators, { kind: "method", name: "shift", static: false, private: false, access: { has: obj => "shift" in obj, get: obj => obj.shift }, metadata: _metadata }, null, _instanceExtraInitializers);
2056
+ __esDecorate(_a, null, _sort_decorators, { kind: "method", name: "sort", static: false, private: false, access: { has: obj => "sort" in obj, get: obj => obj.sort }, metadata: _metadata }, null, _instanceExtraInitializers);
2057
+ __esDecorate(_a, null, _splice_decorators, { kind: "method", name: "splice", static: false, private: false, access: { has: obj => "splice" in obj, get: obj => obj.splice }, metadata: _metadata }, null, _instanceExtraInitializers);
2058
+ __esDecorate(_a, null, _unshift_decorators, { kind: "method", name: "unshift", static: false, private: false, access: { has: obj => "unshift" in obj, get: obj => obj.unshift }, metadata: _metadata }, null, _instanceExtraInitializers);
2059
+ if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
2060
+ })(),
2061
+ _a;
2062
+ })();
2063
+
2064
+ /**
2065
+ * Reactive wrapper around JavaScript's WeakMap class
2066
+ * Only tracks individual key operations, no size tracking (WeakMap limitation)
2067
+ */
2068
+ class ReactiveWeakMap extends WeakMap {
2069
+ // Implement WeakMap interface methods with reactivity
2070
+ delete(key) {
2071
+ const hadKey = this.has(key);
2072
+ const result = super.delete(key);
2073
+ if (hadKey)
2074
+ touched1(contentRef(this), { type: 'del', prop: key }, key);
2075
+ return result;
2076
+ }
2077
+ get(key) {
2078
+ dependant(contentRef(this), key);
2079
+ return reactive(super.get(key));
2080
+ }
2081
+ has(key) {
2082
+ dependant(contentRef(this), key);
2083
+ return super.has(key);
2084
+ }
2085
+ set(key, value) {
2086
+ const hadKey = this.has(key);
2087
+ const oldValue = this.get(key);
2088
+ const reactiveValue = reactive(value);
2089
+ this.set(key, reactiveValue);
2090
+ if (!hadKey || oldValue !== reactiveValue) {
2091
+ notifyPropertyChange(contentRef(this), key, oldValue, reactiveValue, hadKey);
2092
+ }
2093
+ return this;
2094
+ }
2095
+ }
2096
+ /**
2097
+ * Reactive wrapper around JavaScript's Map class
2098
+ * Tracks size changes, individual key operations, and collection-wide operations
2099
+ */
2100
+ class ReactiveMap extends Map {
2101
+ // Implement Map interface methods with reactivity
2102
+ get size() {
2103
+ dependant(this, 'size'); // The ReactiveMap instance still goes through proxy
2104
+ return super.size;
2105
+ }
2106
+ clear() {
2107
+ const hadEntries = this.size > 0;
2108
+ super.clear();
2109
+ if (hadEntries) {
2110
+ const evolution = { type: 'bunch', method: 'clear' };
2111
+ // Clear triggers all effects since all keys are affected
2112
+ batch(() => {
2113
+ touched1(this, evolution, 'size');
2114
+ touched(contentRef(this), evolution);
2115
+ });
2116
+ }
2117
+ }
2118
+ entries() {
2119
+ dependant(contentRef(this));
2120
+ return makeReactiveEntriesIterator(this.entries());
2121
+ }
2122
+ forEach(callbackfn, thisArg) {
2123
+ dependant(contentRef(this));
2124
+ this.forEach(callbackfn, thisArg);
2125
+ }
2126
+ keys() {
2127
+ dependant(contentRef(this), keysOf);
2128
+ return this.keys();
2129
+ }
2130
+ values() {
2131
+ dependant(contentRef(this));
2132
+ return makeReactiveIterator(this.values());
2133
+ }
2134
+ [Symbol.iterator]() {
2135
+ dependant(contentRef(this));
2136
+ const it = Map.prototype[Symbol.iterator].call(this);
2137
+ const nativeNext = it.next.bind(it);
2138
+ it.next = () => {
2139
+ const result = nativeNext();
2140
+ if (result.done)
2141
+ return result;
2142
+ const [key, value] = result.value;
2143
+ return { value: [reactive(key), reactive(value)], done: false };
2144
+ };
2145
+ return it;
2146
+ }
2147
+ // Implement Map methods with reactivity
2148
+ delete(key) {
2149
+ const hadKey = this.has(key);
2150
+ const result = super.delete(key);
2151
+ if (hadKey) {
2152
+ const evolution = { type: 'del', prop: key };
2153
+ batch(() => {
2154
+ touched1(contentRef(this), evolution, key);
2155
+ touched1(this, evolution, 'size');
2156
+ });
2157
+ }
2158
+ return result;
2159
+ }
2160
+ get(key) {
2161
+ dependant(contentRef(this), key);
2162
+ return reactive(super.get(key));
2163
+ }
2164
+ has(key) {
2165
+ dependant(contentRef(this), key);
2166
+ return super.has(key);
2167
+ }
2168
+ set(key, value) {
2169
+ const hadKey = this.has(key);
2170
+ const oldValue = this.get(key);
2171
+ const reactiveValue = reactive(value);
2172
+ super.set(key, reactiveValue);
2173
+ if (!hadKey || oldValue !== reactiveValue) {
2174
+ batch(() => {
2175
+ notifyPropertyChange(contentRef(this), key, oldValue, reactiveValue, hadKey);
2176
+ // Also notify size change for Map (WeakMap doesn't track size)
2177
+ const evolution = { type: hadKey ? 'set' : 'add', prop: key };
2178
+ touched1(this, evolution, 'size');
2179
+ });
2180
+ }
2181
+ return this;
2182
+ }
2183
+ }
2184
+
2185
+ /**
2186
+ * Reactive wrapper around JavaScript's WeakSet class
2187
+ * Only tracks individual value operations, no size tracking (WeakSet limitation)
2188
+ */
2189
+ class ReactiveWeakSet extends WeakSet {
2190
+ add(value) {
2191
+ const had = this.has(value);
2192
+ super.add(value);
2193
+ if (!had) {
2194
+ // touch the specific value and the collection view
2195
+ touched1(contentRef(this), { type: 'add', prop: value }, value);
2196
+ // no size/allProps for WeakSet
2197
+ }
2198
+ return this;
2199
+ }
2200
+ delete(value) {
2201
+ const had = this.has(value);
2202
+ const res = super.delete(value);
2203
+ if (had)
2204
+ touched1(contentRef(this), { type: 'del', prop: value }, value);
2205
+ return res;
2206
+ }
2207
+ has(value) {
2208
+ dependant(contentRef(this), value);
2209
+ return super.has(value);
2210
+ }
2211
+ }
2212
+ /**
2213
+ * Reactive wrapper around JavaScript's Set class
2214
+ * Tracks size changes, individual value operations, and collection-wide operations
2215
+ */
2216
+ class ReactiveSet extends Set {
2217
+ get size() {
2218
+ // size depends on the wrapper instance, like Map counterpart
2219
+ dependant(this, 'size');
2220
+ return this.size;
2221
+ }
2222
+ add(value) {
2223
+ const had = this.has(value);
2224
+ const reactiveValue = reactive(value);
2225
+ super.add(reactiveValue);
2226
+ if (!had) {
2227
+ const evolution = { type: 'add', prop: reactiveValue };
2228
+ // touch for value-specific and aggregate dependencies
2229
+ batch(() => {
2230
+ touched1(contentRef(this), evolution, reactiveValue);
2231
+ touched1(this, evolution, 'size');
2232
+ });
2233
+ }
2234
+ return this;
2235
+ }
2236
+ clear() {
2237
+ const hadEntries = this.size > 0;
2238
+ super.clear();
2239
+ if (hadEntries) {
2240
+ const evolution = { type: 'bunch', method: 'clear' };
2241
+ batch(() => {
2242
+ touched1(this, evolution, 'size');
2243
+ touched(contentRef(this), evolution);
2244
+ });
2245
+ }
2246
+ }
2247
+ delete(value) {
2248
+ const had = this.has(value);
2249
+ const res = super.delete(value);
2250
+ if (had) {
2251
+ const evolution = { type: 'del', prop: value };
2252
+ batch(() => {
2253
+ touched1(contentRef(this), evolution, value);
2254
+ touched1(this, evolution, 'size');
2255
+ });
2256
+ }
2257
+ return res;
2258
+ }
2259
+ has(value) {
2260
+ dependant(contentRef(this), value);
2261
+ return this.has(value);
2262
+ }
2263
+ entries() {
2264
+ dependant(contentRef(this));
2265
+ return makeReactiveEntriesIterator(this.entries());
2266
+ }
2267
+ forEach(callbackfn, thisArg) {
2268
+ dependant(contentRef(this));
2269
+ this.forEach(callbackfn, thisArg);
2270
+ }
2271
+ keys() {
2272
+ dependant(contentRef(this));
2273
+ return makeReactiveIterator(this.keys());
2274
+ }
2275
+ values() {
2276
+ dependant(contentRef(this));
2277
+ return makeReactiveIterator(this.values());
2278
+ }
2279
+ [Symbol.iterator]() {
2280
+ dependant(contentRef(this));
2281
+ const it = Set.prototype[Symbol.iterator].call(this);
2282
+ const nativeNext = it.next.bind(it);
2283
+ it.next = () => {
2284
+ const result = nativeNext();
2285
+ if (result.done)
2286
+ return result;
2287
+ return { value: reactive(result.value), done: false };
2288
+ };
2289
+ return it;
2290
+ }
2291
+ }
2292
+
2293
+ // Register native collection types to use specialized reactive wrappers
2294
+ metaProtos.set(Array, ReactiveArray.prototype);
2295
+ metaProtos.set(Set, ReactiveSet.prototype);
2296
+ metaProtos.set(WeakSet, ReactiveWeakSet.prototype);
2297
+ metaProtos.set(Map, ReactiveMap.prototype);
2298
+ metaProtos.set(WeakMap, ReactiveWeakMap.prototype);
2299
+ wrapProtos.set(Array, ReactiveArrayWrapper.prototype);
2300
+ /**
2301
+ * Object containing internal reactive system state for debugging and profiling
2302
+ */
2303
+ const profileInfo = {
2304
+ objectToProxy,
2305
+ proxyToObject,
2306
+ effectToReactiveObjects,
2307
+ watchers,
2308
+ objectParents,
2309
+ objectsWithDeepWatchers,
2310
+ deepWatchers,
2311
+ effectToDeepWatchedObjects,
2312
+ };
2313
+
2314
+ // In order to avoid async re-entrance, we could use zone.js or something like that.
2315
+ const syncCalculating = [];
2316
+ /**
2317
+ * Decorator that caches the result of a getter method and only recomputes when dependencies change
2318
+ * Prevents circular dependencies and provides automatic cache invalidation
2319
+ */
2320
+ const cached = decorator({
2321
+ getter(original, _target, propertyKey) {
2322
+ return function () {
2323
+ const alreadyCalculating = syncCalculating.findIndex((c) => c.object === this && c.prop === propertyKey);
2324
+ if (alreadyCalculating > -1)
2325
+ throw new Error(`Circular dependency detected: ${syncCalculating
2326
+ .slice(alreadyCalculating)
2327
+ .map((c) => `${c.object.constructor.name}.${String(c.prop)}`)
2328
+ .join(' -> ')} -> again`);
2329
+ syncCalculating.push({ object: this, prop: propertyKey });
2330
+ try {
2331
+ const rv = original.call(this);
2332
+ cache(this, propertyKey, rv);
2333
+ return rv;
2334
+ }
2335
+ finally {
2336
+ syncCalculating.pop();
2337
+ }
2338
+ };
2339
+ },
2340
+ });
2341
+ /**
2342
+ * Checks if a property is cached (has a cached value)
2343
+ * @param object - The object to check
2344
+ * @param propertyKey - The property key to check
2345
+ * @returns True if the property has a cached value
2346
+ */
2347
+ function isCached(object, propertyKey) {
2348
+ return !!Object.getOwnPropertyDescriptor(object, propertyKey);
2349
+ }
2350
+ /**
2351
+ * Caches a value for a property on an object
2352
+ * @param object - The object to cache the value on
2353
+ * @param propertyKey - The property key to cache
2354
+ * @param value - The value to cache
2355
+ */
2356
+ function cache(object, propertyKey, value) {
2357
+ Object.defineProperty(object, propertyKey, { value });
2358
+ }
2359
+ /**
2360
+ * Creates a decorator that modifies property descriptors for specified properties
2361
+ * @param descriptor - The descriptor properties to apply
2362
+ * @returns A class decorator that applies the descriptor to specified properties
2363
+ */
2364
+ const descriptor = flavored(function descriptor(descriptor) {
2365
+ return (...properties) => (Base) => {
2366
+ return class extends Base {
2367
+ constructor(...args) {
2368
+ super(...args);
2369
+ for (const key of properties) {
2370
+ const existing = Object.getOwnPropertyDescriptor(this, key);
2371
+ Object.defineProperty(this, key, Object.assign(existing || {}, descriptor));
2372
+ }
2373
+ }
2374
+ };
2375
+ };
2376
+ }, {
2377
+ /**
2378
+ * enumerable: true
2379
+ */
2380
+ get enumerable() {
2381
+ return descriptor({ enumerable: true });
2382
+ },
2383
+ /**
2384
+ * enumerable: false
2385
+ */
2386
+ get hidden() {
2387
+ return descriptor({ enumerable: false });
2388
+ },
2389
+ /**
2390
+ * configurable: true
2391
+ */
2392
+ get configurable() {
2393
+ return descriptor({ configurable: true });
2394
+ },
2395
+ /**
2396
+ * configurable: false
2397
+ */
2398
+ get frozen() {
2399
+ return descriptor({ configurable: false });
2400
+ },
2401
+ /**
2402
+ * writable: true
2403
+ */
2404
+ get writable() {
2405
+ return descriptor({ writable: true });
2406
+ },
2407
+ /**
2408
+ * writable: false
2409
+ */
2410
+ get readonly() {
2411
+ return descriptor({ writable: false });
2412
+ },
2413
+ });
2414
+ /**
2415
+ * Decorator that marks methods, properties, or classes as deprecated
2416
+ * Provides warning messages when deprecated items are used
2417
+ */
2418
+ const deprecated = Object.assign(decorator({
2419
+ method(original, _target, propertyKey) {
2420
+ return function (...args) {
2421
+ deprecated.warn(this, propertyKey);
2422
+ return original.apply(this, args);
2423
+ };
2424
+ },
2425
+ getter(original, _target, propertyKey) {
2426
+ return function () {
2427
+ deprecated.warn(this, propertyKey);
2428
+ return original.call(this);
2429
+ };
2430
+ },
2431
+ setter(original, _target, propertyKey) {
2432
+ return function (value) {
2433
+ deprecated.warn(this, propertyKey);
2434
+ return original.call(this, value);
2435
+ };
2436
+ },
2437
+ class(original) {
2438
+ return class extends original {
2439
+ constructor(...args) {
2440
+ super(...args);
2441
+ deprecated.warn(this, 'constructor');
2442
+ }
2443
+ };
2444
+ },
2445
+ default(message) {
2446
+ return decorator({
2447
+ method(original, _target, propertyKey) {
2448
+ return function (...args) {
2449
+ deprecated.warn(this, propertyKey, message);
2450
+ return original.apply(this, args);
2451
+ };
2452
+ },
2453
+ getter(original, _target, propertyKey) {
2454
+ return function () {
2455
+ deprecated.warn(this, propertyKey, message);
2456
+ return original.call(this);
2457
+ };
2458
+ },
2459
+ setter(original, _target, propertyKey) {
2460
+ return function (value) {
2461
+ deprecated.warn(this, propertyKey, message);
2462
+ return original.call(this, value);
2463
+ };
2464
+ },
2465
+ class(original) {
2466
+ return class extends original {
2467
+ constructor(...args) {
2468
+ super(...args);
2469
+ deprecated.warn(this, 'constructor', message);
2470
+ }
2471
+ };
2472
+ },
2473
+ });
2474
+ },
2475
+ }), {
2476
+ warn: (target, propertyKey, message) => {
2477
+ options.warn(`${target.constructor.name}.${String(propertyKey)} is deprecated${message ? `: ${message}` : ''}`);
2478
+ },
2479
+ });
2480
+ /**
2481
+ * Creates a debounced method decorator that delays execution until after the delay period has passed
2482
+ * @param delay - The delay in milliseconds
2483
+ * @returns A method decorator that debounces method calls
2484
+ */
2485
+ function debounce(delay) {
2486
+ return decorator({
2487
+ method(original, _target, _propertyKey) {
2488
+ let timeoutId = null;
2489
+ return function (...args) {
2490
+ // Clear existing timeout
2491
+ if (timeoutId) {
2492
+ clearTimeout(timeoutId);
2493
+ }
2494
+ // Set new timeout
2495
+ timeoutId = setTimeout(() => {
2496
+ original.apply(this, args);
2497
+ timeoutId = null;
2498
+ }, delay);
2499
+ };
2500
+ },
2501
+ });
2502
+ }
2503
+ /**
2504
+ * Creates a throttled method decorator that limits execution to once per delay period
2505
+ * @param delay - The delay in milliseconds
2506
+ * @returns A method decorator that throttles method calls
2507
+ */
2508
+ function throttle(delay) {
2509
+ return decorator({
2510
+ method(original, _target, _propertyKey) {
2511
+ let lastCallTime = 0;
2512
+ let timeoutId = null;
2513
+ return function (...args) {
2514
+ const now = Date.now();
2515
+ // If enough time has passed since last call, execute immediately
2516
+ if (now - lastCallTime >= delay) {
2517
+ // Clear any pending timeout since we're executing now
2518
+ if (timeoutId) {
2519
+ clearTimeout(timeoutId);
2520
+ timeoutId = null;
2521
+ }
2522
+ lastCallTime = now;
2523
+ return original.apply(this, args);
2524
+ }
2525
+ // Otherwise, schedule execution for when the delay period ends
2526
+ if (!timeoutId) {
2527
+ const remainingTime = delay - (now - lastCallTime);
2528
+ const scheduledArgs = [...args]; // Capture args at scheduling time
2529
+ timeoutId = setTimeout(() => {
2530
+ lastCallTime = Date.now();
2531
+ original.apply(this, scheduledArgs);
2532
+ timeoutId = null;
2533
+ }, remainingTime);
2534
+ }
2535
+ };
2536
+ },
2537
+ });
2538
+ }
2539
+
2540
+ var version$1 = "1.0.11";
2541
+ var pkg = {
2542
+ version: version$1};
2543
+
2544
+ const { version } = pkg;
2545
+ const GLOBAL_MUTTS_KEY = '__MUTTS_INSTANCE__';
2546
+ const globalScope = (typeof globalThis !== 'undefined'
2547
+ ? globalThis
2548
+ : typeof window !== 'undefined'
2549
+ ? window
2550
+ : typeof global !== 'undefined'
2551
+ ? global
2552
+ : false);
2553
+ if (globalScope) {
2554
+ let source = 'mutts/index';
2555
+ try {
2556
+ if (typeof __filename !== 'undefined')
2557
+ source = __filename;
2558
+ else if (typeof import.meta !== 'undefined' && import.meta.url) {
2559
+ source = import.meta.url;
2560
+ }
2561
+ }
2562
+ catch (_e) { }
2563
+ const currentSourceInfo = { version, source, timestamp: Date.now() };
2564
+ if (globalScope[GLOBAL_MUTTS_KEY]) {
2565
+ const existing = globalScope[GLOBAL_MUTTS_KEY];
2566
+ throw new Error(`[Mutts] Multiple instances detected!\n` +
2567
+ `Existing instance: ${JSON.stringify(existing, null, 2)}\n` +
2568
+ `New instance: ${JSON.stringify(currentSourceInfo, null, 2)}\n` +
2569
+ `This usually happens when 'mutts' is both installed as a dependency and bundled, ` +
2570
+ `or when different versions are loaded. ` +
2571
+ `Please check your build configuration (aliases, externals) to ensure a single source of truth.`);
2572
+ }
2573
+ globalScope[GLOBAL_MUTTS_KEY] = currentSourceInfo;
2574
+ }
2575
+
2576
+ export { ArrayReadForward as A, unreactive as B, watch as C, Destroyable as D, Eventful as E, when as F, Indexable as I, DestructionError as a, allocated as b, allocatedValues as c, arrayDiff as d, attend as e, cache as f, cached as g, callOnGC as h, chainPromise as i, debounce as j, deepWatch as k, deprecated as l, descriptor as m, destructor as n, forwardArray as o, getAt as p, isCached as q, lift as r, memoize as s, morph as t, organize as u, organized as v, profileInfo as w, resource as x, setAt as y, throttle as z };
2577
+ //# sourceMappingURL=index-Sf74wXTV.esm.js.map