@zeix/cause-effect 0.17.1 → 0.17.3

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 (57) hide show
  1. package/.ai-context.md +13 -0
  2. package/.github/copilot-instructions.md +4 -0
  3. package/.zed/settings.json +3 -0
  4. package/CLAUDE.md +41 -7
  5. package/README.md +48 -25
  6. package/archive/benchmark.ts +0 -5
  7. package/archive/collection.ts +6 -65
  8. package/archive/composite.ts +85 -0
  9. package/archive/computed.ts +18 -20
  10. package/archive/list.ts +7 -75
  11. package/archive/memo.ts +15 -15
  12. package/archive/state.ts +2 -1
  13. package/archive/store.ts +8 -78
  14. package/archive/task.ts +20 -25
  15. package/index.dev.js +508 -526
  16. package/index.js +1 -1
  17. package/index.ts +9 -11
  18. package/package.json +6 -6
  19. package/src/classes/collection.ts +70 -107
  20. package/src/classes/computed.ts +165 -149
  21. package/src/classes/list.ts +145 -107
  22. package/src/classes/ref.ts +19 -17
  23. package/src/classes/state.ts +21 -17
  24. package/src/classes/store.ts +125 -73
  25. package/src/diff.ts +2 -1
  26. package/src/effect.ts +17 -10
  27. package/src/errors.ts +14 -1
  28. package/src/resolve.ts +1 -1
  29. package/src/signal.ts +3 -2
  30. package/src/system.ts +159 -61
  31. package/src/util.ts +0 -6
  32. package/test/batch.test.ts +4 -11
  33. package/test/benchmark.test.ts +4 -2
  34. package/test/collection.test.ts +106 -107
  35. package/test/computed.test.ts +351 -112
  36. package/test/effect.test.ts +2 -2
  37. package/test/list.test.ts +62 -102
  38. package/test/ref.test.ts +128 -2
  39. package/test/state.test.ts +16 -22
  40. package/test/store.test.ts +101 -108
  41. package/test/util/dependency-graph.ts +2 -2
  42. package/tsconfig.build.json +11 -0
  43. package/tsconfig.json +5 -7
  44. package/types/index.d.ts +3 -3
  45. package/types/src/classes/collection.d.ts +9 -10
  46. package/types/src/classes/computed.d.ts +17 -20
  47. package/types/src/classes/list.d.ts +8 -6
  48. package/types/src/classes/ref.d.ts +8 -12
  49. package/types/src/classes/state.d.ts +5 -8
  50. package/types/src/classes/store.d.ts +14 -13
  51. package/types/src/effect.d.ts +1 -2
  52. package/types/src/errors.d.ts +2 -1
  53. package/types/src/signal.d.ts +3 -2
  54. package/types/src/system.d.ts +47 -34
  55. package/types/src/util.d.ts +1 -2
  56. package/src/classes/composite.ts +0 -176
  57. package/types/src/classes/composite.d.ts +0 -15
