@zeix/cause-effect 0.17.2 → 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 (50) hide show
  1. package/.ai-context.md +11 -5
  2. package/.github/copilot-instructions.md +1 -1
  3. package/.zed/settings.json +3 -0
  4. package/CLAUDE.md +18 -79
  5. package/README.md +23 -37
  6. package/archive/benchmark.ts +0 -5
  7. package/archive/collection.ts +5 -62
  8. package/archive/composite.ts +85 -0
  9. package/archive/computed.ts +17 -20
  10. package/archive/list.ts +6 -67
  11. package/archive/memo.ts +13 -14
  12. package/archive/store.ts +7 -66
  13. package/archive/task.ts +18 -20
  14. package/index.dev.js +438 -614
  15. package/index.js +1 -1
  16. package/index.ts +8 -19
  17. package/package.json +6 -6
  18. package/src/classes/collection.ts +59 -112
  19. package/src/classes/computed.ts +146 -189
  20. package/src/classes/list.ts +138 -105
  21. package/src/classes/ref.ts +16 -42
  22. package/src/classes/state.ts +16 -45
  23. package/src/classes/store.ts +107 -72
  24. package/src/effect.ts +9 -12
  25. package/src/errors.ts +12 -8
  26. package/src/signal.ts +3 -1
  27. package/src/system.ts +136 -154
  28. package/test/batch.test.ts +4 -11
  29. package/test/benchmark.test.ts +4 -2
  30. package/test/collection.test.ts +46 -306
  31. package/test/computed.test.ts +205 -223
  32. package/test/list.test.ts +35 -303
  33. package/test/ref.test.ts +38 -66
  34. package/test/state.test.ts +6 -12
  35. package/test/store.test.ts +37 -489
  36. package/test/util/dependency-graph.ts +2 -2
  37. package/tsconfig.build.json +11 -0
  38. package/tsconfig.json +5 -7
  39. package/types/index.d.ts +2 -2
  40. package/types/src/classes/collection.d.ts +4 -6
  41. package/types/src/classes/computed.d.ts +17 -37
  42. package/types/src/classes/list.d.ts +8 -6
  43. package/types/src/classes/ref.d.ts +7 -20
  44. package/types/src/classes/state.d.ts +5 -17
  45. package/types/src/classes/store.d.ts +12 -11
  46. package/types/src/errors.d.ts +2 -4
  47. package/types/src/signal.d.ts +3 -2
  48. package/types/src/system.d.ts +41 -44
  49. package/src/classes/composite.ts +0 -171
  50. package/types/src/classes/composite.d.ts +0 -15
@@ -1,8 +1,11 @@
1
- import { type Cleanup, type HookCallback, type WatchHook } from '../system';
1
+ import { type SignalOptions } from '../system';
2
2
  type Computed<T extends {}> = {
3
3
  readonly [Symbol.toStringTag]: 'Computed';
4
4
  get(): T;
5
5
  };
6
+ type ComputedOptions<T extends {}> = SignalOptions<T> & {
7
+ initialValue?: T;
8
+ };
6
9
  type MemoCallback<T extends {} & {
7
10
  then?: undefined;
8
11
  }> = (oldValue: T) => T;
@@ -14,18 +17,14 @@ declare const TYPE_COMPUTED: "Computed";
14
17
  * Create a new memoized signal for a synchronous function.
15
18
  *
16
19
  * @since 0.17.0
20
+ * @param {MemoCallback<T>} callback - Callback function to compute the memoized value
21
+ * @param {T} [initialValue = UNSET] - Initial value of the signal
22
+ * @throws {InvalidCallbackError} If the callback is not an sync function
23
+ * @throws {InvalidSignalValueError} If the initial value is not valid
17
24
  */
