@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.
Files changed (89) hide show
  1. package/.ai-context.md +163 -232
  2. package/.cursorrules +41 -35
  3. package/.github/copilot-instructions.md +166 -116
  4. package/ARCHITECTURE.md +274 -0
  5. package/CLAUDE.md +199 -143
  6. package/COLLECTION_REFACTORING.md +161 -0
  7. package/GUIDE.md +298 -0
  8. package/README.md +232 -197
  9. package/REQUIREMENTS.md +100 -0
  10. package/bench/reactivity.bench.ts +577 -0
  11. package/index.dev.js +1325 -997
  12. package/index.js +1 -1
  13. package/index.ts +58 -74
  14. package/package.json +4 -1
  15. package/src/errors.ts +118 -74
  16. package/src/graph.ts +601 -0
  17. package/src/nodes/collection.ts +474 -0
  18. package/src/nodes/effect.ts +149 -0
  19. package/src/nodes/list.ts +588 -0
  20. package/src/nodes/memo.ts +120 -0
  21. package/src/nodes/sensor.ts +139 -0
  22. package/src/nodes/state.ts +135 -0
  23. package/src/nodes/store.ts +383 -0
  24. package/src/nodes/task.ts +146 -0
  25. package/src/signal.ts +112 -66
  26. package/src/util.ts +26 -57
  27. package/test/batch.test.ts +96 -62
  28. package/test/benchmark.test.ts +473 -487
  29. package/test/collection.test.ts +466 -706
  30. package/test/effect.test.ts +293 -696
  31. package/test/list.test.ts +335 -592
  32. package/test/memo.test.ts +380 -0
  33. package/test/regression.test.ts +156 -0
  34. package/test/scope.test.ts +191 -0
  35. package/test/sensor.test.ts +454 -0
  36. package/test/signal.test.ts +220 -213
  37. package/test/state.test.ts +217 -265
  38. package/test/store.test.ts +346 -446
  39. package/test/task.test.ts +395 -0
  40. package/test/untrack.test.ts +167 -0
  41. package/types/index.d.ts +13 -15
  42. package/types/src/errors.d.ts +73 -17
  43. package/types/src/graph.d.ts +208 -0
  44. package/types/src/nodes/collection.d.ts +64 -0
  45. package/types/src/nodes/effect.d.ts +48 -0
  46. package/types/src/nodes/list.d.ts +65 -0
  47. package/types/src/nodes/memo.d.ts +57 -0
  48. package/types/src/nodes/sensor.d.ts +75 -0
  49. package/types/src/nodes/state.d.ts +78 -0
  50. package/types/src/nodes/store.d.ts +51 -0
  51. package/types/src/nodes/task.d.ts +73 -0
  52. package/types/src/signal.d.ts +43 -29
  53. package/types/src/util.d.ts +9 -16
  54. package/archive/benchmark.ts +0 -683
  55. package/archive/collection.ts +0 -253
  56. package/archive/composite.ts +0 -85
  57. package/archive/computed.ts +0 -195
  58. package/archive/list.ts +0 -483
  59. package/archive/memo.ts +0 -139
  60. package/archive/state.ts +0 -90
  61. package/archive/store.ts +0 -298
  62. package/archive/task.ts +0 -189
  63. package/src/classes/collection.ts +0 -245
  64. package/src/classes/computed.ts +0 -349
  65. package/src/classes/list.ts +0 -343
  66. package/src/classes/ref.ts +0 -70
  67. package/src/classes/state.ts +0 -102
  68. package/src/classes/store.ts +0 -262
  69. package/src/diff.ts +0 -138
  70. package/src/effect.ts +0 -93
  71. package/src/match.ts +0 -45
  72. package/src/resolve.ts +0 -49
  73. package/src/system.ts +0 -257
  74. package/test/computed.test.ts +0 -1108
  75. package/test/diff.test.ts +0 -955
  76. package/test/match.test.ts +0 -388
  77. package/test/ref.test.ts +0 -353
  78. package/test/resolve.test.ts +0 -154
  79. package/types/src/classes/collection.d.ts +0 -45
  80. package/types/src/classes/computed.d.ts +0 -94
  81. package/types/src/classes/list.d.ts +0 -43
  82. package/types/src/classes/ref.d.ts +0 -35
  83. package/types/src/classes/state.d.ts +0 -49
  84. package/types/src/classes/store.d.ts +0 -52
  85. package/types/src/diff.d.ts +0 -28
  86. package/types/src/effect.d.ts +0 -15
  87. package/types/src/match.d.ts +0 -21
  88. package/types/src/resolve.d.ts +0 -29
  89. package/types/src/system.d.ts +0 -78
