@zeix/cause-effect 0.17.3 → 0.18.0
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/.ai-context.md +163 -232
- package/.cursorrules +41 -35
- package/.github/copilot-instructions.md +166 -116
- package/ARCHITECTURE.md +274 -0
- package/CLAUDE.md +199 -143
- package/COLLECTION_REFACTORING.md +161 -0
- package/GUIDE.md +298 -0
- package/README.md +232 -197
- package/REQUIREMENTS.md +100 -0
- package/bench/reactivity.bench.ts +577 -0
- package/index.dev.js +1325 -997
- package/index.js +1 -1
- package/index.ts +58 -74
- package/package.json +4 -1
- package/src/errors.ts +118 -74
- package/src/graph.ts +601 -0
- package/src/nodes/collection.ts +474 -0
- package/src/nodes/effect.ts +149 -0
- package/src/nodes/list.ts +588 -0
- package/src/nodes/memo.ts +120 -0
- package/src/nodes/sensor.ts +139 -0
- package/src/nodes/state.ts +135 -0
- package/src/nodes/store.ts +383 -0
- package/src/nodes/task.ts +146 -0
- package/src/signal.ts +112 -66
- package/src/util.ts +26 -57
- package/test/batch.test.ts +96 -62
- package/test/benchmark.test.ts +473 -487
- package/test/collection.test.ts +466 -706
- package/test/effect.test.ts +293 -696
- package/test/list.test.ts +335 -592
- package/test/memo.test.ts +380 -0
- package/test/regression.test.ts +156 -0
- package/test/scope.test.ts +191 -0
- package/test/sensor.test.ts +454 -0
- package/test/signal.test.ts +220 -213
- package/test/state.test.ts +217 -265
- package/test/store.test.ts +346 -446
- package/test/task.test.ts +395 -0
- package/test/untrack.test.ts +167 -0
- package/types/index.d.ts +13 -15
- package/types/src/errors.d.ts +73 -17
- package/types/src/graph.d.ts +208 -0
- package/types/src/nodes/collection.d.ts +64 -0
- package/types/src/nodes/effect.d.ts +48 -0
- package/types/src/nodes/list.d.ts +65 -0
- package/types/src/nodes/memo.d.ts +57 -0
- package/types/src/nodes/sensor.d.ts +75 -0
- package/types/src/nodes/state.d.ts +78 -0
- package/types/src/nodes/store.d.ts +51 -0
- package/types/src/nodes/task.d.ts +73 -0
- package/types/src/signal.d.ts +43 -29
- package/types/src/util.d.ts +9 -16
- package/archive/benchmark.ts +0 -683
- package/archive/collection.ts +0 -253
- package/archive/composite.ts +0 -85
- package/archive/computed.ts +0 -195
- package/archive/list.ts +0 -483
- package/archive/memo.ts +0 -139
- package/archive/state.ts +0 -90
- package/archive/store.ts +0 -298
- package/archive/task.ts +0 -189
- package/src/classes/collection.ts +0 -245
- package/src/classes/computed.ts +0 -349
- package/src/classes/list.ts +0 -343
- package/src/classes/ref.ts +0 -70
- package/src/classes/state.ts +0 -102
- package/src/classes/store.ts +0 -262
- package/src/diff.ts +0 -138
- package/src/effect.ts +0 -93
- package/src/match.ts +0 -45
- package/src/resolve.ts +0 -49
- package/src/system.ts +0 -257
- package/test/computed.test.ts +0 -1108
- package/test/diff.test.ts +0 -955
- package/test/match.test.ts +0 -388
- package/test/ref.test.ts +0 -353
- package/test/resolve.test.ts +0 -154
- package/types/src/classes/collection.d.ts +0 -45
- package/types/src/classes/computed.d.ts +0 -94
- package/types/src/classes/list.d.ts +0 -43
- package/types/src/classes/ref.d.ts +0 -35
- package/types/src/classes/state.d.ts +0 -49
- package/types/src/classes/store.d.ts +0 -52
- package/types/src/diff.d.ts +0 -28
- package/types/src/effect.d.ts +0 -15
- package/types/src/match.d.ts +0 -21
- package/types/src/resolve.d.ts +0 -29
- package/types/src/system.d.ts +0 -78
package/archive/collection.ts
DELETED
|
@@ -1,253 +0,0 @@
|
|
|
1
|
-
import type { UnknownArray } from '../src/diff'
|
|
2
|
-
import { match } from '../src/match'
|
|
3
|
-
import { resolve } from '../src/resolve'
|
|
4
|
-
import type { Signal } from '../src/signal'
|
|
5
|
-
import {
|
|
6
|
-
createWatcher,
|
|
7
|
-
notifyWatchers,
|
|
8
|
-
subscribeActiveWatcher,
|
|
9
|
-
UNSET,
|
|
10
|
-
type Watcher,
|
|
11
|
-
} from '../src/system'
|
|
12
|
-
import { isAsyncFunction, isObjectOfType, isSymbol } from '../src/util'
|
|
13
|
-
import { type Computed, createComputed } from './computed'
|
|
14
|
-
import type { List } from './list'
|
|
15
|
-
|
|
16
|
-
/* === Types === */
|
|
17
|
-
|
|
18
|
-
type CollectionKeySignal<T extends {}> = T extends UnknownArray
|
|
19
|
-
? Collection<T>
|
|
20
|
-
: Computed<T>
|
|
21
|
-
|
|
22
|
-
type CollectionCallback<T extends {} & { then?: undefined }, O extends {}> =
|
|
23
|
-
| ((originValue: O, abort: AbortSignal) => Promise<T>)
|
|
24
|
-
| ((originValue: O) => T)
|
|
25
|
-
|
|
26
|
-
type Collection<T extends {}> = {
|
|
27
|
-
readonly [Symbol.toStringTag]: typeof TYPE_COLLECTION
|
|
28
|
-
readonly [Symbol.isConcatSpreadable]: boolean
|
|
29
|
-
[Symbol.iterator](): IterableIterator<CollectionKeySignal<T>>
|
|
30
|
-
readonly [n: number]: CollectionKeySignal<T>
|
|
31
|
-
readonly length: number
|
|
32
|
-
|
|
33
|
-
byKey(key: string): CollectionKeySignal<T> | undefined
|
|
34
|
-
get(): T[]
|
|
35
|
-
keyAt(index: number): string | undefined
|
|
36
|
-
indexOfKey(key: string): number
|
|
37
|
-
sort(compareFn?: (a: T, b: T) => number): void
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/* === Constants === */
|
|
41
|
-
|
|
42
|
-
const TYPE_COLLECTION = 'Collection' as const
|
|
43
|
-
|
|
44
|
-
/* === Exported Functions === */
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Collections - Read-Only Derived Array-Like Stores
|
|
48
|
-
*
|
|
49
|
-
* Collections are the read-only, derived counterpart to array-like Stores.
|
|
50
|
-
* They provide reactive, memoized, and lazily-evaluated array transformations
|
|
51
|
-
* while maintaining the familiar array-like store interface.
|
|
52
|
-
*
|
|
53
|
-
* @since 0.16.2
|
|
54
|
-
* @param {List<O> | Collection<O>} origin - Origin of collection to derive values from
|
|
55
|
-
* @param {ComputedCallback<ArrayItem<T>>} callback - Callback function to transform array items
|
|
56
|
-
* @returns {Collection<T>} - New collection with reactive properties that preserves the original type T
|
|
57
|
-
*/
|
|
58
|
-
const createCollection = <T extends {}, O extends {}>(
|
|
59
|
-
origin: List<O> | Collection<O>,
|
|
60
|
-
callback: CollectionCallback<T, O>,
|
|
61
|
-
): Collection<T> => {
|
|
62
|
-
const watchers = new Set<Watcher>()
|
|
63
|
-
const signals = new Map<string, Signal<T>>()
|
|
64
|
-
const signalWatchers = new Map<string, Watcher>()
|
|
65
|
-
|
|
66
|
-
let order: string[] = []
|
|
67
|
-
|
|
68
|
-
// Add nested signal and effect
|
|
69
|
-
const addProperty = (key: string): boolean => {
|
|
70
|
-
const computedCallback = isAsyncFunction(callback)
|
|
71
|
-
? async (_: T, abort: AbortSignal) => {
|
|
72
|
-
const originSignal = origin.byKey(key)
|
|
73
|
-
if (!originSignal) return UNSET
|
|
74
|
-
|
|
75
|
-
let result = UNSET
|
|
76
|
-
match(resolve({ originSignal }), {
|
|
77
|
-
ok: async ({ originSignal: originValue }) => {
|
|
78
|
-
result = await callback(originValue, abort)
|
|
79
|
-
},
|
|
80
|
-
err: (errors: readonly Error[]) => {
|
|
81
|
-
console.log(errors)
|
|
82
|
-
},
|
|
83
|
-
})
|
|
84
|
-
return result
|
|
85
|
-
}
|
|
86
|
-
: () => {
|
|
87
|
-
const originSignal = origin.byKey(key)
|
|
88
|
-
if (!originSignal) return UNSET
|
|
89
|
-
|
|
90
|
-
let result = UNSET
|
|
91
|
-
match(resolve({ originSignal }), {
|
|
92
|
-
ok: ({ originSignal: originValue }) => {
|
|
93
|
-
result = (callback as (originValue: O) => T)(
|
|
94
|
-
originValue as unknown as O,
|
|
95
|
-
)
|
|
96
|
-
},
|
|
97
|
-
err: (errors: readonly Error[]) => {
|
|
98
|
-
console.log(errors)
|
|
99
|
-
},
|
|
100
|
-
})
|
|
101
|
-
return result
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const signal = createComputed(computedCallback)
|
|
105
|
-
|
|
106
|
-
// Set internal states
|
|
107
|
-
signals.set(key, signal)
|
|
108
|
-
if (!order.includes(key)) order.push(key)
|
|
109
|
-
const watcher = createWatcher(
|
|
110
|
-
() => {
|
|
111
|
-
signal.get() // Subscribe to the signal
|
|
112
|
-
},
|
|
113
|
-
() => {},
|
|
114
|
-
)
|
|
115
|
-
watcher()
|
|
116
|
-
signalWatchers.set(key, watcher)
|
|
117
|
-
return true
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Initialize properties
|
|
121
|
-
for (let i = 0; i < origin.length; i++) {
|
|
122
|
-
const key = origin.keyAt(i)
|
|
123
|
-
if (!key) continue
|
|
124
|
-
addProperty(key)
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Get signal by key or index
|
|
128
|
-
const getSignal = (prop: string): Signal<T> | undefined => {
|
|
129
|
-
let key = prop
|
|
130
|
-
const index = Number(prop)
|
|
131
|
-
if (Number.isInteger(index) && index >= 0) key = order[index] ?? prop
|
|
132
|
-
return signals.get(key)
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Get current array
|
|
136
|
-
const current = (): T =>
|
|
137
|
-
order
|
|
138
|
-
.map(key => signals.get(key)?.get())
|
|
139
|
-
.filter(v => v !== UNSET) as unknown as T
|
|
140
|
-
|
|
141
|
-
// Methods and Properties
|
|
142
|
-
const collection: Record<PropertyKey, unknown> = {}
|
|
143
|
-
Object.defineProperties(collection, {
|
|
144
|
-
[Symbol.toStringTag]: {
|
|
145
|
-
value: TYPE_COLLECTION,
|
|
146
|
-
},
|
|
147
|
-
[Symbol.isConcatSpreadable]: {
|
|
148
|
-
value: true,
|
|
149
|
-
},
|
|
150
|
-
[Symbol.iterator]: {
|
|
151
|
-
value: function* () {
|
|
152
|
-
for (const key of order) {
|
|
153
|
-
const signal = signals.get(key)
|
|
154
|
-
if (signal) yield signal
|
|
155
|
-
}
|
|
156
|
-
},
|
|
157
|
-
},
|
|
158
|
-
byKey: {
|
|
159
|
-
value(key: string) {
|
|
160
|
-
return getSignal(key)
|
|
161
|
-
},
|
|
162
|
-
},
|
|
163
|
-
keyAt: {
|
|
164
|
-
value(index: number): string | undefined {
|
|
165
|
-
return order[index]
|
|
166
|
-
},
|
|
167
|
-
},
|
|
168
|
-
indexOfKey: {
|
|
169
|
-
value(key: string): number {
|
|
170
|
-
return order.indexOf(key)
|
|
171
|
-
},
|
|
172
|
-
},
|
|
173
|
-
get: {
|
|
174
|
-
value: (): T => {
|
|
175
|
-
subscribeActiveWatcher(watchers)
|
|
176
|
-
return current()
|
|
177
|
-
},
|
|
178
|
-
},
|
|
179
|
-
sort: {
|
|
180
|
-
value: (compareFn?: (a: T, b: T) => number): void => {
|
|
181
|
-
const entries = order
|
|
182
|
-
.map((key, index) => {
|
|
183
|
-
const signal = signals.get(key)
|
|
184
|
-
return [
|
|
185
|
-
index,
|
|
186
|
-
key,
|
|
187
|
-
signal ? signal.get() : undefined,
|
|
188
|
-
] as [number, string, T]
|
|
189
|
-
})
|
|
190
|
-
.sort(
|
|
191
|
-
compareFn
|
|
192
|
-
? (a, b) => compareFn(a[2], b[2])
|
|
193
|
-
: (a, b) =>
|
|
194
|
-
String(a[2]).localeCompare(String(b[2])),
|
|
195
|
-
)
|
|
196
|
-
|
|
197
|
-
// Set new order
|
|
198
|
-
order = entries.map(([_, key]) => key)
|
|
199
|
-
|
|
200
|
-
notifyWatchers(watchers)
|
|
201
|
-
},
|
|
202
|
-
},
|
|
203
|
-
length: {
|
|
204
|
-
get(): number {
|
|
205
|
-
subscribeActiveWatcher(watchers)
|
|
206
|
-
return signals.size
|
|
207
|
-
},
|
|
208
|
-
},
|
|
209
|
-
})
|
|
210
|
-
|
|
211
|
-
// Return proxy directly with integrated signal methods
|
|
212
|
-
return new Proxy(collection as Collection<T>, {
|
|
213
|
-
get(target, prop) {
|
|
214
|
-
if (prop in target) return Reflect.get(target, prop)
|
|
215
|
-
if (!isSymbol(prop)) return getSignal(prop)
|
|
216
|
-
},
|
|
217
|
-
has(target, prop) {
|
|
218
|
-
if (prop in target) return true
|
|
219
|
-
return signals.has(String(prop))
|
|
220
|
-
},
|
|
221
|
-
ownKeys(target) {
|
|
222
|
-
const staticKeys = Reflect.ownKeys(target)
|
|
223
|
-
return [...new Set([...order, ...staticKeys])]
|
|
224
|
-
},
|
|
225
|
-
getOwnPropertyDescriptor(target, prop) {
|
|
226
|
-
if (prop in target)
|
|
227
|
-
return Reflect.getOwnPropertyDescriptor(target, prop)
|
|
228
|
-
if (isSymbol(prop)) return undefined
|
|
229
|
-
|
|
230
|
-
const signal = getSignal(prop)
|
|
231
|
-
return signal
|
|
232
|
-
? {
|
|
233
|
-
enumerable: true,
|
|
234
|
-
configurable: true,
|
|
235
|
-
writable: true,
|
|
236
|
-
value: signal,
|
|
237
|
-
}
|
|
238
|
-
: undefined
|
|
239
|
-
},
|
|
240
|
-
})
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
const isCollection = /*#__PURE__*/ <T extends UnknownArray>(
|
|
244
|
-
value: unknown,
|
|
245
|
-
): value is Collection<T> => isObjectOfType(value, TYPE_COLLECTION)
|
|
246
|
-
|
|
247
|
-
export {
|
|
248
|
-
type Collection,
|
|
249
|
-
type CollectionCallback,
|
|
250
|
-
createCollection,
|
|
251
|
-
isCollection,
|
|
252
|
-
TYPE_COLLECTION,
|
|
253
|
-
}
|
package/archive/composite.ts
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import type { DiffResult, UnknownRecord } from '../src/diff'
|
|
2
|
-
import { guardMutableSignal } from '../src/errors'
|
|
3
|
-
import type { Signal } from '../src/signal'
|
|
4
|
-
import { batch } from '../src/system'
|
|
5
|
-
|
|
6
|
-
/* === Class Definitions === */
|
|
7
|
-
|
|
8
|
-
class Composite<T extends UnknownRecord, S extends Signal<T[keyof T] & {}>> {
|
|
9
|
-
signals = new Map<string, S>()
|
|
10
|
-
#validate: <K extends keyof T & string>(
|
|
11
|
-
key: K,
|
|
12
|
-
value: unknown,
|
|
13
|
-
) => value is T[K] & {}
|
|
14
|
-
#create: <V extends T[keyof T] & {}>(value: V) => S
|
|
15
|
-
|
|
16
|
-
constructor(
|
|
17
|
-
values: T,
|
|
18
|
-
validate: <K extends keyof T & string>(
|
|
19
|
-
key: K,
|
|
20
|
-
value: unknown,
|
|
21
|
-
) => value is T[K] & {},
|
|
22
|
-
create: <V extends T[keyof T] & {}>(value: V) => S,
|
|
23
|
-
) {
|
|
24
|
-
this.#validate = validate
|
|
25
|
-
this.#create = create
|
|
26
|
-
this.change({
|
|
27
|
-
add: values,
|
|
28
|
-
change: {},
|
|
29
|
-
remove: {},
|
|
30
|
-
changed: true,
|
|
31
|
-
})
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
add<K extends keyof T & string>(key: K, value: T[K]): boolean {
|
|
35
|
-
if (!this.#validate(key, value)) return false
|
|
36
|
-
|
|
37
|
-
this.signals.set(key, this.#create(value))
|
|
38
|
-
return true
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
remove<K extends keyof T & string>(key: K): boolean {
|
|
42
|
-
return this.signals.delete(key)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
change(changes: DiffResult): boolean {
|
|
46
|
-
// Additions
|
|
47
|
-
if (Object.keys(changes.add).length) {
|
|
48
|
-
for (const key in changes.add)
|
|
49
|
-
this.add(
|
|
50
|
-
key as Extract<keyof T, string>,
|
|
51
|
-
changes.add[key] as T[Extract<keyof T, string>] & {},
|
|
52
|
-
)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Changes
|
|
56
|
-
if (Object.keys(changes.change).length) {
|
|
57
|
-
batch(() => {
|
|
58
|
-
for (const key in changes.change) {
|
|
59
|
-
const value = changes.change[key]
|
|
60
|
-
if (!this.#validate(key as keyof T & string, value))
|
|
61
|
-
continue
|
|
62
|
-
|
|
63
|
-
const signal = this.signals.get(key)
|
|
64
|
-
if (guardMutableSignal(`list item "${key}"`, value, signal))
|
|
65
|
-
signal.set(value)
|
|
66
|
-
}
|
|
67
|
-
})
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Removals
|
|
71
|
-
if (Object.keys(changes.remove).length) {
|
|
72
|
-
for (const key in changes.remove)
|
|
73
|
-
this.remove(key as keyof T & string)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return changes.changed
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
clear(): boolean {
|
|
80
|
-
this.signals.clear()
|
|
81
|
-
return true
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export { Composite }
|
package/archive/computed.ts
DELETED
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
import { isEqual } from '../src/diff'
|
|
2
|
-
import {
|
|
3
|
-
CircularDependencyError,
|
|
4
|
-
createError,
|
|
5
|
-
InvalidCallbackError,
|
|
6
|
-
NullishSignalValueError,
|
|
7
|
-
} from '../src/errors'
|
|
8
|
-
import {
|
|
9
|
-
createWatcher,
|
|
10
|
-
flush,
|
|
11
|
-
notifyWatchers,
|
|
12
|
-
subscribeActiveWatcher,
|
|
13
|
-
UNSET,
|
|
14
|
-
type Watcher,
|
|
15
|
-
} from '../src/system'
|
|
16
|
-
import {
|
|
17
|
-
isAbortError,
|
|
18
|
-
isAsyncFunction,
|
|
19
|
-
isFunction,
|
|
20
|
-
isObjectOfType,
|
|
21
|
-
} from '../src/util'
|
|
22
|
-
|
|
23
|
-
/* === Types === */
|
|
24
|
-
|
|
25
|
-
type Computed<T extends {}> = {
|
|
26
|
-
readonly [Symbol.toStringTag]: 'Computed'
|
|
27
|
-
get(): T
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
type ComputedCallback<T extends {} & { then?: undefined }> =
|
|
31
|
-
| ((oldValue: T, abort: AbortSignal) => Promise<T>)
|
|
32
|
-
| ((oldValue: T) => T)
|
|
33
|
-
|
|
34
|
-
/* === Constants === */
|
|
35
|
-
|
|
36
|
-
const TYPE_COMPUTED = 'Computed' as const
|
|
37
|
-
|
|
38
|
-
/* === Functions === */
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Create a derived signal from existing signals
|
|
42
|
-
*
|
|
43
|
-
* @since 0.9.0
|
|
44
|
-
* @param {ComputedCallback<T>} callback - Computation callback function
|
|
45
|
-
* @returns {Computed<T>} - Computed signal
|
|
46
|
-
*/
|
|
47
|
-
const createComputed = <T extends {}>(
|
|
48
|
-
callback: ComputedCallback<T>,
|
|
49
|
-
initialValue: T = UNSET,
|
|
50
|
-
): Computed<T> => {
|
|
51
|
-
if (!isComputedCallback(callback))
|
|
52
|
-
throw new InvalidCallbackError('computed', callback)
|
|
53
|
-
if (initialValue == null) throw new NullishSignalValueError('computed')
|
|
54
|
-
|
|
55
|
-
const watchers: Set<Watcher> = new Set()
|
|
56
|
-
|
|
57
|
-
// Internal state
|
|
58
|
-
let value: T = initialValue
|
|
59
|
-
let error: Error | undefined
|
|
60
|
-
let controller: AbortController | undefined
|
|
61
|
-
let dirty = true
|
|
62
|
-
let changed = false
|
|
63
|
-
let computing = false
|
|
64
|
-
|
|
65
|
-
// Functions to update internal state
|
|
66
|
-
const ok = (v: T): undefined => {
|
|
67
|
-
if (!isEqual(v, value)) {
|
|
68
|
-
value = v
|
|
69
|
-
changed = true
|
|
70
|
-
}
|
|
71
|
-
error = undefined
|
|
72
|
-
dirty = false
|
|
73
|
-
}
|
|
74
|
-
const nil = (): undefined => {
|
|
75
|
-
changed = UNSET !== value
|
|
76
|
-
value = UNSET
|
|
77
|
-
error = undefined
|
|
78
|
-
}
|
|
79
|
-
const err = (e: unknown): undefined => {
|
|
80
|
-
const newError = createError(e)
|
|
81
|
-
changed =
|
|
82
|
-
!error ||
|
|
83
|
-
newError.name !== error.name ||
|
|
84
|
-
newError.message !== error.message
|
|
85
|
-
value = UNSET
|
|
86
|
-
error = newError
|
|
87
|
-
}
|
|
88
|
-
const settle =
|
|
89
|
-
<T>(fn: (arg: T) => void) =>
|
|
90
|
-
(arg: T) => {
|
|
91
|
-
computing = false
|
|
92
|
-
controller = undefined
|
|
93
|
-
fn(arg)
|
|
94
|
-
if (changed) notifyWatchers(watchers)
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Own watcher: called when notified from sources (push)
|
|
98
|
-
const watcher = createWatcher(
|
|
99
|
-
() => {
|
|
100
|
-
dirty = true
|
|
101
|
-
controller?.abort()
|
|
102
|
-
if (watchers.size) notifyWatchers(watchers)
|
|
103
|
-
else watcher.stop()
|
|
104
|
-
},
|
|
105
|
-
() => {
|
|
106
|
-
if (computing) throw new CircularDependencyError('computed')
|
|
107
|
-
changed = false
|
|
108
|
-
if (isAsyncFunction(callback)) {
|
|
109
|
-
// Return current value until promise resolves
|
|
110
|
-
if (controller) return value
|
|
111
|
-
controller = new AbortController()
|
|
112
|
-
controller.signal.addEventListener(
|
|
113
|
-
'abort',
|
|
114
|
-
() => {
|
|
115
|
-
computing = false
|
|
116
|
-
controller = undefined
|
|
117
|
-
watcher.run() // Retry computation with updated state
|
|
118
|
-
},
|
|
119
|
-
{
|
|
120
|
-
once: true,
|
|
121
|
-
},
|
|
122
|
-
)
|
|
123
|
-
}
|
|
124
|
-
let result: T | Promise<T>
|
|
125
|
-
computing = true
|
|
126
|
-
try {
|
|
127
|
-
result = controller
|
|
128
|
-
? callback(value, controller.signal)
|
|
129
|
-
: (callback as (oldValue: T) => T)(value)
|
|
130
|
-
} catch (e) {
|
|
131
|
-
if (isAbortError(e)) nil()
|
|
132
|
-
else err(e)
|
|
133
|
-
computing = false
|
|
134
|
-
return
|
|
135
|
-
}
|
|
136
|
-
if (result instanceof Promise) result.then(settle(ok), settle(err))
|
|
137
|
-
else if (null == result || UNSET === result) nil()
|
|
138
|
-
else ok(result)
|
|
139
|
-
computing = false
|
|
140
|
-
},
|
|
141
|
-
)
|
|
142
|
-
watcher.onCleanup(() => {
|
|
143
|
-
controller?.abort()
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
const computed: Record<PropertyKey, unknown> = {}
|
|
147
|
-
Object.defineProperties(computed, {
|
|
148
|
-
[Symbol.toStringTag]: {
|
|
149
|
-
value: TYPE_COMPUTED,
|
|
150
|
-
},
|
|
151
|
-
get: {
|
|
152
|
-
value: (): T => {
|
|
153
|
-
subscribeActiveWatcher(watchers)
|
|
154
|
-
flush()
|
|
155
|
-
if (dirty) watcher.run()
|
|
156
|
-
if (error) throw error
|
|
157
|
-
return value
|
|
158
|
-
},
|
|
159
|
-
},
|
|
160
|
-
})
|
|
161
|
-
return computed as Computed<T>
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Check if a value is a computed signal
|
|
166
|
-
*
|
|
167
|
-
* @since 0.9.0
|
|
168
|
-
* @param {unknown} value - Value to check
|
|
169
|
-
* @returns {boolean} - true if value is a computed signal, false otherwise
|
|
170
|
-
*/
|
|
171
|
-
const isComputed = /*#__PURE__*/ <T extends {}>(
|
|
172
|
-
value: unknown,
|
|
173
|
-
): value is Computed<T> => isObjectOfType(value, TYPE_COMPUTED)
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Check if the provided value is a callback that may be used as input for toSignal() to derive a computed state
|
|
177
|
-
*
|
|
178
|
-
* @since 0.12.0
|
|
179
|
-
* @param {unknown} value - Value to check
|
|
180
|
-
* @returns {boolean} - true if value is a callback or callbacks object, false otherwise
|
|
181
|
-
*/
|
|
182
|
-
const isComputedCallback = /*#__PURE__*/ <T extends {}>(
|
|
183
|
-
value: unknown,
|
|
184
|
-
): value is ComputedCallback<T> => isFunction(value) && value.length < 3
|
|
185
|
-
|
|
186
|
-
/* === Exports === */
|
|
187
|
-
|
|
188
|
-
export {
|
|
189
|
-
TYPE_COMPUTED,
|
|
190
|
-
createComputed,
|
|
191
|
-
isComputed,
|
|
192
|
-
isComputedCallback,
|
|
193
|
-
type Computed,
|
|
194
|
-
type ComputedCallback,
|
|
195
|
-
}
|