mutts 1.0.9 → 1.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. package/README.md +60 -35
  2. package/dist/browser.cjs +1333 -1760
  3. package/dist/browser.cjs.map +1 -1
  4. package/dist/browser.d.ts +2 -1392
  5. package/dist/browser.dev.cjs +114 -0
  6. package/dist/browser.dev.cjs.map +1 -0
  7. package/dist/browser.dev.d.ts +2 -0
  8. package/dist/browser.dev.esm.js +5 -0
  9. package/dist/browser.dev.esm.js.map +1 -0
  10. package/dist/browser.esm.js +18 -97
  11. package/dist/browser.esm.js.map +1 -1
  12. package/dist/chunks/{async-browser-lvzLOCgk.cjs → async-browser-Dgr5CreQ.cjs} +16 -95
  13. package/dist/chunks/async-browser-Dgr5CreQ.cjs.map +1 -0
  14. package/dist/chunks/{async-node-C3DeIb0y.cjs → async-node-3PrbVAbB.cjs} +3 -1
  15. package/dist/chunks/async-node-3PrbVAbB.cjs.map +1 -0
  16. package/dist/chunks/index-Sf74wXTV.esm.js +2577 -0
  17. package/dist/chunks/index-Sf74wXTV.esm.js.map +1 -0
  18. package/dist/chunks/node-Bo7WU5S2.esm.js +96 -0
  19. package/dist/chunks/node-Bo7WU5S2.esm.js.map +1 -0
  20. package/dist/chunks/{index-VTO-b2vR.cjs → proxy-Cc79Lrzj.cjs} +2593 -3216
  21. package/dist/chunks/proxy-Cc79Lrzj.cjs.map +1 -0
  22. package/dist/chunks/{index-CtA2AWl3.esm.js → proxy-D2C49sXH.esm.js} +2570 -3174
  23. package/dist/chunks/proxy-D2C49sXH.esm.js.map +1 -0
  24. package/dist/debug.cjs +987 -28
  25. package/dist/debug.cjs.map +1 -1
  26. package/dist/debug.d.ts +37 -38
  27. package/dist/debug.esm.js +963 -1
  28. package/dist/debug.esm.js.map +1 -1
  29. package/dist/devtools/manifest.json +1 -1
  30. package/dist/devtools/panel.html +1 -1
  31. package/dist/devtools/panel.js +107 -94
  32. package/dist/devtools/panel.js.map +1 -1
  33. package/dist/index.d.ts +1322 -1
  34. package/dist/mutts.umd.js +6647 -1
  35. package/dist/mutts.umd.js.map +1 -1
  36. package/dist/mutts.umd.min.js +1 -1
  37. package/dist/mutts.umd.min.js.map +1 -1
  38. package/dist/node.cjs +69 -63
  39. package/dist/node.cjs.map +1 -1
  40. package/dist/node.d.ts +2 -2
  41. package/dist/node.dev.cjs +114 -0
  42. package/dist/node.dev.cjs.map +1 -0
  43. package/dist/node.dev.d.ts +2 -0
  44. package/dist/node.dev.esm.js +6 -0
  45. package/dist/node.dev.esm.js.map +1 -0
  46. package/dist/node.esm.js +4 -97
  47. package/dist/node.esm.js.map +1 -1
  48. package/dist/{types-DaHFfhlN.d.ts → types-Bx2PhORg.d.ts} +134 -88
  49. package/docs/ai/api-reference.md +11 -32
  50. package/docs/ai/manual.md +297 -239
  51. package/docs/reactive/advanced.md +318 -9
  52. package/docs/reactive/attend.md +2 -4
  53. package/docs/reactive/collections.md +22 -187
  54. package/docs/reactive/core.md +223 -131
  55. package/docs/reactive/debugging.md +119 -12
  56. package/docs/reactive/error-handling.md +10 -10
  57. package/docs/reactive/resource.md +125 -0
  58. package/docs/reactive.md +3 -4
  59. package/docs/utils.md +70 -0
  60. package/docs/zone.md +1 -1
  61. package/package.json +76 -38
  62. package/dist/chunks/async-browser-lvzLOCgk.cjs.map +0 -1
  63. package/dist/chunks/async-node-C3DeIb0y.cjs.map +0 -1
  64. package/dist/chunks/index-2vea86wD.esm.js +0 -3011
  65. package/dist/chunks/index-2vea86wD.esm.js.map +0 -1
  66. package/dist/chunks/index-CtA2AWl3.esm.js.map +0 -1
  67. package/dist/chunks/index-VTO-b2vR.cjs.map +0 -1
  68. package/dist/debug/debug.d.ts +0 -122
  69. package/dist/debug/debug.d.ts.map +0 -1
  70. package/dist/debug/index.d.ts +0 -4
  71. package/dist/debug/index.d.ts.map +0 -1
  72. package/dist/debug/lineage-panel.d.ts +0 -5
  73. package/dist/debug/lineage-panel.d.ts.map +0 -1
  74. package/dist/debug/lineage.d.ts +0 -79
  75. package/dist/debug/lineage.d.ts.map +0 -1
  76. package/dist/src/async/browser.d.ts +0 -2
  77. package/dist/src/async/browser.d.ts.map +0 -1
  78. package/dist/src/async/index.d.ts +0 -19
  79. package/dist/src/async/index.d.ts.map +0 -1
  80. package/dist/src/async/node.d.ts +0 -2
  81. package/dist/src/async/node.d.ts.map +0 -1
  82. package/dist/src/decorator.d.ts +0 -106
  83. package/dist/src/decorator.d.ts.map +0 -1
  84. package/dist/src/destroyable.d.ts +0 -87
  85. package/dist/src/destroyable.d.ts.map +0 -1
  86. package/dist/src/entry-browser.d.ts +0 -3
  87. package/dist/src/entry-browser.d.ts.map +0 -1
  88. package/dist/src/entry-node.d.ts +0 -3
  89. package/dist/src/entry-node.d.ts.map +0 -1
  90. package/dist/src/eventful.d.ts +0 -20
  91. package/dist/src/eventful.d.ts.map +0 -1
  92. package/dist/src/flavored.d.ts +0 -33
  93. package/dist/src/flavored.d.ts.map +0 -1
  94. package/dist/src/index.d.ts +0 -14
  95. package/dist/src/index.d.ts.map +0 -1
  96. package/dist/src/indexable.d.ts +0 -243
  97. package/dist/src/indexable.d.ts.map +0 -1
  98. package/dist/src/introspection.d.ts +0 -27
  99. package/dist/src/introspection.d.ts.map +0 -1
  100. package/dist/src/iterableWeak.d.ts +0 -53
  101. package/dist/src/iterableWeak.d.ts.map +0 -1
  102. package/dist/src/mixins.d.ts +0 -25
  103. package/dist/src/mixins.d.ts.map +0 -1
  104. package/dist/src/promiseChain.d.ts +0 -20
  105. package/dist/src/promiseChain.d.ts.map +0 -1
  106. package/dist/src/reactive/array.d.ts +0 -48
  107. package/dist/src/reactive/array.d.ts.map +0 -1
  108. package/dist/src/reactive/buffer.d.ts +0 -120
  109. package/dist/src/reactive/buffer.d.ts.map +0 -1
  110. package/dist/src/reactive/change.d.ts +0 -29
  111. package/dist/src/reactive/change.d.ts.map +0 -1
  112. package/dist/src/reactive/deep-touch.d.ts +0 -28
  113. package/dist/src/reactive/deep-touch.d.ts.map +0 -1
  114. package/dist/src/reactive/deep-watch-state.d.ts +0 -25
  115. package/dist/src/reactive/deep-watch-state.d.ts.map +0 -1
  116. package/dist/src/reactive/deep-watch.d.ts +0 -20
  117. package/dist/src/reactive/deep-watch.d.ts.map +0 -1
  118. package/dist/src/reactive/describe.d.ts +0 -12
  119. package/dist/src/reactive/describe.d.ts.map +0 -1
  120. package/dist/src/reactive/effect-context.d.ts +0 -34
  121. package/dist/src/reactive/effect-context.d.ts.map +0 -1
  122. package/dist/src/reactive/effects.d.ts +0 -164
  123. package/dist/src/reactive/effects.d.ts.map +0 -1
  124. package/dist/src/reactive/index.d.ts +0 -19
  125. package/dist/src/reactive/index.d.ts.map +0 -1
  126. package/dist/src/reactive/map.d.ts +0 -28
  127. package/dist/src/reactive/map.d.ts.map +0 -1
  128. package/dist/src/reactive/memoize.d.ts +0 -28
  129. package/dist/src/reactive/memoize.d.ts.map +0 -1
  130. package/dist/src/reactive/non-reactive-state.d.ts +0 -9
  131. package/dist/src/reactive/non-reactive-state.d.ts.map +0 -1
  132. package/dist/src/reactive/non-reactive.d.ts +0 -11
  133. package/dist/src/reactive/non-reactive.d.ts.map +0 -1
  134. package/dist/src/reactive/project.d.ts +0 -40
  135. package/dist/src/reactive/project.d.ts.map +0 -1
  136. package/dist/src/reactive/proxy-state.d.ts +0 -8
  137. package/dist/src/reactive/proxy-state.d.ts.map +0 -1
  138. package/dist/src/reactive/proxy.d.ts +0 -23
  139. package/dist/src/reactive/proxy.d.ts.map +0 -1
  140. package/dist/src/reactive/record.d.ts +0 -115
  141. package/dist/src/reactive/record.d.ts.map +0 -1
  142. package/dist/src/reactive/register.d.ts +0 -125
  143. package/dist/src/reactive/register.d.ts.map +0 -1
  144. package/dist/src/reactive/registry.d.ts +0 -21
  145. package/dist/src/reactive/registry.d.ts.map +0 -1
  146. package/dist/src/reactive/set.d.ts +0 -26
  147. package/dist/src/reactive/set.d.ts.map +0 -1
  148. package/dist/src/reactive/tracking.d.ts +0 -7
  149. package/dist/src/reactive/tracking.d.ts.map +0 -1
  150. package/dist/src/reactive/types.d.ts +0 -424
  151. package/dist/src/reactive/types.d.ts.map +0 -1
  152. package/dist/src/reactive/watch.d.ts +0 -48
  153. package/dist/src/reactive/watch.d.ts.map +0 -1
  154. package/dist/src/std-decorators.d.ts +0 -45
  155. package/dist/src/std-decorators.d.ts.map +0 -1
  156. package/dist/src/utils.d.ts +0 -49
  157. package/dist/src/utils.d.ts.map +0 -1
  158. package/dist/src/zone.d.ts +0 -40
  159. package/dist/src/zone.d.ts.map +0 -1
  160. package/docs/reactive/describe.md +0 -85
  161. package/docs/reactive/project.md +0 -93
  162. package/docs/reactive/scan.md +0 -293
  163. package/src/async/browser.ts +0 -323
  164. package/src/async/index.ts +0 -27
  165. package/src/async/node.ts +0 -92
  166. package/src/decorator.ts +0 -272
  167. package/src/destroyable.ts +0 -199
  168. package/src/entry-browser.ts +0 -5
  169. package/src/entry-node.ts +0 -5
  170. package/src/eventful.ts +0 -110
  171. package/src/flavored.ts +0 -106
  172. package/src/index.d.ts +0 -12
  173. package/src/index.ts +0 -64
  174. package/src/indexable.ts +0 -526
  175. package/src/introspection.ts +0 -59
  176. package/src/iterableWeak.ts +0 -233
  177. package/src/mixins.ts +0 -123
  178. package/src/promiseChain.ts +0 -110
  179. package/src/reactive/array.ts +0 -500
  180. package/src/reactive/buffer.ts +0 -328
  181. package/src/reactive/change.ts +0 -131
  182. package/src/reactive/deep-touch.ts +0 -273
  183. package/src/reactive/deep-watch-state.ts +0 -82
  184. package/src/reactive/deep-watch.ts +0 -171
  185. package/src/reactive/describe.ts +0 -39
  186. package/src/reactive/effect-context.ts +0 -83
  187. package/src/reactive/effects.ts +0 -1434
  188. package/src/reactive/index.ts +0 -72
  189. package/src/reactive/map.ts +0 -142
  190. package/src/reactive/memoize.ts +0 -186
  191. package/src/reactive/non-reactive-state.ts +0 -49
  192. package/src/reactive/non-reactive.ts +0 -43
  193. package/src/reactive/project.md +0 -107
  194. package/src/reactive/project.ts +0 -430
  195. package/src/reactive/proxy-state.ts +0 -27
  196. package/src/reactive/proxy.ts +0 -282
  197. package/src/reactive/record.ts +0 -181
  198. package/src/reactive/register.ts +0 -538
  199. package/src/reactive/registry.ts +0 -72
  200. package/src/reactive/set.ts +0 -117
  201. package/src/reactive/tracking.ts +0 -41
  202. package/src/reactive/types.ts +0 -520
  203. package/src/reactive/watch.ts +0 -180
  204. package/src/std-decorators.ts +0 -256
  205. package/src/utils.ts +0 -300
  206. package/src/zone.ts +0 -142