@@ -0,0 +1,395 @@
1
+ import { describe, expect, test } from 'bun:test'
2
+ import {
3
+ createEffect,
4
+ createMemo,
5
+ createState,
6
+ createTask,
7
+ isMemo,
8
+ isTask,
9
+ UnsetSignalValueError,
10
+ } from '../index.ts'
11
+
12
+ /* === Utility Functions === */
13
+
14
+ const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
15
+
16
+ /* === Tests === */
17
+
18
+ describe('Task', () => {
19
+ describe('createTask', () => {
20
+ test('should resolve async computation', async () => {
21
+ const task = createTask(
22
+ async () => {
23
+ await wait(50)
24
+ return 42
25
+ },
26
+ { value: 0 },
27
+ )
28
+ expect(task.get()).toBe(0)
29
+ await wait(60)
30
+ expect(task.get()).toBe(42)
31
+ })
32
+
33
+ test('should have Symbol.toStringTag of "Task"', () => {
34
+ const task = createTask(async () => 1, { value: 0 })
35
+ expect(task[Symbol.toStringTag]).toBe('Task')
36
+ })
37
+
38
+ test('should throw UnsetSignalValueError before resolution with no initial value', () => {
39
+ const task = createTask(async () => {
40
+ await wait(50)
41
+ return 42
42
+ })
43
+ expect(() => task.get()).toThrow(UnsetSignalValueError)
44
+ })
45
+ })
46
+
47
+ describe('isTask', () => {
48
+ test('should identify task signals', () => {
49
+ expect(isTask(createTask(async () => 1, { value: 0 }))).toBe(true)
50
+ })
51
+
52
+ test('should return false for non-task values', () => {
53
+ expect(isTask(42)).toBe(false)
54
+ expect(isTask(null)).toBe(false)
55
+ expect(isTask({})).toBe(false)
56
+ expect(isMemo(createTask(async () => 1, { value: 0 }))).toBe(false)
57
+ })
58
+ })
59
+
60
+ describe('isPending', () => {
61
+ test('should return true while computation is in-flight', async () => {
62
+ const task = createTask(
63
+ async () => {
64
+ await wait(50)
65
+ return 42
66
+ },
67
+ { value: 0 },
68
+ )
69
+ task.get() // trigger computation
70
+ expect(task.isPending()).toBe(true)
71
+ await wait(60)
72
+ task.get() // read resolved value
73
+ expect(task.isPending()).toBe(false)
74
+ })
75
+
76
+ test('should return false before first get()', () => {
77
+ const task = createTask(async () => 42, { value: 0 })
78
+ expect(task.isPending()).toBe(false)
79
+ })
80
+ })
81
+
82
+ describe('abort', () => {
83
+ test('should abort the current computation', async () => {
84
+ let completed = false
85
+ const task = createTask(
86
+ async (_prev, signal) => {
87
+ await wait(50)
88
+ if (!signal.aborted) completed = true
89
+ return 42
90
+ },
91
+ { value: 0 },
92
+ )
93
+ task.get() // trigger computation
94
+ expect(task.isPending()).toBe(true)
95
+ task.abort()
96
+ expect(task.isPending()).toBe(false)
97
+ await wait(60)
98
+ expect(completed).toBe(false)
99
+ })
100
+ })
101
+
102
+ describe('Dependency Tracking', () => {
103
+ test('should re-execute when dependencies change', async () => {
104
+ const source = createState(1)
105
+ const task = createTask(
106
+ async () => {
107
+ const val = source.get() // dependency tracked before await
108
+ await wait(50)
109
+ return val * 2
110
+ },
111
+ { value: 0 },
112
+ )
113
+
114
+ let result = 0
115
+ createEffect(() => {
116
+ result = task.get()
117
+ })
118
+ expect(result).toBe(0)
119
+ await wait(60)
120
+ expect(result).toBe(2)
121
+
122
+ source.set(5)
123
+ await wait(60)
124
+ expect(result).toBe(10)
125
+ })
126
+
127
+ test('should work with downstream memos', async () => {
128
+ const status = createState('pending')
129
+ const task = createTask(async () => {
130
+ await wait(50)
131
+ status.set('success')
132
+ return 42
133
+ })
134
+ const derived = createMemo(() => {
135
+ try {
136
+ return task.get() + 1
137
+ } catch {
138
+ return 0
139
+ }
140
+ })
141
+ expect(derived.get()).toBe(0)
142
+ expect(status.get()).toBe('pending')
143
+ await wait(60)
144
+ expect(derived.get()).toBe(43)
145
+ expect(status.get()).toBe('success')
146
+ })
147
+
148
+ test('should run tasks in parallel without waterfalls', async () => {
149
+ const a = createTask(
150
+ async () => {
151
+ await wait(80)
152
+ return 10
153
+ },
154
+ { value: 0 },
155
+ )
156
+ const b = createTask(
157
+ async () => {
158
+ await wait(80)
159
+ return 20
160
+ },
161
+ { value: 0 },
162
+ )
163
+ const sum = createMemo(() => a.get() + b.get(), { value: 0 })
164
+ expect(sum.get()).toBe(0)
165
+ await wait(90)
166
+ expect(sum.get()).toBe(30)
167
+ })
168
+ })
169
+
170
+ describe('AbortSignal', () => {
171
+ test('should signal abort when dependency changes during computation', async () => {
172
+ const source = createState(1)
173
+ let wasAborted = false
174
+ const task = createTask(
175
+ async (_prev, signal) => {
176
+ const val = source.get()
177
+ await wait(100)
178
+ if (signal.aborted) wasAborted = true
179
+ return val
180
+ },
181
+ { value: 0 },
182
+ )
183
+
184
+ task.get() // start computation
185
+ await wait(10)
186
+ source.set(2) // change dependency mid-flight
187
+
188
+ await wait(110)
189
+ expect(wasAborted).toBe(true)
190
+ })
191
+
192
+ test('should coalesce multiple rapid changes into one recomputation', async () => {
193
+ const source = createState(1)
194
+ let computationCount = 0
195
+ const task = createTask(
196
+ async () => {
197
+ computationCount++
198
+ await wait(100)
199
+ return source.get()
200
+ },
201
+ { value: 0 },
202
+ )
203
+
204
+ task.get()
205
+ expect(computationCount).toBe(1)
206
+
207
+ source.set(2)
208
+ source.set(3)
209
+ source.set(4)
210
+ await wait(210)
211
+
212
+ expect(task.get()).toBe(4)
213
+ expect(computationCount).toBe(1)
214
+ })
215
+ })
216
+
217
+ describe('Error Handling', () => {
218
+ test('should propagate async errors on get()', async () => {
219
+ const task = createTask(
220
+ async () => {
221
+ await wait(50)
222
+ throw new Error('async failure')
223
+ },
224
+ { value: 0 },
225
+ )
226
+ task.get()
227
+ await wait(60)
228
+ expect(() => task.get()).toThrow('async failure')
229
+ })
230
+
231
+ test('should recover from errors when dependency changes', async () => {
232
+ const source = createState(1)
233
+ const task = createTask(
234
+ async () => {
235
+ const value = source.get()
236
+ await wait(50)
237
+ if (value === 2) throw new Error('bad value')
238
+ return value
239
+ },
240
+ { value: 0 },
241
+ )
242
+
243
+ task.get()
244
+ await wait(60)
245
+ expect(task.get()).toBe(1)
246
+
247
+ source.set(2)
248
+ task.get()
249
+ await wait(60)
250
+ expect(() => task.get()).toThrow('bad value')
251
+
252
+ source.set(3)
253
+ task.get()
254
+ await wait(60)
255
+ expect(task.get()).toBe(3)
256
+ })
257
+ })
258
+
259
+ describe('options.value (prev)', () => {
260
+ test('should return initial value before resolution', () => {
261
+ const task = createTask(
262
+ async () => {
263
+ await wait(50)
264
+ return 42
265
+ },
266
+ { value: 10 },
267
+ )
268
+ expect(task.get()).toBe(10)
269
+ })
270
+
271
+ test('should pass initial value as prev to first computation', async () => {
272
+ let receivedPrev: number | undefined
273
+ const task = createTask(
274
+ async prev => {
275
+ receivedPrev = prev
276
+ await wait(50)
277
+ return prev + 5
278
+ },
279
+ { value: 10 },
280
+ )
281
+
282
+ expect(task.get()).toBe(10)
283
+ await wait(60)
284
+ expect(task.get()).toBe(15)
285
+ expect(receivedPrev).toBe(10)
286
+ })
287
+
288
+ test('should pass previous resolved value on recomputation', async () => {
289
+ const source = createState(1)
290
+ const receivedPrevs: number[] = []
291
+ const task = createTask(
292
+ async prev => {
293
+ const val = source.get() // dependency tracked before await
294
+ receivedPrevs.push(prev)
295
+ await wait(50)
296
+ return val + prev
297
+ },
298
+ { value: 0 },
299
+ )
300
+
301
+ let result = 0
302
+ createEffect(() => {
303
+ result = task.get()
304
+ })
305
+ await wait(60)
306
+ expect(result).toBe(1) // 0 + 1
307
+
308
+ source.set(2)
309
+ await wait(60)
310
+ expect(result).toBe(3) // 1 + 2
311
+ expect(receivedPrevs).toEqual([0, 1])
312
+ })
313
+ })
314
+
315
+ describe('options.equals', () => {
316
+ test('should use custom equality to skip propagation after resolution', async () => {
317
+ const source = createState(1)
318
+ let effectCount = 0
319
+ const task = createTask(
320
+ async () => {
321
+ const val = source.get() // dependency tracked before await
322
+ await wait(50)
323
+ return { x: val % 2 }
324
+ },
325
+ {
326
+ value: { x: -1 },
327
+ equals: (a, b) => a.x === b.x,
328
+ },
329
+ )
330
+
331
+ createEffect(() => {
332
+ task.get()
333
+ effectCount++
334
+ })
335
+ await wait(60) // first resolution: { x: 1 }
336
+
337
+ source.set(3) // still odd — result will be { x: 1 }, structurally equal
338
+ await wait(60)
339
+ const countAfterEqual = effectCount
340
+
341
+ source.set(2) // now even — result will be { x: 0 }, different
342
+ await wait(60)
343
+
344
+ // After the structurally different result resolves, effect should run again
345
+ expect(effectCount).toBeGreaterThan(countAfterEqual)
346
+ })
347
+ })
348
+
349
+ describe('options.guard', () => {
350
+ test('should validate initial value against guard', () => {
351
+ expect(() => {
352
+ createTask(async () => 42, {
353
+ // @ts-expect-error - Testing invalid input
354
+ value: 'foo',
355
+ guard: (v): v is number => typeof v === 'number',
356
+ })
357
+ }).toThrow('[Task] Signal value "foo" is invalid')
358
+ })
359
+
360
+ test('should accept initial value that passes guard', () => {
361
+ const task = createTask(async () => 42, {
362
+ value: 10,
363
+ guard: (v): v is number => typeof v === 'number',
364
+ })
365
+ expect(task.get()).toBe(10)
366
+ })
367
+ })
368
+
369
+ describe('Input Validation', () => {
370
+ test('should throw InvalidCallbackError for sync callback', () => {
371
+ expect(() => {
372
+ // @ts-expect-error - Testing invalid input
373
+ createTask((_a: unknown) => 42)
374
+ }).toThrow('[Task] Callback (_a) => 42 is invalid')
375
+ })
376
+
377
+ test('should throw InvalidCallbackError for non-function callback', () => {
378
+ // @ts-expect-error - Testing invalid input
379
+ expect(() => createTask(null)).toThrow(
380
+ '[Task] Callback null is invalid',
381
+ )
382
+ // @ts-expect-error - Testing invalid input
383
+ expect(() => createTask(42)).toThrow(
384
+ '[Task] Callback 42 is invalid',
385
+ )
386
+ })
387
+
388
+ test('should throw NullishSignalValueError for null initial value', () => {
389
+ expect(() => {
390
+ // @ts-expect-error - Testing invalid input
391
+ createTask(async () => 42, { value: null })
392
+ }).toThrow('[Task] Signal value cannot be null or undefined')
393
+ })
394
+ })
395
+ })
@@ -0,0 +1,167 @@
1
+ import { describe, expect, test } from 'bun:test'
2
+ import {
3
+ createEffect,
4
+ createMemo,
5
+ createScope,
6
+ createState,
7
+ untrack,
8
+ } from '../index.ts'
9
+
10
+ /* === Tests === */
11
+
12
+ describe('untrack', () => {
13
+ test('should return the value of the callback', () => {
14
+ const result = untrack(() => 42)
15
+ expect(result).toBe(42)
16
+ })
17
+
18
+ test('should read a signal without tracking it', () => {
19
+ const tracked = createState('tracked')
20
+ const untracked = createState('untracked')
21
+ let count = 0
22
+ createEffect((): undefined => {
23
+ tracked.get()
24
+ untrack(() => untracked.get())
25
+ count++
26
+ })
27
+ expect(count).toBe(1)
28
+
29
+ // changing tracked signal should re-run the effect
30
+ tracked.set('changed')
31
+ expect(count).toBe(2)
32
+
33
+ // changing untracked signal should not re-run the effect
34
+ untracked.set('changed')
35
+ expect(count).toBe(2)
36
+ })
37
+
38
+ test('should not track dependencies in memos', () => {
39
+ const a = createState(1)
40
+ const b = createState(2)
41
+ const sum = createMemo(() => a.get() + untrack(() => b.get()))
42
+ expect(sum.get()).toBe(3)
43
+
44
+ // changing a should recompute
45
+ a.set(10)
46
+ expect(sum.get()).toBe(12)
47
+
48
+ // changing b should not recompute (stale value of b used)
49
+ b.set(20)
50
+ expect(sum.get()).toBe(12)
51
+ })
52
+
53
+ test('should prevent dependency pollution from subcomponent creation', () => {
54
+ const parentSignal = createState('parent')
55
+ let parentRuns = 0
56
+ let childRuns = 0
57
+
58
+ const dispose = createScope(() => {
59
+ createEffect((): undefined => {
60
+ parentSignal.get()
61
+ parentRuns++
62
+
63
+ // Simulate subcomponent: create local state + effect
64
+ // Without untrack, childSignal.get() in the child effect
65
+ // would link to the parent effect during initial run
66
+ untrack(() => {
67
+ const childSignal = createState('child')
68
+ createEffect((): undefined => {
69
+ childSignal.get()
70
+ childRuns++
71
+ })
72
+ childSignal.set('updated')
73
+ })
74
+ })
75
+ })
76
+
77
+ expect(parentRuns).toBe(1)
78
+ expect(childRuns).toBe(2) // initial + update
79
+
80
+ // parent should re-run when its own signal changes
81
+ parentSignal.set('changed')
82
+ expect(parentRuns).toBe(2)
83
+
84
+ dispose()
85
+ })
86
+
87
+ test('should prevent parent effect from re-running on child signal changes', () => {
88
+ const show = createState(true)
89
+ let parentRuns = 0
90
+ let childValue = ''
91
+
92
+ const dispose = createScope(() => {
93
+ createEffect((): undefined => {
94
+ parentRuns++
95
+ if (show.get()) {
96
+ // Subcomponent with its own reactive state
97
+ untrack(() => {
98
+ const label = createState('hello')
99
+ createEffect((): undefined => {
100
+ childValue = label.get()
101
+ })
102
+ label.set('world')
103
+ })
104
+ }
105
+ })
106
+ })
107
+
108
+ expect(parentRuns).toBe(1)
109
+ expect(childValue).toBe('world')
110
+
111
+ // toggling show re-runs parent (it's tracked)
112
+ show.set(false)
113
+ expect(parentRuns).toBe(2)
114
+
115
+ dispose()
116
+ })
117
+
118
+ test('should nest correctly', () => {
119
+ const a = createState(1)
120
+ const b = createState(2)
121
+ const c = createState(3)
122
+ let count = 0
123
+ createEffect((): undefined => {
124
+ a.get()
125
+ untrack(() => {
126
+ b.get()
127
+ untrack(() => {
128
+ c.get()
129
+ })
130
+ })
131
+ count++
132
+ })
133
+ expect(count).toBe(1)
134
+
135
+ a.set(10)
136
+ expect(count).toBe(2)
137
+
138
+ b.set(20)
139
+ expect(count).toBe(2)
140
+
141
+ c.set(30)
142
+ expect(count).toBe(2)
143
+ })
144
+
145
+ test('should restore tracking after untrack completes', () => {
146
+ const before = createState('before')
147
+ const during = createState('during')
148
+ const after = createState('after')
149
+ let count = 0
150
+ createEffect((): undefined => {
151
+ before.get()
152
+ untrack(() => during.get())
153
+ after.get()
154
+ count++
155
+ })
156
+ expect(count).toBe(1)
157
+
158
+ before.set('x')
159
+ expect(count).toBe(2)
160
+
161
+ during.set('x')
162
+ expect(count).toBe(2)
163
+
164
+ after.set('x')
165
+ expect(count).toBe(3)
166
+ })
167
+ })
package/types/index.d.ts CHANGED
@@ -1,19 +1,17 @@
1
1
  /**
2
2
  * @name Cause & Effect
3
- * @version 0.17.3
3
+ * @version 0.18.0
4
4
  * @author Esther Brunner
5
5
  */