@@ -1,176 +0,0 @@
1
- import type { DiffResult, UnknownRecord } from '../diff'
2
- import { guardMutableSignal } from '../errors'
3
- import type { Signal } from '../signal'
4
- import {
5
- batchSignalWrites,
6
- type Cleanup,
7
- createWatcher,
8
- emitNotification,
9
- type Listener,
10
- type Listeners,
11
- trackSignalReads,
12
- type Watcher,
13
- } from '../system'
14
-
15
- /* === Types === */
16
-
17
- type CompositeListeners = Pick<Listeners, 'add' | 'change' | 'remove'>
18
-
19
- /* === Class Definitions === */
20
-
21
- class Composite<T extends UnknownRecord, S extends Signal<T[keyof T] & {}>> {
22
- signals = new Map<string, S>()
23
- #validate: <K extends keyof T & string>(
24
- key: K,
25
- value: unknown,
26
- ) => value is T[K] & {}
27
- #create: <V extends T[keyof T] & {}>(value: V) => S
28
- #watchers = new Map<string, Watcher>()
29
- #listeners: CompositeListeners = {
30
- add: new Set<Listener<'add'>>(),
31
- change: new Set<Listener<'change'>>(),
32
- remove: new Set<Listener<'remove'>>(),
33
- }
34
- #batching = false
35
-
36
- constructor(
37
- values: T,
38
- validate: <K extends keyof T & string>(
39
- key: K,
40
- value: unknown,
41
- ) => value is T[K] & {},
42
- create: <V extends T[keyof T] & {}>(value: V) => S,
43
- ) {
44
- this.#validate = validate
45
- this.#create = create
46
- this.change(
47
- {
48
- add: values,
49
- change: {},
50
- remove: {},
51
- changed: true,
52
- },
53
- true,
54
- )
55
- }
56
-
57
- #addWatcher(key: string): void {
58
- const watcher = createWatcher(() => {
59
- trackSignalReads(watcher, () => {
60
- this.signals.get(key)?.get() // Subscribe to the signal
61
- if (!this.#batching)
62
- emitNotification(this.#listeners.change, [key])
63
- })
64
- })
65
- this.#watchers.set(key, watcher)
66
- watcher()
67
- }
68
-
69
- add<K extends keyof T & string>(key: K, value: T[K]): boolean {
70
- if (!this.#validate(key, value)) return false
71
-
72
- this.signals.set(key, this.#create(value))
73
- if (this.#listeners.change.size) this.#addWatcher(key)
74
-
75
- if (!this.#batching) emitNotification(this.#listeners.add, [key])
76
- return true
77
- }
78
-
79
- remove<K extends keyof T & string>(key: K): boolean {
80
- const ok = this.signals.delete(key)
81
- if (!ok) return false
82
-
83
- const watcher = this.#watchers.get(key)
84
- if (watcher) {
85
- watcher.stop()
86
- this.#watchers.delete(key)
87
- }
88
-
89
- if (!this.#batching) emitNotification(this.#listeners.remove, [key])
90
- return true
91
- }
92
-
93
- change(changes: DiffResult, initialRun?: boolean): boolean {
94
- this.#batching = true
95
-
96
- // Additions
97
- if (Object.keys(changes.add).length) {
98
- for (const key in changes.add)
99
- this.add(
100
- key as Extract<keyof T, string>,
101
- changes.add[key] as T[Extract<keyof T, string>] & {},
102
- )
103
-
104
- // Queue initial additions event to allow listeners to be added first
105
- const notify = () =>
106
- emitNotification(this.#listeners.add, Object.keys(changes.add))
107
- if (initialRun) setTimeout(notify, 0)
108
- else notify()
109
- }
110
-
111
- // Changes
112
- if (Object.keys(changes.change).length) {
113
- batchSignalWrites(() => {
114
- for (const key in changes.change) {
115
- const value = changes.change[key]
116
- if (!this.#validate(key as keyof T & string, value))
117
- continue
118
-
119
- const signal = this.signals.get(key)
120
- if (guardMutableSignal(`list item "${key}"`, value, signal))
121
- signal.set(value)
122
- }
123
- })
124
- emitNotification(
125
- this.#listeners.change,
126
- Object.keys(changes.change),
127
- )
128
- }
129
-
130
- // Removals
131
- if (Object.keys(changes.remove).length) {
132
- for (const key in changes.remove)
133
- this.remove(key as keyof T & string)
134
- emitNotification(
135
- this.#listeners.remove,
136
- Object.keys(changes.remove),
137
- )
138
- }
139
-
140
- this.#batching = false
141
- return changes.changed
142
- }
143
-
144
- clear(): boolean {
145
- const keys = Array.from(this.signals.keys())
146
- this.signals.clear()
147
- this.#watchers.clear()
148
- emitNotification(this.#listeners.remove, keys)
149
- return true
150
- }
151
-
152
- on<K extends keyof CompositeListeners>(
153
- type: K,
154
- listener: Listener<K>,
155
- ): Cleanup {
156
- this.#listeners[type].add(listener)
157
- if (type === 'change' && !this.#watchers.size) {
158
- this.#batching = true
159
- for (const key of this.signals.keys()) this.#addWatcher(key)
160
- this.#batching = false
161
- }
162
-
163
- return () => {
164
- this.#listeners[type].delete(listener)
165
- if (type === 'change' && !this.#listeners.change.size) {
166
- if (this.#watchers.size) {
167
- for (const watcher of this.#watchers.values())
168
- watcher.stop()
169
- this.#watchers.clear()
170
- }
171
- }
172
- }
173
- }
174
- }
175
-
176
- export { Composite, type CompositeListeners }
@@ -1,15 +0,0 @@
1
- import type { DiffResult, UnknownRecord } from '../diff';
2
- import type { Signal } from '../signal';
3
- import { type Cleanup, type Listener, type Listeners } from '../system';
4
- type CompositeListeners = Pick<Listeners, 'add' | 'change' | 'remove'>;
5
- declare class Composite<T extends UnknownRecord, S extends Signal<T[keyof T] & {}>> {
6
- #private;
7
- signals: Map<string, S>;
8
- constructor(values: T, validate: <K extends keyof T & string>(key: K, value: unknown) => value is T[K] & {}, create: <V extends T[keyof T] & {}>(value: V) => S);
9
- add<K extends keyof T & string>(key: K, value: T[K]): boolean;
10
- remove<K extends keyof T & string>(key: K): boolean;
11
- change(changes: DiffResult, initialRun?: boolean): boolean;
12
- clear(): boolean;
13
- on<K extends keyof CompositeListeners>(type: K, listener: Listener<K>): Cleanup;
14
- }
15
- export { Composite, type CompositeListeners };