@zeix/cause-effect 0.15.1 → 0.16.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 (48) hide show
  1. package/.ai-context.md +254 -0
  2. package/.cursorrules +54 -0
  3. package/.github/copilot-instructions.md +132 -0
  4. package/CLAUDE.md +319 -0
  5. package/README.md +167 -159
  6. package/eslint.config.js +1 -1
  7. package/index.dev.js +528 -407
  8. package/index.js +1 -1
  9. package/index.ts +36 -25
  10. package/package.json +1 -1
  11. package/src/computed.ts +41 -30
  12. package/src/diff.ts +57 -44
  13. package/src/effect.ts +15 -16
  14. package/src/errors.ts +64 -0
  15. package/src/match.ts +2 -2
  16. package/src/resolve.ts +2 -2
  17. package/src/signal.ts +27 -49
  18. package/src/state.ts +27 -19
  19. package/src/store.ts +410 -209
  20. package/src/system.ts +122 -0
  21. package/src/util.ts +45 -6
  22. package/test/batch.test.ts +18 -11
  23. package/test/benchmark.test.ts +4 -4
  24. package/test/computed.test.ts +508 -72
  25. package/test/diff.test.ts +321 -4
  26. package/test/effect.test.ts +61 -61
  27. package/test/match.test.ts +38 -28
  28. package/test/resolve.test.ts +16 -16
  29. package/test/signal.test.ts +19 -147
  30. package/test/state.test.ts +212 -25
  31. package/test/store.test.ts +1370 -134
  32. package/test/util/dependency-graph.ts +1 -1
  33. package/types/index.d.ts +10 -9
  34. package/types/src/collection.d.ts +26 -0
  35. package/types/src/computed.d.ts +9 -9
  36. package/types/src/diff.d.ts +5 -3
  37. package/types/src/effect.d.ts +3 -3
  38. package/types/src/errors.d.ts +22 -0
  39. package/types/src/match.d.ts +1 -1
  40. package/types/src/resolve.d.ts +1 -1
  41. package/types/src/signal.d.ts +12 -19
  42. package/types/src/state.d.ts +5 -5
  43. package/types/src/store.d.ts +40 -36
  44. package/types/src/system.d.ts +44 -0
  45. package/types/src/util.d.ts +7 -5
  46. package/index.d.ts +0 -36
  47. package/src/scheduler.ts +0 -172
  48. package/types/test-new-effect.d.ts +0 -1
@@ -1,6 +1,6 @@
1
+ import { Random } from 'random'
1
2
  import type { TestConfig } from './framework-types'
2
3
  import type { Computed, ReactiveFramework, Signal } from './reactive-framework'
3
- import { Random } from 'random'
4
4
 