6
- export { type Collection, type CollectionCallback, type CollectionSource, DerivedCollection, isCollection, TYPE_COLLECTION, } from './src/classes/collection';
7
- export { type Computed, createComputed, isComputed, isMemoCallback, isTaskCallback, Memo, type MemoCallback, Task, type TaskCallback, TYPE_COMPUTED, } from './src/classes/computed';
8
- export { type ArrayToRecord, isList, type KeyConfig, List, TYPE_LIST, } from './src/classes/list';
9
- export { isRef, Ref, TYPE_REF } from './src/classes/ref';
10
- export { isState, State, TYPE_STATE } from './src/classes/state';
11
- export { BaseStore, createStore, isStore, type Store, TYPE_STORE, } from './src/classes/store';
12
- export { type DiffResult, diff, isEqual, type UnknownArray, type UnknownRecord, } from './src/diff';
13
- export { createEffect, type EffectCallback, type MaybeCleanup, } from './src/effect';
14
- export { CircularDependencyError, createError, DuplicateKeyError, type Guard, guardMutableSignal, InvalidCallbackError, InvalidCollectionSourceError, InvalidSignalValueError, NullishSignalValueError, ReadonlySignalError, validateCallback, validateSignalValue, } from './src/errors';
15
- export { type MatchHandlers, match } from './src/match';
16
- export { type ResolveResult, resolve } from './src/resolve';
17
- export { createSignal, isMutableSignal, isSignal, type Signal, type SignalValues, type UnknownSignalRecord, } from './src/signal';
18
- export { batch, type Cleanup, createWatcher, flush, notifyOf, type SignalOptions, subscribeTo, track, UNSET, untrack, type Watcher, } from './src/system';
19
- export { isAbortError, isAsyncFunction, isFunction, isNumber, isObjectOfType, isRecord, isRecordOrArray, isString, isSymbol, valueString, } from './src/util';
6
+ export { CircularDependencyError, type Guard, InvalidCallbackError, InvalidSignalValueError, NullishSignalValueError, RequiredOwnerError, UnsetSignalValueError, } from './src/errors';
7
+ export { batch, type Cleanup, type ComputedOptions, createScope, type EffectCallback, type MemoCallback, type Signal, type SignalOptions, SKIP_EQUALITY, type TaskCallback, untrack, } from './src/graph';
8
+ export { type Collection, type CollectionCallback, type CollectionOptions, createCollection, type DeriveCollectionCallback, isCollection, } from './src/nodes/collection';
9
+ export { createEffect, type MatchHandlers, type MaybePromise, match, } from './src/nodes/effect';
10
+ export { createList, type DiffResult, isEqual, isList, type KeyConfig, type List, type ListOptions, } from './src/nodes/list';
11
+ export { createMemo, isMemo, type Memo } from './src/nodes/memo';
12
+ export { createSensor, isSensor, type Sensor, type SensorCallback, } from './src/nodes/sensor';
13
+ export { createState, isState, type State, type UpdateCallback, } from './src/nodes/state';
14
+ export { createStore, isStore, type Store, type StoreOptions, } from './src/nodes/store';
15
+ export { createTask, isTask, type Task } from './src/nodes/task';
16
+ export { createComputed, createMutableSignal, createSignal, isComputed, isMutableSignal, isSignal, type MutableSignal, } from './src/signal';
17
+ export { isAsyncFunction, isFunction, isObjectOfType, isRecord, valueString, } from './src/util';
@@ -1,29 +1,85 @@
1
- import { type MutableSignal } from './signal';
2
- type Guard<T> = (value: unknown) => value is T;
1
+ /**
2
+ * A type guard function that validates whether an unknown value is of type T.
3
+ * Used to ensure type safety when updating signals.
4
+ *
5
+ * @template T - The type to guard against
6
+ * @param value - The value to check
7
+ * @returns True if the value is of type T
8
+ */
9
+ type Guard<T extends {}> = (value: unknown) => value is T;
10
+ /**
11
+ * Error thrown on re-entrance on an already running function.
12
+ */
3
13
  declare class CircularDependencyError extends Error {
14
+ /**
15
+ * Constructs a new CircularDependencyError.
16
+ *
17
+ * @param where - The location where the error occurred.
18
+ */
4
19
  constructor(where: string);
5
20
  }