@@ -1,117 +0,0 @@
1
- import { contentRef } from '../utils'
2
- import { touched, touched1 } from './change'
3
- import { makeReactiveEntriesIterator, makeReactiveIterator } from './non-reactive'
4
- import { reactive } from './proxy'
5
- import { dependant } from './tracking'
6
-
7
- /**
8
- * Reactive wrapper around JavaScript's WeakSet class
9
- * Only tracks individual value operations, no size tracking (WeakSet limitation)
10
- */
11
- export abstract class ReactiveWeakSet<T extends object> extends WeakSet<T> {
12
- add(value: T): this {
13
- const had = this.has(value)
14
- this.add(value)
15
- if (!had) {
16
- // touch the specific value and the collection view
17
- touched1(contentRef(this), { type: 'add', prop: value }, value)
18
- // no size/allProps for WeakSet
19
- }
20
- return this
21
- }
22
-
23
- delete(value: T): boolean {
24
- const had = this.has(value)
25
- const res = this.delete(value)
26
- if (had) touched1(contentRef(this), { type: 'del', prop: value }, value)
27
- return res
28
- }
29
-
30
- has(value: T): boolean {
31
- dependant(contentRef(this), value)
32
- return this.has(value)
33
- }
34
- }
35
-
36
- /**
37
- * Reactive wrapper around JavaScript's Set class
38
- * Tracks size changes, individual value operations, and collection-wide operations
39
- */
40
- export abstract class ReactiveSet<T> extends Set<T> {
41
- get size(): number {
42
- // size depends on the wrapper instance, like Map counterpart
43
- dependant(this, 'size')
44
- return this.size
45
- }
46
-
47
- add(value: T): this {
48
- const had = this.has(value)
49
- const reactiveValue = reactive(value)
50
- this.add(reactiveValue)
51
- if (!had) {
52
- const evolution = { type: 'add', prop: reactiveValue } as const
53
- // touch for value-specific and aggregate dependencies
54
- touched1(contentRef(this), evolution, reactiveValue)
55
- touched1(this, evolution, 'size')
56
- }
57
- return this
58
- }
59
-
60
- clear(): void {
61
- const hadEntries = this.size > 0
62
- this.clear()
63
- if (hadEntries) {
64
- const evolution = { type: 'bunch', method: 'clear' } as const
65
- touched1(this, evolution, 'size')
66
- touched(contentRef(this), evolution)
67
- }
68
- }
69
-
70
- delete(value: T): boolean {
71
- const had = this.has(value)
72
- const res = this.delete(value)
73
- if (had) {
74
- const evolution = { type: 'del', prop: value } as const
75
- touched1(contentRef(this), evolution, value)
76
- touched1(this, evolution, 'size')
77
- }
78
- return res
79
- }
80
-
81
- has(value: T): boolean {
82
- dependant(contentRef(this), value)
83
- return this.has(value)
84
- }
85
-
86
- entries(): Generator<[T, T]> {
87
- dependant(contentRef(this))
88
- return makeReactiveEntriesIterator(this.entries())
89
- }
90
-
91
- forEach(callbackfn: (value: T, value2: T, set: Set<T>) => void, thisArg?: any): void {
92
- dependant(contentRef(this))
93
- this.forEach(callbackfn, thisArg)
94
- }
95
-
96
- keys(): Generator<T> {
97
- dependant(contentRef(this))
98
- return makeReactiveIterator(this.keys())
99
- }
100
-
101
- values(): Generator<T> {
102
- dependant(contentRef(this))
103
- return makeReactiveIterator(this.values())
104
- }
105
-
106
- [Symbol.iterator](): SetIterator<T> {
107
- dependant(contentRef(this))
108
- const it: SetIterator<T> = Set.prototype[Symbol.iterator].call(this)
109
- const nativeNext = it.next.bind(it)
110
- it.next = () => {
111
- const result = nativeNext()
112
- if (result.done) return result
113
- return { value: reactive(result.value), done: false }
114
- }
115
- return it
116
- }
117
- }
@@ -1,41 +0,0 @@
1
- import { getActiveEffect } from './effect-context'
2
- import { unwrap } from './proxy-state'
3
- import { effectToReactiveObjects, watchers } from './registry'
4
- import { allProps, type EffectTrigger } from './types'
5
-
6
- /**
7
- * Marks a property as a dependency of the current effect
8
- * @param obj - The object containing the property
9
- * @param prop - The property name (defaults to allProps)
10
- */
11
- export function dependant(obj: any, prop: any = allProps) {
12
- obj = unwrap(obj)
13
- const currentActiveEffect = getActiveEffect()
14
-
15
- // Early return if no active effect, tracking disabled, or invalid prop
16
- if (!currentActiveEffect || (typeof prop === 'symbol' && prop !== allProps)) return
17
-
18
- if ('dependencyHook' in currentActiveEffect) {
19
- // @ts-expect-error We declared it nowhere - it's okay as it's really internal and for edge-case debug purpose only
20
- currentActiveEffect.dependencyHook(obj, prop)
21
- }
22
- let objectWatchers = watchers.get(obj)
23
- if (!objectWatchers) {
24
- objectWatchers = new Map<PropertyKey, Set<EffectTrigger>>()
25
- watchers.set(obj, objectWatchers)
26
- }
27
- let deps = objectWatchers.get(prop)
28
- if (!deps) {
29
- deps = new Set<EffectTrigger>()
30
- objectWatchers.set(prop, deps)
31
- }
32
- deps.add(currentActiveEffect)
33
-
34
- // Track which reactive objects this effect is watching
35
- const effectObjects = effectToReactiveObjects.get(currentActiveEffect)
36
- if (effectObjects) {
37
- effectObjects.add(obj)
38
- } else {
39
- effectToReactiveObjects.set(currentActiveEffect, new Set([obj]))
40
- }
41
- }
@@ -1,520 +0,0 @@
1
- // biome-ignore-all lint/suspicious/noConfusingVoidType: Type 'void' is not assignable to type 'ScopedCallback | undefined'.
2
- // Argument of type '() => void' is not assignable to parameter of type '(dep: DependencyFunction) => ScopedCallback | undefined'.
3
-
4
- import type { FunctionWrapper } from '../zone'
5
-
6
- export type EffectAccessEvents = {
7
- triggered(event: string, ...args: any[]): void
8
- }
9
-
10
- /**
11
- * Effect access passed to user callbacks within effects/watch
12
- * Provides functions to track dependencies and information about the effect execution
13
- */
14
- export interface EffectAccess {
15
- /**
16
- * Tracks dependencies in the current effect context
17
- * Use this for normal dependency tracking within the effect
18
- * @example
19
- * ```typescript
20
- * effect(({ tracked }) => {
21
- * // In async context, use tracked to restore dependency tracking
22
- * await someAsyncOperation()
23
- * const value = tracked(() => state.count) // Tracks state.count in this effect
24
- * })
25
- * ```
26
- */
27
- tracked: FunctionWrapper
28
- /**
29
- * Tracks dependencies in the parent effect context
30
- * Use this when child effects should track dependencies in the parent,
31
- * allowing parent cleanup to manage child effects while dependencies trigger the parent
32
- * @example
33
- * ```typescript
34
- * effect(({ ascend }) => {
35
- * const length = inputs.length
36
- * if (length > 0) {
37
- * ascend(() => {
38
- * // Dependencies here are tracked in the parent effect
39
- * inputs.forEach(item => console.log(item))
40
- * })
41
- * }
42
- * })
43
- * ```
44
- */
45
- ascend: FunctionWrapper
46
- /**
47
- * Indicates whether the effect is running as a reaction (i.e. not the first call)
48
- * - `false`: First execution when the effect is created
49
- * - `true`: Subsequent executions triggered by dependency changes
50
- * @example
51
- * ```typescript
52
- * effect(({ reaction }) => {
53
- * if (!reaction) {
54
- * console.log('Effect initialized')
55
- * // Setup code that should only run once
56
- * } else {
57
- * console.log('Effect re-ran due to dependency change')
58
- * // Code that runs on every update
59
- * }
60
- * })
61
- * ```
62
- */
63
- reaction: boolean
64
- }
65
- // Zone-based async context preservation is implemented in zone.ts
66
- // It automatically preserves effect context across Promise boundaries (.then, .catch, .finally)
67
-
68
- /**
69
- * Base type for effect callbacks - simple function without additional properties
70
- */
71
- export type ScopedCallback = () => void
72
-
73
- /**
74
- * Type for effect cleanup functions with stopped state tracking
75
- */
76
- export type EffectCleanup = ScopedCallback & {
77
- [stopped]: boolean
78
- }
79
-
80
- // Debug type for stack frames
81
- export type StackFrame = {
82
- functionName: string
83
- fileName: string
84
- lineNumber: number
85
- columnNumber: number
86
- raw: string
87
- }
88
-
89
- export type EffectTracking = (obj: any, evolution: Evolution, prop: any, effect: EffectTrigger) => void
90
-
91
- /**
92
- * Centralized node for all effect metadata and relationships
93
- */
94
- export interface EffectNode {
95
- // Graph relationships
96
- parent?: EffectTrigger
97
- children?: Set<EffectCleanup>
98
-
99
- // Lifecycle
100
- cleanup?: ScopedCallback
101
- stopped?: boolean
102
-
103
- // Error handling
104
- forwardThrow?: CatchFunction
105
- catchers?: CatchFunction[]
106
-
107
- // Debug / Metadata
108
- creationStack?: StackFrame[]
109
- dependencyHook?: (obj: any, prop: any) => void
110
-
111
- // Tracking
112
- trackers?: EffectTracking[]
113
-
114
- // Configuration
115
- isOpaque?: boolean
116
- }
117
-
118
- /**
119
- * Type for the `runEffect` function of an effect - argument-less function to call to trigger the effect
120
- */
121
- export type EffectTrigger = ScopedCallback
122
-
123
- /**
124
- * Async execution mode for effects
125
- * - `cancel`: Cancel previous async execution when dependencies change (default)
126
- * - `queue`: Queue next execution to run after current completes
127
- * - `ignore`: Ignore new executions while async work is running
128
- */
129
- export type AsyncExecutionMode = 'cancel' | 'queue' | 'ignore'
130
-
131
- /**
132
- * Options for effect creation
133
- */
134
- export interface EffectOptions {
135
- /**
136
- * How to handle async effect executions when dependencies change
137
- * @default 'cancel'
138
- */
139
- asyncMode?: AsyncExecutionMode
140
- /**
141
- * If true, this effect is "opaque" to deep optimizations: it sees the object reference itself
142
- * and must be notified when it changes, regardless of deep content similarity.
143
- * Use this for effects that depend on object identity (like memoize).
144
- */
145
- opaque?: boolean
146
- /**
147
- * Used for debugging purpose. Provides a callback to be called every time a dependency is created.
148
- */
149
- dependencyHook?: (obj: any, prop: any) => void
150
- /**
151
- * Used for debugging purpose. Provides a name for the effect.
152
- */
153
- name?: string
154
- }
155
-
156
- /**
157
- * Type for property evolution events
158
- */
159
- export type PropEvolution = {
160
- type: 'set' | 'del' | 'add' | 'invalidate'
161
- prop: any
162
- }
163
-
164
- /**
165
- * Type for collection operation evolution events
166
- */
167
- export type BunchEvolution = {
168
- type: 'bunch'
169
- method: string
170
- }
171
- export type Evolution = PropEvolution | BunchEvolution
172
-
173
- type State =
174
- | {
175
- evolution: Evolution
176
- next: State
177
- }
178
- | {}
179
-
180
- // Track native reactivity
181
- const nativeReactive = Symbol('native-reactive')
182
-
183
- /**
184
- * Symbol to mark individual objects as non-reactive
185
- */
186
- export const nonReactiveMark = Symbol('non-reactive')
187
- /**
188
- * Symbol to mark class properties as non-reactive
189
- */
190
- export const unreactiveProperties = Symbol('unreactive-properties')
191
-
192
- /**
193
- * Symbol representing all properties in reactive tracking
194
- */
195
- export const allProps = Symbol('all-props')
196
-
197
- /**
198
- * Symbol for accessing projection information on reactive objects
199
- */
200
- export const projectionInfo = Symbol('projection-info')
201
-
202
- /**
203
- * Symbol to check if an effect is stopped
204
- */
205
- export const stopped = Symbol('stopped')
206
-
207
- /**
208
- * Symbol to access effect cleanup function
209
- */
210
- export const cleanup = Symbol('cleanup')
211
-
212
- export const forwardThrow = Symbol('throw')
213
-
214
- export type EffectCloser = (error?: any) => void
215
- //biome-ignore lint/suspicious/noConfusingVoidType: We have to
216
- export type CatchFunction = (error: any) => EffectCloser | undefined | void
217
-
218
- /**
219
- * Context for a running projection item effect
220
- */
221
- export interface ProjectionContext {
222
- source: any
223
- key?: any
224
- target: any
225
- depth: number
226
- parent?: ProjectionContext
227
- }
228
-
229
- // Symbol to mark functions with their root function
230
- const rootFunction = Symbol('root-function')
231
-
232
- /**
233
- * Structured error codes for machine-readable diagnosis
234
- */
235
- export enum ReactiveErrorCode {
236
- CycleDetected = 'CYCLE_DETECTED',
237
- MaxDepthExceeded = 'MAX_DEPTH_EXCEEDED',
238
- MaxReactionExceeded = 'MAX_REACTION_EXCEEDED',
239
- WriteInComputed = 'WRITE_IN_COMPUTED',
240
- TrackingError = 'TRACKING_ERROR',
241
- BrokenEffects = 'BROKEN_EFFECTS',
242
- }
243
-
244
- export type CycleDebugInfo = {
245
- code: ReactiveErrorCode.CycleDetected
246
- cycle: string[]
247
- details?: string
248
- }
249
-
250
- export type MaxDepthDebugInfo = {
251
- code: ReactiveErrorCode.MaxDepthExceeded
252
- depth: number
253
- chain: string[]
254
- }
255
-
256
- export type MaxReactionDebugInfo = {
257
- code: ReactiveErrorCode.MaxReactionExceeded
258
- count: number
259
- effect: string
260
- }
261
-
262
- export type GenericDebugInfo = {
263
- code: ReactiveErrorCode
264
- causalChain?: string[]
265
- creationStack?: string | any[]
266
- lineage?: any[]
267
- [key: string]: any
268
- }
269
-
270
- export type ReactiveDebugInfo =
271
- | CycleDebugInfo
272
- | MaxDepthDebugInfo
273
- | MaxReactionDebugInfo
274
- | GenericDebugInfo
275
-
276
- /**
277
- * Error class for reactive system errors
278
- */
279
- export class ReactiveError extends Error {
280
- constructor(
281
- message: string,
282
- public debugInfo?: ReactiveDebugInfo
283
- ) {
284
- super(message)
285
- this.name = 'ReactiveError'
286
- }
287
-
288
- get code(): ReactiveErrorCode | undefined {
289
- return this.debugInfo?.code
290
- }
291
-
292
- get cause(): any {
293
- return (this.debugInfo as any)?.cause
294
- }
295
- }
296
-
297
- // biome-ignore-start lint/correctness/noUnusedFunctionParameters: Interface declaration with empty defaults
298
- /**
299
- * Global options for the reactive system
300
- */
301
- export const options = {
302
- /**
303
- * Debug purpose: called when an effect is entered
304
- * @param effect - The effect that is entered
305
- */
306
- enter: (_effect: Function) => {},
307
- /**
308
- * Debug purpose: called when an effect is left
309
- * @param effect - The effect that is left
310
- */
311
- leave: (_effect: Function) => {},
312
- /**
313
- * Debug purpose: called when an effect is chained
314
- * @param target - The effect that is being triggered
315
- * @param caller - The effect that is calling the target
316
- */
317
- chain: (_targets: Function[], _caller?: Function) => {},
318
- /**
319
- * Debug purpose: called when an effect chain is started
320
- * @param target - The effect that is being triggered
321
- */
322
- beginChain: (_targets: Function[]) => {},
323
- /**
324
- * Debug purpose: called when an effect chain is ended
325
- */
326
- endChain: () => {},
327
- garbageCollected: (_fn: Function) => {},
328
- /**
329
- * Debug purpose: called when an object is touched
330
- * @param obj - The object that is touched
331
- * @param evolution - The type of change
332
- * @param props - The properties that changed
333
- * @param deps - The dependencies that changed
334
- */
335
- touched: (_obj: any, _evolution: Evolution, _props?: any[], _deps?: Set<EffectTrigger>) => {},
336
- /**
337
- * Debug purpose: called when an effect is skipped because it's already running
338
- * @param effect - The effect that is already running
339
- * @param runningChain - The array of effects from the detected one to the currently running one
340
- */
341
- skipRunningEffect: (_effect: EffectTrigger) => {},
342
- /**
343
- * Debug purpose: maximum effect chain (like call stack max depth)
344
- * Used to prevent infinite loops
345
- * @default 100
346
- */
347
- maxEffectChain: 100,
348
- /**
349
- * Maximum number of times an effect can be triggered by the same cause in a single batch
350
- * Used to detect aggressive re-computation or infinite loops
351
- * @default 10
352
- */
353
- maxTriggerPerBatch: 10,
354
- /**
355
- * Debug purpose: maximum effect reaction (like call stack max depth)
356
- * Used to prevent infinite loops
357
- * @default 'throw'
358
- */
359
- maxEffectReaction: 'throw' as 'throw' | 'debug' | 'warn',
360
- /**
361
- * Callback called when a memoization discrepancy is detected (debug only)
362
- * When defined, memoized functions will run a second time (untracked) to verify consistency.
363
- * If the untracked run returns a different value than the cached one, this callback is triggered.
364
- *
365
- * This is the primary tool for detecting missing reactive dependencies in computed values.
366
- *
367
- * @param cached - The value currently in the memoization cache
368
- * @param fresh - The value obtained by re-running the function untracked
369
- * @param fn - The memoized function itself
370
- * @param args - Arguments passed to the function
371
- *
372
- * @example
373
- * ```typescript
374
- * reactiveOptions.onMemoizationDiscrepancy = (cached, fresh, fn, args) => {
375
- * throw new Error(`Memoization discrepancy in ${fn.name}!`);
376
- * };
377
- * ```
378
- */
379
- onMemoizationDiscrepancy: undefined as
380
- | ((
381
- cached: any,
382
- fresh: any,
383
- fn: Function,
384
- args: any[],
385
- cause: 'calculation' | 'comparison'
386
- ) => void)
387
- | undefined,
388
- /**
389
- * How to handle cycles detected in effect batches.
390
- *
391
- * - `'production'` (Default): High-performance mode. Disables dependency graph maintenance and
392
- * Topological Sorting in favor of a simple FIFO queue. Use this for trustworthy, acyclic UI code.
393
- * Cycle detection is heuristic (uses maxEffectChain execution counts).
394
- *
395
- * - `'development'`: Maintains direct dependency graph for early cycle detection during edge creation.
396
- * Catches cycles before effects execute via DFS check when adding edges. Throws immediately with
397
- * basic path information. Good balance of debugging help with moderate overhead.
398
- *
399
- * - `'debug'`: Full diagnostic mode with transitive closures and topological sorting.
400
- * Provides detailed cycle path reporting. Highest overhead but most informative for bug hunting.
401
- *
402
- * @default 'production'
403
- */
404
- cycleHandling: 'development' as 'production' | 'development' | 'debug',
405
- /**
406
- * Internal flag used by memoization discrepancy detector to avoid counting calls in tests
407
- * @warning Do not modify this flag manually, this flag is given by the engine
408
- */
409
- isVerificationRun: false,
410
- /**
411
- * Maximum depth for deep watching traversal
412
- * Used to prevent infinite recursion in circular references
413
- * @default 100
414
- */
415
- maxDeepWatchDepth: 100,
416
- /**
417
- * Only react on instance members modification (not inherited properties)
418
- * For instance, do not track class methods
419
- * @default true
420
- */
421
- instanceMembers: true,
422
- /**
423
- * Ignore accessors (getters and setters) and only track direct properties
424
- * @default true
425
- */
426
- ignoreAccessors: true,
427
- /**
428
- * Enable recursive touching when objects with the same prototype are replaced
429
- * When enabled, replacing an object with another of the same prototype triggers
430
- * recursive diffing instead of notifying parent effects
431
- * @default true
432
- */
433
- recursiveTouching: true,
434
- /**
435
- * Default async execution mode for effects that return Promises
436
- * - 'cancel': Cancel previous async execution when dependencies change (default, enables async zone)
437
- * - 'queue': Queue next execution to run after current completes (enables async zone)
438
- * - 'ignore': Ignore new executions while async work is running (enables async zone)
439
- * - false: Disable async zone and async mode handling (effects run concurrently)
440
- *
441
- * **When truthy:** Enables async zone (Promise.prototype wrapping) for automatic context
442
- * preservation in Promise callbacks. Warning: This modifies Promise.prototype globally.
443
- * Only enable if no other library modifies Promise.prototype.
444
- *
445
- * **When false:** Async zone is disabled. Use `tracked()` manually in Promise callbacks.
446
- *
447
- * Can be overridden per-effect via EffectOptions
448
- * @default 'cancel'
449
- */
450
- asyncMode: 'cancel' as AsyncExecutionMode | false,
451
- // biome-ignore lint/suspicious/noConsole: This is the whole point here
452
- warn: (...args: any[]) => console.warn(...args),
453
-
454
- /**
455
- * Configuration for the introspection system
456
- */
457
- introspection: {
458
- /**
459
- * Whether to keep a history of mutations for debugging
460
- * @default false
461
- */
462
- enableHistory: false,
463
- /**
464
- * Number of mutations to keep in history
465
- * @default 50
466
- */
467
- historySize: 50,
468
- },
469
-
470
- /**
471
- * Configuration for zone hooks - control which async APIs are hooked
472
- * Each option controls whether the corresponding async API is wrapped to preserve effect context
473
- * Only applies when asyncMode is enabled (truthy)
474
- * @deprecated Should take all when we made sure PIXI.create, Game.create, ... are -> .root()
475
- */
476
- zones: {
477
- /**
478
- * Hook setTimeout to preserve effect context
479
- * @default true
480
- */
481
- setTimeout: true,
482
- /**
483
- * Hook setInterval to preserve effect context
484
- * @default true
485
- */
486
- setInterval: true,
487
- /**
488
- * Hook requestAnimationFrame (runs in untracked context when hooked)
489
- * @default true
490
- */
491
- requestAnimationFrame: true,
492
- /**
493
- * Hook queueMicrotask to preserve effect context
494
- * @default true
495
- */
496
- queueMicrotask: true,
497
- },
498
- }
499
- // biome-ignore-end lint/correctness/noUnusedFunctionParameters: Interface declaration with empty defaults
500
-
501
- type CallableOption = {
502
- [K in keyof typeof options]: (typeof options)[K] extends ((...args: any[]) => any) | undefined
503
- ? K
504
- : never
505
- }[keyof typeof options]
506
-
507
- export function optionCall<K extends CallableOption>(
508
- name: K,
509
- ...args: NonNullable<(typeof options)[K]> extends (...a: infer A) => unknown ? A : never
510
- ): void {
511
- const fn = options[name]
512
- if (typeof fn !== 'function') return
513
- try {
514
- ;(fn as Function)(...args)
515
- } catch (error) {
516
- options.warn(`options.${name} threw`, error)
517
- }
518
- }
519
-
520
- export { type State, nativeReactive, rootFunction }