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.
Files changed (195) hide show
  1. package/README.md +77 -35
  2. package/dist/browser.cjs +2144 -5355
  3. package/dist/browser.cjs.map +1 -1
  4. package/dist/browser.d.ts +2 -1655
  5. package/dist/browser.dev.cjs +110 -0
  6. package/dist/browser.dev.cjs.map +1 -0
  7. package/dist/browser.dev.d.ts +2 -0
  8. package/dist/browser.dev.esm.js +5 -0
  9. package/dist/browser.dev.esm.js.map +1 -0
  10. package/dist/browser.esm.js +20 -107
  11. package/dist/browser.esm.js.map +1 -1
  12. package/dist/chunks/{async-browser-CA0jPWIi.cjs → async-browser-BU_IfxYD.cjs} +19 -107
  13. package/dist/chunks/async-browser-BU_IfxYD.cjs.map +1 -0
  14. package/dist/chunks/{async-core-UqHzvJ-S.cjs → async-core-CRLKP3l-.cjs} +8 -4
  15. package/dist/chunks/async-core-CRLKP3l-.cjs.map +1 -0
  16. package/dist/chunks/{async-node-BYHuGTni.cjs → async-node-3PrbVAbB.cjs} +7 -12
  17. package/dist/chunks/async-node-3PrbVAbB.cjs.map +1 -0
  18. package/dist/chunks/index-CaaQQlPJ.esm.js +3174 -0
  19. package/dist/chunks/index-CaaQQlPJ.esm.js.map +1 -0
  20. package/dist/chunks/node-nKJBk8iJ.esm.js +96 -0
  21. package/dist/chunks/node-nKJBk8iJ.esm.js.map +1 -0
  22. package/dist/chunks/proxy-Dtg-bJ3T.cjs +3763 -0
  23. package/dist/chunks/proxy-Dtg-bJ3T.cjs.map +1 -0
  24. package/dist/chunks/proxy-r7lARftl.esm.js +3701 -0
  25. package/dist/chunks/proxy-r7lARftl.esm.js.map +1 -0
  26. package/dist/debug.cjs +980 -0
  27. package/dist/debug.cjs.map +1 -0
  28. package/dist/debug.d.ts +208 -0
  29. package/dist/debug.esm.js +950 -0
  30. package/dist/debug.esm.js.map +1 -0
  31. package/dist/devtools/manifest.json +1 -1
  32. package/dist/devtools/panel.html +1 -1
  33. package/dist/devtools/panel.js +107 -94
  34. package/dist/devtools/panel.js.map +1 -1
  35. package/dist/index.d.ts +1447 -13
  36. package/dist/mutts.umd.js +7167 -1
  37. package/dist/mutts.umd.js.map +1 -1
  38. package/dist/mutts.umd.min.js +1 -1
  39. package/dist/mutts.umd.min.js.map +1 -1
  40. package/dist/node.cjs +70 -66
  41. package/dist/node.cjs.map +1 -1
  42. package/dist/node.d.ts +2 -1
  43. package/dist/node.dev.cjs +110 -0
  44. package/dist/node.dev.cjs.map +1 -0
  45. package/dist/node.dev.d.ts +2 -0
  46. package/dist/node.dev.esm.js +6 -0
  47. package/dist/node.dev.esm.js.map +1 -0
  48. package/dist/node.esm.js +4 -103
  49. package/dist/node.esm.js.map +1 -1
  50. package/dist/{reactive/types.d.ts → types-W5vD6m2n.d.ts} +186 -93
  51. package/docs/ai/api-reference.md +12 -19
  52. package/docs/ai/manual.md +521 -17
  53. package/docs/events.md +57 -2
  54. package/docs/flavored.md +210 -0
  55. package/docs/reactive/advanced.md +314 -5
  56. package/docs/reactive/attend.md +142 -0
  57. package/docs/reactive/collections.md +76 -39
  58. package/docs/reactive/core.md +100 -14
  59. package/docs/reactive/debugging.md +123 -18
  60. package/docs/reactive/error-handling.md +121 -0
  61. package/docs/reactive/resource.md +125 -0
  62. package/docs/reactive/scan.md +247 -1
  63. package/docs/reactive.md +4 -1
  64. package/docs/std-decorators.md +102 -51
  65. package/docs/utils.md +70 -0
  66. package/docs/zone.md +3 -7
  67. package/package.json +64 -30
  68. package/dist/async/browser.d.ts +0 -2
  69. package/dist/async/browser.d.ts.map +0 -1
  70. package/dist/async/index.d.ts +0 -18
  71. package/dist/async/index.d.ts.map +0 -1
  72. package/dist/async/node.d.ts +0 -2
  73. package/dist/async/node.d.ts.map +0 -1
  74. package/dist/chunks/async-browser-CA0jPWIi.cjs.map +0 -1
  75. package/dist/chunks/async-core-UqHzvJ-S.cjs.map +0 -1
  76. package/dist/chunks/async-node-BYHuGTni.cjs.map +0 -1
  77. package/dist/chunks/index-DhaOVusv.esm.js +0 -6411
  78. package/dist/chunks/index-DhaOVusv.esm.js.map +0 -1
  79. package/dist/decorator.d.ts +0 -106
  80. package/dist/decorator.d.ts.map +0 -1
  81. package/dist/destroyable.d.ts +0 -87
  82. package/dist/destroyable.d.ts.map +0 -1
  83. package/dist/entry-browser.d.ts +0 -3
  84. package/dist/entry-browser.d.ts.map +0 -1
  85. package/dist/entry-node.d.ts +0 -3
  86. package/dist/entry-node.d.ts.map +0 -1
  87. package/dist/eventful.d.ts +0 -18
  88. package/dist/eventful.d.ts.map +0 -1
  89. package/dist/index.d.ts.map +0 -1
  90. package/dist/indexable.d.ts +0 -243
  91. package/dist/indexable.d.ts.map +0 -1
  92. package/dist/introspection.d.ts +0 -27
  93. package/dist/introspection.d.ts.map +0 -1
  94. package/dist/iterableWeak.d.ts +0 -53
  95. package/dist/iterableWeak.d.ts.map +0 -1
  96. package/dist/mixins.d.ts +0 -25
  97. package/dist/mixins.d.ts.map +0 -1
  98. package/dist/promiseChain.d.ts +0 -20
  99. package/dist/promiseChain.d.ts.map +0 -1
  100. package/dist/reactive/array.d.ts +0 -49
  101. package/dist/reactive/array.d.ts.map +0 -1
  102. package/dist/reactive/buffer.d.ts +0 -44
  103. package/dist/reactive/buffer.d.ts.map +0 -1
  104. package/dist/reactive/change.d.ts +0 -29
  105. package/dist/reactive/change.d.ts.map +0 -1
  106. package/dist/reactive/debug.d.ts +0 -111
  107. package/dist/reactive/debug.d.ts.map +0 -1
  108. package/dist/reactive/deep-touch.d.ts +0 -28
  109. package/dist/reactive/deep-touch.d.ts.map +0 -1
  110. package/dist/reactive/deep-watch-state.d.ts +0 -25
  111. package/dist/reactive/deep-watch-state.d.ts.map +0 -1
  112. package/dist/reactive/deep-watch.d.ts +0 -19
  113. package/dist/reactive/deep-watch.d.ts.map +0 -1
  114. package/dist/reactive/effect-context.d.ts +0 -7
  115. package/dist/reactive/effect-context.d.ts.map +0 -1
  116. package/dist/reactive/effects.d.ts +0 -151
  117. package/dist/reactive/effects.d.ts.map +0 -1
  118. package/dist/reactive/index.d.ts +0 -20
  119. package/dist/reactive/index.d.ts.map +0 -1
  120. package/dist/reactive/interface.d.ts +0 -64
  121. package/dist/reactive/interface.d.ts.map +0 -1
  122. package/dist/reactive/map.d.ts +0 -30
  123. package/dist/reactive/map.d.ts.map +0 -1
  124. package/dist/reactive/memoize.d.ts +0 -5
  125. package/dist/reactive/memoize.d.ts.map +0 -1
  126. package/dist/reactive/non-reactive-state.d.ts +0 -9
  127. package/dist/reactive/non-reactive-state.d.ts.map +0 -1
  128. package/dist/reactive/non-reactive.d.ts +0 -11
  129. package/dist/reactive/non-reactive.d.ts.map +0 -1
  130. package/dist/reactive/project.d.ts +0 -41
  131. package/dist/reactive/project.d.ts.map +0 -1
  132. package/dist/reactive/proxy-state.d.ts +0 -8
  133. package/dist/reactive/proxy-state.d.ts.map +0 -1
  134. package/dist/reactive/proxy.d.ts +0 -23
  135. package/dist/reactive/proxy.d.ts.map +0 -1
  136. package/dist/reactive/record.d.ts +0 -116
  137. package/dist/reactive/record.d.ts.map +0 -1
  138. package/dist/reactive/register.d.ts +0 -64
  139. package/dist/reactive/register.d.ts.map +0 -1
  140. package/dist/reactive/registry.d.ts +0 -20
  141. package/dist/reactive/registry.d.ts.map +0 -1
  142. package/dist/reactive/set.d.ts +0 -28
  143. package/dist/reactive/set.d.ts.map +0 -1
  144. package/dist/reactive/tracking.d.ts +0 -7
  145. package/dist/reactive/tracking.d.ts.map +0 -1
  146. package/dist/reactive/types.d.ts.map +0 -1
  147. package/dist/std-decorators.d.ts +0 -50
  148. package/dist/std-decorators.d.ts.map +0 -1
  149. package/dist/utils.d.ts +0 -49
  150. package/dist/utils.d.ts.map +0 -1
  151. package/dist/zone.d.ts +0 -40
  152. package/dist/zone.d.ts.map +0 -1
  153. package/docs/reactive/project.md +0 -93
  154. package/src/async/browser.ts +0 -319
  155. package/src/async/index.ts +0 -23
  156. package/src/async/node.ts +0 -104
  157. package/src/decorator.ts +0 -272
  158. package/src/destroyable.ts +0 -199
  159. package/src/entry-browser.ts +0 -5
  160. package/src/entry-node.ts +0 -5
  161. package/src/eventful.ts +0 -77
  162. package/src/index.d.ts +0 -12
  163. package/src/index.ts +0 -64
  164. package/src/indexable.ts +0 -526
  165. package/src/introspection.ts +0 -59
  166. package/src/iterableWeak.ts +0 -233
  167. package/src/mixins.ts +0 -123
  168. package/src/promiseChain.ts +0 -110
  169. package/src/reactive/array.ts +0 -507
  170. package/src/reactive/buffer.ts +0 -168
  171. package/src/reactive/change.ts +0 -138
  172. package/src/reactive/debug.ts +0 -517
  173. package/src/reactive/deep-touch.ts +0 -268
  174. package/src/reactive/deep-watch-state.ts +0 -82
  175. package/src/reactive/deep-watch.ts +0 -168
  176. package/src/reactive/effect-context.ts +0 -18
  177. package/src/reactive/effects.ts +0 -1382
  178. package/src/reactive/index.ts +0 -75
  179. package/src/reactive/interface.ts +0 -210
  180. package/src/reactive/map.ts +0 -158
  181. package/src/reactive/memoize.ts +0 -163
  182. package/src/reactive/non-reactive-state.ts +0 -49
  183. package/src/reactive/non-reactive.ts +0 -43
  184. package/src/reactive/project.ts +0 -453
  185. package/src/reactive/proxy-state.ts +0 -27
  186. package/src/reactive/proxy.ts +0 -264
  187. package/src/reactive/record.ts +0 -196
  188. package/src/reactive/register.ts +0 -419
  189. package/src/reactive/registry.ts +0 -59
  190. package/src/reactive/set.ts +0 -130
  191. package/src/reactive/tracking.ts +0 -44
  192. package/src/reactive/types.ts +0 -440
  193. package/src/std-decorators.ts +0 -217
  194. package/src/utils.ts +0 -301
  195. package/src/zone.ts +0 -138
@@ -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
- }
@@ -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
- }
@@ -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
- }