6
- declare class DuplicateKeyError extends Error {
7
- constructor(where: string, key: string, value?: unknown);
21
+ /**
22
+ * Error thrown when a signal value is null or undefined.
23
+ */
24
+ declare class NullishSignalValueError extends TypeError {
25
+ /**
26
+ * Constructs a new NullishSignalValueError.
27
+ *
28
+ * @param where - The location where the error occurred.
29
+ */
30
+ constructor(where: string);
8
31
  }
9
- declare class InvalidCallbackError extends TypeError {
10
- constructor(where: string, value: unknown);
32
+ /**
33
+ * Error thrown when a signal is read before it has a value.
34
+ */
35
+ declare class UnsetSignalValueError extends Error {
36
+ /**
37
+ * Constructs a new UnsetSignalValueError.
38
+ *
39
+ * @param where - The location where the error occurred.
40
+ */
41
+ constructor(where: string);
11
42
  }
12
- declare class InvalidCollectionSourceError extends TypeError {
43
+ /**
44
+ * Error thrown when a signal value is invalid.
45
+ */
46
+ declare class InvalidSignalValueError extends TypeError {
47
+ /**
48
+ * Constructs a new InvalidSignalValueError.
49
+ *
50
+ * @param where - The location where the error occurred.
51
+ * @param value - The invalid value.
52
+ */
13
53
  constructor(where: string, value: unknown);
14
54
  }
15
- declare class InvalidSignalValueError extends TypeError {
55
+ /**
56
+ * Error thrown when a callback is invalid.
57
+ */
58
+ declare class InvalidCallbackError extends TypeError {
59
+ /**
60
+ * Constructs a new InvalidCallbackError.
61
+ *
62
+ * @param where - The location where the error occurred.
63
+ * @param value - The invalid value.
64
+ */
16
65
  constructor(where: string, value: unknown);
17
66
  }
18
- declare class NullishSignalValueError extends TypeError {
67
+ /**
68
+ * Error thrown when an API requiring an owner is called without one.
69
+ */
70
+ declare class RequiredOwnerError extends Error {
71
+ /**
72
+ * Constructs a new RequiredOwnerError.
73
+ *
74
+ * @param where - The location where the error occurred.
75
+ */
19
76
  constructor(where: string);
20
77
  }
21
- declare class ReadonlySignalError extends Error {
22
- constructor(what: string, value: unknown);
78
+ declare class DuplicateKeyError extends Error {
79
+ constructor(where: string, key: string, value?: unknown);
23
80
  }
24
- declare function assert(condition: unknown, msg?: string): asserts condition;
25
- declare const createError: (reason: unknown) => Error;
26
- declare const validateCallback: (where: string, value: unknown, guard?: (value: unknown) => boolean) => void;
27
- declare const validateSignalValue: (where: string, value: unknown, guard?: (value: unknown) => boolean) => void;
28
- declare const guardMutableSignal: <T extends {}>(what: string, value: unknown, signal: unknown) => signal is MutableSignal<T>;
29
- export { type Guard, CircularDependencyError, DuplicateKeyError, InvalidCallbackError, InvalidCollectionSourceError, InvalidSignalValueError, NullishSignalValueError, ReadonlySignalError, assert, createError, validateCallback, validateSignalValue, guardMutableSignal, };
81
+ declare function validateSignalValue<T extends {}>(where: string, value: unknown, guard?: Guard<T>): asserts value is T;
82
+ declare function validateReadValue<T extends {}>(where: string, value: T | null | undefined): asserts value is T;
83
+ declare function validateCallback(where: string, value: unknown): asserts value is (...args: unknown[]) => unknown;
84
+ declare function validateCallback<T>(where: string, value: unknown, guard: (value: unknown) => value is T): asserts value is T;
85
+ export { type Guard, CircularDependencyError, NullishSignalValueError, InvalidSignalValueError, UnsetSignalValueError, InvalidCallbackError, RequiredOwnerError, DuplicateKeyError, validateSignalValue, validateReadValue, validateCallback, };