@zeix/cause-effect 0.14.2 → 0.15.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.
package/tsconfig.json CHANGED
@@ -7,28 +7,27 @@
7
7
  "moduleDetection": "force",
8
8
  "jsx": "react-jsx",
9
9
  "allowJs": true,
10
-
10
+
11
11
  // Bundler mode
12
12
  "moduleResolution": "bundler",
13
13
  "allowImportingTsExtensions": true,
14
14
  "verbatimModuleSyntax": true,
15
-
15
+
16
16
  // Best practices
17
17
  "strict": true,
18
18
  "skipLibCheck": true,
19
19
  "noFallthroughCasesInSwitch": true,
20
-
20
+
21
21
  // Some stricter flags (disabled by default)
22
22
  "noUnusedLocals": false,
23
23
  "noUnusedParameters": false,
24
24
  "noPropertyAccessFromIndexSignature": false,
25
-
25
+
26
26
  // Declarations
27
27
  "declaration": true,
28
- "declarationDir": "./",
29
- "emitDeclarationOnly": true,
28
+ "declarationDir": "./types",
29
+ "emitDeclarationOnly": true
30
30
  },
31
- "include": ["./*.ts", "./lib/*.ts"],
32
- "exclude": ["node_modules", "test"],
33
- }
34
-
31
+ "include": ["./*.ts", "./src/*.ts"],
32
+ "exclude": ["node_modules", "test", "types"]
33
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @name Cause & Effect
3
+ * @version 0.15.1
4
+ * @author Esther Brunner
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';
9
+ export { type MatchHandlers, match } from './src/match';
10
+ 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';
@@ -0,0 +1,30 @@
1
+ type UnknownRecord = Record<string, unknown & {}>;
2
+ type UnknownRecordOrArray = {
3
+ [x: string | number]: unknown & {};
4
+ };
5
+ type DiffResult<T extends UnknownRecordOrArray = UnknownRecord> = {
6
+ changed: boolean;
7
+ add: Partial<T>;
8
+ change: Partial<T>;
9
+ remove: Partial<T>;
10
+ };
11
+ /**
12
+ * Checks if two values are equal with cycle detection
13
+ *
14
+ * @since 0.15.0
15
+ * @param {T} a - First value to compare
16
+ * @param {T} b - Second value to compare
17
+ * @param {WeakSet<object>} visited - Set to track visited objects for cycle detection
18
+ * @returns {boolean} Whether the two values are equal
19
+ */
20
+ declare const isEqual: <T>(a: T, b: T, visited?: WeakSet<object>) => boolean;
21
+ /**
22
+ * Compares two records and returns a result object containing the differences.
23
+ *
24
+ * @since 0.15.0
25
+ * @param {T} oldObj - The old record to compare
26
+ * @param {T} newObj - The new record to compare
27
+ * @returns {DiffResult<T>} The result of the comparison
28
+ */
29
+ declare const diff: <T extends UnknownRecordOrArray>(oldObj: T, newObj: T) => DiffResult<T>;
30
+ export { type DiffResult, diff, isEqual, type UnknownRecord, type UnknownRecordOrArray, };
@@ -0,0 +1,16 @@
1
+ import { type Cleanup } from './scheduler';
2
+ type MaybeCleanup = Cleanup | undefined | void;
3
+ type EffectCallback = (() => MaybeCleanup) | ((abort: AbortSignal) => Promise<MaybeCleanup>);
4
+ /**
5
+ * Define what happens when a reactive state changes
6
+ *
7
+ * The callback can be synchronous or asynchronous. Async callbacks receive
8
+ * an AbortSignal parameter, which is automatically aborted when the effect
9
+ * re-runs or is cleaned up, preventing stale async operations.
10
+ *
11
+ * @since 0.1.0
12
+ * @param {EffectCallback} callback - Synchronous or asynchronous effect callback
13
+ * @returns {Cleanup} - Cleanup function for the effect
14
+ */
15
+ declare const effect: (callback: EffectCallback) => Cleanup;
16
+ export { type MaybeCleanup, type EffectCallback, effect };
@@ -0,0 +1,21 @@
1
+ import type { ResolveResult } from './resolve';
2
+ import type { SignalValues, UnknownSignalRecord } from './signal';
3
+ type MatchHandlers<S extends UnknownSignalRecord> = {
4
+ ok?: (values: SignalValues<S>) => void;
5
+ err?: (errors: readonly Error[]) => void;
6
+ nil?: () => void;
7
+ };
8
+ /**
9
+ * Match on resolve result and call appropriate handler for side effects
10
+ *
11
+ * This is a utility function for those who prefer the handler pattern.
12
+ * All handlers are for side effects only and return void. If you need
13
+ * cleanup logic, use a hoisted let variable in your effect.
14
+ *
15
+ * @since 0.15.0
16
+ * @param {ResolveResult<S>} result - Result from resolve()
17
+ * @param {MatchHandlers<S>} handlers - Handlers for different states (side effects only)
18
+ * @returns {void} - Always returns void
19
+ */
20
+ declare function match<S extends UnknownSignalRecord>(result: ResolveResult<S>, handlers: MatchHandlers<S>): void;
21
+ export { match, type MatchHandlers };
@@ -0,0 +1,29 @@
1
+ import { type SignalValues, type UnknownSignalRecord } from './signal';
2
+ type ResolveResult<S extends UnknownSignalRecord> = {
3
+ ok: true;
4
+ values: SignalValues<S>;
5
+ errors?: never;
6
+ pending?: never;
7
+ } | {
8
+ ok: false;
9
+ errors: readonly Error[];
10
+ values?: never;
11
+ pending?: never;
12
+ } | {
13
+ ok: false;
14
+ pending: true;
15
+ values?: never;
16
+ errors?: never;
17
+ };
18
+ /**
19
+ * Resolve signal values with perfect type inference
20
+ *
21
+ * Always returns a discriminated union result, regardless of whether
22
+ * handlers are provided or not. This ensures a predictable API.
23
+ *
24
+ * @since 0.15.0
25
+ * @param {S} signals - Signals to resolve
26
+ * @returns {ResolveResult<S>} - Discriminated union result
27
+ */
28
+ declare function resolve<S extends UnknownSignalRecord>(signals: S): ResolveResult<S>;
29
+ export { resolve, type ResolveResult };
@@ -0,0 +1,44 @@
1
+ import { type Computed, type ComputedCallback } from './computed';
2
+ import { type State } from './state';
3
+ import { type Store } from './store';
4
+ type Signal<T extends {}> = {
5
+ get(): T;
6
+ };
7
+ type MaybeSignal<T extends {}> = T | Signal<T> | ComputedCallback<T>;
8
+ type UnknownSignalRecord = Record<string, Signal<unknown & {}>>;
9
+ type SignalValues<S extends UnknownSignalRecord> = {
10
+ [K in keyof S]: S[K] extends Signal<infer T> ? T : never;
11
+ };
12
+ declare const UNSET: any;
13
+ /**
14
+ * Check whether a value is a Signal or not
15
+ *
16
+ * @since 0.9.0
17
+ * @param {unknown} value - value to check
18
+ * @returns {boolean} - true if value is a Signal, false otherwise
19
+ */
20
+ declare const isSignal: <T extends {}>(value: unknown) => value is Signal<T>;
21
+ /**
22
+ * Convert a value to a Signal if it's not already a Signal
23
+ *
24
+ * @since 0.9.6
25
+ * @param {T} value - value to convert
26
+ * @returns {Signal<T>} - Signal instance
27
+ */
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>;
33
+ /**
34
+ * Convert a value to a mutable Signal if it's not already a Signal
35
+ *
36
+ * @since 0.15.0
37
+ * @param {T} value - value to convert
38
+ * @returns {State<T> | Store<T>} - Signal instance
39
+ */
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<{
42
+ [K in keyof T]: T[K];
43
+ }> : State<T>;
44
+ export { type Signal, type MaybeSignal, type UnknownSignalRecord, type SignalValues, UNSET, isSignal, toSignal, toMutableSignal, };
@@ -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, TYPE_STATE, state, isState };
24
+ export { TYPE_STATE, isState, state, type State };
@@ -0,0 +1,62 @@
1
+ import { type UnknownRecord, type UnknownRecordOrArray } from './diff';
2
+ import { type Signal } from './signal';
3
+ 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>;
21
+ };
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;
28
+ }
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;
35
+ get(): T;
36
+ remove<K extends keyof T>(key: K): void;
37
+ set(value: T): void;
38
+ update(updater: (value: T) => T): void;
39
+ size: State<number>;
40
+ };
41
+ /**
42
+ * Create a new store with deeply nested reactive properties
43
+ *
44
+ * Supports both objects and arrays as initial values. Arrays are converted
45
+ * to records internally for storage but maintain their array type through
46
+ * the .get() method, which automatically converts objects with consecutive
47
+ * numeric keys back to arrays.
48
+ *
49
+ * @since 0.15.0
50
+ * @param {T} initialValue - initial object or array value of the store
51
+ * @returns {Store<T>} - new store with reactive properties that preserves the original type T
52
+ */
53
+ declare const store: <T extends UnknownRecordOrArray>(initialValue: T) => Store<T>;
54
+ /**
55
+ * Check if the provided value is a Store instance
56
+ *
57
+ * @since 0.15.0
58
+ * @param {unknown} value - value to check
59
+ * @returns {boolean} - true if the value is a Store instance, false otherwise
60
+ */
61
+ 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, };
@@ -0,0 +1,14 @@
1
+ declare const isString: (value: unknown) => value is string;
2
+ declare const isNumber: (value: unknown) => value is number;
3
+ declare const isFunction: <T>(fn: unknown) => fn is (...args: unknown[]) => T;
4
+ declare const isAsyncFunction: <T>(fn: unknown) => fn is (...args: unknown[]) => Promise<T>;
5
+ declare const isObjectOfType: <T>(value: unknown, type: string) => value is T;
6
+ declare const isRecord: <T extends Record<string, unknown>>(value: unknown) => value is T;
7
+ declare const validArrayIndexes: (keys: Array<PropertyKey>) => number[] | null;
8
+ declare const hasMethod: <T extends object & Record<string, (...args: unknown[]) => unknown>>(obj: T, methodName: string) => obj is T & Record<string, (...args: unknown[]) => unknown>;
9
+ declare const isAbortError: (error: unknown) => boolean;
10
+ 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, };
@@ -0,0 +1 @@
1
+ export {};
package/src/effect.d.ts DELETED
@@ -1,17 +0,0 @@
1
- import { type Cleanup } from './scheduler';
2
- import { type Signal, type SignalValues } from './signal';
3
- type EffectMatcher<S extends Signal<unknown & {}>[]> = {
4
- signals: S;
5
- ok: (...values: SignalValues<S>) => Cleanup | undefined;
6
- err?: (...errors: Error[]) => Cleanup | undefined;
7
- nil?: () => Cleanup | undefined;
8
- };
9
- /**
10
- * Define what happens when a reactive state changes
11
- *
12
- * @since 0.1.0
13
- * @param {EffectMatcher<S> | (() => Cleanup | undefined)} matcher - effect matcher or callback
14
- * @returns {Cleanup} - cleanup function for the effect
15
- */
16
- declare function effect<S extends Signal<unknown & {}>[]>(matcher: EffectMatcher<S> | (() => Cleanup | undefined)): Cleanup;
17
- export { type EffectMatcher, effect };
package/src/signal.d.ts DELETED
@@ -1,26 +0,0 @@
1
- import { type ComputedCallback, isComputedCallback } from './computed';
2
- type Signal<T extends {}> = {
3
- get(): T;
4
- };
5
- type MaybeSignal<T extends {}> = T | Signal<T> | ComputedCallback<T>;
6
- type SignalValues<S extends Signal<unknown & {}>[]> = {
7
- [K in keyof S]: S[K] extends Signal<infer T> ? T : never;
8
- };
9
- declare const UNSET: any;
10
- /**
11
- * Check whether a value is a Signal or not
12
- *
13
- * @since 0.9.0
14
- * @param {unknown} value - value to check
15
- * @returns {boolean} - true if value is a Signal, false otherwise
16
- */
17
- declare const isSignal: <T extends {}>(value: unknown) => value is Signal<T>;
18
- /**
19
- * Convert a value to a Signal if it's not already a Signal
20
- *
21
- * @since 0.9.6
22
- * @param {MaybeSignal<T>} value - value to convert to a Signal
23
- * @returns {Signal<T>} - converted Signal
24
- */
25
- declare const toSignal: <T extends {}>(value: MaybeSignal<T>) => Signal<T>;
26
- export { type Signal, type MaybeSignal, type SignalValues, UNSET, isSignal, isComputedCallback, toSignal, };
package/src/util.d.ts DELETED
@@ -1,7 +0,0 @@
1
- declare const isFunction: <T>(value: unknown) => value is (...args: unknown[]) => T;
2
- declare const isObjectOfType: <T>(value: unknown, type: string) => value is T;
3
- declare const toError: (reason: unknown) => Error;
4
- declare class CircularDependencyError extends Error {
5
- constructor(where: string);
6
- }
7
- export { isFunction, isObjectOfType, toError, CircularDependencyError };
File without changes
File without changes