@zeix/cause-effect 0.17.2 → 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.
Files changed (94) hide show
  1. package/.ai-context.md +163 -226
  2. package/.cursorrules +41 -35
  3. package/.github/copilot-instructions.md +166 -116
  4. package/.zed/settings.json +3 -0
  5. package/ARCHITECTURE.md +274 -0
  6. package/CLAUDE.md +197 -202
  7. package/COLLECTION_REFACTORING.md +161 -0
  8. package/GUIDE.md +298 -0
  9. package/README.md +241 -220
  10. package/REQUIREMENTS.md +100 -0
  11. package/bench/reactivity.bench.ts +577 -0
  12. package/index.dev.js +1326 -1174
  13. package/index.js +1 -1
  14. package/index.ts +58 -85
  15. package/package.json +9 -6
  16. package/src/errors.ts +118 -70
  17. package/src/graph.ts +601 -0
  18. package/src/nodes/collection.ts +474 -0
  19. package/src/nodes/effect.ts +149 -0
  20. package/src/nodes/list.ts +588 -0
  21. package/src/nodes/memo.ts +120 -0
  22. package/src/nodes/sensor.ts +139 -0
  23. package/src/nodes/state.ts +135 -0
  24. package/src/nodes/store.ts +383 -0
  25. package/src/nodes/task.ts +146 -0
  26. package/src/signal.ts +112 -64
  27. package/src/util.ts +26 -57
  28. package/test/batch.test.ts +96 -69
  29. package/test/benchmark.test.ts +473 -485
  30. package/test/collection.test.ts +455 -955
  31. package/test/effect.test.ts +293 -696
  32. package/test/list.test.ts +332 -857
  33. package/test/memo.test.ts +380 -0
  34. package/test/regression.test.ts +156 -0
  35. package/test/scope.test.ts +191 -0
  36. package/test/sensor.test.ts +454 -0
  37. package/test/signal.test.ts +220 -213
  38. package/test/state.test.ts +217 -271
  39. package/test/store.test.ts +346 -898
  40. package/test/task.test.ts +395 -0
  41. package/test/untrack.test.ts +167 -0
  42. package/test/util/dependency-graph.ts +2 -2
  43. package/tsconfig.build.json +11 -0
  44. package/tsconfig.json +5 -7
  45. package/types/index.d.ts +13 -15
  46. package/types/src/errors.d.ts +73 -19
  47. package/types/src/graph.d.ts +208 -0
  48. package/types/src/nodes/collection.d.ts +64 -0
  49. package/types/src/nodes/effect.d.ts +48 -0
  50. package/types/src/nodes/list.d.ts +65 -0
  51. package/types/src/nodes/memo.d.ts +57 -0
  52. package/types/src/nodes/sensor.d.ts +75 -0
  53. package/types/src/nodes/state.d.ts +78 -0
  54. package/types/src/nodes/store.d.ts +51 -0
  55. package/types/src/nodes/task.d.ts +73 -0
  56. package/types/src/signal.d.ts +43 -28
  57. package/types/src/util.d.ts +9 -16
  58. package/archive/benchmark.ts +0 -688
  59. package/archive/collection.ts +0 -310
  60. package/archive/computed.ts +0 -198
  61. package/archive/list.ts +0 -544
  62. package/archive/memo.ts +0 -140
  63. package/archive/state.ts +0 -90
  64. package/archive/store.ts +0 -357
  65. package/archive/task.ts +0 -191
  66. package/src/classes/collection.ts +0 -298
  67. package/src/classes/composite.ts +0 -171
  68. package/src/classes/computed.ts +0 -392
  69. package/src/classes/list.ts +0 -310
  70. package/src/classes/ref.ts +0 -96
  71. package/src/classes/state.ts +0 -131
  72. package/src/classes/store.ts +0 -227
  73. package/src/diff.ts +0 -138
  74. package/src/effect.ts +0 -96
  75. package/src/match.ts +0 -45
  76. package/src/resolve.ts +0 -49
  77. package/src/system.ts +0 -275
  78. package/test/computed.test.ts +0 -1126
  79. package/test/diff.test.ts +0 -955
  80. package/test/match.test.ts +0 -388
  81. package/test/ref.test.ts +0 -381
  82. package/test/resolve.test.ts +0 -154
  83. package/types/src/classes/collection.d.ts +0 -47
  84. package/types/src/classes/composite.d.ts +0 -15
  85. package/types/src/classes/computed.d.ts +0 -114
  86. package/types/src/classes/list.d.ts +0 -41
  87. package/types/src/classes/ref.d.ts +0 -48
  88. package/types/src/classes/state.d.ts +0 -61
  89. package/types/src/classes/store.d.ts +0 -51
  90. package/types/src/diff.d.ts +0 -28
  91. package/types/src/effect.d.ts +0 -15
  92. package/types/src/match.d.ts +0 -21
  93. package/types/src/resolve.d.ts +0 -29
  94. package/types/src/system.d.ts +0 -81
