@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/list.ts DELETED
@@ -1,544 +0,0 @@
1
- import { type DiffResult, diff, isEqual, type UnknownArray } 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 Hook,
14
- type HookCallback,
15
- type HookCallbacks,
16
- notifyWatchers,
17
- subscribeActiveWatcher,
18
- trackSignalReads,
19
- triggerHook,
20
- UNSET,
21
- type Watcher,
22
- } from '../src/system'
23
- import {
24
- isFunction,
25
- isNumber,
26
- isObjectOfType,
27
- isRecord,
28
- isString,
29
- isSymbol,
30
- } from '../src/util'
31
- import {
32
- type Collection,
33
- type CollectionCallback,
34
- createCollection,
35
- } from './collection'
36
- import { isComputed } from './computed'
37
- import { createState, isState } from './state'
38
- import { createStore, isStore } from './store'
39
-
40
- /* === Types === */
41
-
42
- type ArrayToRecord<T extends UnknownArray> = {
43
- [key: string]: T extends Array<infer U extends {}> ? U : never
44
- }
45
-
46
- type KeyConfig<T> = string | ((item: T) => string)
47
-
48
- type List<T extends {}> = {
49
- readonly [Symbol.toStringTag]: 'List'
50
- [Symbol.iterator](): IterableIterator<MutableSignal<T>>
51
- readonly [Symbol.isConcatSpreadable]: boolean
52
- [n: number]: MutableSignal<T>
53
- readonly length: number
54
- add(value: T): string
55
- byKey(key: string): MutableSignal<T> | undefined
56
- deriveCollection<U extends {}>(
57
- callback: CollectionCallback<U, T extends UnknownArray ? T : never>,
58
- ): Collection<U>
59
- get(): T
60
- keyAt(index: number): string | undefined
61
- indexOfKey(key: string): number
62
- set(value: T): void
63
- update(fn: (value: T) => T): void
64
- sort<U = T>(compareFn?: (a: U, b: U) => number): void
65
- splice(start: number, deleteCount?: number, ...items: T[]): T[]
66
- on(type: Hook, callback: HookCallback): Cleanup
67
- remove(index: number): void
68
- }
69
-
70
- /* === Constants === */
71
-
72
- const TYPE_LIST = 'List' as const
73
-
74
- /* === Functions === */
75
-
76
- /**
77
- * Create a new list with deeply nested reactive list items
78
- *
79
- * @since 0.16.2
80
- * @param {T} initialValue - Initial array of the list
81
- * @param {KeyConfig<T>} keyConfig - Optional key configuration:
82
- * - string: used as prefix for auto-incrementing IDs (e.g., "item" → "item0", "item1")
83
- * - function: computes key from array item at creation time
84
- * @returns {List<T>} - New list with reactive items of type T
85
- */
86
- const createList = <T extends {}>(
87
- initialValue: T[],
88
- keyConfig?: KeyConfig<T>,
89
- ): List<T> => {
90
- if (initialValue == null) throw new NullishSignalValueError('store')
91
-
92
- const watchers = new Set<Watcher>()
93
- const hookCallbacks: HookCallbacks = {}
94
- const signals = new Map<string, MutableSignal<T>>()
95
- const ownWatchers = new Map<string, Watcher>()
96
-
97
- // Stable key support for lists
98
- let keyCounter = 0
99
- let order: string[] = []
100
-
101
- // Get signal by key or index
102
- const getSignal = (prop: string): MutableSignal<T> | undefined => {
103
- let key = prop
104
- const index = Number(prop)
105
- if (Number.isInteger(index) && index >= 0) key = order[index] ?? prop
106
- return signals.get(key)
107
- }
108
-
109
- // Generate stable key for array items
110
- const generateKey = (item: T): string => {
111
- const id = keyCounter++
112
- return isString(keyConfig)
113
- ? `${keyConfig}${id}`
114
- : isFunction<string>(keyConfig)
115
- ? keyConfig(item)
116
- : String(id)
117
- }
118
-
119
- // Convert array to record with stable keys
120
- const arrayToRecord = (array: T[]): ArrayToRecord<T[]> => {
121
- const record = {} as Record<string, T>
122
-
123
- for (let i = 0; i < array.length; i++) {
124
- const value = array[i]
125
- if (value === undefined) continue // Skip sparse array positions
126
-
127
- let key = order[i]
128
- if (!key) {
129
- key = generateKey(value)
130
- order[i] = key
131
- }
132
- record[key] = value
133
- }
134
- return record
135
- }
136
-
137
- // Get current record
138
- const current = (): T[] =>
139
- order
140
- .map(key => signals.get(key)?.get())
141
- .filter(v => v !== undefined) as T[]
142
-
143
- // Validate input
144
- const isValidValue = <T>(
145
- key: string,
146
- value: T,
147
- ): value is NonNullable<T> => {
148
- if (value == null)
149
- throw new NullishSignalValueError(`store for key "${key}"`)
150
- if (value === UNSET) return true
151
- if (isSymbol(value) || isFunction(value) || isComputed(value))
152
- throw new InvalidSignalValueError(`store for key "${key}"`, value)
153
- return true
154
- }
155
-
156
- // Add own watcher for nested signal
157
- const addOwnWatcher = (key: string, signal: MutableSignal<T>) => {
158
- const watcher = createWatcher(() => {
159
- trackSignalReads(watcher, () => {
160
- signal.get() // Subscribe to the signal
161
- triggerHook(hookCallbacks.change, [key])
162
- })
163
- })
164
- ownWatchers.set(key, watcher)
165
- }
166
-
167
- // Add nested signal and own watcher
168
- const addProperty = <K extends keyof T & string>(
169
- key: K,
170
- value: T[K],
171
- single = false,
172
- ): boolean => {
173
- if (!isValidValue(key, value)) return false
174
-
175
- // Create signal for key
176
- // @ts-expect-error ignore
177
- const signal: MutableSignal<T[K] & {}> =
178
- isState(value) || isStore(value)
179
- ? (value as unknown as MutableSignal<T[K] & {}>)
180
- : isRecord(value) || Array.isArray(value)
181
- ? createStore(value)
182
- : createState(value)
183
-
184
- // Set internal states
185
- // @ts-expect-error ignore
186
- signals.set(key, signal)
187
- if (!order.includes(key)) order.push(key)
188
- // @ts-expect-error ignore
189
- if (hookCallbacks.change?.size) addOwnWatcher(key, signal)
190
-
191
- if (single) {
192
- notifyWatchers(watchers)
193
- triggerHook(hookCallbacks.add, [key])
194
- }
195
- return true
196
- }
197
-
198
- // Remove nested signal and effect
199
- const removeProperty = (key: string, single = false) => {
200
- // Remove signal for key
201
- const ok = signals.delete(key)
202
- if (!ok) return
203
-
204
- // Clean up internal states
205
- const index = order.indexOf(key)
206
- if (index >= 0) order.splice(index, 1)
207
- const watcher = ownWatchers.get(key)
208
- if (watcher) {
209
- watcher.stop()
210
- ownWatchers.delete(key)
211
- }
212
-
213
- if (single) {
214
- order = order.filter(() => true) // Compact array
215
- notifyWatchers(watchers)
216
- triggerHook(hookCallbacks.remove, [key])
217
- }
218
- }
219
-
220
- // Commit batched changes and emit notifications
221
- const batchChanges = (changes: DiffResult, initialRun?: boolean) => {
222
- // Additions
223
- if (Object.keys(changes.add).length) {
224
- for (const key in changes.add)
225
- // @ts-expect-error ignore
226
- addProperty(key, changes.add[key] as T, false)
227
-
228
- // Queue initial additions event to allow listeners to be added first
229
- if (initialRun)
230
- setTimeout(() => {
231
- triggerHook(hookCallbacks.add, Object.keys(changes.add))
232
- }, 0)
233
- else triggerHook(hookCallbacks.add, Object.keys(changes.add))
234
- }
235
-
236
- // Changes
237
- if (Object.keys(changes.change).length) {
238
- batchSignalWrites(() => {
239
- for (const key in changes.change) {
240
- const value = changes.change[key] as T
241
- if (!isValidValue(key, value)) continue
242
-
243
- const signal = signals.get(key)
244
- if (isMutableSignal(signal)) signal.set(value)
245
- else throw new ReadonlySignalError(key, value)
246
- }
247
- triggerHook(hookCallbacks.change, Object.keys(changes.change))
248
- })
249
- }
250
-
251
- // Removals
252
- if (Object.keys(changes.remove).length) {
253
- for (const key in changes.remove) removeProperty(key)
254
- order = order.filter(() => true)
255
- triggerHook(hookCallbacks.remove, Object.keys(changes.remove))
256
- }
257
-
258
- return changes.changed
259
- }
260
-
261
- // Reconcile data and dispatch events
262
- const reconcile = (
263
- oldValue: T[],
264
- newValue: T[],
265
- initialRun?: boolean,
266
- ): boolean =>
267
- batchChanges(
268
- diff(arrayToRecord(oldValue), arrayToRecord(newValue)),
269
- initialRun,
270
- )
271
-
272
- // Initialize data
273
- reconcile([] as T[], initialValue, true)
274
-
275
- // Methods and Properties
276
- const prototype: Record<PropertyKey, unknown> = {}
277
- Object.defineProperties(prototype, {
278
- [Symbol.toStringTag]: {
279
- value: TYPE_LIST,
280
- },
281
- [Symbol.isConcatSpreadable]: {
282
- value: true,
283
- },
284
- [Symbol.iterator]: {
285
- value: function* () {
286
- for (const key of order) {
287
- const signal = signals.get(key)
288
- if (signal) yield signal
289
- }
290
- },
291
- },
292
- length: {
293
- get(): number {
294
- subscribeActiveWatcher(watchers)
295
- return signals.size
296
- },
297
- },
298
- order: {
299
- get(): string[] {
300
- subscribeActiveWatcher(watchers)
301
- return order
302
- },
303
- },
304
- at: {
305
- value(index: number): MutableSignal<T> | undefined {
306
- return signals.get(order[index])
307
- },
308
- },
309
- byKey: {
310
- value: (key: string): MutableSignal<T> | undefined => {
311
- return getSignal(key)
312
- },
313
- },
314
- deriveCollection: {
315
- value: <U extends {}>(
316
- callback: CollectionCallback<U, T>,
317
- ): Collection<U> => {
318
- const collection = createCollection(list, callback)
319
- return collection
320
- },
321
- },
322
- keyAt: {
323
- value(index: number): string | undefined {
324
- return order[index]
325
- },
326
- },
327
- indexOfKey: {
328
- value(key: string): number {
329
- return order.indexOf(key)
330
- },
331
- },
332
- get: {
333
- value: (): T[] => {
334
- subscribeActiveWatcher(watchers)
335
- return current()
336
- },
337
- },
338
- set: {
339
- value: (newValue: T[]): void => {
340
- if (reconcile(current(), newValue)) {
341
- notifyWatchers(watchers)
342
- if (UNSET === newValue) watchers.clear()
343
- }
344
- },
345
- },
346
- update: {
347
- value: (fn: (oldValue: T[]) => T[]): void => {
348
- const oldValue = current()
349
- const newValue = fn(oldValue)
350
- if (reconcile(oldValue, newValue)) {
351
- notifyWatchers(watchers)
352
- if (UNSET === newValue) watchers.clear()
353
- }
354
- },
355
- },
356
- add: {
357
- value: (value: T): string => {
358
- const key = generateKey(value)
359
- if (!signals.has(key)) {
360
- // @ts-expect-error ignore
361
- addProperty(key, value, true)
362
- return key
363
- } else throw new DuplicateKeyError('store', key, value)
364
- },
365
- },
366
- remove: {
367
- value: (keyOrIndex: string | number): void => {
368
- const key = isNumber(keyOrIndex)
369
- ? order[keyOrIndex]
370
- : keyOrIndex
371
- if (key && signals.has(key)) removeProperty(key, true)
372
- },
373
- },
374
- sort: {
375
- value: (
376
- compareFn?: <
377
- U = T extends UnknownArray
378
- ? T
379
- : T[Extract<keyof T, string>],
380
- >(
381
- a: U,
382
- b: U,
383
- ) => number,
384
- ): void => {
385
- const entries = order
386
- .map(key => [key, signals.get(key)?.get()] as [string, T])
387
- .sort(
388
- compareFn
389
- ? (a, b) => compareFn(a[1], b[1])
390
- : (a, b) =>
391
- String(a[1]).localeCompare(String(b[1])),
392
- )
393
-
394
- // Set new order
395
- const newOrder = entries.map(([key]) => key)
396
- if (!isEqual(newOrder, order)) {
397
- order = newOrder
398
- notifyWatchers(watchers)
399
- triggerHook(hookCallbacks.sort, order)
400
- }
401
- },
402
- },
403
- splice: {
404
- value: (
405
- start: number,
406
- deleteCount?: number,
407
- ...items: T[]
408
- ): T[] => {
409
- // Normalize start and deleteCount
410
- const length = signals.size
411
- const actualStart =
412
- start < 0
413
- ? Math.max(0, length + start)
414
- : Math.min(start, length)
415
- const actualDeleteCount = Math.max(
416
- 0,
417
- Math.min(
418
- deleteCount ??
419
- Math.max(0, length - Math.max(0, actualStart)),
420
- length - actualStart,
421
- ),
422
- )
423
-
424
- const add = {} as Record<string, T>
425
- const remove = {} as Record<string, T>
426
-
427
- // Collect items to delete and their keys
428
- for (let i = 0; i < actualDeleteCount; i++) {
429
- const index = actualStart + i
430
- const key = order[index]
431
- if (key) {
432
- const signal = signals.get(key)
433
- if (signal) remove[key] = signal.get() as T
434
- }
435
- }
436
-
437
- // Build new order: items before splice point
438
- const newOrder = order.slice(0, actualStart)
439
-
440
- // Add new items
441
- for (const item of items) {
442
- const key = generateKey(item)
443
- newOrder.push(key)
444
- add[key] = item as T
445
- }
446
-
447
- // Add items after splice point
448
- newOrder.push(...order.slice(actualStart + actualDeleteCount))
449
-
450
- // Update the order array
451
- order = newOrder.filter(() => true) // Compact array
452
-
453
- const changed = !!(
454
- Object.keys(add).length || Object.keys(remove).length
455
- )
456
-
457
- if (changed)
458
- batchChanges({
459
- add,
460
- change: {} as Record<string, T>,
461
- remove,
462
- changed,
463
- })
464
-
465
- notifyWatchers(watchers)
466
-
467
- return Object.values(remove) as T[]
468
- },
469
- },
470
- on: {
471
- value: (type: Hook, callback: HookCallback): Cleanup => {
472
- hookCallbacks[type] ||= new Set()
473
- hookCallbacks[type].add(callback)
474
- if (type === 'change' && !ownWatchers.size) {
475
- for (const [key, signal] of signals)
476
- addOwnWatcher(key, signal)
477
- }
478
- return () => {
479
- hookCallbacks[type]?.delete(callback)
480
- if (type === 'change' && !hookCallbacks.change?.size) {
481
- if (ownWatchers.size) {
482
- for (const watcher of ownWatchers.values())
483
- watcher.stop()
484
- ownWatchers.clear()
485
- }
486
- }
487
- }
488
- },
489
- },
490
- })
491
-
492
- // Return proxy directly with integrated signal methods
493
- const list = new Proxy(prototype as List<T>, {
494
- get(target, prop) {
495
- if (prop in target) return Reflect.get(target, prop)
496
- if (!isSymbol(prop)) return getSignal(prop)
497
- },
498
- has(target, prop) {
499
- if (prop in target) return true
500
- return signals.has(String(prop))
501
- },
502
- ownKeys(target) {
503
- const staticKeys = Reflect.ownKeys(target)
504
- return [...new Set([...order, ...staticKeys])]
505
- },
506
- getOwnPropertyDescriptor(target, prop) {
507
- if (prop in target)
508
- return Reflect.getOwnPropertyDescriptor(target, prop)
509
- if (isSymbol(prop)) return undefined
510
-
511
- const signal = getSignal(prop)
512
- return signal
513
- ? {
514
- enumerable: true,
515
- configurable: true,
516
- writable: true,
517
- value: signal,
518
- }
519
- : undefined
520
- },
521
- })
522
- return list
523
- }
524
-
525
- /**
526
- * Check if the provided value is a List instance
527
- *
528
- * @since 0.15.0
529
- * @param {unknown} value - value to check
530
- * @returns {boolean} - true if the value is a List instance, false otherwise
531
- */
532
- const isList = <T extends {}>(value: unknown): value is List<T> =>
533
- isObjectOfType(value, TYPE_LIST)
534
-
535
- /* === Exports === */
536
-
537
- export {
538
- TYPE_LIST,
539
- isList,
540
- createList,
541
- type ArrayToRecord,
542
- type List,
543
- type KeyConfig,
544
- }
package/archive/memo.ts DELETED
@@ -1,140 +0,0 @@
1
- import {
2
- CircularDependencyError,
3
- createError,
4
- InvalidCallbackError,
5
- NullishSignalValueError,
6
- } from '../src/errors'
7
- import {
8
- createWatcher,
9
- flushPendingReactions,
10
- notifyWatchers,
11
- subscribeActiveWatcher,
12
- trackSignalReads,
13
- UNSET,
14
- type Watcher,
15
- } from '../src/system'
16
- import { isObjectOfType, isSyncFunction } from '../src/util'
17
-
18
- /* === Types === */
19
-
20
- type Memo<T extends {}> = {
21
- readonly [Symbol.toStringTag]: 'Memo'
22
- get(): T
23
- }
24
-
25
- type MemoCallback<T extends {} & { then?: undefined }> = (oldValue: T) => T
26
-
27
- /* === Constants === */
28
-
29
- const TYPE_MEMO = 'Memo' as const
30
-
31
- /* === Functions === */
32
-
33
- /**
34
- * Create a derived signal from existing signals
35
- *
36
- * @since 0.9.0
37
- * @param {MemoCallback<T>} callback - Computation callback function
38
- * @returns {Memo<T>} - Computed signal
39
- */
40
- const createMemo = <T extends {}>(
41
- callback: MemoCallback<T>,
42
- initialValue: T = UNSET,
43
- ): Memo<T> => {
44
- if (!isMemoCallback(callback))
45
- throw new InvalidCallbackError('memo', callback)
46
- if (initialValue == null) throw new NullishSignalValueError('memo')
47
-
48
- const watchers: Set<Watcher> = new Set()
49
-
50
- // Internal state
51
- let value: T = initialValue
52
- let error: Error | undefined
53
- let dirty = true
54
- let computing = false
55
-
56
- // Own watcher: called when notified from sources (push)
57
- const watcher = createWatcher(() => {
58
- dirty = true
59
- if (watchers.size) notifyWatchers(watchers)
60
- else watcher.stop()
61
- })
62
-
63
- // Called when requested by dependencies (pull)
64
- const compute = () =>
65
- trackSignalReads(watcher, () => {
66
- if (computing) throw new CircularDependencyError('memo')
67
- let result: T
68
- computing = true
69
- try {
70
- result = callback(value)
71
- } catch (e) {
72
- // Err track
73
- value = UNSET
74
- error = createError(e)
75
- computing = false
76
- return
77
- }
78
-
79
- if (null == result || UNSET === result) {
80
- // Nil track
81
- value = UNSET
82
- error = undefined
83
- } else {
84
- // Ok track
85
- value = result
86
- error = undefined
87
- dirty = false
88
- }
89
- computing = false
90
- })
91
-
92
- const memo: Record<PropertyKey, unknown> = {}
93
- Object.defineProperties(memo, {
94
- [Symbol.toStringTag]: {
95
- value: TYPE_MEMO,
96
- },
97
- get: {
98
- value: (): T => {
99
- subscribeActiveWatcher(watchers)
100
- flushPendingReactions()
101
- if (dirty) compute()
102
- if (error) throw error
103
- return value
104
- },
105
- },
106
- })
107
- return memo as Memo<T>
108
- }
109
-
110
- /**
111
- * Check if a value is a memoized signal
112
- *
113
- * @since 0.9.0
114
- * @param {unknown} value - Value to check
115
- * @returns {boolean} - True if value is a memo signal, false otherwise
116
- */
117
- const isMemo = /*#__PURE__*/ <T extends {}>(value: unknown): value is Memo<T> =>
118
- isObjectOfType(value, TYPE_MEMO)
119
-
120
- /**
121
- * Check if the provided value is a callback that may be used as input for toSignal() to derive a computed state
122
- *
123
- * @since 0.12.0
124
- * @param {unknown} value - Value to check
125
- * @returns {boolean} - True if value is a sync callback, false otherwise
126
- */
127
- const isMemoCallback = /*#__PURE__*/ <T extends {} & { then?: undefined }>(
128
- value: unknown,
129
- ): value is MemoCallback<T> => isSyncFunction(value) && value.length < 2
130
-
131
- /* === Exports === */
132
-
133
- export {
134
- TYPE_MEMO,
135
- createMemo,
136
- isMemo,
137
- isMemoCallback,
138
- type Memo,
139
- type MemoCallback,
140
- }