18
25
  declare class Memo<T extends {}> {
19
26
  #private;
20
- /**
21
- * Create a new memoized signal.
22
- *
23
- * @param {MemoCallback<T>} callback - Callback function to compute the memoized value
24
- * @param {T} [initialValue = UNSET] - Initial value of the signal
25
- * @throws {InvalidCallbackError} If the callback is not an sync function
26
- * @throws {InvalidSignalValueError} If the initial value is not valid
27
- */
28
- constructor(callback: MemoCallback<T>, initialValue?: T);
27
+ constructor(callback: MemoCallback<T>, options?: ComputedOptions<T>);
29
28
  get [Symbol.toStringTag](): 'Computed';
30
29
  /**
31
30
  * Return the memoized value after computing it if necessary.
@@ -35,31 +34,19 @@ declare class Memo<T extends {}> {
35
34
  * @throws {Error} If an error occurs during computation
36
35
  */
37
36
  get(): T;
38
- /**
39
- * Register a callback to be called when HOOK_WATCH is triggered.
40
- *
41
- * @param {WatchHook} type - The type of hook to register the callback for; only HOOK_WATCH is supported
42
- * @param {HookCallback} callback - The callback to register
43
- * @returns {Cleanup} - A function to unregister the callback
44
- */
45
- on(type: WatchHook, callback: HookCallback): Cleanup;
46
37
  }
47
38
  /**
48
39
  * Create a new task signals that memoizes the result of an asynchronous function.
49
40
  *
50
41
  * @since 0.17.0
42
+ * @param {TaskCallback<T>} callback - The asynchronous function to compute the memoized value
43
+ * @param {T} [initialValue = UNSET] - Initial value of the signal
44
+ * @throws {InvalidCallbackError} If the callback is not an async function
45
+ * @throws {InvalidSignalValueError} If the initial value is not valid
51
46
  */
52
47
  declare class Task<T extends {}> {
53
48
  #private;
54
- /**
55
- * Create a new task signal for an asynchronous function.
56
- *
57
- * @param {TaskCallback<T>} callback - The asynchronous function to compute the memoized value
58
- * @param {T} [initialValue = UNSET] - Initial value of the signal
59
- * @throws {InvalidCallbackError} If the callback is not an async function
60
- * @throws {InvalidSignalValueError} If the initial value is not valid
61
- */
62
- constructor(callback: TaskCallback<T>, initialValue?: T);
49
+ constructor(callback: TaskCallback<T>, options?: ComputedOptions<T>);
63
50
  get [Symbol.toStringTag](): 'Computed';
64
51
  /**
65
52
  * Return the memoized value after executing the async function if necessary.
@@ -69,22 +56,15 @@ declare class Task<T extends {}> {
69
56
  * @throws {Error} If an error occurs during computation
70
57
  */
71
58
  get(): T;
72
- /**
73
- * Register a callback to be called when HOOK_WATCH is triggered.
74
- *
75
- * @param {WatchHook} type - The type of hook to register the callback for; only HOOK_WATCH is supported
76
- * @param {HookCallback} callback - The callback to register
77
- * @returns {Cleanup} - A function to unregister the callback
78
- */
79
- on(type: WatchHook, callback: HookCallback): Cleanup;
80
59
  }
81
60
  /**
82
61
  * Create a derived signal from existing signals
83
62
  *
84
63
  * @since 0.9.0
85
64
  * @param {MemoCallback<T> | TaskCallback<T>} callback - Computation callback function
65
+ * @param {ComputedOptions<T>} options - Optional configuration
86
66
  */
87
- declare const createComputed: <T extends {}>(callback: TaskCallback<T> | MemoCallback<T>, initialValue?: T) => Task<T> | Memo<T>;
67
+ declare const createComputed: <T extends {}>(callback: TaskCallback<T> | MemoCallback<T>, options?: ComputedOptions<T>) => Task<T> | Memo<T>;
88
68
  /**
89
69
  * Check if a value is a computed signal
90
70
  *
@@ -111,4 +91,4 @@ declare const isMemoCallback: <T extends {} & {
111
91
  * @returns {boolean} - True if value is an async callback, false otherwise
112
92
  */
113
93
  declare const isTaskCallback: <T extends {}>(value: unknown) => value is TaskCallback<T>;
