mutts 1.0.6 → 1.0.8
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 +61 -23
- package/dist/async/browser.d.ts +2 -0
- package/dist/async/browser.d.ts.map +1 -0
- package/dist/async/index.d.ts +18 -0
- package/dist/async/index.d.ts.map +1 -0
- package/dist/async/node.d.ts +2 -0
- package/dist/async/node.d.ts.map +1 -0
- package/dist/{chunks/index-CDCOjzTy.js → browser.cjs} +5913 -4382
- package/dist/browser.cjs.map +1 -0
- package/dist/browser.d.ts +1655 -0
- package/dist/browser.esm.js +305 -0
- package/dist/browser.esm.js.map +1 -0
- package/dist/chunks/async-browser-CA0jPWIi.cjs +304 -0
- package/dist/chunks/async-browser-CA0jPWIi.cjs.map +1 -0
- package/dist/chunks/async-core-UqHzvJ-S.cjs +25 -0
- package/dist/chunks/async-core-UqHzvJ-S.cjs.map +1 -0
- package/dist/chunks/async-node-BYHuGTni.cjs +103 -0
- package/dist/chunks/async-node-BYHuGTni.cjs.map +1 -0
- package/dist/chunks/{index-DiP0RXoZ.esm.js → index-DhaOVusv.esm.js} +5851 -4345
- package/dist/chunks/index-DhaOVusv.esm.js.map +1 -0
- package/dist/decorator.d.ts +17 -18
- package/dist/decorator.d.ts.map +1 -0
- package/dist/destroyable.d.ts +12 -15
- package/dist/destroyable.d.ts.map +1 -0
- package/dist/devtools/devtool/devtools.d.ts +1 -0
- package/dist/devtools/devtool/devtools.d.ts.map +1 -0
- package/dist/devtools/devtool/panel.d.ts +2 -0
- package/dist/devtools/devtool/panel.d.ts.map +1 -0
- package/dist/devtools/panel.js.map +1 -1
- package/dist/entry-browser.d.ts +3 -0
- package/dist/entry-browser.d.ts.map +1 -0
- package/dist/entry-node.d.ts +3 -0
- package/dist/entry-node.d.ts.map +1 -0
- package/dist/eventful.d.ts +3 -5
- package/dist/eventful.d.ts.map +1 -0
- package/dist/index.d.ts +13 -19
- package/dist/index.d.ts.map +1 -0
- package/dist/indexable.d.ts +10 -10
- package/dist/indexable.d.ts.map +1 -0
- package/dist/introspection.d.ts +27 -0
- package/dist/introspection.d.ts.map +1 -0
- package/dist/iterableWeak.d.ts +53 -0
- package/dist/iterableWeak.d.ts.map +1 -0
- package/dist/mixins.d.ts +25 -0
- package/dist/mixins.d.ts.map +1 -0
- package/dist/mutts.umd.js +1 -1
- package/dist/mutts.umd.js.map +1 -1
- package/dist/mutts.umd.min.js +1 -1
- package/dist/mutts.umd.min.js.map +1 -1
- package/dist/node.cjs +105 -0
- package/dist/node.cjs.map +1 -0
- package/dist/node.d.ts +1 -0
- package/dist/node.esm.js +104 -0
- package/dist/node.esm.js.map +1 -0
- package/dist/promiseChain.d.ts +4 -5
- package/dist/promiseChain.d.ts.map +1 -0
- package/dist/reactive/array.d.ts +49 -0
- package/dist/reactive/array.d.ts.map +1 -0
- package/dist/reactive/buffer.d.ts +44 -0
- package/dist/reactive/buffer.d.ts.map +1 -0
- package/dist/reactive/change.d.ts +29 -0
- package/dist/reactive/change.d.ts.map +1 -0
- package/dist/reactive/debug.d.ts +111 -0
- package/dist/reactive/debug.d.ts.map +1 -0
- package/dist/reactive/deep-touch.d.ts +28 -0
- package/dist/reactive/deep-touch.d.ts.map +1 -0
- package/dist/reactive/deep-watch-state.d.ts +25 -0
- package/dist/reactive/deep-watch-state.d.ts.map +1 -0
- package/dist/reactive/deep-watch.d.ts +19 -0
- package/dist/reactive/deep-watch.d.ts.map +1 -0
- package/dist/reactive/effect-context.d.ts +7 -0
- package/dist/reactive/effect-context.d.ts.map +1 -0
- package/dist/reactive/effects.d.ts +151 -0
- package/dist/reactive/effects.d.ts.map +1 -0
- package/dist/reactive/index.d.ts +20 -0
- package/dist/reactive/index.d.ts.map +1 -0
- package/dist/reactive/interface.d.ts +64 -0
- package/dist/reactive/interface.d.ts.map +1 -0
- package/dist/reactive/map.d.ts +30 -0
- package/dist/reactive/map.d.ts.map +1 -0
- package/dist/reactive/memoize.d.ts +5 -0
- package/dist/reactive/memoize.d.ts.map +1 -0
- package/dist/reactive/non-reactive-state.d.ts +9 -0
- package/dist/reactive/non-reactive-state.d.ts.map +1 -0
- package/dist/reactive/non-reactive.d.ts +11 -0
- package/dist/reactive/non-reactive.d.ts.map +1 -0
- package/dist/reactive/project.d.ts +41 -0
- package/dist/reactive/project.d.ts.map +1 -0
- package/dist/reactive/proxy-state.d.ts +8 -0
- package/dist/reactive/proxy-state.d.ts.map +1 -0
- package/dist/reactive/proxy.d.ts +23 -0
- package/dist/reactive/proxy.d.ts.map +1 -0
- package/dist/reactive/record.d.ts +116 -0
- package/dist/reactive/record.d.ts.map +1 -0
- package/dist/reactive/register.d.ts +64 -0
- package/dist/reactive/register.d.ts.map +1 -0
- package/dist/reactive/registry.d.ts +20 -0
- package/dist/reactive/registry.d.ts.map +1 -0
- package/dist/reactive/set.d.ts +28 -0
- package/dist/reactive/set.d.ts.map +1 -0
- package/dist/reactive/tracking.d.ts +7 -0
- package/dist/reactive/tracking.d.ts.map +1 -0
- package/dist/reactive/types.d.ts +376 -0
- package/dist/reactive/types.d.ts.map +1 -0
- package/dist/std-decorators.d.ts +9 -11
- package/dist/std-decorators.d.ts.map +1 -0
- package/dist/utils.d.ts +49 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/zone.d.ts +40 -0
- package/dist/zone.d.ts.map +1 -0
- package/docs/ai/api-reference.md +0 -2
- package/docs/reactive/advanced.md +2 -5
- package/docs/reactive/collections.md +0 -125
- package/docs/reactive/core.md +27 -24
- package/docs/reactive/debugging.md +12 -2
- package/docs/reactive/project.md +1 -1
- package/docs/reactive/scan.md +78 -0
- package/docs/reactive.md +2 -1
- package/docs/std-decorators.md +69 -0
- package/docs/zone.md +95 -0
- package/package.json +67 -23
- package/src/async/browser.ts +319 -0
- package/src/async/index.ts +23 -0
- package/src/async/node.ts +104 -0
- package/src/decorator.ts +5 -1
- package/src/destroyable.ts +1 -1
- package/src/entry-browser.ts +5 -0
- package/src/entry-node.ts +5 -0
- package/src/index.d.ts +12 -9
- package/src/index.ts +23 -14
- package/src/indexable.ts +42 -0
- package/src/mixins.ts +2 -2
- package/src/reactive/array.ts +274 -179
- package/src/reactive/buffer.ts +168 -0
- package/src/reactive/change.ts +2 -2
- package/src/reactive/effect-context.ts +15 -91
- package/src/reactive/effects.ts +119 -179
- package/src/reactive/index.ts +11 -13
- package/src/reactive/interface.ts +19 -33
- package/src/reactive/map.ts +49 -62
- package/src/reactive/memoize.ts +19 -9
- package/src/reactive/project.ts +43 -22
- package/src/reactive/proxy.ts +16 -41
- package/src/reactive/record.ts +3 -3
- package/src/reactive/register.ts +5 -7
- package/src/reactive/registry.ts +9 -17
- package/src/reactive/set.ts +43 -57
- package/src/reactive/tracking.ts +1 -29
- package/src/reactive/types.ts +46 -23
- package/src/utils.ts +80 -37
- package/src/zone.ts +138 -0
- package/dist/chunks/_tslib-BgjropY9.js +0 -81
- package/dist/chunks/_tslib-BgjropY9.js.map +0 -1
- package/dist/chunks/_tslib-MCKDzsSq.esm.js +0 -75
- package/dist/chunks/_tslib-MCKDzsSq.esm.js.map +0 -1
- package/dist/chunks/decorator-BGILvPtN.esm.js +0 -627
- package/dist/chunks/decorator-BGILvPtN.esm.js.map +0 -1
- package/dist/chunks/decorator-BQ2eBTCj.js +0 -651
- package/dist/chunks/decorator-BQ2eBTCj.js.map +0 -1
- package/dist/chunks/index-CDCOjzTy.js.map +0 -1
- package/dist/chunks/index-DiP0RXoZ.esm.js.map +0 -1
- package/dist/decorator.esm.js +0 -2
- package/dist/decorator.esm.js.map +0 -1
- package/dist/decorator.js +0 -11
- package/dist/decorator.js.map +0 -1
- package/dist/destroyable.esm.js +0 -109
- package/dist/destroyable.esm.js.map +0 -1
- package/dist/destroyable.js +0 -116
- package/dist/destroyable.js.map +0 -1
- package/dist/eventful.esm.js +0 -66
- package/dist/eventful.esm.js.map +0 -1
- package/dist/eventful.js +0 -68
- package/dist/eventful.js.map +0 -1
- package/dist/index.esm.js +0 -53
- package/dist/index.esm.js.map +0 -1
- package/dist/index.js +0 -139
- package/dist/index.js.map +0 -1
- package/dist/indexable.esm.js +0 -285
- package/dist/indexable.esm.js.map +0 -1
- package/dist/indexable.js +0 -291
- package/dist/indexable.js.map +0 -1
- package/dist/promiseChain.esm.js +0 -78
- package/dist/promiseChain.esm.js.map +0 -1
- package/dist/promiseChain.js +0 -80
- package/dist/promiseChain.js.map +0 -1
- package/dist/reactive.d.ts +0 -910
- package/dist/reactive.esm.js +0 -5
- package/dist/reactive.esm.js.map +0 -1
- package/dist/reactive.js +0 -59
- package/dist/reactive.js.map +0 -1
- package/dist/std-decorators.esm.js +0 -196
- package/dist/std-decorators.esm.js.map +0 -1
- package/dist/std-decorators.js +0 -204
- package/dist/std-decorators.js.map +0 -1
- package/src/reactive/mapped.ts +0 -129
- package/src/reactive/zone.ts +0 -208
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { FoolProof } from '../utils'
|
|
2
|
+
import { effect, untracked } from './effects'
|
|
3
|
+
import { cleanedBy, cleanup } from './interface'
|
|
4
|
+
import { memoize } from './memoize'
|
|
5
|
+
import { reactive } from './proxy'
|
|
6
|
+
import type { ScopedCallback } from './types'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Result of a reactive scan, which is a reactive array of accumulated values
|
|
10
|
+
* with an attached cleanup function.
|
|
11
|
+
*/
|
|
12
|
+
export type ScanResult<Output> = readonly Output[] & { [cleanup]: ScopedCallback }
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Perform a reactive scan over an array of items.
|
|
16
|
+
*
|
|
17
|
+
* This implementation is highly optimized for performance and fine-grained reactivity:
|
|
18
|
+
* - **Incremental Updates**: Changes to an item only trigger re-computation from that
|
|
19
|
+
* point onwards in the result chain.
|
|
20
|
+
* - **Move Optimization**: If items are moved within the array, their accumulated
|
|
21
|
+
* values are reused as long as their predecessor remains the same.
|
|
22
|
+
* - **Duplicate Support**: Correctly handles multiple occurrences of the same object
|
|
23
|
+
* instance using an internal occurrence tracking mechanism.
|
|
24
|
+
* - **Memory Efficient**: Uses `WeakMap` for caching intermediates, which are
|
|
25
|
+
* automatically cleared when source items are garbage collected.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* const source = reactive([{ val: 1 }, { val: 2 }, { val: 3 }])
|
|
30
|
+
* const sum = scan(source, (acc, item) => acc + item.val, 0)
|
|
31
|
+
*
|
|
32
|
+
* expect([...sum]).toEqual([1, 3, 6])
|
|
33
|
+
*
|
|
34
|
+
* // Modifying an item only re-computes subsequent sums
|
|
35
|
+
* source[1].val = 10
|
|
36
|
+
* expect([...sum]).toEqual([1, 11, 14])
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* @param source The source array of objects (will be made reactive)
|
|
40
|
+
* @param callback The accumulator function called with (accumulator, currentItem)
|
|
41
|
+
* @param initialValue The starting value for the accumulation
|
|
42
|
+
* @returns A reactive array of accumulated values, with a [cleanup] property to stop the tracking
|
|
43
|
+
*/
|
|
44
|
+
export function scan<Input extends object, Output>(
|
|
45
|
+
source: readonly Input[],
|
|
46
|
+
callback: (acc: Output, val: Input) => Output,
|
|
47
|
+
initialValue: Output
|
|
48
|
+
): ScanResult<Output> {
|
|
49
|
+
const observedSource = reactive(source)
|
|
50
|
+
const result = reactive([] as Output[])
|
|
51
|
+
|
|
52
|
+
// Track effects for each index to dispose them when the array shrinks
|
|
53
|
+
const indexEffects = new Map<number, ScopedCallback>()
|
|
54
|
+
// Mapping from index to its current intermediate object
|
|
55
|
+
const indexToIntermediate = reactive([] as Intermediate[])
|
|
56
|
+
const intermediaries = new WeakMap<Input, Intermediate[]>()
|
|
57
|
+
|
|
58
|
+
class Intermediate {
|
|
59
|
+
public prev: Intermediate | undefined
|
|
60
|
+
constructor(public val: Input, prev: Intermediate | undefined) {
|
|
61
|
+
this.prev = prev
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@memoize
|
|
65
|
+
get acc(): Output {
|
|
66
|
+
const prevAcc = this.prev ? this.prev.acc : initialValue
|
|
67
|
+
return callback(prevAcc, this.val)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function disposeIndex(index: number) {
|
|
72
|
+
const stop = indexEffects.get(index)
|
|
73
|
+
if (stop) {
|
|
74
|
+
stop()
|
|
75
|
+
indexEffects.delete(index)
|
|
76
|
+
untracked(() => {
|
|
77
|
+
Reflect.deleteProperty(indexToIntermediate as any, index)
|
|
78
|
+
Reflect.deleteProperty(result as any, index)
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const mainEffect = effect(function scanMainEffect({ ascend }) {
|
|
84
|
+
const length = observedSource.length
|
|
85
|
+
const occurrenceCount = new Map<Input, number>()
|
|
86
|
+
let prev: Intermediate | undefined = undefined
|
|
87
|
+
|
|
88
|
+
for (let i = 0; i < length; i++) {
|
|
89
|
+
const val = FoolProof.get(observedSource as any, i, observedSource) as Input
|
|
90
|
+
|
|
91
|
+
if (!(val && (typeof val === 'object' || typeof val === 'function' || typeof val === 'symbol'))) {
|
|
92
|
+
throw new Error('scan: items must be objects (WeakKey) for intermediate caching')
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const count = occurrenceCount.get(val) ?? 0
|
|
96
|
+
occurrenceCount.set(val, count + 1)
|
|
97
|
+
|
|
98
|
+
let list = intermediaries.get(val)
|
|
99
|
+
if (!list) {
|
|
100
|
+
list = []
|
|
101
|
+
intermediaries.set(val, list)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
let intermediate = list[count]
|
|
105
|
+
if (!intermediate) {
|
|
106
|
+
intermediate = reactive(new Intermediate(val, prev))
|
|
107
|
+
list[count] = intermediate
|
|
108
|
+
} else {
|
|
109
|
+
// Update the link.
|
|
110
|
+
if (untracked(() => intermediate.prev) !== prev) {
|
|
111
|
+
intermediate.prev = prev
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Update the reactive mapping for this index
|
|
116
|
+
if (indexToIntermediate[i] !== intermediate) {
|
|
117
|
+
indexToIntermediate[i] = intermediate
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// If we don't have an effect for this index yet, create one
|
|
121
|
+
if (!indexEffects.has(i)) {
|
|
122
|
+
ascend(() => {
|
|
123
|
+
const index = i
|
|
124
|
+
const stop = effect(function scanIndexSyncEffect() {
|
|
125
|
+
const inter = indexToIntermediate[index]
|
|
126
|
+
if (inter) {
|
|
127
|
+
const accValue = inter.acc
|
|
128
|
+
untracked(() => {
|
|
129
|
+
result[index] = accValue
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
indexEffects.set(index, stop)
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
prev = intermediate
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Cleanup trailing indices
|
|
141
|
+
for (const index of Array.from(indexEffects.keys())) {
|
|
142
|
+
if (index >= length) disposeIndex(index)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Ensure result length matches source length
|
|
146
|
+
untracked(() => {
|
|
147
|
+
if (result.length !== length) {
|
|
148
|
+
FoolProof.set(result as any, 'length', length, result)
|
|
149
|
+
}
|
|
150
|
+
})
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
return cleanedBy(result, () => {
|
|
154
|
+
mainEffect()
|
|
155
|
+
for (const stop of indexEffects.values()) stop()
|
|
156
|
+
indexEffects.clear()
|
|
157
|
+
}) as ScanResult<Output>
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function resolve<Output>(cb: () => Output[]): Output[] & { [cleanup]: ScopedCallback } {
|
|
161
|
+
const result = reactive([] as Output[])
|
|
162
|
+
return cleanedBy(result, effect(() => {
|
|
163
|
+
const source = cb()
|
|
164
|
+
if (result.length !== source.length) result.length = source.length
|
|
165
|
+
for (let i = 0; i < source.length; i++)
|
|
166
|
+
if (result[i] !== source[i]) result[i] = source[i]
|
|
167
|
+
}))
|
|
168
|
+
}
|
package/src/reactive/change.ts
CHANGED
|
@@ -46,7 +46,7 @@ export function collectEffects(
|
|
|
46
46
|
for (const effect of deps) {
|
|
47
47
|
const runningChain = isRunning(effect)
|
|
48
48
|
if (runningChain) {
|
|
49
|
-
options.skipRunningEffect(effect
|
|
49
|
+
options.skipRunningEffect(effect)
|
|
50
50
|
continue
|
|
51
51
|
}
|
|
52
52
|
if (!effects.has(effect)) {
|
|
@@ -118,7 +118,7 @@ export function touchedOpaque(obj: any, evolution: Evolution, prop: any) {
|
|
|
118
118
|
|
|
119
119
|
const runningChain = isRunning(effect)
|
|
120
120
|
if (runningChain) {
|
|
121
|
-
options.skipRunningEffect(effect
|
|
121
|
+
options.skipRunningEffect(effect)
|
|
122
122
|
continue
|
|
123
123
|
}
|
|
124
124
|
effects.add(effect)
|
|
@@ -1,94 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const rootEffect = getRoot(effect)
|
|
15
|
-
|
|
16
|
-
// Check if the effect is directly in the stack
|
|
17
|
-
const rootIndex = stack.indexOf(rootEffect)
|
|
18
|
-
if (rootIndex !== -1) {
|
|
19
|
-
return stack.slice(0, rootIndex + 1).reverse()
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// Check if any effect in the stack is a descendant of this effect
|
|
23
|
-
// (i.e., walk up the parent chain from each stack effect to see if we reach this effect)
|
|
24
|
-
for (let i = 0; i < stack.length; i++) {
|
|
25
|
-
const stackEffect = stack[i]
|
|
26
|
-
let current: ScopedCallback | undefined = stackEffect
|
|
27
|
-
const visited = new WeakSet<ScopedCallback>()
|
|
28
|
-
const ancestorChain: ScopedCallback[] = []
|
|
29
|
-
// TODO: That's perhaps a lot of computations for an `assert`
|
|
30
|
-
// Walk up the parent chain to find if this effect is an ancestor
|
|
31
|
-
while (current && !visited.has(current)) {
|
|
32
|
-
visited.add(current)
|
|
33
|
-
const currentRoot = getRoot(current)
|
|
34
|
-
ancestorChain.push(currentRoot)
|
|
35
|
-
if (currentRoot === rootEffect) {
|
|
36
|
-
// Found a descendant - build the full chain from ancestor to active
|
|
37
|
-
// The ancestorChain contains [descendant, parent, ..., ancestor] (walking up)
|
|
38
|
-
// We need [ancestor (effect), ..., parent, descendant, ...stack from descendant to active]
|
|
39
|
-
const chainFromAncestor = ancestorChain.reverse() // [ancestor, ..., descendant]
|
|
40
|
-
// Prepend the actual effect we're checking (in case current is a wrapper)
|
|
41
|
-
if (chainFromAncestor[0] !== rootEffect) {
|
|
42
|
-
chainFromAncestor.unshift(rootEffect)
|
|
43
|
-
}
|
|
44
|
-
// Append the rest of the stack from the descendant to the active effect
|
|
45
|
-
const stackFromDescendant = stack.slice(0, i + 1).reverse() // [descendant, ..., active]
|
|
46
|
-
// Remove duplicate descendant (it's both at end of chainFromAncestor and start of stackFromDescendant)
|
|
47
|
-
if (chainFromAncestor.length > 0 && stackFromDescendant.length > 0) {
|
|
48
|
-
stackFromDescendant.shift() // Remove duplicate descendant
|
|
49
|
-
}
|
|
50
|
-
return [...chainFromAncestor, ...stackFromDescendant]
|
|
51
|
-
}
|
|
52
|
-
current = effectParent.get(current)
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return false
|
|
57
|
-
}
|
|
58
|
-
export function withEffectStack<T>(snapshot: (ScopedCallback | undefined)[], fn: () => T): T {
|
|
59
|
-
const previousStack = stack.slice()
|
|
60
|
-
assignStack(snapshot)
|
|
61
|
-
try {
|
|
62
|
-
return fn()
|
|
63
|
-
} finally {
|
|
64
|
-
assignStack(previousStack)
|
|
65
|
-
}
|
|
1
|
+
import { tag } from '../utils'
|
|
2
|
+
import { asyncZone, ZoneAggregator, ZoneHistory } from '../zone'
|
|
3
|
+
import { getRoot } from './registry'
|
|
4
|
+
import { type ScopedCallback } from './types'
|
|
5
|
+
|
|
6
|
+
export const effectHistory = tag('effectHistory', new ZoneHistory<ScopedCallback>())
|
|
7
|
+
tag('effectHistory.present', effectHistory.present)
|
|
8
|
+
asyncZone.add(effectHistory)
|
|
9
|
+
export const effectAggregator = tag('effectAggregator', new ZoneAggregator(effectHistory.present))
|
|
10
|
+
|
|
11
|
+
export function isRunning(effect: ScopedCallback): boolean {
|
|
12
|
+
const root = getRoot(effect)
|
|
13
|
+
return effectHistory.some((e) => getRoot(e) === root)
|
|
66
14
|
}
|
|
67
15
|
|
|
68
16
|
export function getActiveEffect() {
|
|
69
|
-
return
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Executes a function with a specific effect context
|
|
74
|
-
* @param effect - The effect to use as context
|
|
75
|
-
* @param fn - The function to execute
|
|
76
|
-
* @param keepParent - Whether to keep the parent effect context
|
|
77
|
-
* @returns The result of the function
|
|
78
|
-
*/
|
|
79
|
-
export function withEffect<T>(effect: ScopedCallback | undefined, fn: () => T): T {
|
|
80
|
-
|
|
81
|
-
if (getRoot(effect) === getRoot(getActiveEffect())) return fn()
|
|
82
|
-
stack.unshift(effect)
|
|
83
|
-
try {
|
|
84
|
-
return fn()
|
|
85
|
-
} finally {
|
|
86
|
-
const recoveredEffect = stack.shift()
|
|
87
|
-
if (recoveredEffect !== effect) throw new ReactiveError('[reactive] Effect stack mismatch')
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function assignStack(values: (ScopedCallback | undefined)[]) {
|
|
92
|
-
stack.length = 0
|
|
93
|
-
stack.push(...values)
|
|
94
|
-
}
|
|
17
|
+
return effectHistory.present.active
|
|
18
|
+
}
|