mutts 1.0.12 → 1.0.13
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 +5 -2
- package/dist/browser.cjs +7 -3
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.ts +1407 -2
- package/dist/browser.dev.cjs +7 -3
- package/dist/browser.dev.cjs.map +1 -1
- package/dist/browser.dev.d.ts +2 -2
- package/dist/browser.dev.esm.js +2 -2
- package/dist/browser.esm.js +3 -3
- package/dist/chunks/{index-yK0HVxHv.cjs → index-CAdnMJev.cjs} +202 -79
- package/dist/chunks/index-CAdnMJev.cjs.map +1 -0
- package/dist/chunks/{index-BUop6B2U.esm.js → index-XsYTUhHx.esm.js} +200 -77
- package/dist/chunks/index-XsYTUhHx.esm.js.map +1 -0
- package/dist/chunks/{node-Dd0esp5F.cjs → node-DrrphEPf.cjs} +2 -2
- package/dist/chunks/{node-Dd0esp5F.cjs.map → node-DrrphEPf.cjs.map} +1 -1
- package/dist/chunks/{node-Bo7WU5S2.esm.js → node-NEZvVo4M.esm.js} +2 -2
- package/dist/chunks/{node-Bo7WU5S2.esm.js.map → node-NEZvVo4M.esm.js.map} +1 -1
- package/dist/chunks/{proxy-D2C49sXH.esm.js → proxy-BtmPFjSr.esm.js} +307 -66
- package/dist/chunks/proxy-BtmPFjSr.esm.js.map +1 -0
- package/dist/chunks/{proxy-BvM4yewA.cjs → proxy-DBHj3kGK.cjs} +313 -66
- package/dist/chunks/proxy-DBHj3kGK.cjs.map +1 -0
- package/dist/debug.cjs +537 -166
- package/dist/debug.cjs.map +1 -1
- package/dist/debug.d.ts +96 -80
- package/dist/debug.esm.js +533 -166
- package/dist/debug.esm.js.map +1 -1
- package/dist/devtools/panel.js.map +1 -1
- package/dist/mutts.umd.js +508 -140
- 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.cjs +8 -4
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.ts +2 -2
- package/dist/node.dev.cjs +8 -4
- package/dist/node.dev.cjs.map +1 -1
- package/dist/node.dev.d.ts +2 -2
- package/dist/node.dev.esm.js +3 -3
- package/dist/node.esm.js +3 -3
- package/dist/{types-Bx2PhORg.d.ts → types.d.ts} +12 -0
- package/docs/ai/api-reference.md +102 -12
- package/docs/ai/manual.md +60 -24
- package/docs/debug-getReason.md +161 -0
- package/docs/flavored.md +98 -1
- package/docs/reactive/advanced.md +15 -2
- package/docs/reactive/attend.md +32 -0
- package/docs/reactive/core.md +40 -6
- package/docs/reactive/debugging.md +25 -2
- package/docs/reactive.md +2 -0
- package/package.json +2 -3
- package/dist/chunks/index-BUop6B2U.esm.js.map +0 -1
- package/dist/chunks/index-yK0HVxHv.cjs.map +0 -1
- package/dist/chunks/proxy-BvM4yewA.cjs.map +0 -1
- package/dist/chunks/proxy-D2C49sXH.esm.js.map +0 -1
- package/dist/index.d.ts +0 -1322
package/dist/mutts.umd.js
CHANGED
|
@@ -357,13 +357,10 @@
|
|
|
357
357
|
return !!(opd?.get || opd?.set);
|
|
358
358
|
}
|
|
359
359
|
/**
|
|
360
|
-
*
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
* @param b - Second value
|
|
365
|
-
* @param cache - Map for circular reference protection (internal use)
|
|
366
|
-
* @returns True if values are deeply equal
|
|
360
|
+
* Symbol used to provide custom comparison logic for an object.
|
|
361
|
+
*/
|
|
362
|
+
const CompareSymbol = Symbol.for('mutts.compare');
|
|
363
|
+
/**
|
|
367
364
|
*/
|
|
368
365
|
function deepCompare(a, b, cache = new Map()) {
|
|
369
366
|
if (a === b)
|
|
@@ -371,6 +368,13 @@
|
|
|
371
368
|
if (typeof a !== 'object' || a === null || typeof b !== 'object' || b === null) {
|
|
372
369
|
return a === b;
|
|
373
370
|
}
|
|
371
|
+
// Custom comparison support
|
|
372
|
+
if (typeof a[CompareSymbol] === 'function') {
|
|
373
|
+
return a[CompareSymbol](b, (x, y) => deepCompare(x, y, cache));
|
|
374
|
+
}
|
|
375
|
+
if (typeof b[CompareSymbol] === 'function') {
|
|
376
|
+
return b[CompareSymbol](a, (x, y) => deepCompare(x, y, cache));
|
|
377
|
+
}
|
|
374
378
|
// Prototype check
|
|
375
379
|
if (Object.getPrototypeOf(a) !== Object.getPrototypeOf(b))
|
|
376
380
|
return false;
|
|
@@ -489,7 +493,8 @@
|
|
|
489
493
|
});
|
|
490
494
|
return fn;
|
|
491
495
|
}
|
|
492
|
-
const
|
|
496
|
+
const runtimeGlobals$1 = globalThis;
|
|
497
|
+
const _mode = runtimeGlobals$1.process?.env?.NODE_ENV ||
|
|
493
498
|
(typeof ({ url: (typeof document === 'undefined' && typeof location === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : typeof document === 'undefined' ? location.href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('mutts.umd.js', document.baseURI).href)) }) !== 'undefined' && undefined?.MODE) ||
|
|
494
499
|
'production';
|
|
495
500
|
const isDev = _mode === 'development';
|
|
@@ -893,46 +898,57 @@
|
|
|
893
898
|
var _a$1, _b$1;
|
|
894
899
|
const events = Symbol('events');
|
|
895
900
|
const hooks = Symbol('hooks');
|
|
901
|
+
function getEventMap(target) {
|
|
902
|
+
return target[events];
|
|
903
|
+
}
|
|
904
|
+
function getHookSet(target) {
|
|
905
|
+
return target[hooks];
|
|
906
|
+
}
|
|
896
907
|
const eventBehavior = {
|
|
897
908
|
on(eventOrEvents, cb) {
|
|
909
|
+
const self = this;
|
|
910
|
+
const eventMap = getEventMap(self);
|
|
898
911
|
if (typeof eventOrEvents === 'object') {
|
|
899
912
|
for (const e of Object.keys(eventOrEvents)) {
|
|
900
913
|
this.on(e, eventOrEvents[e]);
|
|
901
914
|
}
|
|
902
915
|
}
|
|
903
916
|
else if (cb !== undefined) {
|
|
904
|
-
const callbacks =
|
|
917
|
+
const callbacks = eventMap.get(eventOrEvents) ?? new Set();
|
|
905
918
|
if (!callbacks.has(cb))
|
|
906
919
|
callbacks.add(cb);
|
|
907
|
-
|
|
920
|
+
eventMap.set(eventOrEvents, callbacks);
|
|
908
921
|
}
|
|
909
922
|
return () => this.off(eventOrEvents, cb);
|
|
910
923
|
},
|
|
911
924
|
off(eventOrEvents, cb) {
|
|
925
|
+
const self = this;
|
|
926
|
+
const eventMap = getEventMap(self);
|
|
912
927
|
if (typeof eventOrEvents === 'object') {
|
|
913
928
|
for (const e of Object.keys(eventOrEvents)) {
|
|
914
929
|
this.off(e, eventOrEvents[e]);
|
|
915
930
|
}
|
|
916
931
|
}
|
|
917
932
|
else if (cb !== null && cb !== undefined) {
|
|
918
|
-
const callbacks =
|
|
933
|
+
const callbacks = eventMap.get(eventOrEvents);
|
|
919
934
|
if (callbacks) {
|
|
920
935
|
callbacks.delete(cb);
|
|
921
936
|
if (!callbacks.size)
|
|
922
|
-
|
|
937
|
+
eventMap.delete(eventOrEvents);
|
|
923
938
|
}
|
|
924
939
|
}
|
|
925
940
|
else {
|
|
926
941
|
// Remove all listeners for this event
|
|
927
|
-
|
|
942
|
+
eventMap.delete(eventOrEvents);
|
|
928
943
|
}
|
|
929
944
|
},
|
|
930
945
|
emit(event, ...args) {
|
|
931
|
-
const
|
|
946
|
+
const self = this;
|
|
947
|
+
const callbacks = getEventMap(self).get(event);
|
|
932
948
|
if (callbacks)
|
|
933
949
|
for (const cb of callbacks)
|
|
934
950
|
cb.apply(this, args);
|
|
935
|
-
for (const cb of
|
|
951
|
+
for (const cb of getHookSet(self))
|
|
936
952
|
cb.call(this, event, ...args);
|
|
937
953
|
},
|
|
938
954
|
};
|
|
@@ -942,7 +958,7 @@
|
|
|
942
958
|
get(target, prop) {
|
|
943
959
|
if (typeof prop !== 'string')
|
|
944
960
|
return target[prop];
|
|
945
|
-
if (use && !eventful
|
|
961
|
+
if (use && !getEventMap(eventful).has(prop) && !getHookSet(eventful).size)
|
|
946
962
|
return () => { };
|
|
947
963
|
// Return cached function or create and cache
|
|
948
964
|
let cached = cache.get(prop);
|
|
@@ -1003,6 +1019,93 @@
|
|
|
1003
1019
|
* flavoredGreet.loud('World') // "HELLO, WORLD!"
|
|
1004
1020
|
* ```
|
|
1005
1021
|
*/
|
|
1022
|
+
const captionedOptionsSymbol = Symbol('mutts.captioned.options');
|
|
1023
|
+
function isTemplateStringsArray(value) {
|
|
1024
|
+
return (Array.isArray(value) &&
|
|
1025
|
+
Object.hasOwn(value, 'raw') &&
|
|
1026
|
+
Array.isArray(value.raw));
|
|
1027
|
+
}
|
|
1028
|
+
function renderTemplate(strings, values) {
|
|
1029
|
+
let result = strings[0] ?? '';
|
|
1030
|
+
for (let i = 0; i < values.length; i++)
|
|
1031
|
+
result += String(values[i]) + (strings[i + 1] ?? '');
|
|
1032
|
+
return result;
|
|
1033
|
+
}
|
|
1034
|
+
function renameCallback(caption, callback) {
|
|
1035
|
+
Object.defineProperty(callback, 'name', {
|
|
1036
|
+
value: caption,
|
|
1037
|
+
writable: false,
|
|
1038
|
+
configurable: true,
|
|
1039
|
+
});
|
|
1040
|
+
return callback;
|
|
1041
|
+
}
|
|
1042
|
+
function isAnonymousCallback(callback) {
|
|
1043
|
+
return !callback.name || callback.name === 'anonymous';
|
|
1044
|
+
}
|
|
1045
|
+
/**
|
|
1046
|
+
* Wraps a callback-first function so it also accepts a tagged-template call form.
|
|
1047
|
+
*
|
|
1048
|
+
* The template caption is applied to one callback argument before the base
|
|
1049
|
+
* function runs. By default, `captioned` targets the first argument, but
|
|
1050
|
+
* `callbackIndex` can point to any callback position.
|
|
1051
|
+
*
|
|
1052
|
+
* This is intended for APIs such as `effect`, `lift`, or `watch` where naming
|
|
1053
|
+
* is useful but should remain separate from the flavor system.
|
|
1054
|
+
*
|
|
1055
|
+
* Plain calls still work:
|
|
1056
|
+
* `run(callback)`
|
|
1057
|
+
*
|
|
1058
|
+
* Captioned calls add a runtime name to the first callback:
|
|
1059
|
+
* `` run`task:${id}`(callback) ``
|
|
1060
|
+
*
|
|
1061
|
+
* Anonymous uncaptioned callbacks may trigger a warning depending on
|
|
1062
|
+
* `shouldWarnAnonymous`.
|
|
1063
|
+
*/
|
|
1064
|
+
function captioned(fn, options = {}) {
|
|
1065
|
+
const settings = {
|
|
1066
|
+
callbackIndex: options.callbackIndex ?? 0,
|
|
1067
|
+
name: options.name ?? (fn.name || 'callback'),
|
|
1068
|
+
rename: options.rename ?? ((caption, callback) => renameCallback(caption, callback)),
|
|
1069
|
+
// biome-ignore lint/suspicious/noConsole: This is the whole point here
|
|
1070
|
+
warn: options.warn ?? ((message) => console.warn(message)),
|
|
1071
|
+
shouldWarnAnonymous: options.shouldWarnAnonymous,
|
|
1072
|
+
};
|
|
1073
|
+
fn[captionedOptionsSymbol] = settings;
|
|
1074
|
+
return new Proxy(fn, {
|
|
1075
|
+
get(target, prop, receiver) {
|
|
1076
|
+
if (prop === captionedOptionsSymbol)
|
|
1077
|
+
return settings;
|
|
1078
|
+
return Reflect.get(target, prop, receiver);
|
|
1079
|
+
},
|
|
1080
|
+
apply(target, thisArg, args) {
|
|
1081
|
+
if (isTemplateStringsArray(args[0])) {
|
|
1082
|
+
const caption = renderTemplate(args[0], args.slice(1));
|
|
1083
|
+
return function captionedCall(...callArgs) {
|
|
1084
|
+
const callback = callArgs[settings.callbackIndex];
|
|
1085
|
+
if (typeof callback !== 'function')
|
|
1086
|
+
throw new TypeError(`${settings.name} template calls require a callback at argument index ${settings.callbackIndex}`);
|
|
1087
|
+
const nextArgs = [...callArgs];
|
|
1088
|
+
nextArgs[settings.callbackIndex] = settings.rename(caption, callback);
|
|
1089
|
+
return Reflect.apply(target, this, nextArgs);
|
|
1090
|
+
};
|
|
1091
|
+
}
|
|
1092
|
+
const callback = args[settings.callbackIndex];
|
|
1093
|
+
if (typeof callback === 'function' && isAnonymousCallback(callback)) {
|
|
1094
|
+
const shouldWarn = settings.shouldWarnAnonymous?.(callback, args) ?? true;
|
|
1095
|
+
if (shouldWarn)
|
|
1096
|
+
settings.warn(`${settings.name}: anonymous callback detected. Use template syntax for automatic naming:\n` +
|
|
1097
|
+
` Current: ${settings.name}(() => { ... })\n` +
|
|
1098
|
+
` Fix: ${settings.name}\`descriptive-name\`(() => { ... })\n` +
|
|
1099
|
+
`The captioned system uses the template literal as the effect name for better debugging.`);
|
|
1100
|
+
}
|
|
1101
|
+
return Reflect.apply(target, thisArg, args);
|
|
1102
|
+
},
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
function inheritCaption(source, target) {
|
|
1106
|
+
const settings = source[captionedOptionsSymbol];
|
|
1107
|
+
return settings ? captioned(target, settings) : target;
|
|
1108
|
+
}
|
|
1006
1109
|
/**
|
|
1007
1110
|
* Creates a flavored (extensible) version of a function with chainable property modifiers.
|
|
1008
1111
|
*/
|
|
@@ -1035,7 +1138,7 @@
|
|
|
1035
1138
|
};
|
|
1036
1139
|
if (name)
|
|
1037
1140
|
named(name, fct);
|
|
1038
|
-
return flavored(fct, fn.flavors || {});
|
|
1141
|
+
return flavored(inheritCaption(fn, fct), fn.flavors || {});
|
|
1039
1142
|
}
|
|
1040
1143
|
/**
|
|
1041
1144
|
* Creates a new flavored function that merges options objects at a specific index.
|
|
@@ -1068,7 +1171,7 @@
|
|
|
1068
1171
|
// Preserve arity and options track
|
|
1069
1172
|
Object.defineProperty(fct, 'length', { value: fn.length });
|
|
1070
1173
|
fct.optionsIndex = targetIndex;
|
|
1071
|
-
return flavored(fct, fn.flavors || {});
|
|
1174
|
+
return flavored(inheritCaption(fn, fct), fn.flavors || {});
|
|
1072
1175
|
}
|
|
1073
1176
|
|
|
1074
1177
|
/**
|
|
@@ -2065,6 +2168,7 @@
|
|
|
2065
2168
|
* @returns The marked function
|
|
2066
2169
|
*/
|
|
2067
2170
|
function markWithRoot(fn, root) {
|
|
2171
|
+
const marked = fn;
|
|
2068
2172
|
// Check for collision
|
|
2069
2173
|
const existingRef = reverseRoots.get(root);
|
|
2070
2174
|
const existing = existingRef?.deref();
|
|
@@ -2079,8 +2183,8 @@
|
|
|
2079
2183
|
// (Last writer wins for the check)
|
|
2080
2184
|
reverseRoots.set(root, new WeakRef(fn));
|
|
2081
2185
|
// Store root mapping as symbol property on the function
|
|
2082
|
-
|
|
2083
|
-
return
|
|
2186
|
+
marked[rootFunctionSymbol] = getRoot(root);
|
|
2187
|
+
return marked;
|
|
2084
2188
|
}
|
|
2085
2189
|
/**
|
|
2086
2190
|
* Gets the root function of a function for effect tracking
|
|
@@ -2100,11 +2204,30 @@
|
|
|
2100
2204
|
const effectHistory = tag('effectHistory', new ZoneHistory());
|
|
2101
2205
|
tag('effectHistory.present', effectHistory.present);
|
|
2102
2206
|
asyncZone.add(effectHistory);
|
|
2207
|
+
const externalReason = tag('externalReason', new Zone());
|
|
2208
|
+
asyncZone.add(externalReason);
|
|
2103
2209
|
/**
|
|
2104
2210
|
* Aggregator for zones that need to be tracked along effects.
|
|
2105
2211
|
* ie. in each effect, the active zone of the given zoning will be the one active at effect's definition
|
|
2106
2212
|
*/
|
|
2107
2213
|
const effectAggregator = tag('effectAggregator', new ZoneAggregator(effectHistory.present));
|
|
2214
|
+
effectAggregator.add(externalReason);
|
|
2215
|
+
function chainExternalReason(reason) {
|
|
2216
|
+
const external = externalReason.active;
|
|
2217
|
+
if (!external)
|
|
2218
|
+
return reason;
|
|
2219
|
+
if (!reason)
|
|
2220
|
+
return external;
|
|
2221
|
+
let current = reason;
|
|
2222
|
+
while (current) {
|
|
2223
|
+
if (current.type === 'external' &&
|
|
2224
|
+
external.type === 'external' &&
|
|
2225
|
+
current.detail === external.detail)
|
|
2226
|
+
return reason;
|
|
2227
|
+
current = current.chain;
|
|
2228
|
+
}
|
|
2229
|
+
return { ...reason, chain: chainExternalReason(reason.chain) };
|
|
2230
|
+
}
|
|
2108
2231
|
function isRunning(effect) {
|
|
2109
2232
|
const root = getRoot(effect);
|
|
2110
2233
|
return effectHistory.some((e) => getRoot(e) === root);
|
|
@@ -2112,6 +2235,35 @@
|
|
|
2112
2235
|
function getActiveEffect() {
|
|
2113
2236
|
return effectHistory.present.active;
|
|
2114
2237
|
}
|
|
2238
|
+
/**
|
|
2239
|
+
* Captures the current effect context so that deferred code can later
|
|
2240
|
+
* create child effects parented to this point in the effect tree.
|
|
2241
|
+
*
|
|
2242
|
+
* @returns An opaque token to pass to `withEffectContext()`
|
|
2243
|
+
*
|
|
2244
|
+
* @example
|
|
2245
|
+
* ```ts
|
|
2246
|
+
* const ctx = effectContext() // inside an effect or root()
|
|
2247
|
+
* // later, in a deferred callback:
|
|
2248
|
+
* withEffectContext(ctx, () => {
|
|
2249
|
+
* effect(() => { /* child of the captured context */ })
|
|
2250
|
+
* })
|
|
2251
|
+
* ```
|
|
2252
|
+
*/
|
|
2253
|
+
function effectContext() {
|
|
2254
|
+
return effectHistory.active;
|
|
2255
|
+
}
|
|
2256
|
+
/**
|
|
2257
|
+
* Runs `fn` within a previously captured effect context.
|
|
2258
|
+
* Any effects created inside `fn` become children of the captured parent.
|
|
2259
|
+
*
|
|
2260
|
+
* @param ctx - The context token from `effectContext()`, or `undefined` for root context
|
|
2261
|
+
* @param fn - The function to execute within the restored context
|
|
2262
|
+
* @returns The return value of `fn`
|
|
2263
|
+
*/
|
|
2264
|
+
function withEffectContext(ctx, fn) {
|
|
2265
|
+
return effectHistory.with(ctx, fn);
|
|
2266
|
+
}
|
|
2115
2267
|
const cleanups = new WeakMap();
|
|
2116
2268
|
/**
|
|
2117
2269
|
* Attach cleanup dependencies to an object. When `unlink(obj)` is called,
|
|
@@ -2139,7 +2291,7 @@
|
|
|
2139
2291
|
function link(obj, ...cleanupFns) {
|
|
2140
2292
|
const set = cleanups.get(obj);
|
|
2141
2293
|
if (!set)
|
|
2142
|
-
cleanups.set(obj, new Set(cleanupFns.filter(
|
|
2294
|
+
cleanups.set(obj, new Set(cleanupFns.filter((fn) => fn !== undefined)));
|
|
2143
2295
|
else
|
|
2144
2296
|
for (const fn of cleanupFns)
|
|
2145
2297
|
if (fn)
|
|
@@ -2205,18 +2357,61 @@
|
|
|
2205
2357
|
parts.push(',');
|
|
2206
2358
|
parts.push(...formatTrigger(reason.triggers[i]));
|
|
2207
2359
|
}
|
|
2360
|
+
if (reason.chain) {
|
|
2361
|
+
parts.push('\n', ...formatCleanupReason(reason.chain, depth));
|
|
2362
|
+
}
|
|
2363
|
+
return parts;
|
|
2364
|
+
}
|
|
2365
|
+
case 'stopped': {
|
|
2366
|
+
const parts = [`${indent}stopped`];
|
|
2367
|
+
if (reason.detail)
|
|
2368
|
+
parts.push(`(${reason.detail})`);
|
|
2369
|
+
if (reason.chain) {
|
|
2370
|
+
parts.push('\n', ...formatCleanupReason(reason.chain, depth));
|
|
2371
|
+
}
|
|
2372
|
+
return parts;
|
|
2373
|
+
}
|
|
2374
|
+
case 'external': {
|
|
2375
|
+
const parts = [`${indent}external:`, reason.detail];
|
|
2376
|
+
if (reason.chain) {
|
|
2377
|
+
parts.push('\n', ...formatCleanupReason(reason.chain, depth));
|
|
2378
|
+
}
|
|
2379
|
+
return parts;
|
|
2380
|
+
}
|
|
2381
|
+
case 'gc': {
|
|
2382
|
+
const parts = [`${indent}gc`];
|
|
2383
|
+
if (reason.chain) {
|
|
2384
|
+
parts.push('\n', ...formatCleanupReason(reason.chain, depth));
|
|
2385
|
+
}
|
|
2386
|
+
return parts;
|
|
2387
|
+
}
|
|
2388
|
+
case 'error': {
|
|
2389
|
+
const parts = [`${indent}error:`, reason.error];
|
|
2390
|
+
if (reason.chain) {
|
|
2391
|
+
parts.push('\n', ...formatCleanupReason(reason.chain, depth));
|
|
2392
|
+
}
|
|
2393
|
+
return parts;
|
|
2394
|
+
}
|
|
2395
|
+
case 'lineage': {
|
|
2396
|
+
const parts = [
|
|
2397
|
+
`${indent}lineage ←\n`,
|
|
2398
|
+
...formatCleanupReason(reason.parent, depth + 1),
|
|
2399
|
+
];
|
|
2400
|
+
if (reason.chain) {
|
|
2401
|
+
parts.push('\n', ...formatCleanupReason(reason.chain, depth));
|
|
2402
|
+
}
|
|
2403
|
+
return parts;
|
|
2404
|
+
}
|
|
2405
|
+
case 'invalidate': {
|
|
2406
|
+
const parts = [
|
|
2407
|
+
`${indent}invalidate ←\n`,
|
|
2408
|
+
...formatCleanupReason(reason.cause, depth + 1),
|
|
2409
|
+
];
|
|
2410
|
+
if (reason.chain) {
|
|
2411
|
+
parts.push('\n', ...formatCleanupReason(reason.chain, depth));
|
|
2412
|
+
}
|
|
2208
2413
|
return parts;
|
|
2209
2414
|
}
|
|
2210
|
-
case 'stopped':
|
|
2211
|
-
return [`${indent}stopped`];
|
|
2212
|
-
case 'gc':
|
|
2213
|
-
return [`${indent}gc`];
|
|
2214
|
-
case 'error':
|
|
2215
|
-
return [`${indent}error:`, reason.error];
|
|
2216
|
-
case 'lineage':
|
|
2217
|
-
return [`${indent}lineage ←\n`, ...formatCleanupReason(reason.parent, depth + 1)];
|
|
2218
|
-
case 'invalidate':
|
|
2219
|
-
return [`${indent}invalidate ←\n`, ...formatCleanupReason(reason.cause, depth + 1)];
|
|
2220
2415
|
case 'multiple': {
|
|
2221
2416
|
const parts = [];
|
|
2222
2417
|
for (let i = 0; i < reason.reasons.length; i++) {
|
|
@@ -2224,6 +2419,9 @@
|
|
|
2224
2419
|
parts.push('\n');
|
|
2225
2420
|
parts.push(...formatCleanupReason(reason.reasons[i], depth));
|
|
2226
2421
|
}
|
|
2422
|
+
if (reason.chain) {
|
|
2423
|
+
parts.push('\n', ...formatCleanupReason(reason.chain, depth));
|
|
2424
|
+
}
|
|
2227
2425
|
return parts;
|
|
2228
2426
|
}
|
|
2229
2427
|
}
|
|
@@ -2424,6 +2622,8 @@
|
|
|
2424
2622
|
asyncMode: 'cancel',
|
|
2425
2623
|
// biome-ignore lint/suspicious/noConsole: This is the whole point here
|
|
2426
2624
|
warn: (...args) => console.warn(...args),
|
|
2625
|
+
// biome-ignore lint/suspicious/noConsole: This is the whole point here
|
|
2626
|
+
error: (...args) => console.error(...args),
|
|
2427
2627
|
/**
|
|
2428
2628
|
* Introspection and debug aids. Set to `null` to disable all debug overhead in production.
|
|
2429
2629
|
*
|
|
@@ -2553,7 +2753,7 @@
|
|
|
2553
2753
|
return;
|
|
2554
2754
|
const node = getEffectNode(currentActiveEffect);
|
|
2555
2755
|
if ('dependencyHook' in node) {
|
|
2556
|
-
node.dependencyHook(obj, prop);
|
|
2756
|
+
node.dependencyHook?.(obj, prop);
|
|
2557
2757
|
}
|
|
2558
2758
|
let objectWatchers = watchers.get(obj);
|
|
2559
2759
|
if (!objectWatchers) {
|
|
@@ -2619,6 +2819,9 @@
|
|
|
2619
2819
|
const end = names.slice(-10);
|
|
2620
2820
|
return `${start.join(' → ')} ... (${names.length - 15} more) ... ${end.join(' → ')}`;
|
|
2621
2821
|
}
|
|
2822
|
+
function externalReasonFrom(fn) {
|
|
2823
|
+
return fn.name ? { type: 'external', detail: fn.name } : undefined;
|
|
2824
|
+
}
|
|
2622
2825
|
// Nested map structure for efficient counting and batch cleanup
|
|
2623
2826
|
// batchId -> effect root -> obj -> prop -> count
|
|
2624
2827
|
let activationRegistry;
|
|
@@ -3061,6 +3264,14 @@
|
|
|
3061
3264
|
// Build reason from pending triggers if not provided
|
|
3062
3265
|
if (!reason && node.pendingTriggers) {
|
|
3063
3266
|
reason = { type: 'propChange', triggers: node.pendingTriggers };
|
|
3267
|
+
// Add chain: if this is being triggered from another effect, get its reason
|
|
3268
|
+
if (caller) {
|
|
3269
|
+
const callerNode = getEffectNode(caller);
|
|
3270
|
+
if (callerNode.currentReason) {
|
|
3271
|
+
reason.chain = callerNode.currentReason;
|
|
3272
|
+
}
|
|
3273
|
+
}
|
|
3274
|
+
reason = chainExternalReason(reason);
|
|
3064
3275
|
}
|
|
3065
3276
|
node.pendingTriggers = undefined;
|
|
3066
3277
|
if (reason) {
|
|
@@ -3329,7 +3540,7 @@
|
|
|
3329
3540
|
}
|
|
3330
3541
|
// Track which sub-effects have been executed to prevent infinite loops
|
|
3331
3542
|
// These are all the effects triggered under `activeEffect` and all their sub-effects
|
|
3332
|
-
function batch(effect, immediate) {
|
|
3543
|
+
function batch(effect, immediate, caller) {
|
|
3333
3544
|
if (broken) {
|
|
3334
3545
|
throw new ReactiveError('[reactive] Reactive system is broken after an unrecoverable error. Call reset() to recover.', { code: exports.ReactiveErrorCode.BrokenEffects });
|
|
3335
3546
|
}
|
|
@@ -3345,11 +3556,12 @@
|
|
|
3345
3556
|
optionCall('beginChain', roots);
|
|
3346
3557
|
}
|
|
3347
3558
|
// TODO: Consider this has been produced but was useless - it might be more correct ?const caller = executingStack.length > 0 ? getActiveEffect() : undefined
|
|
3348
|
-
const
|
|
3559
|
+
const activeCaller = getActiveEffect();
|
|
3560
|
+
const callerToUse = caller || activeCaller;
|
|
3349
3561
|
// Optimization: If nested and NOT immediate, just join the existing batch
|
|
3350
3562
|
if (!isNewBatch && !immediate) {
|
|
3351
3563
|
for (let i = 0; i < effect.length; i++) {
|
|
3352
|
-
addToBatch(effect[i],
|
|
3564
|
+
addToBatch(effect[i], callerToUse);
|
|
3353
3565
|
}
|
|
3354
3566
|
return;
|
|
3355
3567
|
}
|
|
@@ -3388,7 +3600,7 @@
|
|
|
3388
3600
|
else {
|
|
3389
3601
|
// Add initial effects to batch and compute dependencies
|
|
3390
3602
|
for (let i = 0; i < effect.length; i++) {
|
|
3391
|
-
addToBatch(effect[i],
|
|
3603
|
+
addToBatch(effect[i], callerToUse, false);
|
|
3392
3604
|
}
|
|
3393
3605
|
computeAllInDegrees(currentBatch);
|
|
3394
3606
|
}
|
|
@@ -3442,6 +3654,11 @@
|
|
|
3442
3654
|
success = true;
|
|
3443
3655
|
return firstReturn.value;
|
|
3444
3656
|
}
|
|
3657
|
+
catch (error) {
|
|
3658
|
+
if (batchStack.length === 1)
|
|
3659
|
+
optionCall('error', '[reactive] Root batch failure before broken state:', error);
|
|
3660
|
+
throw error;
|
|
3661
|
+
}
|
|
3445
3662
|
finally {
|
|
3446
3663
|
if (!success && batchStack.length === 1) {
|
|
3447
3664
|
broken = true;
|
|
@@ -3542,7 +3759,7 @@
|
|
|
3542
3759
|
* @param options - Options for effect execution
|
|
3543
3760
|
* @returns A cleanup function to stop the effect
|
|
3544
3761
|
*/
|
|
3545
|
-
const effect = named(effectMarker.leave, flavored(function effect(fn, effectOptions = {}) {
|
|
3762
|
+
const effect = captioned(named(effectMarker.leave, flavored(function effect(fn, effectOptions = {}) {
|
|
3546
3763
|
if (effectOptions?.name)
|
|
3547
3764
|
Object.defineProperty(fn, 'name', { value: effectOptions.name });
|
|
3548
3765
|
// Use per-effect asyncMode or fall back to global option
|
|
@@ -3555,7 +3772,10 @@
|
|
|
3555
3772
|
const prevCleanup = node.cleanup;
|
|
3556
3773
|
node.cleanup = undefined;
|
|
3557
3774
|
try {
|
|
3558
|
-
untracked(() => prevCleanup(node.nextReason || {
|
|
3775
|
+
untracked `effect:cleanup`(() => prevCleanup(chainExternalReason(node.nextReason || {
|
|
3776
|
+
type: 'stopped',
|
|
3777
|
+
chain: node.currentReason,
|
|
3778
|
+
})));
|
|
3559
3779
|
}
|
|
3560
3780
|
catch (error) {
|
|
3561
3781
|
// If we want to report them, we could use options.warn or similar
|
|
@@ -3588,6 +3808,9 @@
|
|
|
3588
3808
|
}
|
|
3589
3809
|
// Set reaction reason for the upcoming run
|
|
3590
3810
|
access.reaction = node.nextReason || access.reaction;
|
|
3811
|
+
node.currentReason =
|
|
3812
|
+
node.nextReason ||
|
|
3813
|
+
(access.reaction && access.reaction !== true ? access.reaction : undefined);
|
|
3591
3814
|
node.nextReason = undefined;
|
|
3592
3815
|
optionCall('enter', getRoot(fn));
|
|
3593
3816
|
optionCall('effectRun', getRoot(fn), access.reaction);
|
|
@@ -3650,7 +3873,8 @@
|
|
|
3650
3873
|
// This ensures that when we cancel, the original promise's .catch() handlers are triggered
|
|
3651
3874
|
// We do this by rejecting the race promise, which makes the original promise chain see the rejection
|
|
3652
3875
|
// through the zone-wrapped .then()/.catch() handlers
|
|
3653
|
-
runningPromise = runningPromise
|
|
3876
|
+
runningPromise = runningPromise
|
|
3877
|
+
.catch((error) => {
|
|
3654
3878
|
// Propagate async errors to the effect's error handler
|
|
3655
3879
|
// This ensures onEffectThrow handlers are triggered for async errors
|
|
3656
3880
|
if (error !== cancelError) {
|
|
@@ -3658,6 +3882,10 @@
|
|
|
3658
3882
|
}
|
|
3659
3883
|
// If thrower didn't throw (handled), we absorb the error.
|
|
3660
3884
|
// If thrower threw (unhandled), it propagates as a new unhandled rejection, which is correct.
|
|
3885
|
+
})
|
|
3886
|
+
.finally(() => {
|
|
3887
|
+
// Clear currentReason when async effect completes
|
|
3888
|
+
node.currentReason = undefined;
|
|
3661
3889
|
});
|
|
3662
3890
|
}
|
|
3663
3891
|
else {
|
|
@@ -3667,7 +3895,13 @@
|
|
|
3667
3895
|
}
|
|
3668
3896
|
catch (error) {
|
|
3669
3897
|
// catcher:self`
|
|
3670
|
-
errorToThrow = error;
|
|
3898
|
+
errorToThrow = error instanceof Error ? error : new Error(String(error));
|
|
3899
|
+
}
|
|
3900
|
+
finally {
|
|
3901
|
+
// Clear currentReason for synchronous effects
|
|
3902
|
+
if (!runningPromise) {
|
|
3903
|
+
node.currentReason = undefined;
|
|
3904
|
+
}
|
|
3671
3905
|
}
|
|
3672
3906
|
// Create cleanup function for next run
|
|
3673
3907
|
node.cleanup = (reason) => {
|
|
@@ -3698,8 +3932,11 @@
|
|
|
3698
3932
|
const childReason = reason
|
|
3699
3933
|
? reason.type === 'lineage'
|
|
3700
3934
|
? reason
|
|
3701
|
-
: { type: 'lineage', parent: reason }
|
|
3702
|
-
: { type: 'stopped' }
|
|
3935
|
+
: { type: 'lineage', parent: reason, chain: node.currentReason }
|
|
3936
|
+
: (chainExternalReason({ type: 'stopped', chain: node.currentReason }) ?? {
|
|
3937
|
+
type: 'stopped',
|
|
3938
|
+
chain: node.currentReason,
|
|
3939
|
+
});
|
|
3703
3940
|
for (const childCleanup of children)
|
|
3704
3941
|
childCleanup(childReason);
|
|
3705
3942
|
delete node.children;
|
|
@@ -3763,7 +4000,7 @@
|
|
|
3763
4000
|
runningPromise = null;
|
|
3764
4001
|
}
|
|
3765
4002
|
try {
|
|
3766
|
-
node.cleanup?.(reason || { type: 'stopped' });
|
|
4003
|
+
node.cleanup?.(chainExternalReason(reason || { type: 'stopped', chain: node.currentReason }));
|
|
3767
4004
|
}
|
|
3768
4005
|
catch (error) {
|
|
3769
4006
|
// Cleanup errors should basically be ignored or at least not stop the world
|
|
@@ -3806,30 +4043,35 @@
|
|
|
3806
4043
|
named(name) {
|
|
3807
4044
|
return flavorOptions(this, { name }, { name: 'named' });
|
|
3808
4045
|
},
|
|
3809
|
-
}))
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
return
|
|
3817
|
-
|
|
4046
|
+
})), {
|
|
4047
|
+
name: 'effect',
|
|
4048
|
+
warn: (message) => options.warn(`[reactive] ${message}`),
|
|
4049
|
+
shouldWarnAnonymous: (_callback, args) => !(args[1] && typeof args[1] === 'object' && 'name' in args[1]),
|
|
4050
|
+
});
|
|
4051
|
+
const untracked = captioned(function untracked(fn) {
|
|
4052
|
+
const external = externalReasonFrom(fn);
|
|
4053
|
+
return external
|
|
4054
|
+
? externalReason.with(external, () => effectHistory.present.root(fn))
|
|
4055
|
+
: effectHistory.present.root(fn);
|
|
4056
|
+
});
|
|
3818
4057
|
/**
|
|
3819
4058
|
* Executes a function from a virgin/root context - no parent effect, no tracking
|
|
3820
4059
|
* Creates completely independent effects that won't be cleaned up by any parent
|
|
3821
4060
|
* @param fn - The function to execute
|
|
3822
4061
|
*/
|
|
3823
|
-
function root(fn) {
|
|
3824
|
-
|
|
3825
|
-
|
|
4062
|
+
const root = captioned(function root(fn) {
|
|
4063
|
+
const external = externalReasonFrom(fn);
|
|
4064
|
+
return external
|
|
4065
|
+
? externalReason.with(external, () => effectHistory.root(fn))
|
|
4066
|
+
: effectHistory.root(fn);
|
|
4067
|
+
});
|
|
3826
4068
|
function biDi(received, get, set) {
|
|
3827
4069
|
if (typeof get !== 'function') {
|
|
3828
4070
|
set = get.set;
|
|
3829
4071
|
get = get.get;
|
|
3830
4072
|
}
|
|
3831
4073
|
let programmaticallySetValue = Symbol();
|
|
3832
|
-
effect
|
|
4074
|
+
effect `biDi`(markWithRoot(() => {
|
|
3833
4075
|
const newValue = get();
|
|
3834
4076
|
const pValue = programmaticallySetValue;
|
|
3835
4077
|
programmaticallySetValue = Symbol();
|
|
@@ -3980,7 +4222,8 @@
|
|
|
3980
4222
|
const deps = objectWatchers.get(key);
|
|
3981
4223
|
if (deps) {
|
|
3982
4224
|
// Make sure `some.prop++` does not keep a dependency to `some.props`
|
|
3983
|
-
|
|
4225
|
+
if (sourceEffect)
|
|
4226
|
+
deps.delete(sourceEffect);
|
|
3984
4227
|
for (const effect of deps) {
|
|
3985
4228
|
const runningChain = isRunning(effect);
|
|
3986
4229
|
if (runningChain) {
|
|
@@ -4025,6 +4268,7 @@
|
|
|
4025
4268
|
else
|
|
4026
4269
|
collectEffects(obj, evolution, effects, objectWatchers, objectWatchers.keys());
|
|
4027
4270
|
const triggers = Array.from(effects.keys());
|
|
4271
|
+
const sourceEffect = getActiveEffect();
|
|
4028
4272
|
optionCall('touched', obj, evolution, props, triggers);
|
|
4029
4273
|
// Store pending triggers for CleanupReason before batching
|
|
4030
4274
|
if (options.introspection?.gatherReasons) {
|
|
@@ -4046,7 +4290,7 @@
|
|
|
4046
4290
|
});
|
|
4047
4291
|
}
|
|
4048
4292
|
}
|
|
4049
|
-
batch(triggers);
|
|
4293
|
+
batch(triggers, undefined, sourceEffect);
|
|
4050
4294
|
}
|
|
4051
4295
|
// Bubble up changes if this object has deep watchers
|
|
4052
4296
|
if (objectsWithDeepWatchers.has(obj)) {
|
|
@@ -4066,7 +4310,7 @@
|
|
|
4066
4310
|
if (!deps)
|
|
4067
4311
|
return;
|
|
4068
4312
|
const effects = new Set();
|
|
4069
|
-
getActiveEffect();
|
|
4313
|
+
const sourceEffect = getActiveEffect();
|
|
4070
4314
|
const gather = options.introspection?.gatherReasons;
|
|
4071
4315
|
if (gather) {
|
|
4072
4316
|
const lineageConfig = gather.lineages;
|
|
@@ -4118,7 +4362,7 @@
|
|
|
4118
4362
|
}
|
|
4119
4363
|
if (effects.size > 0) {
|
|
4120
4364
|
optionCall('touched', obj, evolution, [prop], Array.from(effects));
|
|
4121
|
-
batch(Array.from(effects));
|
|
4365
|
+
batch(Array.from(effects), undefined, sourceEffect);
|
|
4122
4366
|
}
|
|
4123
4367
|
}
|
|
4124
4368
|
|
|
@@ -4140,9 +4384,10 @@
|
|
|
4140
4384
|
return proto;
|
|
4141
4385
|
}
|
|
4142
4386
|
// Merge sets
|
|
4143
|
-
|
|
4387
|
+
const merged = new Set(existing);
|
|
4388
|
+
proto[unreactiveProperties] = merged;
|
|
4144
4389
|
for (const p of set)
|
|
4145
|
-
|
|
4390
|
+
merged.add(p);
|
|
4146
4391
|
}
|
|
4147
4392
|
// If no set, mark as fully unreactive, otherwise create set
|
|
4148
4393
|
else
|
|
@@ -4241,7 +4486,7 @@
|
|
|
4241
4486
|
const origin = { obj: unwrappedObj, prop };
|
|
4242
4487
|
// Deep touch: only notify nested property changes with origin filtering
|
|
4243
4488
|
// Don't notify direct property change - the whole point is to avoid parent effects re-running
|
|
4244
|
-
const changes = untracked(() => recursiveTouch(oldValue, newValue, new WeakMap(), [], origin));
|
|
4489
|
+
const changes = untracked `deepTouch:recursive`(() => recursiveTouch(oldValue, newValue, new WeakMap(), [], origin));
|
|
4245
4490
|
// When deep touch found no child differences, the object identity still changed.
|
|
4246
4491
|
// Migrate watchers from old → new so the dependency chain is preserved.
|
|
4247
4492
|
if (changes.length === 0) {
|
|
@@ -4467,6 +4712,17 @@
|
|
|
4467
4712
|
// Internal untracked flag for setter/getter operations - only used when testing oldValue while setting a value
|
|
4468
4713
|
// TODO: `touched` trigger also compares to old value and should use the internalUntracked flag
|
|
4469
4714
|
let internalUntracked = false;
|
|
4715
|
+
function wrapReactiveValue(obj, prop, value) {
|
|
4716
|
+
if (!isReactive(value) && typeof value === 'object' && value !== null) {
|
|
4717
|
+
const reactiveValue = reactiveObject(value);
|
|
4718
|
+
// Only create back-references if this object needs them
|
|
4719
|
+
if (needsBackReferences(obj)) {
|
|
4720
|
+
addBackReference(reactiveValue, obj, prop);
|
|
4721
|
+
}
|
|
4722
|
+
return reactiveValue;
|
|
4723
|
+
}
|
|
4724
|
+
return value;
|
|
4725
|
+
}
|
|
4470
4726
|
const reactiveHandlers = {
|
|
4471
4727
|
[Symbol.toStringTag]: 'MutTs Reactive',
|
|
4472
4728
|
get(obj, prop, receiver) {
|
|
@@ -4494,6 +4750,10 @@
|
|
|
4494
4750
|
// Symbols: fast-path — no reactivity tracking
|
|
4495
4751
|
if (typeof prop === 'symbol' || prop === 'constructor' || isUnreactiveProp(obj, prop))
|
|
4496
4752
|
return FoolProof.get(obj, prop, receiver);
|
|
4753
|
+
if (!getActiveEffect()) {
|
|
4754
|
+
const value = (subsRegister.get(obj)?.get || FoolProof.get)(obj, prop, receiver);
|
|
4755
|
+
return wrapReactiveValue(obj, prop, value);
|
|
4756
|
+
}
|
|
4497
4757
|
// Check if property exists using a trap-free walk to avoid triggering
|
|
4498
4758
|
// the has-trap cascade on prototype chains of reactive proxies.
|
|
4499
4759
|
const isOwnProp = Object.hasOwn(obj, prop);
|
|
@@ -4533,15 +4793,7 @@
|
|
|
4533
4793
|
// For arrays, use FoolProof.get (Indexer path) for numeric index reactivity.
|
|
4534
4794
|
// For all other objects, inline Reflect.get directly (skips 3 function calls).
|
|
4535
4795
|
const value = (subsRegister.get(obj)?.get || FoolProof.get)(obj, prop, receiver);
|
|
4536
|
-
|
|
4537
|
-
const reactiveValue = reactiveObject(value);
|
|
4538
|
-
// Only create back-references if this object needs them
|
|
4539
|
-
if (needsBackReferences(obj)) {
|
|
4540
|
-
addBackReference(reactiveValue, obj, prop);
|
|
4541
|
-
}
|
|
4542
|
-
return reactiveValue;
|
|
4543
|
-
}
|
|
4544
|
-
return value;
|
|
4796
|
+
return wrapReactiveValue(obj, prop, value);
|
|
4545
4797
|
},
|
|
4546
4798
|
set(obj, prop, value, receiver) {
|
|
4547
4799
|
const unwrapped = unwrap(receiver);
|
|
@@ -4717,7 +4969,7 @@
|
|
|
4717
4969
|
default: reactiveObject,
|
|
4718
4970
|
});
|
|
4719
4971
|
|
|
4720
|
-
function attend(source, callback) {
|
|
4972
|
+
const attend = captioned(function attend(source, callback) {
|
|
4721
4973
|
const enumerate = typeof source === 'function'
|
|
4722
4974
|
? source
|
|
4723
4975
|
: Array.isArray(source)
|
|
@@ -4728,14 +4980,16 @@
|
|
|
4728
4980
|
? () => source.values()
|
|
4729
4981
|
: () => Object.keys(source);
|
|
4730
4982
|
const keyEffects = new Map();
|
|
4731
|
-
const
|
|
4983
|
+
const callbackLabel = callback.name ? callback.name : '';
|
|
4984
|
+
const outer = effect `attend`(({ ascend }) => {
|
|
4732
4985
|
const keys = new Set();
|
|
4733
4986
|
for (const key of enumerate())
|
|
4734
4987
|
keys.add(key);
|
|
4735
4988
|
for (const key of keys) {
|
|
4736
4989
|
if (keyEffects.has(key))
|
|
4737
4990
|
continue;
|
|
4738
|
-
|
|
4991
|
+
const indexRef = { value: key };
|
|
4992
|
+
keyEffects.set(key, ascend(() => effect `attend${callbackLabel ? `:${callbackLabel}` : ''}:${key}`((access) => callback(indexRef.value, access))));
|
|
4739
4993
|
}
|
|
4740
4994
|
for (const key of Array.from(keyEffects.keys())) {
|
|
4741
4995
|
if (!keys.has(key)) {
|
|
@@ -4750,17 +5004,50 @@
|
|
|
4750
5004
|
stop(reason);
|
|
4751
5005
|
keyEffects.clear();
|
|
4752
5006
|
};
|
|
4753
|
-
}
|
|
4754
|
-
|
|
5007
|
+
}, {
|
|
5008
|
+
name: 'attend',
|
|
5009
|
+
callbackIndex: 1,
|
|
5010
|
+
warn: (message) => options.warn(`[reactive] ${message}`),
|
|
5011
|
+
});
|
|
5012
|
+
/**
|
|
5013
|
+
* Lifts a callback that returns an object into a reactive object that automatically
|
|
5014
|
+
* synchronizes with the source object returned by the callback.
|
|
5015
|
+
*
|
|
5016
|
+
* The returned reactive object will update whenever the callback's dependencies change,
|
|
5017
|
+
* efficiently syncing only the properties that differ from the previous result using
|
|
5018
|
+
* Object.assign(). Properties that no longer exist in the source are automatically removed.
|
|
5019
|
+
*
|
|
5020
|
+
* @example
|
|
5021
|
+
* ```typescript
|
|
5022
|
+
* const user = reactive({ name: 'John', age: 30 })
|
|
5023
|
+
* const profile = lift(() => ({
|
|
5024
|
+
* displayName: user.name.toUpperCase(),
|
|
5025
|
+
* isAdult: user.age >= 18,
|
|
5026
|
+
* description: `${user.name} is ${user.age} years old`
|
|
5027
|
+
* }))
|
|
5028
|
+
*
|
|
5029
|
+
* console.log(profile.displayName) // JOHN
|
|
5030
|
+
* console.log(profile.isAdult) // true
|
|
5031
|
+
*
|
|
5032
|
+
* user.name = 'Jane'
|
|
5033
|
+
* console.log(profile.displayName) // JANE
|
|
5034
|
+
* console.log(profile.description) // Jane is 30 years old
|
|
5035
|
+
* ```
|
|
5036
|
+
*
|
|
5037
|
+
* @param cb Callback function that returns an object
|
|
5038
|
+
* @returns A reactive object synchronized with the callback's result, with a [cleanup] property to stop tracking
|
|
5039
|
+
*/
|
|
5040
|
+
const lift = captioned(function lift(cb) {
|
|
4755
5041
|
let result;
|
|
4756
5042
|
let rawResult;
|
|
4757
|
-
const
|
|
5043
|
+
const resultName = `lift:${cb.name || 'anonymous'}`;
|
|
5044
|
+
const liftCleanup = effect `lift:${cb.name}`(markWithRoot((access) => {
|
|
4758
5045
|
const source = cb(access);
|
|
4759
5046
|
if (!source || typeof source !== 'object')
|
|
4760
5047
|
throw new Error('lift callback must return an array or object');
|
|
4761
5048
|
const sourceProto = Object.getPrototypeOf(source);
|
|
4762
5049
|
if (!result) {
|
|
4763
|
-
rawResult = Array.isArray(source) ? [] : Object.create(sourceProto);
|
|
5050
|
+
rawResult = tag(resultName, Array.isArray(source) ? [] : Object.create(sourceProto));
|
|
4764
5051
|
result = reactive(rawResult);
|
|
4765
5052
|
}
|
|
4766
5053
|
if (sourceProto !== Object.getPrototypeOf(result))
|
|
@@ -4771,6 +5058,7 @@
|
|
|
4771
5058
|
res.splice(indexA, sliceA.length, ...sliceB);
|
|
4772
5059
|
}
|
|
4773
5060
|
else {
|
|
5061
|
+
const recordResult = rawResult;
|
|
4774
5062
|
for (const key of Object.keys(source)) {
|
|
4775
5063
|
const had = key in rawResult;
|
|
4776
5064
|
const newDesc = Object.getOwnPropertyDescriptor(source, key);
|
|
@@ -4779,7 +5067,7 @@
|
|
|
4779
5067
|
const sameAccessor = oldDesc && newDesc.get && oldDesc.get === newDesc.get;
|
|
4780
5068
|
Object.defineProperty(rawResult, key, newDesc);
|
|
4781
5069
|
if (!sameAccessor &&
|
|
4782
|
-
|
|
5070
|
+
recordResult[key] !==
|
|
4783
5071
|
(oldDesc ? (oldDesc.get ? oldDesc.get() : oldDesc.value) : undefined))
|
|
4784
5072
|
touched1(rawResult, { type: 'set', prop: key }, key);
|
|
4785
5073
|
}
|
|
@@ -4790,13 +5078,16 @@
|
|
|
4790
5078
|
}
|
|
4791
5079
|
for (const key of Object.keys(rawResult))
|
|
4792
5080
|
if (!(key in source)) {
|
|
4793
|
-
delete
|
|
5081
|
+
delete recordResult[key];
|
|
4794
5082
|
touched1(rawResult, { type: 'del', prop: key }, key);
|
|
4795
5083
|
}
|
|
4796
5084
|
}
|
|
4797
5085
|
}, cb));
|
|
4798
5086
|
return link(result, liftCleanup);
|
|
4799
|
-
}
|
|
5087
|
+
}, {
|
|
5088
|
+
name: 'lift',
|
|
5089
|
+
warn: (message) => options.warn(`[reactive] ${message}`),
|
|
5090
|
+
});
|
|
4800
5091
|
/**
|
|
4801
5092
|
* Reactively maps an array source through `fn`, producing a lazy reactive output array.
|
|
4802
5093
|
*
|
|
@@ -4820,12 +5111,18 @@
|
|
|
4820
5111
|
}
|
|
4821
5112
|
let track;
|
|
4822
5113
|
const itemEffects = new Map();
|
|
4823
|
-
const cache = [];
|
|
5114
|
+
const cache = tag(`morph:${fn.name || 'anonymous'}`, []);
|
|
4824
5115
|
let input = [];
|
|
4825
5116
|
function stopItem(key) {
|
|
4826
5117
|
const entry = itemEffects.get(key);
|
|
4827
5118
|
if (entry) {
|
|
4828
|
-
|
|
5119
|
+
const activeEffect = getActiveEffect();
|
|
5120
|
+
let chain;
|
|
5121
|
+
if (activeEffect) {
|
|
5122
|
+
const node = getEffectNode(activeEffect);
|
|
5123
|
+
chain = node.currentReason;
|
|
5124
|
+
}
|
|
5125
|
+
entry.stop(chainExternalReason({ type: 'stopped', chain }));
|
|
4829
5126
|
itemEffects.delete(key);
|
|
4830
5127
|
}
|
|
4831
5128
|
}
|
|
@@ -4838,12 +5135,22 @@
|
|
|
4838
5135
|
}
|
|
4839
5136
|
else {
|
|
4840
5137
|
const indexRef = { value: key };
|
|
4841
|
-
const stop = track(() => effect.
|
|
5138
|
+
const stop = track(() => effect.opaque `morph:${fn.name}:${key}`((access) => {
|
|
4842
5139
|
cache[indexRef.value] = fn(input, access);
|
|
4843
5140
|
return (reason) => {
|
|
4844
5141
|
delete cache[indexRef.value];
|
|
4845
5142
|
touched1(cache, { type: 'invalidate', prop: 'morph' }, String(key));
|
|
4846
|
-
|
|
5143
|
+
const activeEffect = getActiveEffect();
|
|
5144
|
+
let chain;
|
|
5145
|
+
if (activeEffect) {
|
|
5146
|
+
const node = getEffectNode(activeEffect);
|
|
5147
|
+
chain = node.currentReason;
|
|
5148
|
+
}
|
|
5149
|
+
stop?.({
|
|
5150
|
+
type: 'invalidate',
|
|
5151
|
+
cause: chainExternalReason(reason ?? { type: 'stopped', chain }),
|
|
5152
|
+
chain: chainExternalReason(chain),
|
|
5153
|
+
});
|
|
4847
5154
|
};
|
|
4848
5155
|
}));
|
|
4849
5156
|
itemEffects.set(key, { stop, index: indexRef });
|
|
@@ -4862,7 +5169,7 @@
|
|
|
4862
5169
|
return Reflect.has(input, prop);
|
|
4863
5170
|
},
|
|
4864
5171
|
});
|
|
4865
|
-
const stopMain = effect
|
|
5172
|
+
const stopMain = effect `morph:${fn.name}`(({ ascend }) => {
|
|
4866
5173
|
track = ascend;
|
|
4867
5174
|
const newInput = [...(typeof source === 'function' ? source() : source)];
|
|
4868
5175
|
const diffs = arrayDiff(input, newInput).toSorted((a, b) => b.indexA - a.indexA);
|
|
@@ -4936,12 +5243,18 @@
|
|
|
4936
5243
|
}
|
|
4937
5244
|
let track;
|
|
4938
5245
|
const itemEffects = new Map();
|
|
4939
|
-
const cache = new Map();
|
|
5246
|
+
const cache = tag(`morph:${fn.name || 'anonymous'}`, new Map());
|
|
4940
5247
|
Object.defineProperty(cache, 'constructor', { value: Object, enumerable: false });
|
|
4941
5248
|
function stopItem(key) {
|
|
4942
5249
|
const stop = itemEffects.get(key);
|
|
4943
5250
|
if (stop) {
|
|
4944
|
-
|
|
5251
|
+
const activeEffect = getActiveEffect();
|
|
5252
|
+
let chain;
|
|
5253
|
+
if (activeEffect) {
|
|
5254
|
+
const node = getEffectNode(activeEffect);
|
|
5255
|
+
chain = node.currentReason;
|
|
5256
|
+
}
|
|
5257
|
+
stop({ type: 'stopped', chain });
|
|
4945
5258
|
itemEffects.delete(key);
|
|
4946
5259
|
}
|
|
4947
5260
|
}
|
|
@@ -4951,12 +5264,25 @@
|
|
|
4951
5264
|
cache.set(key, track(() => fn(val, key)));
|
|
4952
5265
|
}
|
|
4953
5266
|
else {
|
|
4954
|
-
const stop = track(() => effect.
|
|
4955
|
-
|
|
5267
|
+
const stop = track(() => effect.opaque `morph:${fn.name}:${key}`((access) => {
|
|
5268
|
+
const next = source.get(key);
|
|
5269
|
+
if (next === undefined && !source.has(key))
|
|
5270
|
+
return;
|
|
5271
|
+
cache.set(key, fn(next, key, access));
|
|
4956
5272
|
return (reason) => {
|
|
4957
5273
|
cache.delete(key);
|
|
4958
5274
|
touched1(cache, { type: 'invalidate', prop: 'morph' }, String(key));
|
|
4959
|
-
|
|
5275
|
+
const activeEffect = getActiveEffect();
|
|
5276
|
+
let chain;
|
|
5277
|
+
if (activeEffect) {
|
|
5278
|
+
const node = getEffectNode(activeEffect);
|
|
5279
|
+
chain = node.currentReason;
|
|
5280
|
+
}
|
|
5281
|
+
stop?.({
|
|
5282
|
+
type: 'invalidate',
|
|
5283
|
+
cause: chainExternalReason(reason ?? { type: 'stopped', chain }),
|
|
5284
|
+
chain: chainExternalReason(chain),
|
|
5285
|
+
});
|
|
4960
5286
|
};
|
|
4961
5287
|
}));
|
|
4962
5288
|
itemEffects.set(key, stop);
|
|
@@ -5000,7 +5326,7 @@
|
|
|
5000
5326
|
},
|
|
5001
5327
|
});
|
|
5002
5328
|
let stateSnapshot = getState(source);
|
|
5003
|
-
const stopMain = effect
|
|
5329
|
+
const stopMain = effect `morph:${fn.name}`(({ ascend }) => {
|
|
5004
5330
|
track = ascend;
|
|
5005
5331
|
dependant(source, keysOf);
|
|
5006
5332
|
while ('evolution' in stateSnapshot) {
|
|
@@ -5047,7 +5373,13 @@
|
|
|
5047
5373
|
function stopItem(key) {
|
|
5048
5374
|
const stop = itemEffects.get(key);
|
|
5049
5375
|
if (stop) {
|
|
5050
|
-
|
|
5376
|
+
const activeEffect = getActiveEffect();
|
|
5377
|
+
let chain;
|
|
5378
|
+
if (activeEffect) {
|
|
5379
|
+
const node = getEffectNode(activeEffect);
|
|
5380
|
+
chain = node.currentReason;
|
|
5381
|
+
}
|
|
5382
|
+
stop({ type: 'stopped', chain });
|
|
5051
5383
|
itemEffects.delete(key);
|
|
5052
5384
|
}
|
|
5053
5385
|
}
|
|
@@ -5057,12 +5389,22 @@
|
|
|
5057
5389
|
cache[key] = track(() => fn(val, key));
|
|
5058
5390
|
}
|
|
5059
5391
|
else {
|
|
5060
|
-
const stop = track(() => effect.
|
|
5392
|
+
const stop = track(() => effect.opaque `morph:${fn.name}:${key}`((access) => {
|
|
5061
5393
|
cache[key] = fn(source[key], key, access);
|
|
5062
5394
|
return (reason) => {
|
|
5063
5395
|
delete cache[key];
|
|
5064
5396
|
touched1(cache, { type: 'invalidate', prop: 'morph' }, String(key));
|
|
5065
|
-
|
|
5397
|
+
const activeEffect = getActiveEffect();
|
|
5398
|
+
let chain;
|
|
5399
|
+
if (activeEffect) {
|
|
5400
|
+
const node = getEffectNode(activeEffect);
|
|
5401
|
+
chain = node.currentReason;
|
|
5402
|
+
}
|
|
5403
|
+
stop?.({
|
|
5404
|
+
type: 'invalidate',
|
|
5405
|
+
cause: chainExternalReason(reason ?? { type: 'stopped', chain }),
|
|
5406
|
+
chain: chainExternalReason(chain),
|
|
5407
|
+
});
|
|
5066
5408
|
};
|
|
5067
5409
|
}));
|
|
5068
5410
|
itemEffects.set(key, stop);
|
|
@@ -5089,7 +5431,7 @@
|
|
|
5089
5431
|
},
|
|
5090
5432
|
});
|
|
5091
5433
|
let stateSnapshot = getState(source);
|
|
5092
|
-
const stopMain = effect
|
|
5434
|
+
const stopMain = effect `morph:${fn.name}`(({ ascend }) => {
|
|
5093
5435
|
track = ascend;
|
|
5094
5436
|
// Track only structural changes on source
|
|
5095
5437
|
dependant(source, keysOf);
|
|
@@ -5130,7 +5472,7 @@
|
|
|
5130
5472
|
* // Changing users[0].name only recomputes names[0]
|
|
5131
5473
|
* ```
|
|
5132
5474
|
*/
|
|
5133
|
-
const morph = flavored(function morph(source, fn, options) {
|
|
5475
|
+
const morph = captioned(flavored(function morph(source, fn, options) {
|
|
5134
5476
|
if (Array.isArray(source) || typeof source === 'function')
|
|
5135
5477
|
return morphArray(source, fn, options);
|
|
5136
5478
|
if (source instanceof Map)
|
|
@@ -5140,6 +5482,10 @@
|
|
|
5140
5482
|
get pure() {
|
|
5141
5483
|
return (source, fn, _opt) => this(source, fn, { pure: true });
|
|
5142
5484
|
},
|
|
5485
|
+
}), {
|
|
5486
|
+
name: 'morph',
|
|
5487
|
+
callbackIndex: 1,
|
|
5488
|
+
warn: (message) => options.warn(`[reactive] ${message}`),
|
|
5143
5489
|
});
|
|
5144
5490
|
|
|
5145
5491
|
/**
|
|
@@ -5165,7 +5511,7 @@
|
|
|
5165
5511
|
const wrappedCallback = markWithRoot((() => callback(target)), callback);
|
|
5166
5512
|
registerDeepWatcher();
|
|
5167
5513
|
// Use the existing effect system to register dependencies
|
|
5168
|
-
return effect
|
|
5514
|
+
return effect `deepWatch`(() => {
|
|
5169
5515
|
// Mark the target object as having deep watchers
|
|
5170
5516
|
objectsWithDeepWatchers.add(target);
|
|
5171
5517
|
// Track which objects this effect is watching for cleanup
|
|
@@ -5229,7 +5575,7 @@
|
|
|
5229
5575
|
traverseAndTrack(target);
|
|
5230
5576
|
// Only call the callback if immediate is true or if it's not the first run
|
|
5231
5577
|
if (immediate) {
|
|
5232
|
-
untracked(() => callback(target));
|
|
5578
|
+
untracked `deepWatch:callback`(() => callback(target));
|
|
5233
5579
|
}
|
|
5234
5580
|
immediate = true;
|
|
5235
5581
|
// Return a cleanup function that properly removes deep watcher tracking
|
|
@@ -5296,7 +5642,7 @@
|
|
|
5296
5642
|
const wasVerification = options.isVerificationRun;
|
|
5297
5643
|
options.isVerificationRun = true;
|
|
5298
5644
|
try {
|
|
5299
|
-
const fresh = untracked(() => fn.apply(this, args));
|
|
5645
|
+
const fresh = untracked `memoize:verify-calculation`(() => fn.apply(this, args));
|
|
5300
5646
|
if (!deepCompare(node.result, fresh)) {
|
|
5301
5647
|
optionCall('onMemoizationDiscrepancy', node.result, fresh, fn, args, 'calculation');
|
|
5302
5648
|
}
|
|
@@ -5309,7 +5655,7 @@
|
|
|
5309
5655
|
}
|
|
5310
5656
|
// Create memoize internal effect to track dependencies and invalidate cache
|
|
5311
5657
|
// Use untracked to prevent the effect creation from being affected by parent effects
|
|
5312
|
-
node.cleanup = root(() => effect
|
|
5658
|
+
node.cleanup = root `memoize:root`(() => effect `memoize`(() => {
|
|
5313
5659
|
// Execute the function and track its dependencies
|
|
5314
5660
|
// The function execution will automatically track dependencies on reactive objects
|
|
5315
5661
|
node.result = fn.apply(this, args);
|
|
@@ -5320,7 +5666,17 @@
|
|
|
5320
5666
|
// Lazy memoization: stop the effect so it doesn't re-run immediately.
|
|
5321
5667
|
// It will be re-created on next access.
|
|
5322
5668
|
if (node.cleanup) {
|
|
5323
|
-
|
|
5669
|
+
const activeEffect = getActiveEffect();
|
|
5670
|
+
let chain;
|
|
5671
|
+
if (activeEffect) {
|
|
5672
|
+
const effectNode = getEffectNode(activeEffect);
|
|
5673
|
+
chain = effectNode.currentReason;
|
|
5674
|
+
}
|
|
5675
|
+
node.cleanup({
|
|
5676
|
+
type: 'invalidate',
|
|
5677
|
+
cause: chainExternalReason(reason ?? { type: 'stopped', chain }),
|
|
5678
|
+
chain: chainExternalReason(chain),
|
|
5679
|
+
});
|
|
5324
5680
|
node.cleanup = undefined;
|
|
5325
5681
|
}
|
|
5326
5682
|
};
|
|
@@ -5329,7 +5685,7 @@
|
|
|
5329
5685
|
const wasVerification = options.isVerificationRun;
|
|
5330
5686
|
options.isVerificationRun = true;
|
|
5331
5687
|
try {
|
|
5332
|
-
const fresh = untracked(() => fn.apply(this, args));
|
|
5688
|
+
const fresh = untracked `memoize:verify-comparison`(() => fn.apply(this, args));
|
|
5333
5689
|
if (!deepCompare(node.result, fresh)) {
|
|
5334
5690
|
optionCall('onMemoizationDiscrepancy', node.result, fresh, fn, args, 'comparison');
|
|
5335
5691
|
}
|
|
@@ -5521,12 +5877,12 @@
|
|
|
5521
5877
|
function organized(source, apply, baseTarget = {}) {
|
|
5522
5878
|
const observedSource = reactive(source);
|
|
5523
5879
|
const target = reactive(baseTarget);
|
|
5524
|
-
const stop = attend(()
|
|
5880
|
+
const stop = attend `organized:entries`(function enumerateObservedSourceKeys() {
|
|
5525
5881
|
const keys = [];
|
|
5526
5882
|
for (const key in observedSource)
|
|
5527
5883
|
keys.push(key);
|
|
5528
5884
|
return keys;
|
|
5529
|
-
}, (key)
|
|
5885
|
+
}, function applyObservedSourceKey(key) {
|
|
5530
5886
|
const sourceKey = key;
|
|
5531
5887
|
const accessBase = {
|
|
5532
5888
|
key: sourceKey,
|
|
@@ -5564,7 +5920,7 @@
|
|
|
5564
5920
|
|
|
5565
5921
|
//#region watch
|
|
5566
5922
|
const unsetYet = Symbol('unset-yet');
|
|
5567
|
-
const watch = flavored(function watch(value, //object | ((dep: DependencyAccess) => object),
|
|
5923
|
+
const watch = captioned(flavored(function watch(value, //object | ((dep: DependencyAccess) => object),
|
|
5568
5924
|
changed, options = {}) {
|
|
5569
5925
|
return typeof value === 'function'
|
|
5570
5926
|
? watchCallBack(value, changed, options)
|
|
@@ -5580,11 +5936,14 @@
|
|
|
5580
5936
|
get immediate() {
|
|
5581
5937
|
return flavorOptions(this, { immediate: true });
|
|
5582
5938
|
},
|
|
5939
|
+
}), {
|
|
5940
|
+
name: 'watch',
|
|
5941
|
+
warn: (message) => options.warn(`[reactive] ${message}`),
|
|
5583
5942
|
});
|
|
5584
5943
|
function watchObject(value, changed, { immediate = false, deep = false } = {}) {
|
|
5585
5944
|
if (deep)
|
|
5586
5945
|
return deepWatch(value, changed, { immediate });
|
|
5587
|
-
return effect
|
|
5946
|
+
return effect `watch:object`(() => {
|
|
5588
5947
|
dependant(value);
|
|
5589
5948
|
if (immediate)
|
|
5590
5949
|
changed(value);
|
|
@@ -5594,16 +5953,16 @@
|
|
|
5594
5953
|
function watchCallBack(value, changed, { immediate = false, deep = false } = {}) {
|
|
5595
5954
|
let oldValue = unsetYet;
|
|
5596
5955
|
let deepCleanup;
|
|
5597
|
-
const cbCleanup = effect
|
|
5956
|
+
const cbCleanup = effect `watch:callback`(markWithRoot((access) => {
|
|
5598
5957
|
const newValue = value(access);
|
|
5599
5958
|
if (oldValue !== newValue) {
|
|
5600
5959
|
const old = oldValue;
|
|
5601
5960
|
if (old === unsetYet) {
|
|
5602
5961
|
if (immediate)
|
|
5603
|
-
untracked(() => changed(newValue));
|
|
5962
|
+
untracked `watch:changed`(() => changed(newValue));
|
|
5604
5963
|
}
|
|
5605
5964
|
else
|
|
5606
|
-
untracked(() => changed(newValue, old));
|
|
5965
|
+
untracked `watch:changed`(() => changed(newValue, old));
|
|
5607
5966
|
}
|
|
5608
5967
|
oldValue = newValue;
|
|
5609
5968
|
if (deep) {
|
|
@@ -5630,7 +5989,7 @@
|
|
|
5630
5989
|
function when(predicate, timeout) {
|
|
5631
5990
|
return new Promise((resolve, reject) => {
|
|
5632
5991
|
let timer;
|
|
5633
|
-
const stop = effect
|
|
5992
|
+
const stop = effect `watch:when`((access) => {
|
|
5634
5993
|
try {
|
|
5635
5994
|
const value = predicate(access);
|
|
5636
5995
|
if (value) {
|
|
@@ -5737,7 +6096,7 @@
|
|
|
5737
6096
|
// Solve race conditions: make sure a new fast request is not overloaded by a slow old one
|
|
5738
6097
|
let counter = 0;
|
|
5739
6098
|
return lazyInit(resource, () => {
|
|
5740
|
-
link(resource, effect
|
|
6099
|
+
link(resource, effect `watch:resource`((access) => {
|
|
5741
6100
|
// Track reload signal to enable manual reloading
|
|
5742
6101
|
void reloadSignal.value;
|
|
5743
6102
|
const id = ++counter;
|
|
@@ -5903,6 +6262,7 @@
|
|
|
5903
6262
|
return super.findLastIndex((v, i, a) => predicate.call(thisArg, reactive(v), i, a), thisArg);
|
|
5904
6263
|
}
|
|
5905
6264
|
flat(depth) {
|
|
6265
|
+
dependant(this, keysOf);
|
|
5906
6266
|
return reactive(super.flat(depth));
|
|
5907
6267
|
}
|
|
5908
6268
|
flatMap(callbackfn, thisArg) {
|
|
@@ -6329,59 +6689,62 @@
|
|
|
6329
6689
|
function cache(object, propertyKey, value) {
|
|
6330
6690
|
Object.defineProperty(object, propertyKey, { value });
|
|
6331
6691
|
}
|
|
6692
|
+
function descriptorBase(descriptor) {
|
|
6693
|
+
return function descriptorDecorator(...properties) {
|
|
6694
|
+
return (Base) => {
|
|
6695
|
+
return class extends Base {
|
|
6696
|
+
constructor(...args) {
|
|
6697
|
+
super(...args);
|
|
6698
|
+
for (const key of properties) {
|
|
6699
|
+
const existing = Object.getOwnPropertyDescriptor(this, key);
|
|
6700
|
+
Object.defineProperty(this, key, Object.assign(existing || {}, descriptor));
|
|
6701
|
+
}
|
|
6702
|
+
}
|
|
6703
|
+
};
|
|
6704
|
+
};
|
|
6705
|
+
};
|
|
6706
|
+
}
|
|
6332
6707
|
/**
|
|
6333
6708
|
* Creates a decorator that modifies property descriptors for specified properties
|
|
6334
6709
|
* @param descriptor - The descriptor properties to apply
|
|
6335
6710
|
* @returns A class decorator that applies the descriptor to specified properties
|
|
6336
6711
|
*/
|
|
6337
|
-
const descriptor =
|
|
6338
|
-
return (...properties) => (Base) => {
|
|
6339
|
-
return class extends Base {
|
|
6340
|
-
constructor(...args) {
|
|
6341
|
-
super(...args);
|
|
6342
|
-
for (const key of properties) {
|
|
6343
|
-
const existing = Object.getOwnPropertyDescriptor(this, key);
|
|
6344
|
-
Object.defineProperty(this, key, Object.assign(existing || {}, descriptor));
|
|
6345
|
-
}
|
|
6346
|
-
}
|
|
6347
|
-
};
|
|
6348
|
-
};
|
|
6349
|
-
}, {
|
|
6712
|
+
const descriptor = Object.assign(descriptorBase, {
|
|
6350
6713
|
/**
|
|
6351
6714
|
* enumerable: true
|
|
6352
6715
|
*/
|
|
6353
6716
|
get enumerable() {
|
|
6354
|
-
return
|
|
6717
|
+
return descriptorBase({ enumerable: true });
|
|
6355
6718
|
},
|
|
6356
6719
|
/**
|
|
6357
6720
|
* enumerable: false
|
|
6358
6721
|
*/
|
|
6359
6722
|
get hidden() {
|
|
6360
|
-
return
|
|
6723
|
+
return descriptorBase({ enumerable: false });
|
|
6361
6724
|
},
|
|
6362
6725
|
/**
|
|
6363
6726
|
* configurable: true
|
|
6364
6727
|
*/
|
|
6365
6728
|
get configurable() {
|
|
6366
|
-
return
|
|
6729
|
+
return descriptorBase({ configurable: true });
|
|
6367
6730
|
},
|
|
6368
6731
|
/**
|
|
6369
6732
|
* configurable: false
|
|
6370
6733
|
*/
|
|
6371
6734
|
get frozen() {
|
|
6372
|
-
return
|
|
6735
|
+
return descriptorBase({ configurable: false });
|
|
6373
6736
|
},
|
|
6374
6737
|
/**
|
|
6375
6738
|
* writable: true
|
|
6376
6739
|
*/
|
|
6377
6740
|
get writable() {
|
|
6378
|
-
return
|
|
6741
|
+
return descriptorBase({ writable: true });
|
|
6379
6742
|
},
|
|
6380
6743
|
/**
|
|
6381
6744
|
* writable: false
|
|
6382
6745
|
*/
|
|
6383
6746
|
get readonly() {
|
|
6384
|
-
return
|
|
6747
|
+
return descriptorBase({ writable: false });
|
|
6385
6748
|
},
|
|
6386
6749
|
});
|
|
6387
6750
|
/**
|
|
@@ -6510,24 +6873,25 @@
|
|
|
6510
6873
|
});
|
|
6511
6874
|
}
|
|
6512
6875
|
|
|
6513
|
-
var version$1 = "1.0.
|
|
6876
|
+
var version$1 = "1.0.13";
|
|
6514
6877
|
var pkg = {
|
|
6515
6878
|
version: version$1};
|
|
6516
6879
|
|
|
6517
6880
|
const { version } = pkg;
|
|
6518
6881
|
const GLOBAL_MUTTS_KEY = '__MUTTS_INSTANCE__';
|
|
6882
|
+
const runtimeGlobals = globalThis;
|
|
6519
6883
|
const globalScope = (typeof globalThis !== 'undefined'
|
|
6520
6884
|
? globalThis
|
|
6521
|
-
:
|
|
6522
|
-
? window
|
|
6523
|
-
:
|
|
6524
|
-
? global
|
|
6885
|
+
: runtimeGlobals.window
|
|
6886
|
+
? runtimeGlobals.window
|
|
6887
|
+
: runtimeGlobals.global
|
|
6888
|
+
? runtimeGlobals.global
|
|
6525
6889
|
: false);
|
|
6526
6890
|
if (globalScope) {
|
|
6527
6891
|
let source = 'mutts/index';
|
|
6528
6892
|
try {
|
|
6529
|
-
if (
|
|
6530
|
-
source = __filename;
|
|
6893
|
+
if (runtimeGlobals.__filename)
|
|
6894
|
+
source = runtimeGlobals.__filename;
|
|
6531
6895
|
else if (typeof ({ url: (typeof document === 'undefined' && typeof location === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : typeof document === 'undefined' ? location.href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('mutts.umd.js', document.baseURI).href)) }) !== 'undefined' && (typeof document === 'undefined' && typeof location === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : typeof document === 'undefined' ? location.href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('mutts.umd.js', document.baseURI).href))) {
|
|
6532
6896
|
source = (typeof document === 'undefined' && typeof location === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : typeof document === 'undefined' ? location.href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('mutts.umd.js', document.baseURI).href));
|
|
6533
6897
|
}
|
|
@@ -6548,6 +6912,7 @@
|
|
|
6548
6912
|
|
|
6549
6913
|
exports.AZone = AZone;
|
|
6550
6914
|
exports.ArrayReadForward = ArrayReadForward;
|
|
6915
|
+
exports.CompareSymbol = CompareSymbol;
|
|
6551
6916
|
exports.DecoratorError = DecoratorError;
|
|
6552
6917
|
exports.Destroyable = Destroyable;
|
|
6553
6918
|
exports.DestructionError = DestructionError;
|
|
@@ -6577,6 +6942,7 @@
|
|
|
6577
6942
|
exports.cache = cache;
|
|
6578
6943
|
exports.cached = cached;
|
|
6579
6944
|
exports.callOnGC = callOnGC;
|
|
6945
|
+
exports.captioned = captioned;
|
|
6580
6946
|
exports.captured = captured;
|
|
6581
6947
|
exports.caught = caught;
|
|
6582
6948
|
exports.chainPromise = chainPromise;
|
|
@@ -6593,6 +6959,7 @@
|
|
|
6593
6959
|
exports.devPreset = devPreset;
|
|
6594
6960
|
exports.effect = effect;
|
|
6595
6961
|
exports.effectAggregator = effectAggregator;
|
|
6962
|
+
exports.effectContext = effectContext;
|
|
6596
6963
|
exports.flavorOptions = flavorOptions;
|
|
6597
6964
|
exports.flavored = flavored;
|
|
6598
6965
|
exports.formatCleanupReason = formatCleanupReason;
|
|
@@ -6602,6 +6969,7 @@
|
|
|
6602
6969
|
exports.getAt = getAt;
|
|
6603
6970
|
exports.getState = getState;
|
|
6604
6971
|
exports.hooks = hooks$1;
|
|
6972
|
+
exports.inheritCaption = inheritCaption;
|
|
6605
6973
|
exports.isCached = isCached;
|
|
6606
6974
|
exports.isConstructor = isConstructor;
|
|
6607
6975
|
exports.isDev = isDev;
|
|
@@ -6624,7 +6992,6 @@
|
|
|
6624
6992
|
exports.organized = organized;
|
|
6625
6993
|
exports.prodPreset = prodPreset;
|
|
6626
6994
|
exports.profileInfo = profileInfo;
|
|
6627
|
-
exports.project = morph;
|
|
6628
6995
|
exports.proxyToObject = proxyToObject;
|
|
6629
6996
|
exports.reactive = reactive;
|
|
6630
6997
|
exports.reactiveOptions = options;
|
|
@@ -6642,6 +7009,7 @@
|
|
|
6642
7009
|
exports.unwrap = unwrap;
|
|
6643
7010
|
exports.watch = watch;
|
|
6644
7011
|
exports.when = when;
|
|
7012
|
+
exports.withEffectContext = withEffectContext;
|
|
6645
7013
|
exports.zip = zip;
|
|
6646
7014
|
|
|
6647
7015
|
}));
|