114
- export { TYPE_COMPUTED, createComputed, isComputed, isMemoCallback, isTaskCallback, Memo, Task, type Computed, type MemoCallback, type TaskCallback, };
94
+ export { TYPE_COMPUTED, createComputed, isComputed, isMemoCallback, isTaskCallback, Memo, Task, type Computed, type ComputedOptions, type MemoCallback, type TaskCallback, };
@@ -1,15 +1,18 @@
1
1
  import { type UnknownArray } from '../diff';
2
- import { type Cleanup, type Hook, type HookCallback } from '../system';
2
+ import { type SignalOptions } from '../system';
3
3
  import { DerivedCollection } from './collection';
4
4
  import { State } from './state';
5
5
  type ArrayToRecord<T extends UnknownArray> = {
6
6
  [key: string]: T extends Array<infer U extends {}> ? U : never;
7
7
  };
8
8
  type KeyConfig<T> = string | ((item: T) => string);
9
+ type ListOptions<T extends {}> = SignalOptions<T> & {
10
+ keyConfig?: KeyConfig<T>;
11
+ };
9
12
  declare const TYPE_LIST: "List";
10
13
  declare class List<T extends {}> {
11
14
  #private;
12
- constructor(initialValue: T[], keyConfig?: KeyConfig<T>);
15
+ constructor(initialValue: T[], options?: ListOptions<T>);
13
16
  get [Symbol.toStringTag](): 'List';
14
17
  get [Symbol.isConcatSpreadable](): true;
15
18
  [Symbol.iterator](): IterableIterator<State<T>>;
@@ -26,9 +29,8 @@ declare class List<T extends {}> {
26
29
  remove(keyOrIndex: string | number): void;
27
30
  sort(compareFn?: (a: T, b: T) => number): void;
28
31
  splice(start: number, deleteCount?: number, ...items: T[]): T[];
29
- on(type: Hook, callback: HookCallback): Cleanup;
30
- deriveCollection<R extends {}>(callback: (sourceValue: T) => R): DerivedCollection<R, T>;
31
- deriveCollection<R extends {}>(callback: (sourceValue: T, abort: AbortSignal) => Promise<R>): DerivedCollection<R, T>;
32
+ deriveCollection<R extends {}>(callback: (sourceValue: T) => R, options?: SignalOptions<R[]>): DerivedCollection<R, T>;
33
+ deriveCollection<R extends {}>(callback: (sourceValue: T, abort: AbortSignal) => Promise<R>, options?: SignalOptions<R[]>): DerivedCollection<R, T>;
32
34
  }
33
35
  /**
34
36
  * Check if the provided value is a List instance
@@ -38,4 +40,4 @@ declare class List<T extends {}> {
38
40
  * @returns {boolean} - True if the value is a List instance, false otherwise
39
41
  */
40
42
  declare const isList: <T extends {}>(value: unknown) => value is List<T>;
41
- export { isList, List, TYPE_LIST, type ArrayToRecord, type KeyConfig };
43
+ export { isList, List, TYPE_LIST, type ArrayToRecord, type KeyConfig, type ListOptions, };
@@ -1,22 +1,17 @@
1
- import { type Guard } from '../errors';
2
- import { type Cleanup, type HookCallback, type WatchHook } from '../system';
1
+ import { type SignalOptions } from '../system';
3
2
  declare const TYPE_REF = "Ref";
4
3
  /**
5
4
  * Create a new ref signal.
6
5
  *
7
6
  * @since 0.17.1
7
+ * @param {T} value - Reference to external object
8
+ * @param {Guard<T>} guard - Optional guard function to validate the value
9
+ * @throws {NullishSignalValueError} - If the value is null or undefined
10
+ * @throws {InvalidSignalValueError} - If the value is invalid
8
11
  */
9
12
  declare class Ref<T extends {}> {
10
13
  #private;
11
- /**
12
- * Create a new ref signal.
13
- *
14
- * @param {T} value - Reference to external object
15
- * @param {Guard<T>} guard - Optional guard function to validate the value
16
- * @throws {NullishSignalValueError} - If the value is null or undefined
17
- * @throws {InvalidSignalValueError} - If the value is invalid
18
- */
19
- constructor(value: T, guard?: Guard<T>);
14
+ constructor(value: T, options?: SignalOptions<T>);
20
15
  get [Symbol.toStringTag](): string;
21
16
  /**
22
17
  * Get the value of the ref signal.
@@ -28,21 +23,13 @@ declare class Ref<T extends {}> {
28
23
  * Notify watchers of relevant changes in the external reference.
29
24
  */
30
25
  notify(): void;
31
- /**
32
- * Register a callback to be called when HOOK_WATCH is triggered.
33
- *
34
- * @param {WatchHook} type - The type of hook to register the callback for; only HOOK_WATCH is supported
35
- * @param {HookCallback} callback - The callback to register
36
- * @returns {Cleanup} - A function to unregister the callback
37
- */
38
- on(type: WatchHook, callback: HookCallback): Cleanup;
39
26
  }
40
27
  /**
41
28
  * Check if the provided value is a Ref instance
42
29
  *
43
30
  * @since 0.17.1
44
31
  * @param {unknown} value - Value to check
45
- * @returns {boolean} - True if the value is a Ref instance, false otherwise
32
+ * @returns {boolean} - Whether the value is a Ref instance
46
33
  */
47
34
  declare const isRef: <T extends {}>(value: unknown) => value is Ref<T>;
48
35
  export { TYPE_REF, Ref, isRef };
@@ -1,20 +1,16 @@
1
- import { type Cleanup, type HookCallback, type WatchHook } from '../system';
1
+ import { type SignalOptions } from '../system';
2
2
  declare const TYPE_STATE: "State";
3
3
  /**
4
4
  * Create a new state signal.
5
5
  *
6
6
  * @since 0.17.0
7
+ * @param {T} initialValue - Initial value of the state
8
+ * @throws {NullishSignalValueError} - If the initial value is null or undefined
9
+ * @throws {InvalidSignalValueError} - If the initial value is invalid
7
10
  */
8
11
  declare class State<T extends {}> {
9
12
  #private;
10
- /**
11
- * Create a new state signal.
12
- *
13
- * @param {T} initialValue - Initial value of the state
14
- * @throws {NullishSignalValueError} - If the initial value is null or undefined
15
- * @throws {InvalidSignalValueError} - If the initial value is invalid
16
- */
17
- constructor(initialValue: T);
13
+ constructor(initialValue: T, options?: SignalOptions<T>);
18
14
  get [Symbol.toStringTag](): string;
19
15
  /**
20
16
  * Get the current value of the state signal.
@@ -41,14 +37,6 @@ declare class State<T extends {}> {
41
37
  * @throws {InvalidSignalValueError} - If the initial value is invalid
42
38
  */
43
39
  update(updater: (oldValue: T) => T): void;
44
- /**
45
- * Register a callback to be called when HOOK_WATCH is triggered.
46
- *
47
- * @param {WatchHook} type - The type of hook to register the callback for; only HOOK_WATCH is supported
48
- * @param {HookCallback} callback - The callback to register
49
- * @returns {Cleanup} - A function to unregister the callback
50
- */
51
- on(type: WatchHook, callback: HookCallback): Cleanup;
52
40
  }
53
41
  /**
54
42
  * Check if the provided value is a State instance
@@ -1,22 +1,23 @@
1
1
  import { type UnknownRecord } from '../diff';
2
2
  import { type MutableSignal } from '../signal';
3
- import { type Cleanup, type Hook, type HookCallback } from '../system';
3
+ import { type SignalOptions } from '../system';
4
4
  import type { List } from './list';
5
5
  import type { State } from './state';
6
6
  type Store<T extends UnknownRecord> = BaseStore<T> & {
7
7
  [K in keyof T]: T[K] extends readonly (infer U extends {})[] ? List<U> : T[K] extends UnknownRecord ? Store<T[K]> : State<T[K] & {}>;
8
8
  };
9
9
  declare const TYPE_STORE: "Store";
10
+ /**
11
+ * Create a new store with the given initial value.
12
+ *
13
+ * @since 0.17.0
14
+ * @param {T} initialValue - The initial value of the store
15
+ * @throws {NullishSignalValueError} - If the initial value is null or undefined
16
+ * @throws {InvalidSignalValueError} - If the initial value is not an object
17
+ */
10
18
  declare class BaseStore<T extends UnknownRecord> {
11
19
  #private;
12
- /**
13
- * Create a new store with the given initial value.
14
- *
15
- * @param {T} initialValue - The initial value of the store
16
- * @throws {NullishSignalValueError} - If the initial value is null or undefined
17
- * @throws {InvalidSignalValueError} - If the initial value is not an object
18
- */
19
- constructor(initialValue: T);
20
+ constructor(initialValue: T, options?: SignalOptions<T>);
20
21
  get [Symbol.toStringTag](): 'Store';
21
22
  get [Symbol.isConcatSpreadable](): boolean;
22
23
  [Symbol.iterator](): IterableIterator<[
@@ -30,16 +31,16 @@ declare class BaseStore<T extends UnknownRecord> {
30
31
  update(fn: (oldValue: T) => T): void;
31
32
  add<K extends keyof T & string>(key: K, value: T[K]): K;
32
33
  remove(key: string): void;
33
- on(type: Hook, callback: HookCallback): Cleanup;
34
34
  }
35
35
  /**
36
36
  * Create a new store with deeply nested reactive properties
37
37
  *
38
38
  * @since 0.15.0
39
39
  * @param {T} initialValue - Initial object or array value of the store
40
+ * @param {SignalOptions<T>} options - Options for the store
40
41
  * @returns {Store<T>} - New store with reactive properties that preserves the original type T
41
42
  */
42
- declare const createStore: <T extends UnknownRecord>(initialValue: T) => Store<T>;
43
+ declare const createStore: <T extends UnknownRecord>(initialValue: T, options?: SignalOptions<T>) => Store<T>;
43
44
  /**
44
45
  * Check if the provided value is a Store instance
45
46
  *
@@ -12,9 +12,6 @@ declare class InvalidCallbackError extends TypeError {
12
12
  declare class InvalidCollectionSourceError extends TypeError {
13
13
  constructor(where: string, value: unknown);
14
14
  }
15
- declare class InvalidHookError extends TypeError {
16
- constructor(where: string, type: string);
17
- }
18
15
  declare class InvalidSignalValueError extends TypeError {
19
16
  constructor(where: string, value: unknown);
20
17
  }
@@ -24,8 +21,9 @@ declare class NullishSignalValueError extends TypeError {
24
21
  declare class ReadonlySignalError extends Error {
25
22
  constructor(what: string, value: unknown);
26
23
  }
24
+ declare function assert(condition: unknown, msg?: string): asserts condition;
27
25
  declare const createError: (reason: unknown) => Error;
28
26
  declare const validateCallback: (where: string, value: unknown, guard?: (value: unknown) => boolean) => void;
29
27
  declare const validateSignalValue: (where: string, value: unknown, guard?: (value: unknown) => boolean) => void;
30
28
  declare const guardMutableSignal: <T extends {}>(what: string, value: unknown, signal: unknown) => signal is MutableSignal<T>;
31
- export { type Guard, CircularDependencyError, DuplicateKeyError, InvalidCallbackError, InvalidCollectionSourceError, InvalidHookError, InvalidSignalValueError, NullishSignalValueError, ReadonlySignalError, createError, validateCallback, validateSignalValue, guardMutableSignal, };
29
+ export { type Guard, CircularDependencyError, DuplicateKeyError, InvalidCallbackError, InvalidCollectionSourceError, InvalidSignalValueError, NullishSignalValueError, ReadonlySignalError, assert, createError, validateCallback, validateSignalValue, guardMutableSignal, };
@@ -6,9 +6,10 @@ import type { UnknownRecord } from './diff';
6
6
  type Signal<T extends {}> = {
7
7
  get(): T;
8
8
  };
9
+ type UnknownSignal = Signal<unknown & {}>;
9
10
  type MutableSignal<T extends {}> = T extends readonly (infer U extends {})[] ? List<U> : T extends UnknownRecord ? Store<T> : State<T>;
10
11
  type ReadonlySignal<T extends {}> = Computed<T>;
11
- type UnknownSignalRecord = Record<string, Signal<unknown & {}>>;
12
+ type UnknownSignalRecord = Record<string, UnknownSignal>;
12
13
  type SignalValues<S extends UnknownSignalRecord> = {
13
14
  [K in keyof S]: S[K] extends Signal<infer T> ? T : never;
14
15
  };
@@ -47,4 +48,4 @@ declare function createMutableSignal<T extends {}>(value: readonly T[]): List<T>
47
48
  declare function createMutableSignal<T extends {}>(value: T[]): List<T>;
48
49
  declare function createMutableSignal<T extends UnknownRecord>(value: T): Store<T>;
49
50
  declare function createMutableSignal<T extends {}>(value: T): State<T>;
50
- export { createMutableSignal, createSignal, isMutableSignal, isSignal, type MutableSignal, type ReadonlySignal, type Signal, type SignalValues, type UnknownSignalRecord, };
51
+ export { createMutableSignal, createSignal, isMutableSignal, isSignal, type MutableSignal, type ReadonlySignal, type Signal, type SignalValues, type UnknownSignal, type UnknownSignalRecord, };
@@ -1,58 +1,71 @@
1
+ import { type Guard } from './errors';
2
+ import type { UnknownSignal } from './signal';
1
3
  type Cleanup = () => void;
2
4
  type MaybeCleanup = Cleanup | undefined | void;
3
- type Hook = 'add' | 'change' | 'cleanup' | 'remove' | 'sort' | 'watch';
4
- type CleanupHook = 'cleanup';
5
- type WatchHook = 'watch';
6
- type HookCallback = (payload?: readonly string[]) => MaybeCleanup;
7
- type HookCallbacks = {
8
- [K in Hook]?: Set<HookCallback>;
9
- };
10
5
  type Watcher = {
11
6
  (): void;
12
- on(type: CleanupHook, cleanup: Cleanup): void;
7
+ run(): void;
8
+ onCleanup(cleanup: Cleanup): void;
13
9
  stop(): void;
14
10
  };
11
+ type SignalOptions<T extends unknown & {}> = {
12
+ guard?: Guard<T>;
13
+ watched?: () => void;
14
+ unwatched?: () => void;
15
+ };
15
16
  declare const UNSET: any;
16
- declare const HOOK_ADD = "add";
17
- declare const HOOK_CHANGE = "change";
18
- declare const HOOK_CLEANUP = "cleanup";
19
- declare const HOOK_REMOVE = "remove";
20
- declare const HOOK_SORT = "sort";
21
- declare const HOOK_WATCH = "watch";
22
17
  /**
23
- * Create a watcher to observe changes to a signal.
18
+ * Create a watcher to observe changes in signals.
24
19
  *
25
- * A watcher is a reaction function with onCleanup and stop methods
20
+ * A watcher combines push and pull reaction functions with onCleanup and stop methods
26
21
  *
27
- * @since 0.14.1
28
- * @param {() => void} react - Function to be called when the state changes
22
+ * @since 0.17.3
23
+ * @param {() => void} push - Function to be called when the state changes (push)
24
+ * @param {() => void} pull - Function to be called on demand from consumers (pull)
29
25
  * @returns {Watcher} - Watcher object with off and cleanup methods
30
26
  */
31
- declare const createWatcher: (react: () => void) => Watcher;
27
+ declare const createWatcher: (push: () => void, pull: () => void) => Watcher;
28
+ /**
29
+ * Run a function with signal reads in a non-tracking context.
30
+ *
31
+ * @param {() => void} callback - Callback
32
+ */
33
+ declare const untrack: (callback: () => void) => void;
34
+ declare const registerWatchCallbacks: (signal: UnknownSignal, watched: () => void, unwatched?: () => void) => void;
35
+ /**
36
+ * Subscribe active watcher to a signal.
37
+ *
38
+ * @param {UnknownSignal} signal - Signal to subscribe to
39
+ * @returns {boolean} - true if the active watcher was subscribed,
40
+ * false if the watcher was already subscribed or there was no active watcher
41
+ */
42
+ declare const subscribeTo: (signal: UnknownSignal) => boolean;
43
+ declare const subscribeActiveWatcher: (watchers: Set<Watcher>) => boolean;
32
44
  /**
33
- * Subscribe by adding active watcher to the Set of watchers of a signal.
45
+ * Unsubscribe all watchers from a signal so it can be garbage collected.
34
46
  *
35
- * @param {Set<Watcher>} watchers - Watchers of the signal
36
- * @param {Set<HookCallback>} watchHookCallbacks - HOOK_WATCH callbacks of the signal
47
+ * @param {UnknownSignal} signal - Signal to unsubscribe from
48
+ * @returns {void}
37
49
  */
38
- declare const subscribeActiveWatcher: (watchers: Set<Watcher>, watchHookCallbacks?: Set<HookCallback>) => void;
50
+ declare const unsubscribeAllFrom: (signal: UnknownSignal) => void;
39
51
  /**
40
52
  * Notify watchers of a signal change.
41
53
  *
42
- * @param {Set<Watcher>} watchers - Watchers of the signal
54
+ * @param {UnknownSignal} signal - Signal to notify watchers of
43
55
  * @returns {boolean} - Whether any watchers were notified
44
56
  */
57
+ declare const notifyOf: (signal: UnknownSignal) => boolean;
45
58
  declare const notifyWatchers: (watchers: Set<Watcher>) => boolean;
46
59
  /**
47
60
  * Flush all pending reactions of enqueued watchers.
48
61
  */
49
- declare const flushPendingReactions: () => void;
62
+ declare const flush: () => void;
50
63
  /**
51
64
  * Batch multiple signal writes.
52
65
  *
53
66
  * @param {() => void} callback - Function with multiple signal writes to be batched
54
67
  */
55
- declare const batchSignalWrites: (callback: () => void) => void;
68
+ declare const batch: (callback: () => void) => void;
56
69
  /**
57
70
  * Run a function with signal reads in a tracking context (or temporarily untrack).
58
71
  *
@@ -61,21 +74,5 @@ declare const batchSignalWrites: (callback: () => void) => void;
61
74
  * that might read signals (e.g., Web Components)
62
75
  * @param {() => void} run - Function to run the computation or effect
63
76
  */
64
- declare const trackSignalReads: (watcher: Watcher | false, run: () => void) => void;
65
- /**
66
- * Trigger a hook.
67
- *
68
- * @param {Set<HookCallback> | undefined} callbacks - Callbacks to be called when the hook is triggered
69
- * @param {readonly string[] | undefined} payload - Payload to be sent to listeners
70
- * @return {Cleanup | undefined} Cleanup function to be called when the hook is unmounted
71
- */
72
- declare const triggerHook: (callbacks: Set<HookCallback> | undefined, payload?: readonly string[]) => Cleanup | undefined;
73
- /**
74
- * Check whether a hook type is handled in a signal.
75
- *
76
- * @param {Hook} type - Type of hook to check
77
- * @param {T} handled - List of handled hook types
78
- * @returns {type is T[number]} - Whether the hook type is handled
79
- */
80
- declare const isHandledHook: <T extends readonly Hook[]>(type: Hook, handled: T) => type is T[number];
81
- export { type Cleanup, type MaybeCleanup, type Watcher, type Hook, type CleanupHook, type WatchHook, type HookCallback, type HookCallbacks, HOOK_ADD, HOOK_CHANGE, HOOK_CLEANUP, HOOK_REMOVE, HOOK_SORT, HOOK_WATCH, UNSET, createWatcher, subscribeActiveWatcher, notifyWatchers, flushPendingReactions, batchSignalWrites, trackSignalReads, triggerHook, isHandledHook, };
77
+ declare const track: (watcher: Watcher | false, run: () => void) => void;
78
+ export { type Cleanup, type MaybeCleanup, type Watcher, type SignalOptions, UNSET, createWatcher, registerWatchCallbacks, subscribeTo, subscribeActiveWatcher, unsubscribeAllFrom, notifyOf, notifyWatchers, flush, batch, track, untrack, };
@@ -1,171 +0,0 @@
1
- import type { DiffResult, UnknownRecord } from '../diff'
2
- import { guardMutableSignal, InvalidHookError } from '../errors'
3
- import type { Signal } from '../signal'
4
- import {
5
- batchSignalWrites,
6
- type Cleanup,
7
- createWatcher,
8
- HOOK_ADD,
9
- HOOK_CHANGE,
10
- HOOK_REMOVE,
11
- type HookCallback,
12
- type HookCallbacks,
13
- isHandledHook,
14
- trackSignalReads,
15
- triggerHook,
16
- type Watcher,
17
- } from '../system'
18
-
19
- /* === Types === */
20
-
21
- type CompositeHook = 'add' | 'change' | 'remove'
22
-
23
- /* === Class Definitions === */
24
-
25
- class Composite<T extends UnknownRecord, S extends Signal<T[keyof T] & {}>> {
26
- signals = new Map<string, S>()
27
- #validate: <K extends keyof T & string>(
28
- key: K,
29
- value: unknown,
30
- ) => value is T[K] & {}
31
- #create: <V extends T[keyof T] & {}>(value: V) => S
32
- #watchers = new Map<string, Watcher>()
33
- #hookCallbacks: HookCallbacks = {}
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
- triggerHook(this.#hookCallbacks.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.#hookCallbacks.change?.size) this.#addWatcher(key)
74
-
75
- if (!this.#batching) triggerHook(this.#hookCallbacks.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) triggerHook(this.#hookCallbacks.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
- triggerHook(this.#hookCallbacks.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
- triggerHook(this.#hookCallbacks.change, Object.keys(changes.change))
125
- }
126
-
127
- // Removals
128
- if (Object.keys(changes.remove).length) {
129
- for (const key in changes.remove)
130
- this.remove(key as keyof T & string)
131
- triggerHook(this.#hookCallbacks.remove, Object.keys(changes.remove))
132
- }
133
-
134
- this.#batching = false
135
- return changes.changed
136
- }
137
-
138
- clear(): boolean {
139
- const keys = Array.from(this.signals.keys())
140
- this.signals.clear()
141
- this.#watchers.clear()
142
- triggerHook(this.#hookCallbacks.remove, keys)
143
- return true
144
- }
145
-
146
- on(type: CompositeHook, callback: HookCallback): Cleanup {
147
- if (!isHandledHook(type, [HOOK_ADD, HOOK_CHANGE, HOOK_REMOVE]))
148
- throw new InvalidHookError('Composite', type)
149
-
150
- this.#hookCallbacks[type] ||= new Set()
151
- this.#hookCallbacks[type].add(callback)
152
- if (type === HOOK_CHANGE && !this.#watchers.size) {
153
- this.#batching = true
154
- for (const key of this.signals.keys()) this.#addWatcher(key)
155
- this.#batching = false
156
- }
157
-
158
- return () => {
159
- this.#hookCallbacks[type]?.delete(callback)
160
- if (type === HOOK_CHANGE && !this.#hookCallbacks.change?.size) {
161
- if (this.#watchers.size) {
162
- for (const watcher of this.#watchers.values())
163
- watcher.stop()
164
- this.#watchers.clear()
165
- }
166
- }
167
- }
168
- }
169
- }
170
-
171
- export { Composite, type CompositeHook as CompositeListeners }
@@ -1,15 +0,0 @@
1
- import type { DiffResult, UnknownRecord } from '../diff';
2
- import type { Signal } from '../signal';
3
- import { type Cleanup, type HookCallback } from '../system';
4
- type CompositeHook = '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(type: CompositeHook, callback: HookCallback): Cleanup;
14
- }
15
- export { Composite, type CompositeHook as CompositeListeners };