mutts 1.0.8 → 1.0.10
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 +77 -35
- package/dist/browser.cjs +2144 -5355
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.ts +2 -1655
- package/dist/browser.dev.cjs +110 -0
- package/dist/browser.dev.cjs.map +1 -0
- package/dist/browser.dev.d.ts +2 -0
- package/dist/browser.dev.esm.js +5 -0
- package/dist/browser.dev.esm.js.map +1 -0
- package/dist/browser.esm.js +20 -107
- package/dist/browser.esm.js.map +1 -1
- package/dist/chunks/{async-browser-CA0jPWIi.cjs → async-browser-BU_IfxYD.cjs} +19 -107
- package/dist/chunks/async-browser-BU_IfxYD.cjs.map +1 -0
- package/dist/chunks/{async-core-UqHzvJ-S.cjs → async-core-CRLKP3l-.cjs} +8 -4
- package/dist/chunks/async-core-CRLKP3l-.cjs.map +1 -0
- package/dist/chunks/{async-node-BYHuGTni.cjs → async-node-3PrbVAbB.cjs} +7 -12
- package/dist/chunks/async-node-3PrbVAbB.cjs.map +1 -0
- package/dist/chunks/index-CaaQQlPJ.esm.js +3174 -0
- package/dist/chunks/index-CaaQQlPJ.esm.js.map +1 -0
- package/dist/chunks/node-nKJBk8iJ.esm.js +96 -0
- package/dist/chunks/node-nKJBk8iJ.esm.js.map +1 -0
- package/dist/chunks/proxy-Dtg-bJ3T.cjs +3763 -0
- package/dist/chunks/proxy-Dtg-bJ3T.cjs.map +1 -0
- package/dist/chunks/proxy-r7lARftl.esm.js +3701 -0
- package/dist/chunks/proxy-r7lARftl.esm.js.map +1 -0
- package/dist/debug.cjs +980 -0
- package/dist/debug.cjs.map +1 -0
- package/dist/debug.d.ts +208 -0
- package/dist/debug.esm.js +950 -0
- package/dist/debug.esm.js.map +1 -0
- package/dist/devtools/manifest.json +1 -1
- package/dist/devtools/panel.html +1 -1
- package/dist/devtools/panel.js +107 -94
- package/dist/devtools/panel.js.map +1 -1
- package/dist/index.d.ts +1447 -13
- package/dist/mutts.umd.js +7167 -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.cjs +70 -66
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.ts +2 -1
- package/dist/node.dev.cjs +110 -0
- package/dist/node.dev.cjs.map +1 -0
- package/dist/node.dev.d.ts +2 -0
- package/dist/node.dev.esm.js +6 -0
- package/dist/node.dev.esm.js.map +1 -0
- package/dist/node.esm.js +4 -103
- package/dist/node.esm.js.map +1 -1
- package/dist/{reactive/types.d.ts → types-W5vD6m2n.d.ts} +186 -93
- package/docs/ai/api-reference.md +12 -19
- package/docs/ai/manual.md +521 -17
- package/docs/events.md +57 -2
- package/docs/flavored.md +210 -0
- package/docs/reactive/advanced.md +314 -5
- package/docs/reactive/attend.md +142 -0
- package/docs/reactive/collections.md +76 -39
- package/docs/reactive/core.md +100 -14
- package/docs/reactive/debugging.md +123 -18
- package/docs/reactive/error-handling.md +121 -0
- package/docs/reactive/resource.md +125 -0
- package/docs/reactive/scan.md +247 -1
- package/docs/reactive.md +4 -1
- package/docs/std-decorators.md +102 -51
- package/docs/utils.md +70 -0
- package/docs/zone.md +3 -7
- package/package.json +64 -30
- package/dist/async/browser.d.ts +0 -2
- package/dist/async/browser.d.ts.map +0 -1
- package/dist/async/index.d.ts +0 -18
- package/dist/async/index.d.ts.map +0 -1
- package/dist/async/node.d.ts +0 -2
- package/dist/async/node.d.ts.map +0 -1
- package/dist/chunks/async-browser-CA0jPWIi.cjs.map +0 -1
- package/dist/chunks/async-core-UqHzvJ-S.cjs.map +0 -1
- package/dist/chunks/async-node-BYHuGTni.cjs.map +0 -1
- package/dist/chunks/index-DhaOVusv.esm.js +0 -6411
- package/dist/chunks/index-DhaOVusv.esm.js.map +0 -1
- package/dist/decorator.d.ts +0 -106
- package/dist/decorator.d.ts.map +0 -1
- package/dist/destroyable.d.ts +0 -87
- package/dist/destroyable.d.ts.map +0 -1
- package/dist/entry-browser.d.ts +0 -3
- package/dist/entry-browser.d.ts.map +0 -1
- package/dist/entry-node.d.ts +0 -3
- package/dist/entry-node.d.ts.map +0 -1
- package/dist/eventful.d.ts +0 -18
- package/dist/eventful.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/indexable.d.ts +0 -243
- package/dist/indexable.d.ts.map +0 -1
- package/dist/introspection.d.ts +0 -27
- package/dist/introspection.d.ts.map +0 -1
- package/dist/iterableWeak.d.ts +0 -53
- package/dist/iterableWeak.d.ts.map +0 -1
- package/dist/mixins.d.ts +0 -25
- package/dist/mixins.d.ts.map +0 -1
- package/dist/promiseChain.d.ts +0 -20
- package/dist/promiseChain.d.ts.map +0 -1
- package/dist/reactive/array.d.ts +0 -49
- package/dist/reactive/array.d.ts.map +0 -1
- package/dist/reactive/buffer.d.ts +0 -44
- package/dist/reactive/buffer.d.ts.map +0 -1
- package/dist/reactive/change.d.ts +0 -29
- package/dist/reactive/change.d.ts.map +0 -1
- package/dist/reactive/debug.d.ts +0 -111
- package/dist/reactive/debug.d.ts.map +0 -1
- package/dist/reactive/deep-touch.d.ts +0 -28
- package/dist/reactive/deep-touch.d.ts.map +0 -1
- package/dist/reactive/deep-watch-state.d.ts +0 -25
- package/dist/reactive/deep-watch-state.d.ts.map +0 -1
- package/dist/reactive/deep-watch.d.ts +0 -19
- package/dist/reactive/deep-watch.d.ts.map +0 -1
- package/dist/reactive/effect-context.d.ts +0 -7
- package/dist/reactive/effect-context.d.ts.map +0 -1
- package/dist/reactive/effects.d.ts +0 -151
- package/dist/reactive/effects.d.ts.map +0 -1
- package/dist/reactive/index.d.ts +0 -20
- package/dist/reactive/index.d.ts.map +0 -1
- package/dist/reactive/interface.d.ts +0 -64
- package/dist/reactive/interface.d.ts.map +0 -1
- package/dist/reactive/map.d.ts +0 -30
- package/dist/reactive/map.d.ts.map +0 -1
- package/dist/reactive/memoize.d.ts +0 -5
- package/dist/reactive/memoize.d.ts.map +0 -1
- package/dist/reactive/non-reactive-state.d.ts +0 -9
- package/dist/reactive/non-reactive-state.d.ts.map +0 -1
- package/dist/reactive/non-reactive.d.ts +0 -11
- package/dist/reactive/non-reactive.d.ts.map +0 -1
- package/dist/reactive/project.d.ts +0 -41
- package/dist/reactive/project.d.ts.map +0 -1
- package/dist/reactive/proxy-state.d.ts +0 -8
- package/dist/reactive/proxy-state.d.ts.map +0 -1
- package/dist/reactive/proxy.d.ts +0 -23
- package/dist/reactive/proxy.d.ts.map +0 -1
- package/dist/reactive/record.d.ts +0 -116
- package/dist/reactive/record.d.ts.map +0 -1
- package/dist/reactive/register.d.ts +0 -64
- package/dist/reactive/register.d.ts.map +0 -1
- package/dist/reactive/registry.d.ts +0 -20
- package/dist/reactive/registry.d.ts.map +0 -1
- package/dist/reactive/set.d.ts +0 -28
- package/dist/reactive/set.d.ts.map +0 -1
- package/dist/reactive/tracking.d.ts +0 -7
- package/dist/reactive/tracking.d.ts.map +0 -1
- package/dist/reactive/types.d.ts.map +0 -1
- package/dist/std-decorators.d.ts +0 -50
- package/dist/std-decorators.d.ts.map +0 -1
- package/dist/utils.d.ts +0 -49
- package/dist/utils.d.ts.map +0 -1
- package/dist/zone.d.ts +0 -40
- package/dist/zone.d.ts.map +0 -1
- package/docs/reactive/project.md +0 -93
- package/src/async/browser.ts +0 -319
- package/src/async/index.ts +0 -23
- package/src/async/node.ts +0 -104
- package/src/decorator.ts +0 -272
- package/src/destroyable.ts +0 -199
- package/src/entry-browser.ts +0 -5
- package/src/entry-node.ts +0 -5
- package/src/eventful.ts +0 -77
- package/src/index.d.ts +0 -12
- package/src/index.ts +0 -64
- package/src/indexable.ts +0 -526
- package/src/introspection.ts +0 -59
- package/src/iterableWeak.ts +0 -233
- package/src/mixins.ts +0 -123
- package/src/promiseChain.ts +0 -110
- package/src/reactive/array.ts +0 -507
- package/src/reactive/buffer.ts +0 -168
- package/src/reactive/change.ts +0 -138
- package/src/reactive/debug.ts +0 -517
- package/src/reactive/deep-touch.ts +0 -268
- package/src/reactive/deep-watch-state.ts +0 -82
- package/src/reactive/deep-watch.ts +0 -168
- package/src/reactive/effect-context.ts +0 -18
- package/src/reactive/effects.ts +0 -1382
- package/src/reactive/index.ts +0 -75
- package/src/reactive/interface.ts +0 -210
- package/src/reactive/map.ts +0 -158
- package/src/reactive/memoize.ts +0 -163
- package/src/reactive/non-reactive-state.ts +0 -49
- package/src/reactive/non-reactive.ts +0 -43
- package/src/reactive/project.ts +0 -453
- package/src/reactive/proxy-state.ts +0 -27
- package/src/reactive/proxy.ts +0 -264
- package/src/reactive/record.ts +0 -196
- package/src/reactive/register.ts +0 -419
- package/src/reactive/registry.ts +0 -59
- package/src/reactive/set.ts +0 -130
- package/src/reactive/tracking.ts +0 -44
- package/src/reactive/types.ts +0 -440
- package/src/std-decorators.ts +0 -217
- package/src/utils.ts +0 -301
- package/src/zone.ts +0 -138
package/src/reactive/index.ts
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
export { getState, touched, touched1 } from './change'
|
|
2
|
-
export type { ReactivityGraph } from './debug'
|
|
3
|
-
export {
|
|
4
|
-
buildReactivityGraph,
|
|
5
|
-
enableDevTools,
|
|
6
|
-
isDevtoolsEnabled,
|
|
7
|
-
registerEffectForDebug,
|
|
8
|
-
registerObjectForDebug,
|
|
9
|
-
setEffectName,
|
|
10
|
-
setObjectName,
|
|
11
|
-
} from './debug'
|
|
12
|
-
export { deepWatch } from './deep-watch'
|
|
13
|
-
export {
|
|
14
|
-
addBatchCleanup,
|
|
15
|
-
atomic,
|
|
16
|
-
biDi,
|
|
17
|
-
defer,
|
|
18
|
-
effect,
|
|
19
|
-
getActivationLog,
|
|
20
|
-
root,
|
|
21
|
-
trackEffect,
|
|
22
|
-
untracked,
|
|
23
|
-
} from './effects'
|
|
24
|
-
export { cleanedBy, cleanup, derived, unreactive, watch } from './interface'
|
|
25
|
-
export { type Memoizable, memoize } from './memoize'
|
|
26
|
-
export { immutables, isNonReactive, registerNativeReactivity } from './non-reactive'
|
|
27
|
-
export { getActiveProjection, project } from './project'
|
|
28
|
-
export { isReactive, ReactiveBase, reactive, unwrap } from './proxy'
|
|
29
|
-
export { organize, organized } from './record'
|
|
30
|
-
export { scan, type ScanResult, resolve } from './buffer'
|
|
31
|
-
export { Register, register } from './register'
|
|
32
|
-
export { getActiveEffect, effectAggregator } from './effect-context'
|
|
33
|
-
export {
|
|
34
|
-
type DependencyAccess,
|
|
35
|
-
type Evolution,
|
|
36
|
-
options as reactiveOptions,
|
|
37
|
-
ReactiveError,
|
|
38
|
-
ReactiveErrorCode,
|
|
39
|
-
type ScopedCallback,
|
|
40
|
-
} from './types'
|
|
41
|
-
|
|
42
|
-
import { ReactiveArray } from './array'
|
|
43
|
-
import {
|
|
44
|
-
deepWatchers,
|
|
45
|
-
effectToDeepWatchedObjects,
|
|
46
|
-
objectParents,
|
|
47
|
-
objectsWithDeepWatchers,
|
|
48
|
-
} from './deep-watch'
|
|
49
|
-
import { ReactiveMap, ReactiveWeakMap } from './map'
|
|
50
|
-
import { nonReactiveObjects } from './non-reactive-state'
|
|
51
|
-
import { metaProtos, objectToProxy, proxyToObject } from './proxy'
|
|
52
|
-
import { effectToReactiveObjects, watchers } from './registry'
|
|
53
|
-
import { ReactiveSet, ReactiveWeakSet } from './set'
|
|
54
|
-
|
|
55
|
-
// Register native collection types to use specialized reactive wrappers
|
|
56
|
-
metaProtos.set(Array, ReactiveArray.prototype)
|
|
57
|
-
metaProtos.set(Set, ReactiveSet.prototype)
|
|
58
|
-
metaProtos.set(WeakSet, ReactiveWeakSet.prototype)
|
|
59
|
-
metaProtos.set(Map, ReactiveMap.prototype)
|
|
60
|
-
metaProtos.set(WeakMap, ReactiveWeakMap.prototype)
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Object containing internal reactive system state for debugging and profiling
|
|
64
|
-
*/
|
|
65
|
-
export const profileInfo: any = {
|
|
66
|
-
objectToProxy,
|
|
67
|
-
proxyToObject,
|
|
68
|
-
effectToReactiveObjects,
|
|
69
|
-
watchers,
|
|
70
|
-
objectParents,
|
|
71
|
-
objectsWithDeepWatchers,
|
|
72
|
-
deepWatchers,
|
|
73
|
-
effectToDeepWatchedObjects,
|
|
74
|
-
nonReactiveObjects,
|
|
75
|
-
}
|
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
import { decorator, type GenericClassDecorator } from '../decorator'
|
|
2
|
-
import { deepWatch } from './deep-watch'
|
|
3
|
-
import { effect, untracked } from './effects'
|
|
4
|
-
import { isNonReactive, nonReactiveClass, nonReactiveObjects } from './non-reactive-state'
|
|
5
|
-
import { unwrap } from './proxy-state'
|
|
6
|
-
import { markWithRoot } from './registry'
|
|
7
|
-
import { dependant } from './tracking'
|
|
8
|
-
import {
|
|
9
|
-
type DependencyAccess,
|
|
10
|
-
nonReactiveMark,
|
|
11
|
-
type ScopedCallback,
|
|
12
|
-
unreactiveProperties,
|
|
13
|
-
} from './types'
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Symbol for accessing the cleanup function on cleaned objects
|
|
17
|
-
*/
|
|
18
|
-
export const cleanup = Symbol('cleanup')
|
|
19
|
-
|
|
20
|
-
//#region watch
|
|
21
|
-
|
|
22
|
-
const unsetYet = Symbol('unset-yet')
|
|
23
|
-
/**
|
|
24
|
-
* Options for the watch function
|
|
25
|
-
*/
|
|
26
|
-
export interface WatchOptions {
|
|
27
|
-
/** Whether to call the callback immediately */
|
|
28
|
-
immediate?: boolean
|
|
29
|
-
/** Whether to watch nested properties */
|
|
30
|
-
deep?: boolean
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Watches a reactive value and calls a callback when it changes
|
|
35
|
-
* @param value - Function that returns the value to watch
|
|
36
|
-
* @param changed - Callback to call when the value changes
|
|
37
|
-
* @param options - Watch options
|
|
38
|
-
* @returns Cleanup function to stop watching
|
|
39
|
-
*/
|
|
40
|
-
export function watch<T>(
|
|
41
|
-
value: (dep: DependencyAccess) => T,
|
|
42
|
-
changed: (value: T, oldValue?: T) => void,
|
|
43
|
-
options?: Omit<WatchOptions, 'deep'> & { deep?: false }
|
|
44
|
-
): ScopedCallback
|
|
45
|
-
/**
|
|
46
|
-
* Watches a reactive value with deep watching enabled
|
|
47
|
-
* @param value - Function that returns the value to watch
|
|
48
|
-
* @param changed - Callback to call when the value changes
|
|
49
|
-
* @param options - Watch options with deep watching enabled
|
|
50
|
-
* @returns Cleanup function to stop watching
|
|
51
|
-
*/
|
|
52
|
-
export function watch<T extends object | any[]>(
|
|
53
|
-
value: (dep: DependencyAccess) => T,
|
|
54
|
-
changed: (value: T, oldValue?: T) => void,
|
|
55
|
-
options?: Omit<WatchOptions, 'deep'> & { deep: true }
|
|
56
|
-
): ScopedCallback
|
|
57
|
-
/**
|
|
58
|
-
* Watches a reactive object directly
|
|
59
|
-
* @param value - The reactive object to watch
|
|
60
|
-
* @param changed - Callback to call when the object changes
|
|
61
|
-
* @param options - Watch options
|
|
62
|
-
* @returns Cleanup function to stop watching
|
|
63
|
-
*/
|
|
64
|
-
export function watch<T extends object | any[]>(
|
|
65
|
-
value: T,
|
|
66
|
-
changed: (value: T) => void,
|
|
67
|
-
options?: WatchOptions
|
|
68
|
-
): ScopedCallback
|
|
69
|
-
|
|
70
|
-
export function watch(
|
|
71
|
-
value: any, //object | ((dep: DependencyAccess) => object),
|
|
72
|
-
changed: (value?: object, oldValue?: object) => void,
|
|
73
|
-
options: any = {}
|
|
74
|
-
) {
|
|
75
|
-
return typeof value === 'function'
|
|
76
|
-
? watchCallBack(value, changed, options)
|
|
77
|
-
: typeof value === 'object' && value !== null
|
|
78
|
-
? watchObject(value, changed, options)
|
|
79
|
-
: (() => {
|
|
80
|
-
throw new Error('watch: value must be a function or an object')
|
|
81
|
-
})()
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function watchObject(
|
|
85
|
-
value: object,
|
|
86
|
-
changed: (value: object) => void,
|
|
87
|
-
{ immediate = false, deep = false } = {}
|
|
88
|
-
): ScopedCallback {
|
|
89
|
-
if (deep) return deepWatch(value, changed, { immediate })!
|
|
90
|
-
return effect(function watchObjectEffect() {
|
|
91
|
-
dependant(value)
|
|
92
|
-
if (immediate) changed(value)
|
|
93
|
-
immediate = true
|
|
94
|
-
})
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function watchCallBack<T>(
|
|
98
|
-
value: (dep: DependencyAccess) => T,
|
|
99
|
-
changed: (value: T, oldValue?: T) => void,
|
|
100
|
-
{ immediate = false, deep = false } = {}
|
|
101
|
-
): ScopedCallback {
|
|
102
|
-
let oldValue: T | typeof unsetYet = unsetYet
|
|
103
|
-
let deepCleanup: ScopedCallback | undefined
|
|
104
|
-
const cbCleanup = effect(
|
|
105
|
-
markWithRoot(function watchCallBackEffect(access) {
|
|
106
|
-
const newValue = value(access)
|
|
107
|
-
if (oldValue !== newValue)
|
|
108
|
-
if (oldValue === unsetYet) {
|
|
109
|
-
if (immediate) changed(newValue)
|
|
110
|
-
} else changed(newValue, oldValue)
|
|
111
|
-
oldValue = newValue
|
|
112
|
-
if (deep) {
|
|
113
|
-
if (deepCleanup) deepCleanup()
|
|
114
|
-
deepCleanup = deepWatch(newValue as object, (value) => changed(value as T, value as T))
|
|
115
|
-
}
|
|
116
|
-
}, value)
|
|
117
|
-
)
|
|
118
|
-
return () => {
|
|
119
|
-
cbCleanup()
|
|
120
|
-
if (deepCleanup) deepCleanup()
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
//#endregion
|
|
125
|
-
|
|
126
|
-
//#region nonReactive
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Mark an object as non-reactive. This object and all its properties will never be made reactive.
|
|
130
|
-
* @param obj - The object to mark as non-reactive
|
|
131
|
-
*/
|
|
132
|
-
function deepNonReactive<T>(obj: T): T {
|
|
133
|
-
obj = unwrap(obj)
|
|
134
|
-
if (isNonReactive(obj)) return obj
|
|
135
|
-
try {
|
|
136
|
-
Object.defineProperty(obj as object, nonReactiveMark, {
|
|
137
|
-
value: true,
|
|
138
|
-
writable: false,
|
|
139
|
-
enumerable: false,
|
|
140
|
-
configurable: true,
|
|
141
|
-
})
|
|
142
|
-
} catch {}
|
|
143
|
-
if (!(nonReactiveMark in (obj as object))) nonReactiveObjects.add(obj as object)
|
|
144
|
-
// Finally, not deep
|
|
145
|
-
//for (const key in obj) deepNonReactive(obj[key])
|
|
146
|
-
return obj
|
|
147
|
-
}
|
|
148
|
-
function unreactiveApplication<T extends object>(...args: (keyof T)[]): GenericClassDecorator<T>
|
|
149
|
-
function unreactiveApplication<T extends object>(obj: T): T
|
|
150
|
-
function unreactiveApplication<T extends object>(
|
|
151
|
-
arg1: T | keyof T,
|
|
152
|
-
...args: (keyof T)[]
|
|
153
|
-
): GenericClassDecorator<T> | T {
|
|
154
|
-
return typeof arg1 === 'object'
|
|
155
|
-
? deepNonReactive(arg1)
|
|
156
|
-
: (((original) => {
|
|
157
|
-
// Copy the parent's unreactive properties if they exist
|
|
158
|
-
original.prototype[unreactiveProperties] = new Set<PropertyKey>(
|
|
159
|
-
original.prototype[unreactiveProperties] || []
|
|
160
|
-
)
|
|
161
|
-
// Add all arguments (including the first one)
|
|
162
|
-
original.prototype[unreactiveProperties].add(arg1)
|
|
163
|
-
for (const arg of args) original.prototype[unreactiveProperties].add(arg)
|
|
164
|
-
return original // Return the class
|
|
165
|
-
}) as GenericClassDecorator<T>)
|
|
166
|
-
}
|
|
167
|
-
/**
|
|
168
|
-
* Decorator that marks classes or properties as non-reactive
|
|
169
|
-
* Prevents objects from being made reactive
|
|
170
|
-
*/
|
|
171
|
-
export const unreactive = decorator({
|
|
172
|
-
class(original) {
|
|
173
|
-
// Called without arguments, mark entire class as non-reactive
|
|
174
|
-
nonReactiveClass(original)
|
|
175
|
-
},
|
|
176
|
-
default: unreactiveApplication,
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
//#endregion
|
|
180
|
-
|
|
181
|
-
export function cleanedBy<T extends object>(obj: T, cleanupFn: ScopedCallback) {
|
|
182
|
-
return Object.defineProperty(obj, cleanup, {
|
|
183
|
-
value: cleanupFn,
|
|
184
|
-
writable: false,
|
|
185
|
-
enumerable: false,
|
|
186
|
-
configurable: true,
|
|
187
|
-
}) as T & { [cleanup]: ScopedCallback }
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
//#region greedy caching
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Creates a derived value that automatically recomputes when dependencies change
|
|
194
|
-
* @param compute - Function that computes the derived value
|
|
195
|
-
* @returns Object with value and cleanup function
|
|
196
|
-
*/
|
|
197
|
-
export function derived<T>(compute: (dep: DependencyAccess) => T): {
|
|
198
|
-
value: T
|
|
199
|
-
[cleanup]: ScopedCallback
|
|
200
|
-
} {
|
|
201
|
-
const rv = { value: undefined as unknown as T }
|
|
202
|
-
return cleanedBy(
|
|
203
|
-
rv,
|
|
204
|
-
untracked(() =>
|
|
205
|
-
effect(function derivedEffect(access) {
|
|
206
|
-
rv.value = compute(access)
|
|
207
|
-
})
|
|
208
|
-
)
|
|
209
|
-
)
|
|
210
|
-
}
|
package/src/reactive/map.ts
DELETED
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
import { contentRef } from '../utils'
|
|
2
|
-
import { touched, touched1 } from './change'
|
|
3
|
-
import { notifyPropertyChange } from './deep-touch'
|
|
4
|
-
import { makeReactiveEntriesIterator, makeReactiveIterator } from './non-reactive'
|
|
5
|
-
import { reactive } from './proxy'
|
|
6
|
-
import { dependant } from './tracking'
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Reactive wrapper around JavaScript's WeakMap class
|
|
10
|
-
* Only tracks individual key operations, no size tracking (WeakMap limitation)
|
|
11
|
-
*/
|
|
12
|
-
export abstract class ReactiveWeakMap<K extends object, V> extends WeakMap<K, V> {
|
|
13
|
-
get [Symbol.toStringTag]() {
|
|
14
|
-
return 'ReactiveWeakMap'
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// Implement WeakMap interface methods with reactivity
|
|
18
|
-
delete(key: K): boolean {
|
|
19
|
-
const hadKey = this.has(key)
|
|
20
|
-
const result = this.delete(key)
|
|
21
|
-
|
|
22
|
-
if (hadKey) touched1(contentRef(this), { type: 'del', prop: key }, key)
|
|
23
|
-
|
|
24
|
-
return result
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
get(key: K): V | undefined {
|
|
28
|
-
dependant(contentRef(this), key)
|
|
29
|
-
return reactive(this.get(key))
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
has(key: K): boolean {
|
|
33
|
-
dependant(contentRef(this), key)
|
|
34
|
-
return this.has(key)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
set(key: K, value: V): this {
|
|
38
|
-
const hadKey = this.has(key)
|
|
39
|
-
const oldValue = this.get(key)
|
|
40
|
-
const reactiveValue = reactive(value)
|
|
41
|
-
this.set(key, reactiveValue)
|
|
42
|
-
|
|
43
|
-
if (!hadKey || oldValue !== reactiveValue) {
|
|
44
|
-
notifyPropertyChange(contentRef(this), key, oldValue, reactiveValue, hadKey)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return this
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Reactive wrapper around JavaScript's Map class
|
|
53
|
-
* Tracks size changes, individual key operations, and collection-wide operations
|
|
54
|
-
*/
|
|
55
|
-
export abstract class ReactiveMap<K, V> extends Map<K, V> {
|
|
56
|
-
get [Symbol.toStringTag]() {
|
|
57
|
-
return 'ReactiveMap'
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Implement Map interface methods with reactivity
|
|
61
|
-
get size(): number {
|
|
62
|
-
dependant(this, 'size') // The ReactiveMap instance still goes through proxy
|
|
63
|
-
return this.size
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
clear(): void {
|
|
67
|
-
const hadEntries = this.size > 0
|
|
68
|
-
this.clear()
|
|
69
|
-
|
|
70
|
-
if (hadEntries) {
|
|
71
|
-
const evolution = { type: 'bunch', method: 'clear' } as const
|
|
72
|
-
// Clear triggers all effects since all keys are affected
|
|
73
|
-
touched1(this, evolution, 'size')
|
|
74
|
-
touched(contentRef(this), evolution)
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
entries(): Generator<[K, V]> {
|
|
79
|
-
dependant(contentRef(this))
|
|
80
|
-
return makeReactiveEntriesIterator(this.entries())
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: any): void {
|
|
84
|
-
dependant(contentRef(this))
|
|
85
|
-
this.forEach(callbackfn, thisArg)
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
keys(): MapIterator<K> {
|
|
89
|
-
dependant(contentRef(this))
|
|
90
|
-
return this.keys()
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
values(): Generator<V> {
|
|
94
|
-
dependant(contentRef(this))
|
|
95
|
-
return makeReactiveIterator(this.values())
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
[Symbol.iterator](): MapIterator<[K, V]> {
|
|
99
|
-
dependant(contentRef(this))
|
|
100
|
-
const nativeIterator = this[Symbol.iterator]()
|
|
101
|
-
return {
|
|
102
|
-
next() {
|
|
103
|
-
const result = nativeIterator.next()
|
|
104
|
-
if (result.done) {
|
|
105
|
-
return result
|
|
106
|
-
}
|
|
107
|
-
return {
|
|
108
|
-
value: [result.value[0], reactive(result.value[1])],
|
|
109
|
-
done: false,
|
|
110
|
-
}
|
|
111
|
-
},
|
|
112
|
-
[Symbol.iterator]() {
|
|
113
|
-
return this
|
|
114
|
-
},
|
|
115
|
-
[Symbol.dispose]() {},
|
|
116
|
-
} as any // TODO: real iterator? (If easy)
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Implement Map methods with reactivity
|
|
120
|
-
delete(key: K): boolean {
|
|
121
|
-
const hadKey = this.has(key)
|
|
122
|
-
const result = this.delete(key)
|
|
123
|
-
|
|
124
|
-
if (hadKey) {
|
|
125
|
-
const evolution = { type: 'del', prop: key } as const
|
|
126
|
-
touched1(contentRef(this), evolution, key)
|
|
127
|
-
touched1(this, evolution, 'size')
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return result
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
get(key: K): V | undefined {
|
|
134
|
-
dependant(contentRef(this), key)
|
|
135
|
-
return reactive(this.get(key))
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
has(key: K): boolean {
|
|
139
|
-
dependant(contentRef(this), key)
|
|
140
|
-
return this.has(key)
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
set(key: K, value: V): this {
|
|
144
|
-
const hadKey = this.has(key)
|
|
145
|
-
const oldValue = this.get(key)
|
|
146
|
-
const reactiveValue = reactive(value)
|
|
147
|
-
this.set(key, reactiveValue)
|
|
148
|
-
|
|
149
|
-
if (!hadKey || oldValue !== reactiveValue) {
|
|
150
|
-
notifyPropertyChange(contentRef(this), key, oldValue, reactiveValue, hadKey)
|
|
151
|
-
// Also notify size change for Map (WeakMap doesn't track size)
|
|
152
|
-
const evolution = { type: hadKey ? 'set' : 'add', prop: key } as const
|
|
153
|
-
touched1(this, evolution, 'size')
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
return this
|
|
157
|
-
}
|
|
158
|
-
}
|
package/src/reactive/memoize.ts
DELETED
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
import { decorator } from '../decorator'
|
|
2
|
-
import { deepCompare, renamed } from '../utils'
|
|
3
|
-
import { touched1 } from './change'
|
|
4
|
-
import { effect, root, untracked } from './effects'
|
|
5
|
-
import { getRoot, markWithRoot } from './registry'
|
|
6
|
-
import { dependant } from './tracking'
|
|
7
|
-
import { options, rootFunction } from './types'
|
|
8
|
-
|
|
9
|
-
export type Memoizable = object | any[] | symbol | ((...args: any[]) => any)
|
|
10
|
-
|
|
11
|
-
type MemoCacheTree<Result> = {
|
|
12
|
-
result?: Result
|
|
13
|
-
cleanup?: () => void
|
|
14
|
-
branches?: WeakMap<Memoizable, MemoCacheTree<Result>>
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const memoizedRegistry = new WeakMap<any, Function>()
|
|
18
|
-
const wrapperRegistry = new WeakMap<Function, Function>()
|
|
19
|
-
|
|
20
|
-
function getBranch<Result>(tree: MemoCacheTree<Result>, key: Memoizable): MemoCacheTree<Result> {
|
|
21
|
-
tree.branches ??= new WeakMap()
|
|
22
|
-
let branch = tree.branches.get(key)
|
|
23
|
-
if (!branch) {
|
|
24
|
-
branch = {}
|
|
25
|
-
tree.branches.set(key, branch)
|
|
26
|
-
}
|
|
27
|
-
return branch
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function memoizeFunction<Result, Args extends Memoizable[]>(
|
|
31
|
-
fn: (...args: Args) => Result
|
|
32
|
-
): (...args: Args) => Result {
|
|
33
|
-
const fnRoot = getRoot(fn)
|
|
34
|
-
const existing = memoizedRegistry.get(fnRoot)
|
|
35
|
-
if (existing) return existing as (...args: Args) => Result
|
|
36
|
-
|
|
37
|
-
const cacheRoot: MemoCacheTree<Result> = {}
|
|
38
|
-
const memoized = markWithRoot((...args: Args): Result => {
|
|
39
|
-
const localArgs = args //: Args = maxArgs !== undefined ? (args.slice(0, maxArgs) as Args) : args
|
|
40
|
-
if (localArgs.some((arg) => !(arg && ['object', 'symbol', 'function'].includes(typeof arg))))
|
|
41
|
-
throw new Error('memoize expects non-null object arguments')
|
|
42
|
-
|
|
43
|
-
let node: MemoCacheTree<Result> = cacheRoot
|
|
44
|
-
// Note: decorators add `this` as first argument
|
|
45
|
-
for (const arg of localArgs) {
|
|
46
|
-
node = getBranch(node, arg)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
dependant(node, 'memoize')
|
|
50
|
-
if ('result' in node) {
|
|
51
|
-
if (options.onMemoizationDiscrepancy) {
|
|
52
|
-
const wasVerification = options.isVerificationRun
|
|
53
|
-
options.isVerificationRun = true
|
|
54
|
-
try {
|
|
55
|
-
const fresh = untracked(() => fn(...localArgs))
|
|
56
|
-
if (!deepCompare(node.result, fresh)) {
|
|
57
|
-
options.onMemoizationDiscrepancy(node.result, fresh, fn, localArgs, 'calculation')
|
|
58
|
-
}
|
|
59
|
-
} finally {
|
|
60
|
-
options.isVerificationRun = wasVerification
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return node.result!
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Create memoize internal effect to track dependencies and invalidate cache
|
|
67
|
-
// Use untracked to prevent the effect creation from being affected by parent effects
|
|
68
|
-
node.cleanup = root(() =>
|
|
69
|
-
effect(
|
|
70
|
-
() => {
|
|
71
|
-
// Execute the function and track its dependencies
|
|
72
|
-
// The function execution will automatically track dependencies on reactive objects
|
|
73
|
-
node.result = fn(...localArgs)
|
|
74
|
-
return () => {
|
|
75
|
-
// When dependencies change, clear the cache and notify consumers
|
|
76
|
-
delete node.result
|
|
77
|
-
touched1(node, { type: 'invalidate', prop: localArgs }, 'memoize')
|
|
78
|
-
// Lazy memoization: stop the effect so it doesn't re-run immediately.
|
|
79
|
-
// It will be re-created on next access.
|
|
80
|
-
if (node.cleanup) {
|
|
81
|
-
node.cleanup()
|
|
82
|
-
node.cleanup = undefined
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
},
|
|
86
|
-
{ opaque: true }
|
|
87
|
-
)
|
|
88
|
-
)
|
|
89
|
-
|
|
90
|
-
if (options.onMemoizationDiscrepancy) {
|
|
91
|
-
const wasVerification = options.isVerificationRun
|
|
92
|
-
options.isVerificationRun = true
|
|
93
|
-
try {
|
|
94
|
-
const fresh = untracked(() => fn(...localArgs))
|
|
95
|
-
if (!deepCompare(node.result, fresh)) {
|
|
96
|
-
options.onMemoizationDiscrepancy(node.result, fresh, fn, localArgs, 'comparison')
|
|
97
|
-
}
|
|
98
|
-
} finally {
|
|
99
|
-
options.isVerificationRun = wasVerification
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return node.result!
|
|
104
|
-
}, fn)
|
|
105
|
-
|
|
106
|
-
memoizedRegistry.set(fnRoot, memoized)
|
|
107
|
-
memoizedRegistry.set(memoized, memoized)
|
|
108
|
-
return memoized as (...args: Args) => Result
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export const memoize = decorator({
|
|
112
|
-
getter(original, target, propertyKey) {
|
|
113
|
-
return function (this: any) {
|
|
114
|
-
let wrapper = wrapperRegistry.get(original)
|
|
115
|
-
if (!wrapper) {
|
|
116
|
-
wrapper = markWithRoot(
|
|
117
|
-
renamed(
|
|
118
|
-
(that: object) => {
|
|
119
|
-
return original.call(that)
|
|
120
|
-
},
|
|
121
|
-
`${String(target?.constructor?.name ?? target?.name ?? 'Object')}.${String(propertyKey)}`
|
|
122
|
-
),
|
|
123
|
-
{
|
|
124
|
-
method: original,
|
|
125
|
-
propertyKey,
|
|
126
|
-
...((original as any)[rootFunction]
|
|
127
|
-
? { [rootFunction]: (original as any)[rootFunction] }
|
|
128
|
-
: {}),
|
|
129
|
-
}
|
|
130
|
-
)
|
|
131
|
-
wrapperRegistry.set(original, wrapper)
|
|
132
|
-
}
|
|
133
|
-
const memoized = memoizeFunction(wrapper as any)
|
|
134
|
-
return memoized(this)
|
|
135
|
-
}
|
|
136
|
-
},
|
|
137
|
-
method(original, target, name) {
|
|
138
|
-
return function (this: any, ...args: object[]) {
|
|
139
|
-
let wrapper = wrapperRegistry.get(original)
|
|
140
|
-
if (!wrapper) {
|
|
141
|
-
wrapper = markWithRoot(
|
|
142
|
-
renamed(
|
|
143
|
-
(that: object, ...args: object[]) => {
|
|
144
|
-
return original.call(that, ...args)
|
|
145
|
-
},
|
|
146
|
-
`${String(target?.constructor?.name ?? target?.name ?? 'Object')}.${String(name)}`
|
|
147
|
-
),
|
|
148
|
-
{
|
|
149
|
-
method: original,
|
|
150
|
-
propertyKey: name,
|
|
151
|
-
...((original as any)[rootFunction]
|
|
152
|
-
? { [rootFunction]: (original as any)[rootFunction] }
|
|
153
|
-
: {}),
|
|
154
|
-
}
|
|
155
|
-
)
|
|
156
|
-
wrapperRegistry.set(original, wrapper)
|
|
157
|
-
}
|
|
158
|
-
const memoized = memoizeFunction(wrapper as any) as (...args: object[]) => unknown
|
|
159
|
-
return memoized(this, ...args)
|
|
160
|
-
}
|
|
161
|
-
},
|
|
162
|
-
default: memoizeFunction,
|
|
163
|
-
})
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { nativeReactive, nonReactiveMark } from './types'
|
|
2
|
-
|
|
3
|
-
export const nonReactiveObjects = new WeakSet<object>()
|
|
4
|
-
export const immutables = new Set<(tested: any) => boolean>()
|
|
5
|
-
export const absent = Symbol('absent')
|
|
6
|
-
|
|
7
|
-
function markNonReactive<T extends object[]>(...obj: T): T[0] {
|
|
8
|
-
for (const o of obj) {
|
|
9
|
-
try {
|
|
10
|
-
Object.defineProperty(o, nonReactiveMark, {
|
|
11
|
-
value: true,
|
|
12
|
-
writable: false,
|
|
13
|
-
enumerable: false,
|
|
14
|
-
configurable: false,
|
|
15
|
-
})
|
|
16
|
-
} catch {}
|
|
17
|
-
if (!(nonReactiveMark in (o as object))) nonReactiveObjects.add(o as object)
|
|
18
|
-
}
|
|
19
|
-
return obj[0]
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function nonReactiveClass<T extends (new (...args: any[]) => any)[]>(...cls: T): T[0] {
|
|
23
|
-
for (const c of cls) if (c) (c.prototype as any)[nonReactiveMark] = true
|
|
24
|
-
return cls[0]
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function isNonReactive(obj: any): boolean {
|
|
28
|
-
if (obj === null || typeof obj !== 'object') return true
|
|
29
|
-
if (nonReactiveObjects.has(obj)) return true
|
|
30
|
-
if ((obj as any)[nonReactiveMark]) return true
|
|
31
|
-
for (const fn of immutables) if (fn(obj)) return true
|
|
32
|
-
return false
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function registerNativeReactivity(
|
|
36
|
-
originalClass: new (...args: any[]) => any,
|
|
37
|
-
reactiveClass: new (...args: any[]) => any
|
|
38
|
-
) {
|
|
39
|
-
originalClass.prototype[nativeReactive] = reactiveClass
|
|
40
|
-
nonReactiveClass(reactiveClass)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
nonReactiveClass(Date, RegExp, Error, Promise, Function)
|
|
44
|
-
if (typeof window !== 'undefined') {
|
|
45
|
-
markNonReactive(window, document)
|
|
46
|
-
nonReactiveClass(Node, Element, HTMLElement, EventTarget)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export { markNonReactive as nonReactive }
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
absent,
|
|
3
|
-
immutables,
|
|
4
|
-
isNonReactive,
|
|
5
|
-
nonReactive,
|
|
6
|
-
nonReactiveClass,
|
|
7
|
-
nonReactiveObjects,
|
|
8
|
-
registerNativeReactivity,
|
|
9
|
-
} from './non-reactive-state'
|
|
10
|
-
import { reactive } from './proxy'
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Converts an iterator to a generator that yields reactive values
|
|
14
|
-
*/
|
|
15
|
-
export function* makeReactiveIterator<T>(iterator: Iterator<T>): Generator<T> {
|
|
16
|
-
let result = iterator.next()
|
|
17
|
-
while (!result.done) {
|
|
18
|
-
yield reactive(result.value)
|
|
19
|
-
result = iterator.next()
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Converts an iterator of key-value pairs to a generator that yields reactive key-value pairs
|
|
25
|
-
*/
|
|
26
|
-
export function* makeReactiveEntriesIterator<K, V>(iterator: Iterator<[K, V]>): Generator<[K, V]> {
|
|
27
|
-
let result = iterator.next()
|
|
28
|
-
while (!result.done) {
|
|
29
|
-
const [key, value] = result.value
|
|
30
|
-
yield [reactive(key), reactive(value)]
|
|
31
|
-
result = iterator.next()
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export {
|
|
36
|
-
absent,
|
|
37
|
-
immutables,
|
|
38
|
-
isNonReactive,
|
|
39
|
-
nonReactive,
|
|
40
|
-
nonReactiveClass,
|
|
41
|
-
nonReactiveObjects,
|
|
42
|
-
registerNativeReactivity,
|
|
43
|
-
}
|