mutts 1.0.5 → 1.0.7
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.
- package/README.md +2 -1
- package/dist/browser.d.ts +2 -0
- package/dist/browser.esm.js +70 -0
- package/dist/browser.esm.js.map +1 -0
- package/dist/browser.js +161 -0
- package/dist/browser.js.map +1 -0
- package/dist/chunks/{index-Cvxdw6Ax.js → index-BFYK02LG.js} +5377 -4059
- package/dist/chunks/index-BFYK02LG.js.map +1 -0
- package/dist/chunks/{index-qiWwozOc.esm.js → index-CNR6QRUl.esm.js} +5247 -3963
- package/dist/chunks/index-CNR6QRUl.esm.js.map +1 -0
- package/dist/mutts.umd.js +1 -1
- package/dist/mutts.umd.js.map +1 -1
- package/dist/mutts.umd.min.js +1 -1
- package/dist/mutts.umd.min.js.map +1 -1
- package/dist/node.d.ts +2 -0
- package/dist/node.esm.js +45 -0
- package/dist/node.esm.js.map +1 -0
- package/dist/node.js +136 -0
- package/dist/node.js.map +1 -0
- package/docs/ai/api-reference.md +0 -2
- package/docs/ai/manual.md +14 -95
- package/docs/reactive/advanced.md +7 -111
- package/docs/reactive/collections.md +0 -125
- package/docs/reactive/core.md +27 -24
- package/docs/reactive/debugging.md +168 -0
- package/docs/reactive/project.md +1 -1
- package/docs/reactive/scan.md +78 -0
- package/docs/reactive.md +8 -6
- package/docs/std-decorators.md +1 -0
- package/docs/zone.md +88 -0
- package/package.json +47 -65
- package/src/async/browser.ts +87 -0
- package/src/async/index.ts +8 -0
- package/src/async/node.ts +46 -0
- package/src/decorator.ts +15 -9
- package/src/destroyable.ts +4 -4
- package/src/index.ts +54 -0
- package/src/indexable.ts +42 -0
- package/src/mixins.ts +2 -2
- package/src/reactive/array.ts +149 -141
- package/src/reactive/buffer.ts +168 -0
- package/src/reactive/change.ts +3 -3
- package/src/reactive/debug.ts +1 -1
- package/src/reactive/deep-touch.ts +1 -1
- package/src/reactive/deep-watch.ts +1 -1
- package/src/reactive/effect-context.ts +15 -91
- package/src/reactive/effects.ts +138 -170
- package/src/reactive/index.ts +10 -13
- package/src/reactive/interface.ts +20 -33
- package/src/reactive/map.ts +48 -61
- package/src/reactive/memoize.ts +87 -31
- package/src/reactive/project.ts +43 -22
- package/src/reactive/proxy.ts +18 -43
- package/src/reactive/record.ts +3 -3
- package/src/reactive/register.ts +5 -7
- package/src/reactive/registry.ts +59 -0
- package/src/reactive/set.ts +42 -56
- package/src/reactive/tracking.ts +5 -62
- package/src/reactive/types.ts +79 -19
- package/src/std-decorators.ts +9 -9
- package/src/utils.ts +203 -19
- package/src/zone.ts +127 -0
- package/dist/chunks/_tslib-BgjropY9.js +0 -81
- package/dist/chunks/_tslib-BgjropY9.js.map +0 -1
- package/dist/chunks/_tslib-Mzh1rNsX.esm.js +0 -75
- package/dist/chunks/_tslib-Mzh1rNsX.esm.js.map +0 -1
- package/dist/chunks/decorator-DLvrD0UF.js +0 -265
- package/dist/chunks/decorator-DLvrD0UF.js.map +0 -1
- package/dist/chunks/decorator-DqiszP7i.esm.js +0 -253
- package/dist/chunks/decorator-DqiszP7i.esm.js.map +0 -1
- package/dist/chunks/index-Cvxdw6Ax.js.map +0 -1
- package/dist/chunks/index-qiWwozOc.esm.js.map +0 -1
- package/dist/decorator.d.ts +0 -107
- package/dist/decorator.esm.js +0 -2
- package/dist/decorator.esm.js.map +0 -1
- package/dist/decorator.js +0 -11
- package/dist/decorator.js.map +0 -1
- package/dist/destroyable.d.ts +0 -90
- package/dist/destroyable.esm.js +0 -109
- package/dist/destroyable.esm.js.map +0 -1
- package/dist/destroyable.js +0 -116
- package/dist/destroyable.js.map +0 -1
- package/dist/eventful.d.ts +0 -20
- package/dist/eventful.esm.js +0 -66
- package/dist/eventful.esm.js.map +0 -1
- package/dist/eventful.js +0 -68
- package/dist/eventful.js.map +0 -1
- package/dist/index.d.ts +0 -19
- package/dist/index.esm.js +0 -8
- package/dist/index.esm.js.map +0 -1
- package/dist/index.js +0 -95
- package/dist/index.js.map +0 -1
- package/dist/indexable.d.ts +0 -243
- package/dist/indexable.esm.js +0 -285
- package/dist/indexable.esm.js.map +0 -1
- package/dist/indexable.js +0 -291
- package/dist/indexable.js.map +0 -1
- package/dist/promiseChain.d.ts +0 -21
- package/dist/promiseChain.esm.js +0 -78
- package/dist/promiseChain.esm.js.map +0 -1
- package/dist/promiseChain.js +0 -80
- package/dist/promiseChain.js.map +0 -1
- package/dist/reactive.d.ts +0 -885
- package/dist/reactive.esm.js +0 -5
- package/dist/reactive.esm.js.map +0 -1
- package/dist/reactive.js +0 -59
- package/dist/reactive.js.map +0 -1
- package/dist/std-decorators.d.ts +0 -52
- package/dist/std-decorators.esm.js +0 -196
- package/dist/std-decorators.esm.js.map +0 -1
- package/dist/std-decorators.js +0 -204
- package/dist/std-decorators.js.map +0 -1
- package/src/reactive/mapped.ts +0 -129
- package/src/reactive/zone.ts +0 -208
package/dist/node.d.ts
ADDED
package/dist/node.esm.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { createHook } from 'node:async_hooks';
|
|
2
|
+
import { a as asyncHooks } from './chunks/index-CNR6QRUl.esm.js';
|
|
3
|
+
export { A as AZone, b as ArrayReadForward, D as DecoratorError, c as Destroyable, d as DestructionError, E as Eventful, F as FoolProof, I as Indexable, e as IterableWeakMap, f as IterableWeakSet, R as ReactiveBase, g as ReactiveError, h as ReactiveErrorCode, i as Register, Z as Zone, j as ZoneAggregator, k as ZoneHistory, l as addBatchCleanup, m as allocated, n as allocatedValues, o as arrayEquals, p as asyncZone, q as atomic, r as biDi, s as buildReactivityGraph, t as cache, u as cached, v as callOnGC, w as chainPromise, x as cleanedBy, y as cleanup, z as contentRef, B as debounce, C as decorator, G as deepCompare, H as deepWatch, J as defer, K as deprecated, L as derived, M as describe, N as destructor, O as effect, P as enableDevTools, Q as forwardArray, S as getActivationLog, T as getActiveProjection, U as getAt, V as getState, W as immutables, X as isCached, Y as isConstructor, _ as isDevtoolsEnabled, $ as isNonReactive, a0 as isOwnAccessor, a1 as isReactive, a2 as legacyDecorator, a3 as memoize, a4 as mixin, a5 as modernDecorator, a6 as named, a7 as organize, a8 as organized, a9 as profileInfo, aa as project, ab as reactive, ac as reactiveOptions, ad as register, ae as registerEffectForDebug, af as registerNativeReactivity, ag as registerObjectForDebug, ah as renamed, ai as root, aj as scan, ak as setAt, al as setEffectName, am as setObjectName, an as tag, ao as throttle, ap as touched, aq as touched1, ar as trackEffect, as as unreactive, at as untracked, au as unwrap, av as watch, aw as zip } from './chunks/index-CNR6QRUl.esm.js';
|
|
4
|
+
|
|
5
|
+
const hooks = new Set();
|
|
6
|
+
const restorersPerAsyncId = new Map();
|
|
7
|
+
const undoersPerAsyncId = new Map();
|
|
8
|
+
createHook({
|
|
9
|
+
init(asyncId) {
|
|
10
|
+
const restorers = new Set();
|
|
11
|
+
for (const hook of hooks) {
|
|
12
|
+
restorers.add(hook());
|
|
13
|
+
}
|
|
14
|
+
restorersPerAsyncId.set(asyncId, restorers);
|
|
15
|
+
},
|
|
16
|
+
before(asyncId) {
|
|
17
|
+
const restorers = restorersPerAsyncId.get(asyncId);
|
|
18
|
+
if (restorers) {
|
|
19
|
+
const undoers = new Set();
|
|
20
|
+
for (const restore of restorers) {
|
|
21
|
+
undoers.add(restore());
|
|
22
|
+
}
|
|
23
|
+
undoersPerAsyncId.set(asyncId, undoers);
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
after(asyncId) {
|
|
27
|
+
const undoers = undoersPerAsyncId.get(asyncId);
|
|
28
|
+
if (undoers) {
|
|
29
|
+
for (const undo of undoers)
|
|
30
|
+
undo();
|
|
31
|
+
undoersPerAsyncId.delete(asyncId);
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
destroy(asyncId) {
|
|
35
|
+
restorersPerAsyncId.delete(asyncId);
|
|
36
|
+
undoersPerAsyncId.delete(asyncId);
|
|
37
|
+
}
|
|
38
|
+
}).enable();
|
|
39
|
+
asyncHooks.addHook = function (hook) {
|
|
40
|
+
hooks.add(hook);
|
|
41
|
+
return () => {
|
|
42
|
+
hooks.delete(hook);
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
//# sourceMappingURL=node.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.esm.js","sources":["../src/async/node.ts"],"sourcesContent":["import { createHook } from 'node:async_hooks'\nimport { Hook, Restorer, asyncHooks } from '.'\n\nconst hooks = new Set<Hook>()\nconst restorersPerAsyncId = new Map<number, Set<Restorer>>()\nconst undoersPerAsyncId = new Map<number, Set<() => void>>()\n\ncreateHook({\n\tinit(asyncId) {\n\t\tconst restorers = new Set<Restorer>()\n\t\tfor (const hook of hooks) {\n\t\t\trestorers.add(hook())\n\t\t}\n\t\trestorersPerAsyncId.set(asyncId, restorers)\n\t},\n\tbefore(asyncId) {\n\t\tconst restorers = restorersPerAsyncId.get(asyncId)\n\t\tif (restorers) {\n\t\t\tconst undoers = new Set<() => void>()\n\t\t\tfor (const restore of restorers) {\n\t\t\t\tundoers.add(restore())\n\t\t\t}\n\t\t\tundoersPerAsyncId.set(asyncId, undoers)\n\t\t}\n\t},\n\tafter(asyncId) {\n\t\tconst undoers = undoersPerAsyncId.get(asyncId)\n\t\tif (undoers) {\n\t\t\tfor (const undo of undoers) undo()\n\t\t\tundoersPerAsyncId.delete(asyncId)\n\t\t}\n\t},\n\tdestroy(asyncId) {\n\t\trestorersPerAsyncId.delete(asyncId)\n\t\tundoersPerAsyncId.delete(asyncId)\n\t}\n}).enable()\n\nasyncHooks.addHook = function (hook: Hook) {\n\thooks.add(hook)\n\treturn () => {\n\t\thooks.delete(hook)\n\t}\n}\n\nexport * from '../index'\n"],"names":[],"mappings":";;;;AAGA,MAAM,KAAK,GAAG,IAAI,GAAG,EAAQ;AAC7B,MAAM,mBAAmB,GAAG,IAAI,GAAG,EAAyB;AAC5D,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAA2B;AAE5D,UAAU,CAAC;AACV,IAAA,IAAI,CAAC,OAAO,EAAA;AACX,QAAA,MAAM,SAAS,GAAG,IAAI,GAAG,EAAY;AACrC,QAAA,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;AACzB,YAAA,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QACtB;AACA,QAAA,mBAAmB,CAAC,GAAG,CAAC,OAAO,EAAE,SAAS,CAAC;IAC5C,CAAC;AACD,IAAA,MAAM,CAAC,OAAO,EAAA;QACb,MAAM,SAAS,GAAG,mBAAmB,CAAC,GAAG,CAAC,OAAO,CAAC;QAClD,IAAI,SAAS,EAAE;AACd,YAAA,MAAM,OAAO,GAAG,IAAI,GAAG,EAAc;AACrC,YAAA,KAAK,MAAM,OAAO,IAAI,SAAS,EAAE;AAChC,gBAAA,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;YACvB;AACA,YAAA,iBAAiB,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC;QACxC;IACD,CAAC;AACD,IAAA,KAAK,CAAC,OAAO,EAAA;QACZ,MAAM,OAAO,GAAG,iBAAiB,CAAC,GAAG,CAAC,OAAO,CAAC;QAC9C,IAAI,OAAO,EAAE;YACZ,KAAK,MAAM,IAAI,IAAI,OAAO;AAAE,gBAAA,IAAI,EAAE;AAClC,YAAA,iBAAiB,CAAC,MAAM,CAAC,OAAO,CAAC;QAClC;IACD,CAAC;AACD,IAAA,OAAO,CAAC,OAAO,EAAA;AACd,QAAA,mBAAmB,CAAC,MAAM,CAAC,OAAO,CAAC;AACnC,QAAA,iBAAiB,CAAC,MAAM,CAAC,OAAO,CAAC;IAClC;CACA,CAAC,CAAC,MAAM,EAAE;AAEX,UAAU,CAAC,OAAO,GAAG,UAAU,IAAU,EAAA;AACxC,IAAA,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;AACf,IAAA,OAAO,MAAK;AACX,QAAA,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC;AACnB,IAAA,CAAC;AACF,CAAC"}
|
package/dist/node.js
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var node_async_hooks = require('node:async_hooks');
|
|
4
|
+
var index = require('./chunks/index-BFYK02LG.js');
|
|
5
|
+
|
|
6
|
+
const hooks = new Set();
|
|
7
|
+
const restorersPerAsyncId = new Map();
|
|
8
|
+
const undoersPerAsyncId = new Map();
|
|
9
|
+
node_async_hooks.createHook({
|
|
10
|
+
init(asyncId) {
|
|
11
|
+
const restorers = new Set();
|
|
12
|
+
for (const hook of hooks) {
|
|
13
|
+
restorers.add(hook());
|
|
14
|
+
}
|
|
15
|
+
restorersPerAsyncId.set(asyncId, restorers);
|
|
16
|
+
},
|
|
17
|
+
before(asyncId) {
|
|
18
|
+
const restorers = restorersPerAsyncId.get(asyncId);
|
|
19
|
+
if (restorers) {
|
|
20
|
+
const undoers = new Set();
|
|
21
|
+
for (const restore of restorers) {
|
|
22
|
+
undoers.add(restore());
|
|
23
|
+
}
|
|
24
|
+
undoersPerAsyncId.set(asyncId, undoers);
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
after(asyncId) {
|
|
28
|
+
const undoers = undoersPerAsyncId.get(asyncId);
|
|
29
|
+
if (undoers) {
|
|
30
|
+
for (const undo of undoers)
|
|
31
|
+
undo();
|
|
32
|
+
undoersPerAsyncId.delete(asyncId);
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
destroy(asyncId) {
|
|
36
|
+
restorersPerAsyncId.delete(asyncId);
|
|
37
|
+
undoersPerAsyncId.delete(asyncId);
|
|
38
|
+
}
|
|
39
|
+
}).enable();
|
|
40
|
+
index.asyncHooks.addHook = function (hook) {
|
|
41
|
+
hooks.add(hook);
|
|
42
|
+
return () => {
|
|
43
|
+
hooks.delete(hook);
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
exports.AZone = index.AZone;
|
|
48
|
+
exports.ArrayReadForward = index.ArrayReadForward;
|
|
49
|
+
exports.DecoratorError = index.DecoratorError;
|
|
50
|
+
exports.Destroyable = index.Destroyable;
|
|
51
|
+
exports.DestructionError = index.DestructionError;
|
|
52
|
+
exports.Eventful = index.Eventful;
|
|
53
|
+
exports.FoolProof = index.FoolProof;
|
|
54
|
+
exports.Indexable = index.Indexable;
|
|
55
|
+
exports.IterableWeakMap = index.IterableWeakMap;
|
|
56
|
+
exports.IterableWeakSet = index.IterableWeakSet;
|
|
57
|
+
exports.ReactiveBase = index.ReactiveBase;
|
|
58
|
+
exports.ReactiveError = index.ReactiveError;
|
|
59
|
+
Object.defineProperty(exports, "ReactiveErrorCode", {
|
|
60
|
+
enumerable: true,
|
|
61
|
+
get: function () { return index.ReactiveErrorCode; }
|
|
62
|
+
});
|
|
63
|
+
exports.Register = index.Register;
|
|
64
|
+
exports.Zone = index.Zone;
|
|
65
|
+
exports.ZoneAggregator = index.ZoneAggregator;
|
|
66
|
+
exports.ZoneHistory = index.ZoneHistory;
|
|
67
|
+
exports.addBatchCleanup = index.addBatchCleanup;
|
|
68
|
+
exports.allocated = index.allocated;
|
|
69
|
+
exports.allocatedValues = index.allocatedValues;
|
|
70
|
+
exports.arrayEquals = index.arrayEquals;
|
|
71
|
+
exports.asyncZone = index.asyncZone;
|
|
72
|
+
exports.atomic = index.atomic;
|
|
73
|
+
exports.biDi = index.biDi;
|
|
74
|
+
exports.buildReactivityGraph = index.buildReactivityGraph;
|
|
75
|
+
exports.cache = index.cache;
|
|
76
|
+
exports.cached = index.cached;
|
|
77
|
+
exports.callOnGC = index.callOnGC;
|
|
78
|
+
exports.chainPromise = index.chainPromise;
|
|
79
|
+
exports.cleanedBy = index.cleanedBy;
|
|
80
|
+
exports.cleanup = index.cleanup;
|
|
81
|
+
exports.contentRef = index.contentRef;
|
|
82
|
+
exports.debounce = index.debounce;
|
|
83
|
+
exports.decorator = index.decorator;
|
|
84
|
+
exports.deepCompare = index.deepCompare;
|
|
85
|
+
exports.deepWatch = index.deepWatch;
|
|
86
|
+
exports.defer = index.defer;
|
|
87
|
+
exports.deprecated = index.deprecated;
|
|
88
|
+
exports.derived = index.derived;
|
|
89
|
+
exports.describe = index.describe;
|
|
90
|
+
exports.destructor = index.destructor;
|
|
91
|
+
exports.effect = index.effect;
|
|
92
|
+
exports.enableDevTools = index.enableDevTools;
|
|
93
|
+
exports.forwardArray = index.forwardArray;
|
|
94
|
+
exports.getActivationLog = index.getActivationLog;
|
|
95
|
+
exports.getActiveProjection = index.getActiveProjection;
|
|
96
|
+
exports.getAt = index.getAt;
|
|
97
|
+
exports.getState = index.getState;
|
|
98
|
+
exports.immutables = index.immutables;
|
|
99
|
+
exports.isCached = index.isCached;
|
|
100
|
+
exports.isConstructor = index.isConstructor;
|
|
101
|
+
exports.isDevtoolsEnabled = index.isDevtoolsEnabled;
|
|
102
|
+
exports.isNonReactive = index.isNonReactive;
|
|
103
|
+
exports.isOwnAccessor = index.isOwnAccessor;
|
|
104
|
+
exports.isReactive = index.isReactive;
|
|
105
|
+
exports.legacyDecorator = index.legacyDecorator;
|
|
106
|
+
exports.memoize = index.memoize;
|
|
107
|
+
exports.mixin = index.mixin;
|
|
108
|
+
exports.modernDecorator = index.modernDecorator;
|
|
109
|
+
exports.named = index.named;
|
|
110
|
+
exports.organize = index.organize;
|
|
111
|
+
exports.organized = index.organized;
|
|
112
|
+
exports.profileInfo = index.profileInfo;
|
|
113
|
+
exports.project = index.project;
|
|
114
|
+
exports.reactive = index.reactive;
|
|
115
|
+
exports.reactiveOptions = index.options;
|
|
116
|
+
exports.register = index.register;
|
|
117
|
+
exports.registerEffectForDebug = index.registerEffectForDebug;
|
|
118
|
+
exports.registerNativeReactivity = index.registerNativeReactivity;
|
|
119
|
+
exports.registerObjectForDebug = index.registerObjectForDebug;
|
|
120
|
+
exports.renamed = index.renamed;
|
|
121
|
+
exports.root = index.root;
|
|
122
|
+
exports.scan = index.scan;
|
|
123
|
+
exports.setAt = index.setAt;
|
|
124
|
+
exports.setEffectName = index.setEffectName;
|
|
125
|
+
exports.setObjectName = index.setObjectName;
|
|
126
|
+
exports.tag = index.tag;
|
|
127
|
+
exports.throttle = index.throttle;
|
|
128
|
+
exports.touched = index.touched;
|
|
129
|
+
exports.touched1 = index.touched1;
|
|
130
|
+
exports.trackEffect = index.trackEffect;
|
|
131
|
+
exports.unreactive = index.unreactive;
|
|
132
|
+
exports.untracked = index.untracked;
|
|
133
|
+
exports.unwrap = index.unwrap;
|
|
134
|
+
exports.watch = index.watch;
|
|
135
|
+
exports.zip = index.zip;
|
|
136
|
+
//# sourceMappingURL=node.js.map
|
package/dist/node.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.js","sources":["../src/async/node.ts"],"sourcesContent":["import { createHook } from 'node:async_hooks'\nimport { Hook, Restorer, asyncHooks } from '.'\n\nconst hooks = new Set<Hook>()\nconst restorersPerAsyncId = new Map<number, Set<Restorer>>()\nconst undoersPerAsyncId = new Map<number, Set<() => void>>()\n\ncreateHook({\n\tinit(asyncId) {\n\t\tconst restorers = new Set<Restorer>()\n\t\tfor (const hook of hooks) {\n\t\t\trestorers.add(hook())\n\t\t}\n\t\trestorersPerAsyncId.set(asyncId, restorers)\n\t},\n\tbefore(asyncId) {\n\t\tconst restorers = restorersPerAsyncId.get(asyncId)\n\t\tif (restorers) {\n\t\t\tconst undoers = new Set<() => void>()\n\t\t\tfor (const restore of restorers) {\n\t\t\t\tundoers.add(restore())\n\t\t\t}\n\t\t\tundoersPerAsyncId.set(asyncId, undoers)\n\t\t}\n\t},\n\tafter(asyncId) {\n\t\tconst undoers = undoersPerAsyncId.get(asyncId)\n\t\tif (undoers) {\n\t\t\tfor (const undo of undoers) undo()\n\t\t\tundoersPerAsyncId.delete(asyncId)\n\t\t}\n\t},\n\tdestroy(asyncId) {\n\t\trestorersPerAsyncId.delete(asyncId)\n\t\tundoersPerAsyncId.delete(asyncId)\n\t}\n}).enable()\n\nasyncHooks.addHook = function (hook: Hook) {\n\thooks.add(hook)\n\treturn () => {\n\t\thooks.delete(hook)\n\t}\n}\n\nexport * from '../index'\n"],"names":["createHook","asyncHooks"],"mappings":";;;;;AAGA,MAAM,KAAK,GAAG,IAAI,GAAG,EAAQ;AAC7B,MAAM,mBAAmB,GAAG,IAAI,GAAG,EAAyB;AAC5D,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAA2B;AAE5DA,2BAAU,CAAC;AACV,IAAA,IAAI,CAAC,OAAO,EAAA;AACX,QAAA,MAAM,SAAS,GAAG,IAAI,GAAG,EAAY;AACrC,QAAA,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;AACzB,YAAA,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QACtB;AACA,QAAA,mBAAmB,CAAC,GAAG,CAAC,OAAO,EAAE,SAAS,CAAC;IAC5C,CAAC;AACD,IAAA,MAAM,CAAC,OAAO,EAAA;QACb,MAAM,SAAS,GAAG,mBAAmB,CAAC,GAAG,CAAC,OAAO,CAAC;QAClD,IAAI,SAAS,EAAE;AACd,YAAA,MAAM,OAAO,GAAG,IAAI,GAAG,EAAc;AACrC,YAAA,KAAK,MAAM,OAAO,IAAI,SAAS,EAAE;AAChC,gBAAA,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;YACvB;AACA,YAAA,iBAAiB,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC;QACxC;IACD,CAAC;AACD,IAAA,KAAK,CAAC,OAAO,EAAA;QACZ,MAAM,OAAO,GAAG,iBAAiB,CAAC,GAAG,CAAC,OAAO,CAAC;QAC9C,IAAI,OAAO,EAAE;YACZ,KAAK,MAAM,IAAI,IAAI,OAAO;AAAE,gBAAA,IAAI,EAAE;AAClC,YAAA,iBAAiB,CAAC,MAAM,CAAC,OAAO,CAAC;QAClC;IACD,CAAC;AACD,IAAA,OAAO,CAAC,OAAO,EAAA;AACd,QAAA,mBAAmB,CAAC,MAAM,CAAC,OAAO,CAAC;AACnC,QAAA,iBAAiB,CAAC,MAAM,CAAC,OAAO,CAAC;IAClC;CACA,CAAC,CAAC,MAAM,EAAE;AAEXC,gBAAU,CAAC,OAAO,GAAG,UAAU,IAAU,EAAA;AACxC,IAAA,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;AACf,IAAA,OAAO,MAAK;AACX,QAAA,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC;AACnB,IAAA,CAAC;AACF,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/docs/ai/api-reference.md
CHANGED
|
@@ -68,8 +68,6 @@ export interface Register<T, K extends PropertyKey = PropertyKey> extends Iterab
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
export declare function register<T, K extends PropertyKey = PropertyKey>(keyFn: (item: T) => K, initial?: Iterable<T>): Register<T, K>;
|
|
71
|
-
export declare function mapped<T, U>(inputs: readonly T[], compute: (input: T, index: number, output: U[]) => U): readonly U[];
|
|
72
|
-
export declare function reduced<T, U, R extends object = any>(inputs: readonly T[], compute: (input: T, factor: R) => readonly U[]): readonly U[];
|
|
73
71
|
export declare function project<S, R>(source: S, apply: (access: any, target: any) => any): R;
|
|
74
72
|
export declare function organized<S, T>(source: S, apply: (access: any, target: T) => any, baseTarget?: T): T;
|
|
75
73
|
|
package/docs/ai/manual.md
CHANGED
|
@@ -2,104 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
> [!IMPORTANT]
|
|
4
4
|
> **Identity**: You are an AI Agent. This document is written for **YOU**.
|
|
5
|
-
> **Purpose**: This file defines the protocols
|
|
6
|
-
>
|
|
7
|
-
> For exact TypeScript signatures of the entire library, consult the **[API Reference for Agents](./api-reference.md)**.
|
|
5
|
+
> **Purpose**: This file defines the protocols and high-level strategy for working with the `mutts` reactivity system.
|
|
8
6
|
|
|
9
|
-
## 1.
|
|
10
|
-
|
|
7
|
+
## 1. Technical Documentation
|
|
8
|
+
Do not rely on outdated internal knowledge. Use the current documentation as your primary source of truth:
|
|
11
9
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
```typescript
|
|
15
|
-
import { enableIntrospection, options } from 'mutts/introspection'
|
|
10
|
+
- **[Debugging Tools](../reactive/debugging.md)**: How to use introspection, troubleshoot cycles, and detect memoization discrepancies.
|
|
11
|
+
- **[API Reference](./api-reference.md)**: Exact TypeScript signatures for library functions.
|
|
16
12
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
```
|
|
13
|
+
## 2. Debugging Protocol
|
|
14
|
+
When you encounter reactivity bugs (infinite loops, unexpected updates), **DO NOT GUESS**.
|
|
20
15
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
import { getDependencyGraph } from 'mutts/introspection'
|
|
16
|
+
1. **Introspect**: Use `mutts/introspection` to inspect the dependency graph or mutation history.
|
|
17
|
+
2. **Analyze Errors**: `ReactiveError` objects contain `debugInfo` with causal chains and creation stacks. Use them to trace the fault.
|
|
18
|
+
3. **Verify**: Use `reactiveOptions.onMemoizationDiscrepancy` in tests to ensure your changes didn't break dependency tracking.
|
|
25
19
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
**
|
|
20
|
+
## 3. Architecture Constraints
|
|
21
|
+
1. **No Internal Access**: Do not access properties starting with `_mutts_` directly.
|
|
22
|
+
2. **Explicit Naming**: Always name your effects (`effect(fn, { name: '...' })`) to make future debugging easier for yourself or other agents.
|
|
23
|
+
3. **Affirmative State**: Prefer derived state and effects over imperative event-driven updates.
|
|
30
24
|
|
|
31
|
-
### Mutation History
|
|
32
|
-
Understanding "What just happened".
|
|
33
|
-
```typescript
|
|
34
|
-
import { getMutationHistory } from 'mutts/introspection'
|
|
35
|
-
|
|
36
|
-
const history = getMutationHistory()
|
|
37
|
-
/*
|
|
38
|
-
Returns Array<{
|
|
39
|
-
id: number,
|
|
40
|
-
type: 'set' | 'add' | 'delete',
|
|
41
|
-
prop: string | symbol,
|
|
42
|
-
oldValue: any,
|
|
43
|
-
newValue: any,
|
|
44
|
-
objectName: string, // "Human readable name"
|
|
45
|
-
source: string, // "Effect X" or "External"
|
|
46
|
-
timestamp: number
|
|
47
|
-
}>
|
|
48
|
-
*/
|
|
49
|
-
```
|
|
50
|
-
**Agent Protocol**: When diagnosing "why is this value wrong?", check the *last mutation* of that property in history.
|
|
51
|
-
|
|
52
|
-
## 2. Structured Error Handling
|
|
53
|
-
`mutts` throws `ReactiveError` instances. These are designed to be machine-parseable.
|
|
54
|
-
|
|
55
|
-
### ReactiveErrorCode
|
|
56
|
-
Always check `error.debugInfo.code`.
|
|
57
|
-
```typescript
|
|
58
|
-
enum ReactiveErrorCode {
|
|
59
|
-
CycleDetected = 'CYCLE_DETECTED',
|
|
60
|
-
MaxDepthExceeded = 'MAX_DEPTH_EXCEEDED',
|
|
61
|
-
MaxReactionExceeded = 'MAX_REACTION_EXCEEDED',
|
|
62
|
-
WriteInComputed = 'WRITE_IN_COMPUTED',
|
|
63
|
-
TrackingError = 'TRACKING_ERROR'
|
|
64
|
-
}
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
### Debugging Protocols
|
|
68
|
-
|
|
69
|
-
#### Protocol: Handling `CycleDetected` / `MaxDepthExceeded`
|
|
70
|
-
**Symptom**: `ReactiveError` with code `CycleDetected` or `MaxDepthExceeded`.
|
|
71
|
-
**Action**:
|
|
72
|
-
1. Read `error.debugInfo.cycle` (Array of effect names).
|
|
73
|
-
2. **CRITICAL**: Read `error.debugInfo.causalChain`.
|
|
74
|
-
* It tells you the *logical path*: `Effect A` -> (modifies `x`) -> `Effect B` -> (modifies `y`) -> `Effect A`.
|
|
75
|
-
3. **CRITICAL**: Read `error.debugInfo.creationStack`.
|
|
76
|
-
* This gives you the **File path and Line number** where the problematic effect was defined.
|
|
77
|
-
|
|
78
|
-
**Example Error Object**:
|
|
79
|
-
```json
|
|
80
|
-
{
|
|
81
|
-
"message": "[reactive] Max effect chain reached...",
|
|
82
|
-
"debugInfo": {
|
|
83
|
-
"code": "MAX_DEPTH_EXCEEDED",
|
|
84
|
-
"causalChain": [
|
|
85
|
-
"Effect(Anonymous) -> (set 'count') -> Effect(Display)",
|
|
86
|
-
"Effect(Display) -> (set 'dummy') -> Effect(Anonymous)"
|
|
87
|
-
],
|
|
88
|
-
"creationStack": " at tests/my-test.ts:45:10\n at ...",
|
|
89
|
-
"cycle": ["Effect(Anonymous)", "Effect(Display)"]
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
## 3. Strict Mode
|
|
95
|
-
If you are writing new code that might be complex, verify it with "Strict Mode".
|
|
96
|
-
This forces a graph check *before* execution, preventing infinite loops from hanging the runtime.
|
|
97
|
-
```typescript
|
|
98
|
-
import { options } from 'mutts/introspection'
|
|
99
|
-
options.cycleHandling = 'strict'
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
## 4. Architecture Constraints for Agents
|
|
103
|
-
1. **Do not** access properties starting with `_mutts_` directly unless instructed. These are internal implementation details.
|
|
104
|
-
2. **Do not** rely on `batchQueue` global state in your reasoning; it is ephemeral.
|
|
105
|
-
3. **Do** name your effects when possible (`effect(() => {}, { name: 'MyEffect' })`) to make your own future debugging easier.
|
|
@@ -1161,120 +1161,16 @@ class Example {
|
|
|
1161
1161
|
}
|
|
1162
1162
|
```
|
|
1163
1163
|
|
|
1164
|
-
### Working with `mapped()`
|
|
1165
|
-
|
|
1166
|
-
Pair `memoize()` with [`mapped()`](#mapped) to keep derived array elements stable across reorders (see [Identity-preserving mapped arrays](#identity-preserving-mapped-arrays)). Memoizing the mapper ensures each input object is processed at most once and its derived state persists while the input reference lives.
|
|
1167
|
-
|
|
1168
1164
|
## Debugging and Development
|
|
1169
1165
|
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
The reactive system automatically detects circular dependencies between effects. When one effect triggers another effect that eventually triggers the first effect again, a cycle is detected.
|
|
1173
|
-
|
|
1174
|
-
#### Cycle Detection Behavior
|
|
1175
|
-
|
|
1176
|
-
By default, cycles are detected and an error is thrown. You can configure the behavior using `reactiveOptions.cycleHandling`:
|
|
1177
|
-
|
|
1178
|
-
```typescript
|
|
1179
|
-
import { reactiveOptions } from 'mutts/reactive'
|
|
1180
|
-
|
|
1181
|
-
// Options: 'throw' (default), 'warn', or 'break'
|
|
1182
|
-
reactiveOptions.cycleHandling = 'warn' // Warn instead of throwing
|
|
1183
|
-
reactiveOptions.cycleHandling = 'break' // Silently break the cycle
|
|
1184
|
-
```
|
|
1185
|
-
|
|
1186
|
-
**Cycle handling modes:**
|
|
1187
|
-
|
|
1188
|
-
- **`'throw'`** (default): Throws a `ReactiveError` with a detailed cycle path when a cycle is detected
|
|
1189
|
-
- **`'warn'`**: Logs a warning message with the cycle path but continues execution (breaks the cycle)
|
|
1190
|
-
- **`'break'`**: Silently breaks the cycle without any message
|
|
1191
|
-
- **`'strict'`**: Checks the graph BEFORE execution. Prevents infinite loops at the source (higher overhead).
|
|
1192
|
-
|
|
1193
|
-
#### Cycle Error Messages
|
|
1194
|
-
|
|
1195
|
-
When a cycle is detected, the error message includes the full cycle path showing which effects form the cycle:
|
|
1196
|
-
|
|
1197
|
-
```typescript
|
|
1198
|
-
const state = reactive({ a: 0, b: 0, c: 0 })
|
|
1199
|
-
|
|
1200
|
-
effect(() => {
|
|
1201
|
-
state.b = state.a + 1 // Effect A
|
|
1202
|
-
})
|
|
1203
|
-
|
|
1204
|
-
effect(() => {
|
|
1205
|
-
state.c = state.b + 1 // Effect B
|
|
1206
|
-
})
|
|
1207
|
-
|
|
1208
|
-
// This will throw with a detailed cycle path
|
|
1209
|
-
try {
|
|
1210
|
-
effect(() => {
|
|
1211
|
-
state.a = state.c + 1 // Effect C - creates cycle: A → B → C → A
|
|
1212
|
-
})
|
|
1213
|
-
} catch (e) {
|
|
1214
|
-
console.error(e.message)
|
|
1215
|
-
// "[reactive] Cycle detected: effectA → effectB → effectC → effectA"
|
|
1216
|
-
}
|
|
1217
|
-
```
|
|
1218
|
-
|
|
1219
|
-
The cycle path shows the sequence of effects that form the circular dependency, making it easier to identify and fix the issue.
|
|
1220
|
-
|
|
1221
|
-
#### Common Cycle Patterns
|
|
1222
|
-
|
|
1223
|
-
**Direct cycle:**
|
|
1224
|
-
```typescript
|
|
1225
|
-
const state = reactive({ a: 0, b: 0 })
|
|
1226
|
-
|
|
1227
|
-
effect(() => {
|
|
1228
|
-
state.b = state.a + 1 // Effect A
|
|
1229
|
-
})
|
|
1230
|
-
|
|
1231
|
-
effect(() => {
|
|
1232
|
-
state.a = state.b + 1 // Effect B - creates cycle: A → B → A
|
|
1233
|
-
})
|
|
1234
|
-
```
|
|
1235
|
-
|
|
1236
|
-
**Indirect cycle:**
|
|
1237
|
-
```typescript
|
|
1238
|
-
const state = reactive({ a: 0, b: 0, c: 0 })
|
|
1239
|
-
|
|
1240
|
-
effect(() => {
|
|
1241
|
-
state.b = state.a + 1 // Effect A
|
|
1242
|
-
})
|
|
1243
|
-
|
|
1244
|
-
effect(() => {
|
|
1245
|
-
state.c = state.b + 1 // Effect B
|
|
1246
|
-
})
|
|
1247
|
-
|
|
1248
|
-
effect(() => {
|
|
1249
|
-
state.a = state.c + 1 // Effect C - creates cycle: A → B → C → A
|
|
1250
|
-
})
|
|
1251
|
-
```
|
|
1252
|
-
|
|
1253
|
-
#### Preventing Cycles
|
|
1254
|
-
|
|
1255
|
-
To avoid cycles, consider:
|
|
1166
|
+
The `mutts` reactive system includes built-in tools for troubleshooting complex dependency graphs and identifying performance bottlenecks.
|
|
1256
1167
|
|
|
1257
|
-
|
|
1258
|
-
2. **Use `untracked()`**: For operations that shouldn't create dependencies
|
|
1259
|
-
3. **Use `atomic()`**: To batch operations and prevent intermediate triggers
|
|
1260
|
-
4. **Restructure logic**: Break circular dependencies by introducing intermediate state or computed values
|
|
1168
|
+
For a full guide on debugging, including cycle detection and memoization discrepancy tools, see the dedicated **[Debugging Tools](./debugging.md)** documentation.
|
|
1261
1169
|
|
|
1262
|
-
|
|
1263
|
-
```typescript
|
|
1264
|
-
const state = reactive({ count: 0 })
|
|
1265
|
-
|
|
1266
|
-
effect(() => {
|
|
1267
|
-
// Read count
|
|
1268
|
-
const current = state.count
|
|
1269
|
-
|
|
1270
|
-
// Write to count without creating a dependency cycle
|
|
1271
|
-
untracked(() => {
|
|
1272
|
-
if (current < 10) {
|
|
1273
|
-
state.count = current + 1
|
|
1274
|
-
}
|
|
1275
|
-
})
|
|
1276
|
-
})
|
|
1277
|
-
```
|
|
1170
|
+
### Quick Summary
|
|
1278
1171
|
|
|
1279
|
-
|
|
1172
|
+
- **Cycle Detection**: Automatically catch circular dependencies via `reactiveOptions.cycleHandling`. Note: Instant mathematical detection requires choosing a mode other than `'none'`.
|
|
1173
|
+
- **Flat Mode**: The default `reactiveOptions.cycleHandling = 'none'` provides maximum performance in high-frequency update scenarios by disabling graph maintenance.
|
|
1174
|
+
- **Memoization Discrepancy**: Detect "missing dependencies" by running computations twice during development using `reactiveOptions.onMemoizationDiscrepancy`.
|
|
1175
|
+
- **Global Hooks**: Use `reactiveOptions.touched`, `enter`, and `leave` to observe system activity.
|
|
1280
1176
|
|
|
@@ -352,115 +352,6 @@ Notes:
|
|
|
352
352
|
- Assigning to an index (`list[i] = value`) uses the key function to bind that slot to `value`’s key.
|
|
353
353
|
|
|
354
354
|
## Class Reactivity
|
|
355
|
-
## Array Mapping
|
|
356
|
-
|
|
357
|
-
### `mapped()`
|
|
358
|
-
|
|
359
|
-
Creates a reactive array by mapping over an input array. The mapper receives the current item value, its index, and the previous mapped value for that index.
|
|
360
|
-
|
|
361
|
-
```typescript
|
|
362
|
-
import { mapped, reactive } from 'mutts/reactive'
|
|
363
|
-
|
|
364
|
-
const input = reactive([1, 2, 3])
|
|
365
|
-
const doubles = mapped(input, (value, index, oldValue) => value * 2)
|
|
366
|
-
|
|
367
|
-
console.log(doubles) // [2, 4, 6]
|
|
368
|
-
|
|
369
|
-
// When input changes, the mapped output updates in place
|
|
370
|
-
input.push(4)
|
|
371
|
-
console.log(doubles) // [2, 4, 6, 8]
|
|
372
|
-
```
|
|
373
|
-
|
|
374
|
-
**Mapper signature:**
|
|
375
|
-
|
|
376
|
-
```typescript
|
|
377
|
-
(value: T, index: number, oldValue?: U) => U
|
|
378
|
-
```
|
|
379
|
-
|
|
380
|
-
- **value**: current element from the input array
|
|
381
|
-
- **index**: current element index
|
|
382
|
-
- **oldValue**: previously computed value at the same index (useful for incremental updates)
|
|
383
|
-
|
|
384
|
-
**Key features:**
|
|
385
|
-
|
|
386
|
-
- **Live reactivity**: Output array updates when the input array changes (push/pop/splice/assignments).
|
|
387
|
-
- **Granular recompute**: Only indices that change are recomputed; `oldValue` enables incremental updates.
|
|
388
|
-
- **Simple contract**: Mapper works directly with `(value, index, oldValue)` and can freely return reactive objects.
|
|
389
|
-
|
|
390
|
-
**Performance characteristics:**
|
|
391
|
-
|
|
392
|
-
```typescript
|
|
393
|
-
const users = reactive([
|
|
394
|
-
{ name: 'John', age: 30 },
|
|
395
|
-
{ name: 'Jane', age: 25 }
|
|
396
|
-
])
|
|
397
|
-
|
|
398
|
-
let computeCount = 0
|
|
399
|
-
const processedUsers = mapped(users, (user) => {
|
|
400
|
-
computeCount++
|
|
401
|
-
return `${user.name} (${user.age})`
|
|
402
|
-
})
|
|
403
|
-
|
|
404
|
-
console.log(computeCount) // 2 (initial computation)
|
|
405
|
-
|
|
406
|
-
// Modify one user - only that index recomputes
|
|
407
|
-
users[0].age = 31
|
|
408
|
-
console.log(processedUsers[0]) // "John (31)"
|
|
409
|
-
console.log(computeCount) // 3
|
|
410
|
-
```
|
|
411
|
-
|
|
412
|
-
**Advanced usage:**
|
|
413
|
-
|
|
414
|
-
```typescript
|
|
415
|
-
const orders = reactive([
|
|
416
|
-
{ items: [{ price: 10 }, { price: 20 }] },
|
|
417
|
-
{ items: [{ price: 15 }] }
|
|
418
|
-
])
|
|
419
|
-
|
|
420
|
-
const orderTotals = mapped(orders, (order) => (
|
|
421
|
-
order.items.reduce((sum, item) => sum + item.price, 0)
|
|
422
|
-
))
|
|
423
|
-
```
|
|
424
|
-
|
|
425
|
-
### Identity-preserving mapped arrays
|
|
426
|
-
|
|
427
|
-
Combine `mapped()` with [`memoize()`](#memoize) when you need to reuse mapped results for the same input identity. The memoized mapper runs at most once per input object, even when the source array is reordered, and it can host additional reactive state that should survive reordering.
|
|
428
|
-
|
|
429
|
-
```typescript
|
|
430
|
-
import { effect, mapped, memoize, reactive } from 'mutts/reactive'
|
|
431
|
-
|
|
432
|
-
const inputs = reactive([{ name: 'John' }, { name: 'Jane' }])
|
|
433
|
-
|
|
434
|
-
const memoizedCard = memoize((user: { name: string }) => {
|
|
435
|
-
const view: { name?: string; setName(next: string): void } = {
|
|
436
|
-
setName(next) {
|
|
437
|
-
user.name = next
|
|
438
|
-
},
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
effect(() => {
|
|
442
|
-
view.name = user.name.toUpperCase()
|
|
443
|
-
})
|
|
444
|
-
|
|
445
|
-
return view
|
|
446
|
-
})
|
|
447
|
-
|
|
448
|
-
const cards = mapped(inputs, (user) => memoizedCard(user))
|
|
449
|
-
|
|
450
|
-
cards[0].setName('Johnny')
|
|
451
|
-
console.log(cards[0].name) // 'JOHNNY'
|
|
452
|
-
|
|
453
|
-
// Reorder: cached output follows the original object
|
|
454
|
-
const first = inputs.shift()!
|
|
455
|
-
inputs.push(first)
|
|
456
|
-
console.log(cards[1].name) // still 'JOHNNY'
|
|
457
|
-
```
|
|
458
|
-
|
|
459
|
-
Use this pattern when:
|
|
460
|
-
|
|
461
|
-
- You are mapping an array of reactive objects and want to keep derived objects stable across reorders.
|
|
462
|
-
- The mapper returns objects with internal state or nested effects that should survive reordering.
|
|
463
|
-
- You prefer to share memoized helpers across multiple mapped arrays.
|
|
464
355
|
|
|
465
356
|
## Projection
|
|
466
357
|
|
|
@@ -468,8 +359,6 @@ Use this pattern when:
|
|
|
468
359
|
|
|
469
360
|
`project()` provides a unified API for transforming reactive collections (arrays, records, and maps) into new reactive collections. Each source entry gets its own reactive effect that recomputes only when that specific entry changes, enabling granular updates perfect for rendering pipelines.
|
|
470
361
|
|
|
471
|
-
**Note:** `project()` is the modern replacement for `mapped()`. It offers the same per-entry reactivity benefits but works across all collection types with a consistent API.
|
|
472
|
-
|
|
473
362
|
#### Basic Usage
|
|
474
363
|
|
|
475
364
|
```typescript
|
|
@@ -617,23 +506,10 @@ result[cleanup]() // Stops all effects and cleans up
|
|
|
617
506
|
- **Data Transformation**: Convert between collection types while maintaining reactivity
|
|
618
507
|
- **Performance Optimization**: Avoid full recomputation when only a few entries change
|
|
619
508
|
|
|
620
|
-
#### Comparison with `mapped()`
|
|
621
|
-
|
|
622
|
-
`project()` is designed to eventually replace `mapped()`. Key differences:
|
|
623
|
-
|
|
624
|
-
- **Unified API**: Works with arrays, records, and maps (vs. `mapped()` only for arrays)
|
|
625
|
-
- **Access Pattern**: Uses an access object with `get()`/`set()` instead of direct value/index parameters
|
|
626
|
-
- **Automatic Target Creation**: Creates its own reactive target container (no need to provide a base target)
|
|
627
|
-
- **Consistent Behavior**: Same per-entry reactivity model across all collection types
|
|
628
|
-
|
|
629
|
-
For new code, prefer `project()` over `mapped()`. Existing `mapped()` code will continue to work, but consider migrating for better consistency and future features.
|
|
630
|
-
|
|
631
509
|
## Record Organization
|
|
632
510
|
|
|
633
511
|
### `organized()`
|
|
634
512
|
|
|
635
|
-
`organized()` is the record companion to [`mapped()`](#mapped). Instead of iterating over numeric indices, it reacts to property additions, updates, and deletions on any `Record<PropertyKey, T>` (plain objects, dictionaries, even reactive proxies) and lets you build **whatever target structure you need**—a new record, nested buckets, a `Map`, or a more elaborate object with metadata.
|
|
636
|
-
|
|
637
513
|
```typescript
|
|
638
514
|
import { cleanup, organized, reactive } from 'mutts/reactive'
|
|
639
515
|
|
|
@@ -677,7 +553,6 @@ function organized<
|
|
|
677
553
|
|
|
678
554
|
Under the hood there is:
|
|
679
555
|
|
|
680
|
-
- One effect watching `Reflect.ownKeys(source)` (similar to how `mapped()` tracks `length`)
|
|
681
556
|
- A child effect per key that re-runs whenever that key’s value changes, automatically reusing and replacing the cleanup you returned.
|
|
682
557
|
- Automatic disposal when keys disappear or when `target[cleanup]()` is invoked.
|
|
683
558
|
|