5
5
  export interface Graph {
6
6
  sources: Signal<number>[]
package/types/index.d.ts CHANGED
@@ -1,15 +1,16 @@
1
1
  /**
2
2
  * @name Cause & Effect
3
- * @version 0.15.1
3
+ * @version 0.16.0
4
4
  * @author Esther Brunner
5
5
  */
6
- export { type Computed, type ComputedCallback, computed, isComputed, isComputedCallback, TYPE_COMPUTED, } from './src/computed';
7
- export { type DiffResult, diff, isEqual, type UnknownRecord, type UnknownRecordOrArray, } from './src/diff';
8
- export { type EffectCallback, effect, type MaybeCleanup } from './src/effect';
6
+ export { type Computed, type ComputedCallback, createComputed, isComputed, isComputedCallback, TYPE_COMPUTED, } from './src/computed';
7
+ export { type DiffResult, diff, isEqual, type UnknownArray, type UnknownRecord, type UnknownRecordOrArray, } from './src/diff';
8
+ export { createEffect, type EffectCallback, type MaybeCleanup, } from './src/effect';
9
+ export { CircularDependencyError, InvalidCallbackError, InvalidSignalValueError, NullishSignalValueError, StoreKeyExistsError, StoreKeyRangeError, StoreKeyReadonlyError, } from './src/errors';
9
10
  export { type MatchHandlers, match } from './src/match';
10
11
  export { type ResolveResult, resolve } from './src/resolve';
11
- export { batch, type Cleanup, enqueue, flush, notify, observe, subscribe, type Updater, type Watcher, watch, } from './src/scheduler';
12
- export { isSignal, type MaybeSignal, type Signal, type SignalValues, toMutableSignal, toSignal, UNSET, type UnknownSignalRecord, } from './src/signal';
13
- export { isState, type State, state, TYPE_STATE } from './src/state';
14
- export { isStore, type Store, type StoreAddEvent, type StoreChangeEvent, type StoreEventMap, type StoreRemoveEvent, store, TYPE_STORE, } from './src/store';
15
- export { CircularDependencyError, isAbortError, isAsyncFunction, isFunction, isNumber, isString, toError, } from './src/util';
12
+ export { isMutableSignal, isSignal, type Signal, type SignalValues, toSignal, type UnknownSignalRecord, } from './src/signal';
13
+ export { createState, isState, type State, TYPE_STATE } from './src/state';
14
+ export { createStore, isStore, type Store, type StoreChanges, TYPE_STORE, } from './src/store';
15
+ export { batch, type Cleanup, createWatcher, flush, notify, observe, subscribe, type Watcher, } from './src/system';
16
+ export { isAbortError, isAsyncFunction, isFunction, isNumber, isRecord, isRecordOrArray, isString, isSymbol, toError, UNSET, valueString, } from './src/util';
@@ -0,0 +1,26 @@
1
+ import type { UnknownArray } from './diff';
2
+ import { type Store, type StoreChanges } from './store';
3
+ import { type Cleanup } from './system';
4
+ type Collection<T extends UnknownArray> = {
5
+ readonly [Symbol.toStringTag]: 'Collection';
6
+ get(): T;
7
+ on<K extends keyof StoreChanges<T>>(type: K, listener: (change: StoreChanges<T>[K]) => void): Cleanup;
8
+ };
9
+ type CollectionCallback<T extends UnknownArray> = (store: Store<T>) => T;
10
+ declare const TYPE_COLLECTION = "Collection";
11
+ /**
12
+ * Create a collection signal
13
+ *
14
+ * @param {CollectionCallback<T>} fn - callback function to create the collection
15
+ * @returns {Collection<T>} - collection signal
16
+ */
17
+ declare const createCollection: <T extends UnknownArray>(fn: CollectionCallback<T>) => Collection<T>;
18
+ /**
19
+ * Check if a value is a collection signal
20
+ *
21
+ * @since 0.16.0
22
+ * @param {unknown} value - value to check
23
+ * @returns {boolean} - true if value is a computed state, false otherwise
24
+ */
25
+ declare const isCollection: <T extends UnknownArray>(value: unknown) => value is Collection<T>;
26
+ export { TYPE_COLLECTION, createCollection, isCollection, type Collection };
@@ -1,33 +1,33 @@
1
1
  type Computed<T extends {}> = {
2
- [Symbol.toStringTag]: 'Computed';
2
+ readonly [Symbol.toStringTag]: 'Computed';
3
3
  get(): T;
4
4
  };
5
5
  type ComputedCallback<T extends {} & {
6
6
  then?: undefined;
7
- }> = ((abort: AbortSignal) => Promise<T>) | (() => T);
7
+ }> = ((oldValue: T, abort: AbortSignal) => Promise<T>) | ((oldValue: T) => T);
8
8
  declare const TYPE_COMPUTED = "Computed";
9
9
  /**
10
10
  * Create a derived signal from existing signals
11
11
  *
12
12
  * @since 0.9.0
13
- * @param {ComputedCallback<T>} fn - computation callback function
13
+ * @param {ComputedCallback<T>} callback - Computation callback function
14
14
  * @returns {Computed<T>} - Computed signal
15
15
  */
16
- declare const computed: <T extends {}>(fn: ComputedCallback<T>) => Computed<T>;
16
+ declare const createComputed: <T extends {}>(callback: ComputedCallback<T>, initialValue?: T) => Computed<T>;
17
17
  /**
18
- * Check if a value is a computed state
18
+ * Check if a value is a computed signal
19
19
  *
20
20
  * @since 0.9.0
21
- * @param {unknown} value - value to check
22
- * @returns {boolean} - true if value is a computed state, false otherwise
21
+ * @param {unknown} value - Value to check
22
+ * @returns {boolean} - true if value is a computed signal, false otherwise
23
23
  */
24
24
  declare const isComputed: <T extends {}>(value: unknown) => value is Computed<T>;
25
25
  /**
26
26
  * Check if the provided value is a callback that may be used as input for toSignal() to derive a computed state
27
27
  *
28
28
  * @since 0.12.0
29
- * @param {unknown} value - value to check
29
+ * @param {unknown} value - Value to check
30
30
  * @returns {boolean} - true if value is a callback or callbacks object, false otherwise
31
31
  */