package/archive/state.ts DELETED
@@ -1,90 +0,0 @@
1
- import { isEqual } from '../src/diff'
2
- import { InvalidCallbackError, NullishSignalValueError } from '../src/errors'
3
- import {
4
- notifyWatchers,
5
- subscribeActiveWatcher,
6
- UNSET,
7
- type Watcher,
8
- } from '../src/system'
9
- import { isFunction, isObjectOfType } from '../src/util'
10
-
11
- /* === Types === */
12
-
13
- type State<T extends {}> = {
14
- readonly [Symbol.toStringTag]: 'State'
15
- get(): T
16
- set(newValue: T): void
17
- update(updater: (oldValue: T) => T): void
18
- }
19
-
20
- /* === Constants === */
21
-
22
- const TYPE_STATE = 'State' as const
23
-
24
- /* === Functions === */
25
-
26
- /**
27
- * Create a new state signal
28
- *
29
- * @since 0.9.0
30
- * @param {T} initialValue - initial value of the state
31
- * @returns {State<T>} - new state signal
32
- */
33
- const createState = /*#__PURE__*/ <T extends {}>(initialValue: T): State<T> => {
34
- if (initialValue == null) throw new NullishSignalValueError('state')
35
-
36
- const watchers: Set<Watcher> = new Set()
37
- let value: T = initialValue
38
-
39
- const setValue = (newValue: T) => {
40
- if (newValue == null) throw new NullishSignalValueError('state')
41
- if (isEqual(value, newValue)) return
42
- value = newValue
43
- notifyWatchers(watchers)
44
-
45
- // Setting to UNSET clears the watchers so the signal can be garbage collected
46
- if (UNSET === value) watchers.clear()
47
- }
48
-
49
- const state: Record<PropertyKey, unknown> = {}
50
- Object.defineProperties(state, {
51
- [Symbol.toStringTag]: {
52
- value: TYPE_STATE,
53
- },
54
- get: {
55
- value: () => {
56
- subscribeActiveWatcher(watchers)
57
- return value
58
- },
59
- },
60
- set: {
61
- value: (newValue: T) => {
62
- setValue(newValue)
63
- },
64
- },
65
- update: {
66
- value: (updater: (oldValue: T) => T) => {
67
- if (!isFunction(updater))
68
- throw new InvalidCallbackError('state update', updater)
69
-
70
- setValue(updater(value))
71
- },
72
- },
73
- })
74
- return state as State<T>
75
- }
76
-
77
- /**
78
- * Check if the provided value is a State instance
79
- *
80
- * @since 0.9.0
81
- * @param {unknown} value - value to check
82
- * @returns {boolean} - true if the value is a State instance, false otherwise
83
- */
84
- const isState = /*#__PURE__*/ <T extends {}>(
85
- value: unknown,
86
- ): value is State<T> => isObjectOfType(value, TYPE_STATE)
87
-
88
- /* === Exports === */
89
-
90
- export { TYPE_STATE, isState, createState, type State }
package/archive/store.ts DELETED
@@ -1,357 +0,0 @@
1
- import { type DiffResult, diff, type UnknownRecord } from '../src/diff'
2
- import {
3
- DuplicateKeyError,
4
- InvalidSignalValueError,
5
- NullishSignalValueError,
6
- ReadonlySignalError,
7
- } from '../src/errors'
8
- import { isMutableSignal, type MutableSignal } from '../src/signal'
9
- import {
10
- batchSignalWrites,
11
- type Cleanup,
12
- createWatcher,
13
- type HookCallback,
14
- type HookCallbacks,
15
- type Hook,
16
- notifyWatchers,
17
- subscribeActiveWatcher,
18
- trackSignalReads,
19
- type Watcher,
20
- UNSET,
21
- triggerHook,
22
- } from '../src/system'
23
- import { isFunction, isObjectOfType, isRecord, isSymbol } from '../src/util'
24
- import { isComputed } from './computed'
25
- import { createList, isList, type List } from './list'
26
- import { createState, isState, type State } from './state'
27
-
28
- /* === Types === */
29
-
30
- type StoreKeySignal<T extends {}> = T extends readonly (infer U extends {})[]
31
- ? List<U>
32
- : T extends UnknownRecord
33
- ? Store<T>
34
- : State<T>
35
-
36
- type Store<T extends UnknownRecord> = {
37
- [K in keyof T]: T[K] extends readonly (infer U extends {})[]
38
- ? List<U>
39
- : T extends Record<string, unknown>
40
- ? Store<T[K] & {}>
41
- : State<T[K] & {}>
42
- } & {
43
- readonly [Symbol.toStringTag]: 'Store'
44
- [Symbol.iterator](): IterableIterator<
45
- [
46
- Extract<keyof T, string>,
47
- StoreKeySignal<T[Extract<keyof T, string>] & {}>,
48
- ]
49
- >
50
- add<K extends Extract<keyof T, string>>(key: K, value: T[K]): K
51
- byKey<K extends Extract<keyof T, string>>(key: K): StoreKeySignal<T[K] & {}>
52
- get(): T
53
- keyAt(index: number): string | undefined
54
- indexOfKey(key: string): number
55
- set(value: T): void
56
- update(fn: (value: T) => T): void
57
- sort<U = T[Extract<keyof T, string>]>(
58
- compareFn?: (a: U, b: U) => number,
59
- ): void
60
- on(type: Hook, callback: HookCallback): Cleanup
61
- remove<K extends Extract<keyof T, string>>(key: K): void
62
- }
63
-
64
- /* === Constants === */
65
-
66
- const TYPE_STORE = 'Store' as const
67
-
68
- /* === Functions === */
69
-
70
- /**
71
- * Create a new store with deeply nested reactive properties
72
- *
73
- * Supports both objects and arrays as initial values. Arrays are converted
74
- * to records internally for storage but maintain their array type through
75
- * the .get() method, which automatically converts objects with consecutive
76
- * numeric keys back to arrays.
77
- *
78
- * For array-like stores, an optional keyConfig parameter can be provided to
79
- * generate stable keys for array items. This creates persistent references
80
- * that remain stable across sort and compact operations.
81
- *
82
- * @since 0.15.0
83
- * @param {T} initialValue - initial object or array value of the store
84
- * @returns {Store<T>} - new store with reactive properties that preserves the original type T
85
- */
86
- const createStore = <T extends UnknownRecord>(initialValue: T): Store<T> => {
87
- if (initialValue == null) throw new NullishSignalValueError('store')
88
-
89
- const watchers = new Set<Watcher>()
90
- const hookCallbacks: HookCallbacks = {}
91
- const signals = new Map<
92
- string,
93
- MutableSignal<T[Extract<keyof T, string>] & {}>
94
- >()
95
- const ownWatchers = new Map<string, Watcher>()
96
-
97
- // Get current record
98
- const current = (): T => {
99
- const record = {} as Record<string, unknown>
100
- for (const [key, signal] of signals) record[key] = signal.get()
101
- return record as T
102
- }
103
-
104
- // Validate input
105
- const isValidValue = <T>(
106
- key: string,
107
- value: T,
108
- ): value is NonNullable<T> => {
109
- if (value == null)
110
- throw new NullishSignalValueError(`store for key "${key}"`)
111
- if (value === UNSET) return true
112
- if (isSymbol(value) || isFunction(value) || isComputed(value))
113
- throw new InvalidSignalValueError(`store for key "${key}"`, value)
114
- return true
115
- }
116
-
117
- // Add own watcher for nested signal
118
- const addOwnWatcher = <K extends keyof T & string>(
119
- key: K,
120
- signal: MutableSignal<T[K] & {}>,
121
- ) => {
122
- const watcher = createWatcher(() => {
123
- trackSignalReads(watcher, () => {
124
- signal.get() // Subscribe to the signal
125
- triggerHook(hookCallbacks.change, [key])
126
- })
127
- })
128
- ownWatchers.set(key, watcher)
129
- }
130
-
131
- // Add nested signal and effect
132
- const addProperty = <K extends keyof T & string>(
133
- key: K,
134
- value: T[K],
135
- single = false,
136
- ): boolean => {
137
- if (!isValidValue(key, value)) return false
138
-
139
- // Create signal for key
140
- // @ts-expect-error non-matching signal types
141
- const signal: MutableSignal<T[K] & {}> =
142
- isState(value) || isStore(value) || isList(value)
143
- ? (value as unknown as MutableSignal<T[K] & {}>)
144
- : isRecord(value)
145
- ? createStore(value)
146
- : Array.isArray(value)
147
- ? createList(value)
148
- : createState(value)
149
-
150
- // Set internal states
151
- // @ts-expect-error non-matching signal types
152
- signals.set(key, signal)
153
- if (hookCallbacks.change?.size) addOwnWatcher(key, signal)
154
-
155
- if (single) {
156
- notifyWatchers(watchers)
157
- triggerHook(hookCallbacks.add, [key])
158
- }
159
- return true
160
- }
161
-
162
- // Remove nested signal and effect
163
- const removeProperty = (key: string, single = false) => {
164
- // Remove signal for key
165
- const ok = signals.delete(key)
166
- if (!ok) return
167
-
168
- // Clean up internal states
169
- const watcher = ownWatchers.get(key)
170
- if (watcher) {
171
- watcher.stop()
172
- ownWatchers.delete(key)
173
- }
174
-
175
- if (single) {
176
- notifyWatchers(watchers)
177
- triggerHook(hookCallbacks.remove, [key])
178
- }
179
- }
180
-
181
- // Commit batched changes and emit notifications
182
- const batchChanges = (changes: DiffResult, initialRun?: boolean) => {
183
- // Additions
184
- if (Object.keys(changes.add).length) {
185
- for (const key in changes.add)
186
- addProperty(
187
- key,
188
- changes.add[key] as T[Extract<keyof T, string>] & {},
189
- false,
190
- )
191
-
192
- // Queue initial additions event to allow listeners to be added first
193
- if (initialRun)
194
- setTimeout(() => {
195
- triggerHook(hookCallbacks.add, Object.keys(changes.add))
196
- }, 0)
197
- else triggerHook(hookCallbacks.add, Object.keys(changes.add))
198
- }
199
-
200
- // Changes
201
- if (Object.keys(changes.change).length) {
202
- batchSignalWrites(() => {
203
- for (const key in changes.change) {
204
- const value = changes.change[key] as T[Extract<
205
- keyof T,
206
- string
207
- >]
208
- if (!isValidValue(key, value)) continue
209
-
210
- const signal = signals.get(key)
211
- if (isMutableSignal(signal)) signal.set(value)
212
- else throw new ReadonlySignalError(key, value)
213
- }
214
- triggerHook(hookCallbacks.change, Object.keys(changes.change))
215
- })
216
- }
217
-
218
- // Removals
219
- if (Object.keys(changes.remove).length) {
220
- for (const key in changes.remove) removeProperty(key)
221
- triggerHook(hookCallbacks.remove, Object.keys(changes.remove))
222
- }
223
-
224
- return changes.changed
225
- }
226
-
227
- // Reconcile data and dispatch events
228
- const reconcile = (
229
- oldValue: T,
230
- newValue: T,
231
- initialRun?: boolean,
232
- ): boolean => batchChanges(diff(oldValue, newValue), initialRun)
233
-
234
- // Initialize data
235
- reconcile({} as T, initialValue, true)
236
-
237
- // Methods and Properties
238
- const prototype: Record<PropertyKey, unknown> = {}
239
- Object.defineProperties(prototype, {
240
- [Symbol.toStringTag]: {
241
- value: TYPE_STORE,
242
- },
243
- [Symbol.iterator]: {
244
- value: function* () {
245
- for (const [key, signal] of signals) yield [key, signal]
246
- },
247
- },
248
- add: {
249
- value: <K extends Extract<keyof T, string>>(
250
- key: K,
251
- value: T[K],
252
- ): K => {
253
- if (signals.has(key))
254
- throw new DuplicateKeyError('store', key, value)
255
-
256
- addProperty(key, value, true)
257
- return key
258
- },
259
- },
260
- byKey: {
261
- value: (key: string) => {
262
- return signals.get(key)
263
- },
264
- },
265
- get: {
266
- value: (): T => {
267
- subscribeActiveWatcher(watchers)
268
- return current()
269
- },
270
- },
271
- remove: {
272
- value: (key: string): void => {
273
- if (signals.has(key)) removeProperty(key, true)
274
- },
275
- },
276
- set: {
277
- value: (newValue: T): void => {
278
- if (reconcile(current(), newValue)) {
279
- notifyWatchers(watchers)
280
- if (UNSET === newValue) watchers.clear()
281
- }
282
- },
283
- },
284
- update: {
285
- value: (fn: (oldValue: T) => T): void => {
286
- store.set(fn(current()))
287
- },
288
- },
289
- on: {
290
- value: (type: Hook, callback: HookCallback): Cleanup => {
291
- hookCallbacks[type] ||= new Set()
292
- hookCallbacks[type].add(callback)
293
- if (type === 'change' && !ownWatchers.size) {
294
- for (const [key, signal] of signals)
295
- // @ts-expect-error ignore
296
- addOwnWatcher(key, signal)
297
- }
298
- return () => {
299
- hookCallbacks[type]?.delete(callback)
300
- if (type === 'change' && !hookCallbacks.change?.size) {
301
- if (ownWatchers.size) {
302
- for (const watcher of ownWatchers.values())
303
- watcher.stop()
304
- ownWatchers.clear()
305
- }
306
- }
307
- }
308
- },
309
- },
310
- })
311
-
312
- // Return proxy directly with integrated signal methods
313
- const store = new Proxy(prototype as Store<T>, {
314
- get(target, prop) {
315
- if (prop in target) return Reflect.get(target, prop)
316
- if (!isSymbol(prop)) return signals.get(prop)
317
- },
318
- has(target, prop) {
319
- if (prop in target) return true
320
- return signals.has(String(prop))
321
- },
322
- ownKeys(target) {
323
- const staticKeys = Reflect.ownKeys(target)
324
- return [...new Set([...signals.keys(), ...staticKeys])]
325
- },
326
- getOwnPropertyDescriptor(target, prop) {
327
- if (prop in target)
328
- return Reflect.getOwnPropertyDescriptor(target, prop)
329
- if (isSymbol(prop)) return undefined
330
-
331
- const signal = signals.get(prop)
332
- return signal
333
- ? {
334
- enumerable: true,
335
- configurable: true,
336
- writable: true,
337
- value: signal,
338
- }
339
- : undefined
340
- },
341
- })
342
- return store
343
- }
344
-
345
- /**
346
- * Check if the provided value is a Store instance
347
- *
348
- * @since 0.15.0
349
- * @param {unknown} value - Value to check
350
- * @returns {boolean} - True if the value is a Store instance, false otherwise
351
- */
352
- const isStore = <T extends UnknownRecord>(value: unknown): value is Store<T> =>
353
- isObjectOfType(value, TYPE_STORE)
354
-
355
- /* === Exports === */
356
-
357
- export { TYPE_STORE, isStore, createStore, type Store }
package/archive/task.ts DELETED
@@ -1,191 +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
- flushPendingReactions,
11
- HOOK_CLEANUP,
12
- notifyWatchers,
13
- subscribeActiveWatcher,
14
- trackSignalReads,
15
- UNSET,
16
- type Watcher,
17
- } from '../src/system'
18
- import { isAbortError, isAsyncFunction, isObjectOfType } from '../src/util'
19
-
20
- /* === Types === */
21
-
22
- type Task<T extends {}> = {
23
- readonly [Symbol.toStringTag]: 'Task'
24
- get(): T
25
- }
26
-
27
- type TaskCallback<T extends {} & { then?: undefined }> = (
28
- oldValue: T,
29
- abort: AbortSignal,
30
- ) => Promise<T>
31
-
32
- /* === Constants === */
33
-
34
- const TYPE_TASK = 'Task' as const
35
-
36
- /* === Functions === */
37
-
38
- /**
39
- * Create a derived signal from existing signals
40
- *
41
- * @since 0.9.0
42
- * @param {TaskCallback<T>} callback - Computation callback function
43
- * @returns {Task<T>} - Computed signal
44
- */
45
- const createTask = <T extends {}>(
46
- callback: TaskCallback<T>,
47
- initialValue: T = UNSET,
48
- ): Task<T> => {
49
- if (!isTaskCallback(callback))
50
- throw new InvalidCallbackError('task', callback)
51
- if (initialValue == null) throw new NullishSignalValueError('task')
52
-
53
- const watchers: Set<Watcher> = new Set()
54
-
55
- // Internal state
56
- let value: T = initialValue
57
- let error: Error | undefined
58
- let controller: AbortController | undefined
59
- let dirty = true
60
- let changed = false
61
- let computing = false
62
-
63
- // Functions to update internal state
64
- const ok = (v: T): undefined => {
65
- if (!isEqual(v, value)) {
66
- value = v
67
- changed = true
68
- }
69
- error = undefined
70
- dirty = false
71
- }
72
- const nil = (): undefined => {
73
- changed = UNSET !== value
74
- value = UNSET
75
- error = undefined
76
- }
77
- const err = (e: unknown): undefined => {
78
- const newError = createError(e)
79
- changed =
80
- !error ||
81
- newError.name !== error.name ||
82
- newError.message !== error.message
83
- value = UNSET
84
- error = newError
85
- }
86
- const settle =
87
- <T>(fn: (arg: T) => void) =>
88
- (arg: T) => {
89
- computing = false
90
- controller = undefined
91
- fn(arg)
92
- if (changed) notifyWatchers(watchers)
93
- }
94
-
95
- // Own watcher: called when notified from sources (push)
96
- const watcher = createWatcher(() => {
97
- dirty = true
98
- controller?.abort()
99
- if (watchers.size) notifyWatchers(watchers)
100
- else watcher.stop()
101
- })
102
- watcher.on(HOOK_CLEANUP, () => {
103
- controller?.abort()
104
- })
105
-
106
- // Called when requested by dependencies (pull)
107
- const compute = () =>
108
- trackSignalReads(watcher, () => {
109
- if (computing) throw new CircularDependencyError('computed')
110
- changed = false
111
- // Return current value until promise resolves
112
- if (controller) return value
113
-
114
- controller = new AbortController()
115
- controller.signal.addEventListener(
116
- 'abort',
117
- () => {
118
- computing = false
119
- controller = undefined
120
- compute() // Retry computation with updated state
121
- },
122
- {
123
- once: true,
124
- },
125
- )
126
- let result: T | Promise<T>
127
- computing = true
128
- try {
129
- result = callback(value, controller.signal)
130
- } catch (e) {
131
- if (isAbortError(e)) nil()
132
- else err(e)
133
- computing = false
134
- return
135
- }
136
-
137
- if (result instanceof Promise) result.then(settle(ok), settle(err))
138
- else if (null == result || UNSET === result) nil()
139
- else ok(result)
140
- computing = false
141
- })
142
-
143
- const task: Record<PropertyKey, unknown> = {}
144
- Object.defineProperties(task, {
145
- [Symbol.toStringTag]: {
146
- value: TYPE_TASK,
147
- },
148
- get: {
149
- value: (): T => {
150
- subscribeActiveWatcher(watchers)
151
- flushPendingReactions()
152
- if (dirty) compute()
153
- if (error) throw error
154
- return value
155
- },
156
- },
157
- })
158
- return task as Task<T>
159
- }
160
-
161
- /**
162
- * Check if a value is a task signal
163
- *
164
- * @since 0.9.0
165
- * @param {unknown} value - Value to check
166
- * @returns {boolean} - True if value is a task signal, false otherwise
167
- */
168
- const isTask = /*#__PURE__*/ <T extends {}>(value: unknown): value is Task<T> =>
169
- isObjectOfType(value, TYPE_TASK)
170
-
171
- /**
172
- * Check if the provided value is a callback that may be used as input for toSignal() to derive a computed state
173
- *
174
- * @since 0.12.0
175
- * @param {unknown} value - Value to check
176
- * @returns {boolean} - True if value is an async callback, false otherwise
177
- */
178
- const isTaskCallback = /*#__PURE__*/ <T extends {}>(
179
- value: unknown,
180
- ): value is TaskCallback<T> => isAsyncFunction(value) && value.length < 3
181
-
182
- /* === Exports === */
183
-
184
- export {
185
- TYPE_TASK,
186
- createTask,
187
- isTask,
188
- isTaskCallback,
189
- type Task,
190
- type TaskCallback,
191
- }