@zeix/cause-effect 0.17.3 → 0.18.1

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 +169 -227
  2. package/.cursorrules +41 -35
  3. package/.github/copilot-instructions.md +176 -116
  4. package/ARCHITECTURE.md +276 -0
  5. package/CHANGELOG.md +29 -0
  6. package/CLAUDE.md +201 -143
  7. package/GUIDE.md +298 -0
  8. package/README.md +246 -193
  9. package/REQUIREMENTS.md +100 -0
  10. package/bench/reactivity.bench.ts +577 -0
  11. package/context7.json +4 -0
  12. package/examples/events-sensor.ts +187 -0
  13. package/examples/selector-sensor.ts +173 -0
  14. package/index.dev.js +1390 -1008
  15. package/index.js +1 -1
  16. package/index.ts +60 -74
  17. package/package.json +5 -2
  18. package/skills/changelog-keeper/SKILL.md +59 -0
  19. package/skills/changelog-keeper/agents/openai.yaml +4 -0
  20. package/src/errors.ts +118 -74
  21. package/src/graph.ts +612 -0
  22. package/src/nodes/collection.ts +512 -0
  23. package/src/nodes/effect.ts +149 -0
  24. package/src/nodes/list.ts +589 -0
  25. package/src/nodes/memo.ts +148 -0
  26. package/src/nodes/sensor.ts +149 -0
  27. package/src/nodes/state.ts +135 -0
  28. package/src/nodes/store.ts +378 -0
  29. package/src/nodes/task.ts +174 -0
  30. package/src/signal.ts +112 -66
  31. package/src/util.ts +26 -57
  32. package/test/batch.test.ts +96 -62
  33. package/test/benchmark.test.ts +473 -487
  34. package/test/collection.test.ts +456 -707
  35. package/test/effect.test.ts +293 -696
  36. package/test/list.test.ts +335 -592
  37. package/test/memo.test.ts +574 -0
  38. package/test/regression.test.ts +156 -0
  39. package/test/scope.test.ts +191 -0
  40. package/test/sensor.test.ts +454 -0
  41. package/test/signal.test.ts +220 -213
  42. package/test/state.test.ts +217 -265
  43. package/test/store.test.ts +346 -446
  44. package/test/task.test.ts +529 -0
  45. package/test/untrack.test.ts +167 -0
  46. package/types/index.d.ts +13 -15
  47. package/types/src/errors.d.ts +73 -17
  48. package/types/src/graph.d.ts +218 -0
  49. package/types/src/nodes/collection.d.ts +69 -0
  50. package/types/src/nodes/effect.d.ts +48 -0
  51. package/types/src/nodes/list.d.ts +66 -0
  52. package/types/src/nodes/memo.d.ts +63 -0
  53. package/types/src/nodes/sensor.d.ts +81 -0
  54. package/types/src/nodes/state.d.ts +78 -0
  55. package/types/src/nodes/store.d.ts +51 -0
  56. package/types/src/nodes/task.d.ts +79 -0
  57. package/types/src/signal.d.ts +43 -29
  58. package/types/src/util.d.ts +9 -16
  59. package/archive/benchmark.ts +0 -683
  60. package/archive/collection.ts +0 -253
  61. package/archive/composite.ts +0 -85
  62. package/archive/computed.ts +0 -195
  63. package/archive/list.ts +0 -483
  64. package/archive/memo.ts +0 -139
  65. package/archive/state.ts +0 -90
  66. package/archive/store.ts +0 -298
  67. package/archive/task.ts +0 -189
  68. package/src/classes/collection.ts +0 -245
  69. package/src/classes/computed.ts +0 -349
  70. package/src/classes/list.ts +0 -343
  71. package/src/classes/ref.ts +0 -70
  72. package/src/classes/state.ts +0 -102
  73. package/src/classes/store.ts +0 -262
  74. package/src/diff.ts +0 -138
  75. package/src/effect.ts +0 -93
  76. package/src/match.ts +0 -45
  77. package/src/resolve.ts +0 -49
  78. package/src/system.ts +0 -257
  79. package/test/computed.test.ts +0 -1108
  80. package/test/diff.test.ts +0 -955
  81. package/test/match.test.ts +0 -388
  82. package/test/ref.test.ts +0 -353
  83. package/test/resolve.test.ts +0 -154
  84. package/types/src/classes/collection.d.ts +0 -45
  85. package/types/src/classes/computed.d.ts +0 -94
  86. package/types/src/classes/list.d.ts +0 -43
  87. package/types/src/classes/ref.d.ts +0 -35
  88. package/types/src/classes/state.d.ts +0 -49
  89. package/types/src/classes/store.d.ts +0 -52
  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 -78
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.1
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 MaybeCleanup, type MemoCallback, type Signal, type SignalOptions, SKIP_EQUALITY, type TaskCallback, untrack, } from './src/graph';
8
+ export { type Collection, type CollectionCallback, type CollectionChanges, 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, 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, type SensorOptions, } 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, };
@@ -0,0 +1,218 @@
1
+ import { type Guard } from './errors';
2
+ type SourceFields<T extends {}> = {
3
+ value: T;
4
+ sinks: Edge | null;
5
+ sinksTail: Edge | null;
6
+ stop?: Cleanup;
7
+ };
8
+ type OptionsFields<T extends {}> = {
9
+ equals: (a: T, b: T) => boolean;
10
+ guard?: Guard<T>;
11
+ };
12
+ type SinkFields = {
13
+ fn: unknown;
14
+ flags: number;
15
+ sources: Edge | null;
16
+ sourcesTail: Edge | null;
17
+ };
18
+ type OwnerFields = {
19
+ cleanup: Cleanup | Cleanup[] | null;
20
+ };
21
+ type AsyncFields = {
22
+ controller: AbortController | undefined;
23
+ error: Error | undefined;
24
+ };
25
+ type StateNode<T extends {}> = SourceFields<T> & OptionsFields<T>;
26
+ type MemoNode<T extends {}> = SourceFields<T> & OptionsFields<T> & SinkFields & {
27
+ fn: MemoCallback<T>;
28
+ error: Error | undefined;
29
+ };
30
+ type TaskNode<T extends {}> = SourceFields<T> & OptionsFields<T> & SinkFields & AsyncFields & {
31
+ fn: (prev: T, abort: AbortSignal) => Promise<T>;
32
+ };
33
+ type EffectNode = SinkFields & OwnerFields & {
34
+ fn: EffectCallback;
35
+ };
36
+ type Scope = OwnerFields;
37
+ type SourceNode = SourceFields<unknown & {}>;
38
+ type SinkNode = MemoNode<unknown & {}> | TaskNode<unknown & {}> | EffectNode;
39
+ type OwnerNode = EffectNode | Scope;
40
+ type Edge = {
41
+ source: SourceNode;
42
+ sink: SinkNode;
43
+ nextSource: Edge | null;
44
+ prevSink: Edge | null;
45
+ nextSink: Edge | null;
46
+ };
47
+ type Signal<T extends {}> = {
48
+ get(): T;
49
+ };
50
+ /**
51
+ * A cleanup function that can be called to dispose of resources.
52
+ */
53
+ type Cleanup = () => void;
54
+ type MaybeCleanup = Cleanup | undefined | void;
55
+ /**
56
+ * Options for configuring signal behavior.
57
+ *
58
+ * @template T - The type of value in the signal
59
+ */
60
+ type SignalOptions<T extends {}> = {
61
+ /**
62
+ * Optional type guard to validate values.
63
+ * If provided, will throw an error if an invalid value is set.
64
+ */
65
+ guard?: Guard<T>;
66
+ /**
67
+ * Optional custom equality function.
68
+ * Used to determine if a new value is different from the old value.
69
+ * Defaults to reference equality (===).
70
+ */
71
+ equals?: (a: T, b: T) => boolean;
72
+ };
73
+ type ComputedOptions<T extends {}> = SignalOptions<T> & {
74
+ /**
75
+ * Optional initial value.
76
+ * Useful for reducer patterns so that calculations start with a value of correct type.
77
+ */
78
+ value?: T;
79
+ /**
80
+ * Optional callback invoked when the signal is first watched by an effect.
81
+ * Receives an `invalidate` function that marks the signal dirty and triggers re-evaluation.
82
+ * Must return a cleanup function that is called when the signal is no longer watched.
83
+ *
84
+ * This enables lazy resource activation for computed signals that need to
85
+ * react to external events (e.g. DOM mutations, timers) in addition to
86
+ * tracked signal dependencies.
87
+ */
88
+ watched?: (invalidate: () => void) => Cleanup;
89
+ };
90
+ /**
91
+ * A callback function for memos that computes a value based on the previous value.
92
+ *
93
+ * @template T - The type of value computed
94
+ * @param prev - The previous computed value
95
+ * @returns The new computed value
96
+ */
97
+ type MemoCallback<T extends {}> = (prev: T | undefined) => T;
98
+ /**
99
+ * A callback function for tasks that asynchronously computes a value.
100
+ *
101
+ * @template T - The type of value computed
102
+ * @param prev - The previous computed value
103
+ * @param signal - An AbortSignal that will be triggered if the task is aborted
104
+ * @returns A promise that resolves to the new computed value
105
+ */
106
+ type TaskCallback<T extends {}> = (prev: T | undefined, signal: AbortSignal) => Promise<T>;
107
+ /**
108
+ * A callback function for effects that can perform side effects.
109
+ *
110
+ * @returns An optional cleanup function that will be called before the effect re-runs or is disposed
111
+ */
112
+ type EffectCallback = () => MaybeCleanup;
113
+ declare const TYPE_STATE = "State";
114
+ declare const TYPE_MEMO = "Memo";
115
+ declare const TYPE_TASK = "Task";
116
+ declare const TYPE_SENSOR = "Sensor";
117
+ declare const TYPE_LIST = "List";
118
+ declare const TYPE_COLLECTION = "Collection";
119
+ declare const TYPE_STORE = "Store";
120
+ declare const FLAG_CLEAN = 0;
121
+ declare const FLAG_DIRTY: number;
122
+ declare let activeSink: SinkNode | null;
123
+ declare let activeOwner: OwnerNode | null;
124
+ declare let batchDepth: number;
125
+ declare const DEFAULT_EQUALITY: <T extends {}>(a: T, b: T) => boolean;
126
+ /**
127
+ * Equality function that always returns false, causing propagation on every update.
128
+ * Use with `createSensor` for observing mutable objects where the reference stays the same
129
+ * but internal state changes (e.g., DOM elements observed via MutationObserver).
130
+ *
131
+ * @example
132
+ * ```ts
133
+ * const el = createSensor<HTMLElement>((set) => {
134
+ * const node = document.getElementById('box')!;
135
+ * set(node);
136
+ * const obs = new MutationObserver(() => set(node));
137
+ * obs.observe(node, { attributes: true });
138
+ * return () => obs.disconnect();
139
+ * }, { value: node, equals: SKIP_EQUALITY });
140
+ * ```
141
+ */
142
+ declare const SKIP_EQUALITY: (_a?: unknown, _b?: unknown) => boolean;
143
+ declare function link(source: SourceNode, sink: SinkNode): void;
144
+ declare function unlink(edge: Edge): Edge | null;
145
+ declare function trimSources(node: SinkNode): void;
146
+ declare function propagate(node: SinkNode, newFlag?: number): void;
147
+ declare function setState<T extends {}>(node: StateNode<T>, next: T): void;
148
+ declare function registerCleanup(owner: OwnerNode, fn: Cleanup): void;
149
+ declare function runCleanup(owner: OwnerNode): void;
150
+ declare function runEffect(node: EffectNode): void;
151
+ declare function refresh(node: SinkNode): void;
152
+ declare function flush(): void;
153
+ /**
154
+ * Batches multiple signal updates together.
155
+ * Effects will not run until the batch completes.
156
+ * Batches can be nested; effects run when the outermost batch completes.
157
+ *
158
+ * @param fn - The function to execute within the batch
159
+ *
160
+ * @example
161
+ * ```ts
162
+ * const count = createState(0);
163
+ * const double = createMemo(() => count.get() * 2);
164
+ *
165
+ * batch(() => {
166
+ * count.set(1);
167
+ * count.set(2);
168
+ * count.set(3);
169
+ * // Effects run only once at the end with count = 3
170
+ * });
171
+ * ```
172
+ */
173
+ declare function batch(fn: () => void): void;
174
+ /**
175
+ * Runs a callback without tracking dependencies.
176
+ * Any signal reads inside the callback will not create edges to the current active sink.
177
+ *
178
+ * @param fn - The function to execute without tracking
179
+ * @returns The return value of the function
180
+ *
181
+ * @example
182
+ * ```ts
183
+ * const count = createState(0);
184
+ * const label = createState('Count');
185
+ *
186
+ * createEffect(() => {
187
+ * // Only re-runs when count changes, not when label changes
188
+ * const name = untrack(() => label.get());
189
+ * console.log(`${name}: ${count.get()}`);
190
+ * });
191
+ * ```
192
+ */
193
+ declare function untrack<T>(fn: () => T): T;
194
+ /**
195
+ * Creates a new ownership scope for managing cleanup of nested effects and resources.
196
+ * All effects created within the scope will be automatically disposed when the scope is disposed.
197
+ * Scopes can be nested - disposing a parent scope disposes all child scopes.
198
+ *
199
+ * @param fn - The function to execute within the scope, may return a cleanup function
200
+ * @returns A dispose function that cleans up the scope
201
+ *
202
+ * @example
203
+ * ```ts
204
+ * const dispose = createScope(() => {
205
+ * const count = createState(0);
206
+ *
207
+ * createEffect(() => {
208
+ * console.log(count.get());
209
+ * });
210
+ *
211
+ * return () => console.log('Scope disposed');
212
+ * });
213
+ *
214
+ * dispose(); // Cleans up the effect and runs cleanup callbacks
215
+ * ```
216
+ */
217
+ declare function createScope(fn: () => MaybeCleanup): Cleanup;
218
+ export { type Cleanup, type ComputedOptions, type EffectCallback, type EffectNode, type MaybeCleanup, type MemoCallback, type MemoNode, type Scope, type Signal, type SignalOptions, type SinkNode, type StateNode, type TaskCallback, type TaskNode, activeOwner, activeSink, batch, batchDepth, createScope, DEFAULT_EQUALITY, SKIP_EQUALITY, FLAG_CLEAN, FLAG_DIRTY, flush, link, propagate, refresh, registerCleanup, runCleanup, runEffect, setState, trimSources, TYPE_COLLECTION, TYPE_LIST, TYPE_MEMO, TYPE_SENSOR, TYPE_STATE, TYPE_STORE, TYPE_TASK, unlink, untrack, };
@@ -0,0 +1,69 @@
1
+ import { type Cleanup, type Signal } from '../graph';
2
+ import { type KeyConfig, type List } from './list';
3
+ type CollectionSource<T extends {}> = List<T> | Collection<T>;
4
+ type DeriveCollectionCallback<T extends {}, U extends {}> = ((sourceValue: U) => T) | ((sourceValue: U, abort: AbortSignal) => Promise<T>);
5
+ type Collection<T extends {}> = {
6
+ readonly [Symbol.toStringTag]: 'Collection';
7
+ readonly [Symbol.isConcatSpreadable]: true;
8
+ [Symbol.iterator](): IterableIterator<Signal<T>>;
9
+ keys(): IterableIterator<string>;
10
+ get(): T[];
11
+ at(index: number): Signal<T> | undefined;
12
+ byKey(key: string): Signal<T> | undefined;
13
+ keyAt(index: number): string | undefined;
14
+ indexOfKey(key: string): number;
15
+ deriveCollection<R extends {}>(callback: (sourceValue: T) => R): Collection<R>;
16
+ deriveCollection<R extends {}>(callback: (sourceValue: T, abort: AbortSignal) => Promise<R>): Collection<R>;
17
+ readonly length: number;
18
+ };
19
+ type CollectionChanges<T> = {
20
+ add?: T[];
21
+ change?: T[];
22
+ remove?: T[];
23
+ };
24
+ type CollectionOptions<T extends {}> = {
25
+ value?: T[];
26
+ keyConfig?: KeyConfig<T>;
27
+ createItem?: (value: T) => Signal<T>;
28
+ };
29
+ type CollectionCallback<T extends {}> = (apply: (changes: CollectionChanges<T>) => void) => Cleanup;
30
+ /**
31
+ * Creates a derived Collection from a List or another Collection with item-level memoization.
32
+ * Sync callbacks use createMemo, async callbacks use createTask.
33
+ * Structural changes are tracked reactively via the source's keys.
34
+ *
35
+ * @since 0.18.0
36
+ * @param source - The source List or Collection to derive from
37
+ * @param callback - Transformation function applied to each item
38
+ * @returns A Collection signal
39
+ */
40
+ declare function deriveCollection<T extends {}, U extends {}>(source: CollectionSource<U>, callback: (sourceValue: U) => T): Collection<T>;
41
+ declare function deriveCollection<T extends {}, U extends {}>(source: CollectionSource<U>, callback: (sourceValue: U, abort: AbortSignal) => Promise<T>): Collection<T>;
42
+ /**
43
+ * Creates an externally-driven Collection with a watched lifecycle.
44
+ * Items are managed via the `applyChanges(changes)` helper passed to the watched callback.
45
+ * The collection activates when first accessed by an effect and deactivates when no longer watched.
46
+ *
47
+ * @since 0.18.0
48
+ * @param watched - Callback invoked when the collection starts being watched, receives applyChanges helper
49
+ * @param options - Optional configuration including initial value, key generation, and item signal creation
50
+ * @returns A read-only Collection signal
51
+ */
52
+ declare function createCollection<T extends {}>(watched: CollectionCallback<T>, options?: CollectionOptions<T>): Collection<T>;
53
+ /**
54
+ * Checks if a value is a Collection signal.
55
+ *
56
+ * @since 0.17.2
57
+ * @param value - The value to check
58
+ * @returns True if the value is a Collection
59
+ */
60
+ declare function isCollection<T extends {}>(value: unknown): value is Collection<T>;
61
+ /**
62
+ * Checks if a value is a valid Collection source (List or Collection).
63
+ *
64
+ * @since 0.17.2
65
+ * @param value - The value to check
66
+ * @returns True if the value is a List or Collection
67
+ */
68
+ declare function isCollectionSource<T extends {}>(value: unknown): value is CollectionSource<T>;
69
+ export { createCollection, deriveCollection, isCollection, isCollectionSource, type Collection, type CollectionCallback, type CollectionChanges, type CollectionOptions, type CollectionSource, type DeriveCollectionCallback, };
@@ -0,0 +1,48 @@
1
+ import { type Cleanup, type EffectCallback, type MaybeCleanup, type Signal } from '../graph';
2
+ type MaybePromise<T> = T | Promise<T>;
3
+ type MatchHandlers<T extends Signal<unknown & {}>[]> = {
4
+ ok: (values: {
5
+ [K in keyof T]: T[K] extends Signal<infer V> ? V : never;
6
+ }) => MaybePromise<MaybeCleanup>;
7
+ err?: (errors: readonly Error[]) => MaybePromise<MaybeCleanup>;
8
+ nil?: () => MaybePromise<MaybeCleanup>;
9
+ };
10
+ /**
11
+ * Creates a reactive effect that automatically runs when its dependencies change.
12
+ * Effects run immediately upon creation and re-run when any tracked signal changes.
13
+ * Effects are executed during the flush phase, after all updates have been batched.
14
+ *
15
+ * @since 0.1.0
16
+ * @param fn - The effect function that can track dependencies and register cleanup callbacks
17
+ * @returns A cleanup function that can be called to dispose of the effect
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * const count = createState(0);
22
+ * const dispose = createEffect(() => {
23
+ * console.log('Count is:', count.get());
24
+ * });
25
+ *
26
+ * count.set(1); // Logs: "Count is: 1"
27
+ * dispose(); // Stop the effect
28
+ * ```
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * // With cleanup
33
+ * createEffect(() => {
34
+ * const timer = setInterval(() => console.log(count.get()), 1000);
35
+ * return () => clearInterval(timer);
36
+ * });
37
+ * ```
38
+ */
39
+ declare function createEffect(fn: EffectCallback): Cleanup;
40
+ /**
41
+ * Runs handlers based on the current values of signals.
42
+ * Must be called within an active owner (effect or scope) so async cleanup can be registered.
43
+ *
44
+ * @since 0.15.0
45
+ * @throws RequiredOwnerError If called without an active owner.
46
+ */
47
+ declare function match<T extends Signal<unknown & {}>[]>(signals: T, handlers: MatchHandlers<T>): MaybeCleanup;
48
+ export { type MaybePromise, type MatchHandlers, createEffect, match };
@@ -0,0 +1,66 @@
1
+ import { type Cleanup, TYPE_LIST } from '../graph';
2
+ import { type Collection } from './collection';
3
+ import { type State } from './state';
4
+ type UnknownRecord = Record<string, unknown>;
5
+ type DiffResult = {
6
+ changed: boolean;
7
+ add: UnknownRecord;
8
+ change: UnknownRecord;
9
+ remove: UnknownRecord;
10
+ };
11
+ type KeyConfig<T> = string | ((item: T) => string | undefined);
12
+ type ListOptions<T extends {}> = {
13
+ keyConfig?: KeyConfig<T>;
14
+ watched?: () => Cleanup;
15
+ };
16
+ type List<T extends {}> = {
17
+ readonly [Symbol.toStringTag]: 'List';
18
+ readonly [Symbol.isConcatSpreadable]: true;
19
+ [Symbol.iterator](): IterableIterator<State<T>>;
20
+ readonly length: number;
21
+ get(): T[];
22
+ set(next: T[]): void;
23
+ update(fn: (prev: T[]) => T[]): void;
24
+ at(index: number): State<T> | undefined;
25
+ keys(): IterableIterator<string>;
26
+ byKey(key: string): State<T> | undefined;
27
+ keyAt(index: number): string | undefined;
28
+ indexOfKey(key: string): number;
29
+ add(value: T): string;
30
+ remove(keyOrIndex: string | number): void;
31
+ sort(compareFn?: (a: T, b: T) => number): void;
32
+ splice(start: number, deleteCount?: number, ...items: T[]): T[];
33
+ deriveCollection<R extends {}>(callback: (sourceValue: T) => R): Collection<R>;
34
+ deriveCollection<R extends {}>(callback: (sourceValue: T, abort: AbortSignal) => Promise<R>): Collection<R>;
35
+ };
36
+ /**
37
+ * Checks if two values are equal with cycle detection
38
+ *
39
+ * @since 0.15.0
40
+ * @param a - First value to compare
41
+ * @param b - Second value to compare
42
+ * @param visited - Set to track visited objects for cycle detection
43
+ * @returns Whether the two values are equal
44
+ */
45
+ declare function isEqual<T>(a: T, b: T, visited?: WeakSet<object>): boolean;
46
+ /** Shallow equality check for string arrays */
47
+ declare function keysEqual(a: string[], b: string[]): boolean;
48
+ declare function getKeyGenerator<T extends {}>(keyConfig?: KeyConfig<T>): [(item: T) => string, boolean];
49
+ /**
50
+ * Creates a reactive list with stable keys and per-item reactivity.
51
+ *
52
+ * @since 0.18.0
53
+ * @param value - Initial array of items
54
+ * @param options - Optional configuration for key generation and watch lifecycle
55
+ * @returns A List signal
56
+ */
57
+ declare function createList<T extends {}>(value: T[], options?: ListOptions<T>): List<T>;
58
+ /**
59
+ * Checks if a value is a List signal.
60
+ *
61
+ * @since 0.15.0
62
+ * @param value - The value to check
63
+ * @returns True if the value is a List
64
+ */
65
+ declare function isList<T extends {}>(value: unknown): value is List<T>;
66
+ export { type DiffResult, type KeyConfig, type List, type ListOptions, type UnknownRecord, createList, isEqual, isList, getKeyGenerator, keysEqual, TYPE_LIST, };
@@ -0,0 +1,63 @@
1
+ import { type ComputedOptions, type MemoCallback } from '../graph';
2
+ /**
3
+ * A derived reactive computation that caches its result.
4
+ * Automatically tracks dependencies and recomputes when they change.
5
+ *
6
+ * @template T - The type of value computed by the memo
7
+ */
8
+ type Memo<T extends {}> = {
9
+ readonly [Symbol.toStringTag]: 'Memo';
10
+ /**
11
+ * Gets the current value of the memo.
12
+ * Recomputes if dependencies have changed since last access.
13
+ * When called inside another reactive context, creates a dependency.
14
+ * @returns The computed value
15
+ * @throws UnsetSignalValueError If the memo value is still unset when read.
16
+ */
17
+ get(): T;
18
+ };
19
+ /**
20
+ * Creates a derived reactive computation that caches its result.
21
+ * The computation automatically tracks dependencies and recomputes when they change.
22
+ * Uses lazy evaluation - only computes when the value is accessed.
23
+ *
24
+ * @since 0.18.0
25
+ * @template T - The type of value computed by the memo
26
+ * @param fn - The computation function that receives the previous value
27
+ * @param options - Optional configuration for the memo
28
+ * @param options.value - Optional initial value for reducer patterns
29
+ * @param options.equals - Optional equality function. Defaults to strict equality (`===`)
30
+ * @param options.guard - Optional type guard to validate values
31
+ * @param options.watched - Optional callback invoked when the memo is first watched by an effect.
32
+ * Receives an `invalidate` function to mark the memo dirty and trigger recomputation.
33
+ * Must return a cleanup function called when no effects are watching.
34
+ * @returns A Memo object with a get() method
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * const count = createState(0);
39
+ * const doubled = createMemo(() => count.get() * 2);
40
+ * console.log(doubled.get()); // 0
41
+ * count.set(5);
42
+ * console.log(doubled.get()); // 10
43
+ * ```
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * // Using previous value
48
+ * const sum = createMemo((prev) => prev + count.get(), { value: 0, equals: Object.is });
49
+ * ```
50
+ */
51
+ declare function createMemo<T extends {}>(fn: (prev: T) => T, options: ComputedOptions<T> & {
52
+ value: T;
53
+ }): Memo<T>;
54
+ declare function createMemo<T extends {}>(fn: MemoCallback<T>, options?: ComputedOptions<T>): Memo<T>;
55
+ /**
56
+ * Checks if a value is a Memo signal.
57
+ *
58
+ * @since 0.18.0
59
+ * @param value - The value to check
60
+ * @returns True if the value is a Memo
61
+ */
62
+ declare function isMemo<T extends {} = unknown & {}>(value: unknown): value is Memo<T>;
63
+ export { createMemo, isMemo, type Memo };