32
32
  declare const isComputedCallback: <T extends {}>(value: unknown) => value is ComputedCallback<T>;
33
- export { TYPE_COMPUTED, computed, isComputed, isComputedCallback, type Computed, type ComputedCallback, };
33
+ export { TYPE_COMPUTED, createComputed, isComputed, isComputedCallback, type Computed, type ComputedCallback, };
@@ -1,7 +1,9 @@
1
1
  type UnknownRecord = Record<string, unknown & {}>;
2
- type UnknownRecordOrArray = {
3
- [x: string | number]: unknown & {};
2
+ type UnknownArray = ReadonlyArray<unknown & {}>;
3
+ type ArrayToRecord<T extends UnknownArray> = {
4
+ [key: string]: T extends Array<infer U extends {}> ? U : never;
4
5
  };
6
+ type UnknownRecordOrArray = UnknownRecord | ArrayToRecord<UnknownArray>;
5
7
  type DiffResult<T extends UnknownRecordOrArray = UnknownRecord> = {
6
8
  changed: boolean;
7
9
  add: Partial<T>;
@@ -27,4 +29,4 @@ declare const isEqual: <T>(a: T, b: T, visited?: WeakSet<object>) => boolean;
27
29
  * @returns {DiffResult<T>} The result of the comparison
28
30
  */
29
31
  declare const diff: <T extends UnknownRecordOrArray>(oldObj: T, newObj: T) => DiffResult<T>;
30
- export { type DiffResult, diff, isEqual, type UnknownRecord, type UnknownRecordOrArray, };
32
+ export { type ArrayToRecord, type DiffResult, diff, isEqual, type UnknownRecord, type UnknownArray, type UnknownRecordOrArray, };
@@ -1,4 +1,4 @@
1
- import { type Cleanup } from './scheduler';
1
+ import { type Cleanup } from './system';
2
2
  type MaybeCleanup = Cleanup | undefined | void;
3
3
  type EffectCallback = (() => MaybeCleanup) | ((abort: AbortSignal) => Promise<MaybeCleanup>);
4
4
  /**
@@ -12,5 +12,5 @@ type EffectCallback = (() => MaybeCleanup) | ((abort: AbortSignal) => Promise<Ma
12
12
  * @param {EffectCallback} callback - Synchronous or asynchronous effect callback
13
13
  * @returns {Cleanup} - Cleanup function for the effect
14
14
  */
15
- declare const effect: (callback: EffectCallback) => Cleanup;
16
- export { type MaybeCleanup, type EffectCallback, effect };
15
+ declare const createEffect: (callback: EffectCallback) => Cleanup;
16
+ export { type MaybeCleanup, type EffectCallback, createEffect };
@@ -0,0 +1,22 @@
1
+ declare class CircularDependencyError extends Error {
2
+ constructor(where: string);
3
+ }
4
+ declare class InvalidCallbackError extends TypeError {
5
+ constructor(where: string, value: string);
6
+ }
7
+ declare class InvalidSignalValueError extends TypeError {
8
+ constructor(where: string, value: string);
9
+ }
10
+ declare class NullishSignalValueError extends TypeError {
11
+ constructor(where: string);
12
+ }
13
+ declare class StoreKeyExistsError extends Error {
14
+ constructor(key: string, value: string);
15
+ }
16
+ declare class StoreKeyRangeError extends RangeError {
17
+ constructor(index: number);
18
+ }
19
+ declare class StoreKeyReadonlyError extends Error {
20
+ constructor(key: string, value: string);
21
+ }
22
+ export { CircularDependencyError, InvalidCallbackError, InvalidSignalValueError, NullishSignalValueError, StoreKeyExistsError, StoreKeyRangeError, StoreKeyReadonlyError, };
@@ -1,7 +1,7 @@
1
1
  import type { ResolveResult } from './resolve';
2
2
  import type { SignalValues, UnknownSignalRecord } from './signal';
3
3
  type MatchHandlers<S extends UnknownSignalRecord> = {
4
- ok?: (values: SignalValues<S>) => void;
4
+ ok: (values: SignalValues<S>) => void;
5
5
  err?: (errors: readonly Error[]) => void;
6
6
  nil?: () => void;
7
7
  };
@@ -1,4 +1,4 @@
1
- import { type SignalValues, type UnknownSignalRecord } from './signal';
1
+ import type { SignalValues, UnknownSignalRecord } from './signal';
2
2
  type ResolveResult<S extends UnknownSignalRecord> = {
3
3
  ok: true;
4
4
  values: SignalValues<S>;
@@ -4,14 +4,12 @@ import { type Store } from './store';
4
4
  type Signal<T extends {}> = {
5
5
  get(): T;
6
6
  };
7
- type MaybeSignal<T extends {}> = T | Signal<T> | ComputedCallback<T>;
8
7
  type UnknownSignalRecord = Record<string, Signal<unknown & {}>>;
9
8
  type SignalValues<S extends UnknownSignalRecord> = {
10
9
  [K in keyof S]: S[K] extends Signal<infer T> ? T : never;
11
10
  };
12
- declare const UNSET: any;
13
11
  /**
14
- * Check whether a value is a Signal or not
12
+ * Check whether a value is a Signal
15
13
  *
16
14
  * @since 0.9.0
17
15
  * @param {unknown} value - value to check
@@ -19,26 +17,21 @@ declare const UNSET: any;
19
17
  */
20
18
  declare const isSignal: <T extends {}>(value: unknown) => value is Signal<T>;
21
19
  /**
22
- * Convert a value to a Signal if it's not already a Signal
20
+ * Check whether a value is a State or Store
23
21
  *
24
- * @since 0.9.6
25
- * @param {T} value - value to convert
26
- * @returns {Signal<T>} - Signal instance
22
+ * @since 0.15.2
23
+ * @param {unknown} value - value to check
24
+ * @returns {boolean} - true if value is a State or Store, false otherwise
27
25
  */
28
- declare function toSignal<T extends {}>(value: T[]): Store<Record<string, T>>;
29
- declare function toSignal<T extends {}>(value: (() => T) | ((abort: AbortSignal) => Promise<T>)): Computed<T>;
30
- declare function toSignal<T extends {}>(value: T): T extends Store<infer U> ? Store<U> : T extends State<infer U> ? State<U> : T extends Computed<infer U> ? Computed<U> : T extends Signal<infer U> ? Signal<U> : T extends Record<string, unknown & {}> ? Store<{
31
- [K in keyof T]: T[K];
32
- }> : State<T>;
26
+ declare const isMutableSignal: <T extends {}>(value: unknown) => value is State<T> | Store<T>;
33
27
  /**
34
- * Convert a value to a mutable Signal if it's not already a Signal
28
+ * Convert a value to a Signal if it's not already a Signal
35
29
  *
36
- * @since 0.15.0
30
+ * @since 0.9.6
37
31
  * @param {T} value - value to convert
38
- * @returns {State<T> | Store<T>} - Signal instance
32
+ * @returns {Signal<T>} - Signal instance
39
33
  */
40
- declare function toMutableSignal<T extends {}>(value: T[]): Store<Record<string, T>>;
41
- declare function toMutableSignal<T extends {}>(value: T): T extends Store<infer U> ? Store<U> : T extends State<infer U> ? State<U> : T extends Record<string, unknown & {}> ? Store<{
34
+ declare function toSignal<T extends {}>(value: T): T extends Store<infer U> ? Store<U> : T extends State<infer U> ? State<U> : T extends Computed<infer U> ? Computed<U> : T extends Signal<infer U> ? Signal<U> : T extends ReadonlyArray<infer U extends {}> ? Store<U[]> : T extends Record<string, unknown & {}> ? Store<{
42
35
  [K in keyof T]: T[K];
43
- }> : State<T>;
44
- export { type Signal, type MaybeSignal, type UnknownSignalRecord, type SignalValues, UNSET, isSignal, toSignal, toMutableSignal, };
36
+ }> : T extends ComputedCallback<infer U extends {}> ? Computed<U> : State<T>;
37
+ export { type Signal, type UnknownSignalRecord, type SignalValues, isSignal, isMutableSignal, toSignal, };
@@ -1,8 +1,8 @@
1
1
  type State<T extends {}> = {
2
- [Symbol.toStringTag]: 'State';
2
+ readonly [Symbol.toStringTag]: 'State';
3
3
  get(): T;
4
- set(v: T): void;
5
- update(fn: (v: T) => T): void;
4
+ set(newValue: T): void;
5
+ update(updater: (oldValue: T) => T): void;
6
6
  };
7
7
  declare const TYPE_STATE = "State";
8
8
  /**
@@ -12,7 +12,7 @@ declare const TYPE_STATE = "State";
12
12
  * @param {T} initialValue - initial value of the state
13
13
  * @returns {State<T>} - new state signal
14
14
  */
15
- declare const state: <T extends {}>(initialValue: T) => State<T>;
15
+ declare const createState: <T extends {}>(initialValue: T) => State<T>;
16
16
  /**
17
17
  * Check if the provided value is a State instance
18
18
  *
@@ -21,4 +21,4 @@ declare const state: <T extends {}>(initialValue: T) => State<T>;
21
21
  * @returns {boolean} - true if the value is a State instance, false otherwise
22
22
  */
23
23
  declare const isState: <T extends {}>(value: unknown) => value is State<T>;
24
- export { TYPE_STATE, isState, state, type State };
24
+ export { TYPE_STATE, isState, createState, type State };
@@ -1,43 +1,47 @@
1
- import { type UnknownRecord, type UnknownRecordOrArray } from './diff';
2
- import { type Signal } from './signal';
1
+ import { type UnknownArray, type UnknownRecord, type UnknownRecordOrArray } from './diff';
3
2
  import { type State } from './state';
4
- declare const TYPE_STORE = "Store";
5
- interface StoreAddEvent<T extends UnknownRecordOrArray> extends CustomEvent {
6
- type: 'store-add';
7
- detail: Partial<T>;
8
- }
9
- interface StoreChangeEvent<T extends UnknownRecordOrArray> extends CustomEvent {
10
- type: 'store-change';
11
- detail: Partial<T>;
12
- }
13
- interface StoreRemoveEvent<T extends UnknownRecordOrArray> extends CustomEvent {
14
- type: 'store-remove';
15
- detail: Partial<T>;
16
- }
17
- type StoreEventMap<T extends UnknownRecordOrArray> = {
18
- 'store-add': StoreAddEvent<T>;
19
- 'store-change': StoreChangeEvent<T>;
20
- 'store-remove': StoreRemoveEvent<T>;
3
+ import { type Cleanup } from './system';
4
+ type ArrayItem<T> = T extends readonly (infer U extends {})[] ? U : never;
5
+ type StoreChanges<T> = {
6
+ add: Partial<T>;
7
+ change: Partial<T>;
8
+ remove: Partial<T>;
9
+ sort: string[];
21
10
  };
22
- interface StoreEventTarget<T extends UnknownRecordOrArray> extends EventTarget {
23
- addEventListener<K extends keyof StoreEventMap<T>>(type: K, listener: (event: StoreEventMap<T>[K]) => void, options?: boolean | AddEventListenerOptions): void;
24
- addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
25
- removeEventListener<K extends keyof StoreEventMap<T>>(type: K, listener: (event: StoreEventMap<T>[K]) => void, options?: boolean | EventListenerOptions): void;
26
- removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
27
- dispatchEvent(event: Event): boolean;
11
+ interface BaseStore {
12
+ readonly [Symbol.toStringTag]: 'Store';
13
+ readonly size: State<number>;
28
14
  }
29
- type Store<T extends UnknownRecordOrArray = UnknownRecord> = {
30
- [K in keyof T]: T[K] extends UnknownRecord ? Store<T[K]> : State<T[K]>;
31
- } & StoreEventTarget<T> & {
32
- [Symbol.toStringTag]: 'Store';
33
- [Symbol.iterator](): IterableIterator<[keyof T, Signal<T[keyof T]>]>;
34
- add<K extends keyof T>(key: K, value: T[K]): void;
15
+ type RecordStore<T extends UnknownRecord> = BaseStore & {
16
+ [K in keyof T]: T[K] extends readonly unknown[] | Record<string, unknown> ? Store<T[K]> : State<T[K]>;
17
+ } & {
18
+ [Symbol.iterator](): IterableIterator<[
19
+ Extract<keyof T, string>,
20
+ T[Extract<keyof T, string>] extends readonly unknown[] | Record<string, unknown> ? Store<T[Extract<keyof T, string>]> : State<T[Extract<keyof T, string>]>
21
+ ]>;
22
+ add<K extends Extract<keyof T, string>>(key: K, value: T[K]): void;
35
23
  get(): T;
36
- remove<K extends keyof T>(key: K): void;
37
24
  set(value: T): void;
38
- update(updater: (value: T) => T): void;
39
- size: State<number>;
25
+ update(fn: (value: T) => T): void;
26
+ sort<U = T[Extract<keyof T, string>]>(compareFn?: (a: U, b: U) => number): void;
27
+ on<K extends keyof StoreChanges<T>>(type: K, listener: (change: StoreChanges<T>[K]) => void): Cleanup;
28
+ remove<K extends Extract<keyof T, string>>(key: K): void;
40
29
  };
30
+ type ArrayStore<T extends UnknownArray> = BaseStore & {
31
+ [Symbol.iterator](): IterableIterator<ArrayItem<T> extends readonly unknown[] | Record<string, unknown> ? Store<ArrayItem<T>> : State<ArrayItem<T>>>;
32
+ readonly [Symbol.isConcatSpreadable]: boolean;
33
+ [n: number]: ArrayItem<T> extends readonly unknown[] | Record<string, unknown> ? Store<ArrayItem<T>> : State<ArrayItem<T>>;
34
+ add(value: ArrayItem<T>): void;
35
+ get(): T;
36
+ set(value: T): void;
37
+ update(fn: (value: T) => T): void;
38
+ sort<U = ArrayItem<T>>(compareFn?: (a: U, b: U) => number): void;
39
+ on<K extends keyof StoreChanges<T>>(type: K, listener: (change: StoreChanges<T>[K]) => void): Cleanup;
40
+ remove(index: number): void;
41
+ readonly length: number;
42
+ };
43
+ type Store<T extends UnknownRecord | UnknownArray> = T extends UnknownRecord ? RecordStore<T> : T extends UnknownArray ? ArrayStore<T> : never;
44
+ declare const TYPE_STORE = "Store";
41
45
  /**
42
46
  * Create a new store with deeply nested reactive properties
43
47
  *
@@ -50,7 +54,7 @@ type Store<T extends UnknownRecordOrArray = UnknownRecord> = {
50
54
  * @param {T} initialValue - initial object or array value of the store
51
55
  * @returns {Store<T>} - new store with reactive properties that preserves the original type T
52
56
  */
53
- declare const store: <T extends UnknownRecordOrArray>(initialValue: T) => Store<T>;
57
+ declare const createStore: <T extends UnknownRecord | UnknownArray>(initialValue: T) => Store<T>;
54
58
  /**
55
59
  * Check if the provided value is a Store instance
56
60
  *
@@ -59,4 +63,4 @@ declare const store: <T extends UnknownRecordOrArray>(initialValue: T) => Store<
59
63
  * @returns {boolean} - true if the value is a Store instance, false otherwise
60
64
  */
61
65
  declare const isStore: <T extends UnknownRecordOrArray>(value: unknown) => value is Store<T>;
62
- export { TYPE_STORE, isStore, store, type Store, type StoreAddEvent, type StoreChangeEvent, type StoreRemoveEvent, type StoreEventMap, };
66
+ export { TYPE_STORE, isStore, createStore, type Store, type StoreChanges };
@@ -0,0 +1,44 @@
1
+ type Cleanup = () => void;
2
+ type Watcher = {
3
+ (): void;
4
+ unwatch(cleanup: Cleanup): void;
5
+ cleanup(): void;
6
+ };
7
+ /**
8
+ * Create a watcher that can be used to observe changes to a signal
9
+ *
10
+ * @since 0.14.1
11
+ * @param {() => void} watch - Function to be called when the state changes
12
+ * @returns {Watcher} - Watcher object with off and cleanup methods
13
+ */
14
+ declare const createWatcher: (watch: () => void) => Watcher;
15
+ /**
16
+ * Add active watcher to the Set of watchers
17
+ *
18
+ * @param {Set<Watcher>} watchers - watchers of the signal
19
+ */
20
+ declare const subscribe: (watchers: Set<Watcher>) => void;
21
+ /**
22
+ * Add watchers to the pending set of change notifications
23
+ *
24
+ * @param {Set<Watcher>} watchers - watchers of the signal
25
+ */
26
+ declare const notify: (watchers: Set<Watcher>) => void;
27
+ /**
28
+ * Flush all pending changes to notify watchers
29
+ */
30
+ declare const flush: () => void;
31
+ /**
32
+ * Batch multiple changes in a single signal graph and DOM update cycle
33
+ *
34
+ * @param {() => void} fn - function with multiple signal writes to be batched
35
+ */
36
+ declare const batch: (fn: () => void) => void;
37
+ /**
38
+ * Run a function in a reactive context
39
+ *
40
+ * @param {() => void} run - function to run the computation or effect
41
+ * @param {Watcher} watcher - function to be called when the state changes or undefined for temporary unwatching while inserting auto-hydrating DOM nodes that might read signals (e.g., web components)
42
+ */
43
+ declare const observe: (run: () => void, watcher?: Watcher) => void;
44
+ export { type Cleanup, type Watcher, subscribe, notify, flush, batch, createWatcher, observe, };
@@ -1,14 +1,16 @@
1
+ declare const UNSET: any;
1
2
  declare const isString: (value: unknown) => value is string;
2
3
  declare const isNumber: (value: unknown) => value is number;
4
+ declare const isSymbol: (value: unknown) => value is symbol;
3
5
  declare const isFunction: <T>(fn: unknown) => fn is (...args: unknown[]) => T;
4
6
  declare const isAsyncFunction: <T>(fn: unknown) => fn is (...args: unknown[]) => Promise<T>;
5
7
  declare const isObjectOfType: <T>(value: unknown, type: string) => value is T;
6
8
  declare const isRecord: <T extends Record<string, unknown>>(value: unknown) => value is T;
7
- declare const validArrayIndexes: (keys: Array<PropertyKey>) => number[] | null;
9
+ declare const isRecordOrArray: <T extends Record<string | number, unknown> | ReadonlyArray<unknown>>(value: unknown) => value is T;
8
10
  declare const hasMethod: <T extends object & Record<string, (...args: unknown[]) => unknown>>(obj: T, methodName: string) => obj is T & Record<string, (...args: unknown[]) => unknown>;
9
11
  declare const isAbortError: (error: unknown) => boolean;
10
12
  declare const toError: (reason: unknown) => Error;
11
- declare class CircularDependencyError extends Error {
12
- constructor(where: string);
13
- }
14
- export { isString, isNumber, isFunction, isAsyncFunction, isObjectOfType, isRecord, validArrayIndexes, hasMethod, isAbortError, toError, CircularDependencyError, };
13
+ declare const arrayToRecord: <T>(array: T[]) => Record<string, T>;
14
+ declare const recordToArray: <T>(record: Record<string | number, T>) => Record<string, T> | T[];
15
+ declare const valueString: (value: unknown) => string;
16
+ export { UNSET, isString, isNumber, isSymbol, isFunction, isAsyncFunction, isObjectOfType, isRecord, isRecordOrArray, hasMethod, isAbortError, toError, arrayToRecord, recordToArray, valueString, };
package/index.d.ts DELETED
@@ -1,36 +0,0 @@
1
- /**
2
- * @name Cause & Effect
3
- * @version 0.14.2
4
- * @author Esther Brunner
5
- */
6
- export {
7
- type Computed,
8
- type ComputedCallback,
9
- computed,
10
- isComputed,
11
- TYPE_COMPUTED,
12
- } from './src/computed'
13
- export { type EffectMatcher, type MaybeCleanup, effect } from './src/effect'
14
- export {
15
- batch,
16
- type Cleanup,
17
- enqueue,
18
- flush,
19
- notify,
20
- observe,
21
- subscribe,
22
- type Updater,
23
- type Watcher,
24
- watch,
25
- } from './src/scheduler'
26
- export {
27
- isComputedCallback,
28
- isSignal,
29
- type MaybeSignal,
30
- type Signal,
31
- type SignalValues,
32
- toSignal,
33
- UNSET,
34
- } from './src/signal'
35
- export { isState, type State, state, TYPE_STATE } from './src/state'
36
- export { CircularDependencyError, isFunction } from './src/util'
package/src/scheduler.ts DELETED
@@ -1,172 +0,0 @@
1
- /* === Types === */
2
-
3
- type Cleanup = () => void
4
-
5
- type Watcher = {
6
- (): void
7
- off(cleanup: Cleanup): void
8
- cleanup(): void
9
- }
10
-
11
- type Updater = <T>() => T | boolean | undefined
12
-
13
- /* === Internal === */
14
-
15
- // Currently active watcher
16
- let active: Watcher | undefined
17
-
18
- // Pending queue for batched change notifications
19
- const pending = new Set<Watcher>()
20
- let batchDepth = 0
21
-
22
- // Map of deduplication symbols to update functions (using Symbol keys prevents unintended overwrites)
23
- const updateMap = new Map<symbol, Updater>()
24
- let requestId: number | undefined
25
-
26
- const updateDOM = () => {
27
- requestId = undefined
28
- const updates = Array.from(updateMap.values())
29
- updateMap.clear()
30
- for (const update of updates) {
31
- update()
32
- }
33
- }
34
-
35
- const requestTick = () => {
36
- if (requestId) cancelAnimationFrame(requestId)
37
- requestId = requestAnimationFrame(updateDOM)
38
- }
39
-
40
- // Initial render when the call stack is empty
41
- queueMicrotask(updateDOM)
42
-
43
- /* === Functions === */
44
-
45
- /**
46
- * Create a watcher that can be used to observe changes to a signal
47
- *
48
- * @since 0.14.1
49
- * @param {() => void} notice - function to be called when the state changes
50
- * @returns {Watcher} - watcher object with off and cleanup methods
51
- */
52
- const watch = (notice: () => void): Watcher => {
53
- const cleanups = new Set<Cleanup>()
54
- const w = notice as Partial<Watcher>
55
- w.off = (on: Cleanup) => {
56
- cleanups.add(on)
57
- }
58
- w.cleanup = () => {
59
- for (const cleanup of cleanups) {
60
- cleanup()
61
- }
62
- cleanups.clear()
63
- }
64
- return w as Watcher
65
- }
66
-
67
- /**
68
- * Add active watcher to the Set of watchers
69
- *
70
- * @param {Set<Watcher>} watchers - watchers of the signal
71
- */
72
- const subscribe = (watchers: Set<Watcher>) => {
73
- if (active && !watchers.has(active)) {
74
- const watcher = active
75
- watchers.add(watcher)
76
- active.off(() => {
77
- watchers.delete(watcher)
78
- })
79
- }
80
- }
81
-
82
- /**
83
- * Add watchers to the pending set of change notifications
84
- *
85
- * @param {Set<Watcher>} watchers - watchers of the signal
86
- */
87
- const notify = (watchers: Set<Watcher>) => {
88
- for (const watcher of watchers) {
89
- if (batchDepth) pending.add(watcher)
90
- else watcher()
91
- }
92
- }
93
-
94
- /**
95
- * Flush all pending changes to notify watchers
96
- */
97
- const flush = () => {
98
- while (pending.size) {
99
- const watchers = Array.from(pending)
100
- pending.clear()
101
- for (const watcher of watchers) {
102
- watcher()
103
- }
104
- }
105
- }
106
-
107
- /**
108
- * Batch multiple changes in a single signal graph and DOM update cycle
109
- *
110
- * @param {() => void} fn - function with multiple signal writes to be batched
111
- */
112
- const batch = (fn: () => void) => {
113
- batchDepth++
114
- try {
115
- fn()
116
- } finally {
117
- flush()
118
- batchDepth--
119
- }
120
- }
121
-
122
- /**
123
- * Run a function in a reactive context
124
- *
125
- * @param {() => void} run - function to run the computation or effect
126
- * @param {Watcher} watcher - function to be called when the state changes or undefined for temporary unwatching while inserting auto-hydrating DOM nodes that might read signals (e.g., web components)
127
- */
128
- const observe = (run: () => void, watcher?: Watcher): void => {
129
- const prev = active
130
- active = watcher
131
- try {
132
- run()
133
- } finally {
134
- active = prev
135
- }
136
- }
137
-
138
- /**
139
- * Enqueue a function to be executed on the next animation frame
140
- *
141
- * If the same Symbol is provided for multiple calls before the next animation frame,
142
- * only the latest call will be executed (deduplication).
143
- *
144
- * @param {Updater} fn - function to be executed on the next animation frame; can return updated value <T>, success <boolean> or void
145
- * @param {symbol} dedupe - Symbol for deduplication; if not provided, a unique Symbol is created ensuring the update is always executed
146
- */
147
- const enqueue = <T>(fn: Updater, dedupe?: symbol) =>
148
- new Promise<T | boolean | undefined>((resolve, reject) => {
149
- updateMap.set(dedupe || Symbol(), (): undefined => {
150
- try {
151
- resolve(fn())
152
- } catch (error) {
153
- reject(error)
154
- }
155
- })
156
- requestTick()
157
- })
158
-
159
- /* === Exports === */
160
-
161
- export {
162
- type Cleanup,
163
- type Watcher,
164
- type Updater,
165
- subscribe,
166
- notify,
167
- flush,
168
- batch,
169
- watch,
170
- observe,
171
- enqueue,
172
- }
@@ -1 +0,0 @@
1
- export {};