@signaltree/core 5.1.3 → 5.1.4

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/README.md CHANGED
@@ -141,7 +141,7 @@ const selectedUserId = computed(() => $.selected.userId()); // Unnecessary!
141
141
  ```typescript
142
142
  // ✅ SignalTree-native
143
143
  const user = $.users.byId(123)(); // O(1) lookup
144
- const allUsers = $.users.all()(); // Get all
144
+ const allUsers = $.users.all; // Get all
145
145
  $.users.setAll(usersFromApi); // Replace all
146
146
 
147
147
  // ❌ NgRx-style (avoid)
@@ -493,7 +493,7 @@ const tree = signalTree({
493
493
  users: [] as User[],
494
494
  });
495
495
 
496
- // Manual CRUD operations using core methods
496
+ // Entity CRUD operations using core methods
497
497
  function addUser(user: User) {
498
498
  tree.$.users.update((users) => [...users, user]);
499
499
  }
@@ -630,9 +630,7 @@ tree.$.products.addOne(newProduct);
630
630
  tree.$.products.setAll(productsFromApi);
631
631
 
632
632
  // Entity queries
633
- const electronics = tree.$.products
634
- .all()()
635
- .filter((p) => p.category === 'electronics');
633
+ const electronics = tree.$.products.all.filter((p) => p.category === 'electronics');
636
634
  ```
637
635
 
638
636
  **Full-Stack Application:**
@@ -722,7 +720,7 @@ const enhanced = signalTree({
722
720
 
723
721
  enhanced.$.users.addOne(newUser); // ✅ Advanced CRUD operations
724
722
  enhanced.$.users.byId(123)(); // ✅ O(1) lookups
725
- enhanced.$.users.all()(); // ✅ Get all as array
723
+ enhanced.$.users.all; // ✅ Get all as array
726
724
  ```
727
725
 
728
726
  Core includes several performance optimizations:
@@ -945,7 +943,7 @@ const appTree = signalTree({
945
943
  // Access nested entities using tree.$ accessor
946
944
  appTree.$.app.data.users.selectBy((u) => u.isAdmin); // Filtered signal
947
945
  appTree.$.app.data.products.selectTotal(); // Count signal
948
- appTree.$.admin.data.logs.all()(); // All items as array
946
+ appTree.$.admin.data.logs.all; // All items as array
949
947
  appTree.$.admin.data.reports.selectIds(); // ID array signal
950
948
 
951
949
  // For async operations, use manual async or async helpers
@@ -1370,7 +1368,7 @@ tree.destroy(); // Cleanup resources
1370
1368
  // Entity helpers (when using entityMap + withEntities)
1371
1369
  // tree.$.users.addOne(user); // Add single entity
1372
1370
  // tree.$.users.byId(id)(); // O(1) lookup by ID
1373
- // tree.$.users.all()(); // Get all as array
1371
+ // tree.$.users.all; // Get all as array
1374
1372
  // tree.$.users.selectBy(pred); // Filtered signal
1375
1373
  ```
1376
1374
 
@@ -1,4 +1,4 @@
1
- import { EntitySignalImpl } from '../../../lib/entity-signal.js';
1
+ import { createEntitySignal } from '../../../lib/entity-signal.js';
2
2
  import { getPathNotifier } from '../../../lib/path-notifier.js';
3
3
  import { isNodeAccessor } from '../../../lib/utils.js';
4
4
 
@@ -6,7 +6,7 @@ function isEntityMapMarker(value) {
6
6
  return Boolean(value && typeof value === 'object' && value['__isEntityMap'] === true);
7
7
  }
8
8
  function isEntitySignal(value) {
9
- return !!value && typeof value === 'object' && typeof value['addOne'] === 'function' && 'all' in value;
9
+ return !!value && typeof value === 'object' && typeof value['addOne'] === 'function' && typeof value['all'] !== 'undefined';
10
10
  }
11
11
  function materializeEntities(tree, notifier = getPathNotifier()) {
12
12
  const registry = new Map();
@@ -16,7 +16,7 @@ function materializeEntities(tree, notifier = getPathNotifier()) {
16
16
  if (isEntityMapMarker(value)) {
17
17
  const basePath = nextPath.join('.');
18
18
  const config = value.__entityMapConfig ?? {};
19
- const entitySignal = new EntitySignalImpl(config, notifier, basePath);
19
+ const entitySignal = createEntitySignal(config, notifier, basePath);
20
20
  if (parent) {
21
21
  try {
22
22
  parent[key] = entitySignal;
@@ -20,14 +20,6 @@ class EntitySignalImpl {
20
20
  this.countSignal = signal(0);
21
21
  this.idsSignal = signal([]);
22
22
  this.mapSignal = signal(new Map());
23
- return new Proxy(this, {
24
- get: (target, prop) => {
25
- if (typeof prop === 'string' && !isNaN(Number(prop))) {
26
- return target.byId(Number(prop));
27
- }
28
- return target[prop];
29
- }
30
- });
31
23
  }
32
24
  byId(id) {
33
25
  const entity = this.storage.get(id);
@@ -276,5 +268,16 @@ class EntitySignalImpl {
276
268
  return node;
277
269
  }
278
270
  }
271
+ function createEntitySignal(config, pathNotifier, basePath) {
272
+ const impl = new EntitySignalImpl(config, pathNotifier, basePath);
273
+ return new Proxy(impl, {
274
+ get: (target, prop) => {
275
+ if (typeof prop === 'string' && !isNaN(Number(prop))) {
276
+ return target.byId(Number(prop));
277
+ }
278
+ return target[prop];
279
+ }
280
+ });
281
+ }
279
282
 
280
- export { EntitySignalImpl };
283
+ export { EntitySignalImpl, createEntitySignal };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@signaltree/core",
3
- "version": "5.1.3",
3
+ "version": "5.1.4",
4
4
  "description": "Lightweight, type-safe signal-based state management for Angular. Core package providing hierarchical signal trees, basic entity management, and async actions.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
package/src/types.d.ts CHANGED
@@ -1,278 +1,444 @@
1
1
  import { Signal, WritableSignal } from '@angular/core';
2
+
2
3
  import type { SecurityValidatorConfig } from './security/security-validator';
3
4
  export type NotFn<T> = T extends (...args: unknown[]) => unknown ? never : T;
4
5
  declare module '@angular/core' {
5
- interface WritableSignal<T> {
6
- (value: NotFn<T>): void;
7
- (updater: (current: T) => T): void;
8
- }
6
+ interface WritableSignal<T> {
7
+ (value: NotFn<T>): void;
8
+ (updater: (current: T) => T): void;
9
+ }
9
10
  }
10
- export type Primitive = string | number | boolean | null | undefined | bigint | symbol;
11
- export type BuiltInObject = Date | RegExp | ((...args: unknown[]) => unknown) | Map<unknown, unknown> | Set<unknown> | WeakMap<object, unknown> | WeakSet<object> | ArrayBuffer | DataView | Error | Promise<unknown> | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array | BigInt64Array | BigUint64Array | URL | URLSearchParams | FormData | Blob | File | Headers | Request | Response | AbortController | AbortSignal;
12
- export type Unwrap<T> = [T] extends [WritableSignal<infer U>] ? U : [T] extends [Signal<infer U>] ? U : [T] extends [BuiltInObject] ? T : [T] extends [readonly unknown[]] ? T : [T] extends [EntityMapMarker<infer E, infer K>] ? EntitySignal<E, K> : [T] extends [object] ? {
13
- [K in keyof T]: Unwrap<T[K]>;
14
- } : T;
11
+ export type Primitive =
12
+ | string
13
+ | number
14
+ | boolean
15
+ | null
16
+ | undefined
17
+ | bigint
18
+ | symbol;
19
+ export type BuiltInObject =
20
+ | Date
21
+ | RegExp
22
+ | ((...args: unknown[]) => unknown)
23
+ | Map<unknown, unknown>
24
+ | Set<unknown>
25
+ | WeakMap<object, unknown>
26
+ | WeakSet<object>
27
+ | ArrayBuffer
28
+ | DataView
29
+ | Error
30
+ | Promise<unknown>
31
+ | Uint16Array
32
+ | Int32Array
33
+ | Uint32Array
34
+ | Float32Array
35
+ | Float64Array
36
+ | BigInt64Array
37
+ | BigUint64Array
38
+ | URL
39
+ | URLSearchParams
40
+ | FormData
41
+ | Blob
42
+ | File
43
+ | Headers
44
+ | Request
45
+ | Response
46
+ | AbortController
47
+ | AbortSignal;
48
+ export type Unwrap<T> = [T] extends [WritableSignal<infer U>]
49
+ ? U
50
+ : [T] extends [Signal<infer U>]
51
+ ? U
52
+ : [T] extends [BuiltInObject]
53
+ ? T
54
+ : [T] extends [readonly unknown[]]
55
+ ? T
56
+ : [T] extends [EntityMapMarker<infer E, infer K>]
57
+ ? EntitySignal<E, K>
58
+ : [T] extends [object]
59
+ ? {
60
+ [K in keyof T]: Unwrap<T[K]>;
61
+ }
62
+ : T;
15
63
  export interface NodeAccessor<T> {
16
- (): T;
17
- (value: T): void;
18
- (updater: (current: T) => T): void;
64
+ (): T;
65
+ (value: T): void;
66
+ (updater: (current: T) => T): void;
19
67
  }
20
68
  export type AccessibleNode<T> = NodeAccessor<T> & TreeNode<T>;
21
69
  export type CallableWritableSignal<T> = WritableSignal<T> & {
22
- (value: NotFn<T>): void;
23
- (updater: (current: T) => T): void;
70
+ (value: NotFn<T>): void;
71
+ (updater: (current: T) => T): void;
24
72
  };
25
73
  export type TreeNode<T> = {
26
- [K in keyof T]: [T[K]] extends [EntityMapMarker<infer E, infer Key>] ? EntitySignal<E, Key> : [T[K]] extends [readonly unknown[]] ? CallableWritableSignal<T[K]> : [T[K]] extends [object] ? [T[K]] extends [Signal<unknown>] ? T[K] : [T[K]] extends [BuiltInObject] ? CallableWritableSignal<T[K]> : [T[K]] extends [(...args: unknown[]) => unknown] ? CallableWritableSignal<T[K]> : AccessibleNode<T[K]> : CallableWritableSignal<T[K]>;
74
+ [K in keyof T]: [T[K]] extends [EntityMapMarker<infer E, infer Key>]
75
+ ? EntitySignal<E, Key>
76
+ : [T[K]] extends [readonly unknown[]]
77
+ ? CallableWritableSignal<T[K]>
78
+ : [T[K]] extends [object]
79
+ ? [T[K]] extends [Signal<unknown>]
80
+ ? T[K]
81
+ : [T[K]] extends [BuiltInObject]
82
+ ? CallableWritableSignal<T[K]>
83
+ : [T[K]] extends [(...args: unknown[]) => unknown]
84
+ ? CallableWritableSignal<T[K]>
85
+ : AccessibleNode<T[K]>
86
+ : CallableWritableSignal<T[K]>;
27
87
  };
28
88
  export type RemoveSignalMethods<T> = T extends infer U ? U : never;
29
- export type DeepPath<T, Prefix extends string = '', Depth extends readonly number[] = []> = Depth['length'] extends 5 ? never : {
30
- [K in keyof T]: K extends string ? T[K] extends readonly unknown[] ? `${Prefix}${K}` : T[K] extends object ? T[K] extends Signal<unknown> ? never : T[K] extends BuiltInObject ? never : T[K] extends (...args: unknown[]) => unknown ? never : `${Prefix}${K}` | DeepPath<T[K], `${Prefix}${K}.`, [...Depth, 1]> : never : never;
31
- }[keyof T];
32
- export type DeepAccess<T, Path extends string> = Path extends `${infer First}.${infer Rest}` ? First extends keyof T ? DeepAccess<T[First] & object, Rest> : never : Path extends keyof T ? T[Path] : never;
89
+ export type DeepPath<
90
+ T,
91
+ Prefix extends string = '',
92
+ Depth extends readonly number[] = []
93
+ > = Depth['length'] extends 5
94
+ ? never
95
+ : {
96
+ [K in keyof T]: K extends string
97
+ ? T[K] extends readonly unknown[]
98
+ ? `${Prefix}${K}`
99
+ : T[K] extends object
100
+ ? T[K] extends Signal<unknown>
101
+ ? never
102
+ : T[K] extends BuiltInObject
103
+ ? never
104
+ : T[K] extends (...args: unknown[]) => unknown
105
+ ? never
106
+ : `${Prefix}${K}` | DeepPath<T[K], `${Prefix}${K}.`, [...Depth, 1]>
107
+ : never
108
+ : never;
109
+ }[keyof T];
110
+ export type DeepAccess<
111
+ T,
112
+ Path extends string
113
+ > = Path extends `${infer First}.${infer Rest}`
114
+ ? First extends keyof T
115
+ ? DeepAccess<T[First] & object, Rest>
116
+ : never
117
+ : Path extends keyof T
118
+ ? T[Path]
119
+ : never;
33
120
  export interface EnhancerMeta {
34
- name?: string;
35
- requires?: string[];
36
- provides?: string[];
121
+ name?: string;
122
+ requires?: string[];
123
+ provides?: string[];
37
124
  }
38
- export type Enhancer<Input = unknown, Output = unknown> = (input: Input) => Output;
39
- export type EnhancerWithMeta<Input = unknown, Output = unknown> = Enhancer<Input, Output> & {
40
- metadata?: EnhancerMeta;
125
+ export type Enhancer<Input = unknown, Output = unknown> = (
126
+ input: Input
127
+ ) => Output;
128
+ export type EnhancerWithMeta<Input = unknown, Output = unknown> = Enhancer<
129
+ Input,
130
+ Output
131
+ > & {
132
+ metadata?: EnhancerMeta;
41
133
  };
42
134
  export declare const ENHANCER_META: unique symbol;
43
- export type ChainResult<Start, E extends Array<EnhancerWithMeta<unknown, unknown>>> = E extends [infer H, ...infer R] ? H extends EnhancerWithMeta<SignalTree<unknown>, infer O> ? R extends Array<EnhancerWithMeta<unknown, unknown>> ? ChainResult<O, R> : O : H extends EnhancerWithMeta<infer I, infer O> ? Start extends I ? R extends Array<EnhancerWithMeta<unknown, unknown>> ? ChainResult<O, R> : O : unknown : unknown : Start;
135
+ export type ChainResult<
136
+ Start,
137
+ E extends Array<EnhancerWithMeta<unknown, unknown>>
138
+ > = E extends [infer H, ...infer R]
139
+ ? H extends EnhancerWithMeta<SignalTree<unknown>, infer O>
140
+ ? R extends Array<EnhancerWithMeta<unknown, unknown>>
141
+ ? ChainResult<O, R>
142
+ : O
143
+ : H extends EnhancerWithMeta<infer I, infer O>
144
+ ? Start extends I
145
+ ? R extends Array<EnhancerWithMeta<unknown, unknown>>
146
+ ? ChainResult<O, R>
147
+ : O
148
+ : unknown
149
+ : unknown
150
+ : Start;
44
151
  export interface WithMethod<T> {
45
- (): SignalTree<T>;
46
- <O>(enhancer: (input: SignalTree<T>) => O): O;
47
- <O1, O2>(e1: (input: SignalTree<T>) => O1, e2: (input: O1) => O2): O2;
48
- <O1, O2, O3>(e1: (input: SignalTree<T>) => O1, e2: (input: O1) => O2, e3: (input: O2) => O3): O3;
49
- <O>(enhancer: EnhancerWithMeta<SignalTree<T>, O>): O;
50
- <O1, O2>(e1: EnhancerWithMeta<SignalTree<T>, O1>, e2: EnhancerWithMeta<O1, O2>): O2;
51
- <O1, O2, O3>(e1: EnhancerWithMeta<SignalTree<T>, O1>, e2: EnhancerWithMeta<O1, O2>, e3: EnhancerWithMeta<O2, O3>): O3;
152
+ (): SignalTree<T>;
153
+ <O>(enhancer: (input: SignalTree<T>) => O): O;
154
+ <O1, O2>(e1: (input: SignalTree<T>) => O1, e2: (input: O1) => O2): O2;
155
+ <O1, O2, O3>(
156
+ e1: (input: SignalTree<T>) => O1,
157
+ e2: (input: O1) => O2,
158
+ e3: (input: O2) => O3
159
+ ): O3;
160
+ <O>(enhancer: EnhancerWithMeta<SignalTree<T>, O>): O;
161
+ <O1, O2>(
162
+ e1: EnhancerWithMeta<SignalTree<T>, O1>,
163
+ e2: EnhancerWithMeta<O1, O2>
164
+ ): O2;
165
+ <O1, O2, O3>(
166
+ e1: EnhancerWithMeta<SignalTree<T>, O1>,
167
+ e2: EnhancerWithMeta<O1, O2>,
168
+ e3: EnhancerWithMeta<O2, O3>
169
+ ): O3;
52
170
  }
53
171
  export type SignalTree<T> = NodeAccessor<T> & {
54
- state: TreeNode<T>;
55
- $: TreeNode<T>;
56
- with: WithMethod<T>;
57
- destroy(): void;
58
- dispose?(): void;
59
- effect(fn: (tree: T) => void): void;
60
- subscribe(fn: (tree: T) => void): () => void;
61
- batch(updater: (tree: T) => void): void;
62
- batchUpdate(updater: (current: T) => Partial<T>): void;
63
- memoize<R>(fn: (tree: T) => R, cacheKey?: string): Signal<R>;
64
- memoizedUpdate(updater: (current: T) => Partial<T>, cacheKey?: string): void;
65
- clearMemoCache(key?: string): void;
66
- getCacheStats(): {
67
- size: number;
68
- hitRate: number;
69
- totalHits: number;
70
- totalMisses: number;
71
- keys: string[];
72
- };
73
- optimize(): void;
74
- clearCache(): void;
75
- invalidatePattern(pattern: string): number;
76
- updateOptimized?(updates: Partial<T>, options?: {
77
- batch?: boolean;
78
- batchSize?: number;
79
- maxDepth?: number;
80
- ignoreArrayOrder?: boolean;
81
- equalityFn?: (a: unknown, b: unknown) => boolean;
82
- }): {
83
- changed: boolean;
84
- duration: number;
85
- changedPaths: string[];
86
- stats?: {
87
- totalPaths: number;
88
- optimizedPaths: number;
89
- batchedUpdates: number;
90
- };
172
+ state: TreeNode<T>;
173
+ $: TreeNode<T>;
174
+ with: WithMethod<T>;
175
+ destroy(): void;
176
+ dispose?(): void;
177
+ effect(fn: (tree: T) => void): void;
178
+ subscribe(fn: (tree: T) => void): () => void;
179
+ batch(updater: (tree: T) => void): void;
180
+ batchUpdate(updater: (current: T) => Partial<T>): void;
181
+ memoize<R>(fn: (tree: T) => R, cacheKey?: string): Signal<R>;
182
+ memoizedUpdate(updater: (current: T) => Partial<T>, cacheKey?: string): void;
183
+ clearMemoCache(key?: string): void;
184
+ getCacheStats(): {
185
+ size: number;
186
+ hitRate: number;
187
+ totalHits: number;
188
+ totalMisses: number;
189
+ keys: string[];
190
+ };
191
+ optimize(): void;
192
+ clearCache(): void;
193
+ invalidatePattern(pattern: string): number;
194
+ updateOptimized?(
195
+ updates: Partial<T>,
196
+ options?: {
197
+ batch?: boolean;
198
+ batchSize?: number;
199
+ maxDepth?: number;
200
+ ignoreArrayOrder?: boolean;
201
+ equalityFn?: (a: unknown, b: unknown) => boolean;
202
+ }
203
+ ): {
204
+ changed: boolean;
205
+ duration: number;
206
+ changedPaths: string[];
207
+ stats?: {
208
+ totalPaths: number;
209
+ optimizedPaths: number;
210
+ batchedUpdates: number;
91
211
  };
92
- getMetrics(): PerformanceMetrics;
93
- entities<E extends {
94
- id: string | number;
95
- }>(entityKey?: keyof T): EntityHelpers<E>;
96
- undo(): void;
97
- redo(): void;
98
- getHistory(): TimeTravelEntry<T>[];
99
- resetHistory(): void;
100
- jumpTo?: (index: number) => void;
101
- canUndo?: () => boolean;
102
- canRedo?: () => boolean;
103
- getCurrentIndex?: () => number;
212
+ };
213
+ getMetrics(): PerformanceMetrics;
214
+ entities<
215
+ E extends {
216
+ id: string | number;
217
+ }
218
+ >(
219
+ entityKey?: keyof T
220
+ ): EntityHelpers<E>;
221
+ undo(): void;
222
+ redo(): void;
223
+ getHistory(): TimeTravelEntry<T>[];
224
+ resetHistory(): void;
225
+ jumpTo?: (index: number) => void;
226
+ canUndo?: () => boolean;
227
+ canRedo?: () => boolean;
228
+ getCurrentIndex?: () => number;
104
229
  };
105
230
  export type TreePreset = 'basic' | 'performance' | 'development' | 'production';
106
231
  export interface TreeConfig {
107
- batchUpdates?: boolean;
108
- useMemoization?: boolean;
109
- enableTimeTravel?: boolean;
110
- useLazySignals?: boolean;
111
- useShallowComparison?: boolean;
112
- maxCacheSize?: number;
113
- trackPerformance?: boolean;
114
- treeName?: string;
115
- enableDevTools?: boolean;
116
- debugMode?: boolean;
117
- useStructuralSharing?: boolean;
118
- security?: SecurityValidatorConfig;
232
+ batchUpdates?: boolean;
233
+ useMemoization?: boolean;
234
+ enableTimeTravel?: boolean;
235
+ useLazySignals?: boolean;
236
+ useShallowComparison?: boolean;
237
+ maxCacheSize?: number;
238
+ trackPerformance?: boolean;
239
+ treeName?: string;
240
+ enableDevTools?: boolean;
241
+ debugMode?: boolean;
242
+ useStructuralSharing?: boolean;
243
+ security?: SecurityValidatorConfig;
119
244
  }
120
245
  export interface PerformanceMetrics {
121
- updates: number;
122
- computations: number;
123
- cacheHits: number;
124
- cacheMisses: number;
125
- averageUpdateTime: number;
246
+ updates: number;
247
+ computations: number;
248
+ cacheHits: number;
249
+ cacheMisses: number;
250
+ averageUpdateTime: number;
126
251
  }
127
252
  export interface EntityConfig<E, K extends string | number = string> {
128
- selectId?: (entity: E) => K;
129
- hooks?: {
130
- beforeAdd?: (entity: E) => E | false;
131
- beforeUpdate?: (id: K, changes: Partial<E>) => Partial<E> | false;
132
- beforeRemove?: (id: K, entity: E) => boolean;
133
- };
253
+ selectId?: (entity: E) => K;
254
+ hooks?: {
255
+ beforeAdd?: (entity: E) => E | false;
256
+ beforeUpdate?: (id: K, changes: Partial<E>) => Partial<E> | false;
257
+ beforeRemove?: (id: K, entity: E) => boolean;
258
+ };
134
259
  }
135
260
  declare const ENTITY_MAP_BRAND: unique symbol;
136
261
  export interface EntityMapMarker<E, K extends string | number> {
137
- readonly [ENTITY_MAP_BRAND]: {
138
- __entity: E;
139
- __key: K;
140
- };
141
- readonly __isEntityMap: true;
142
- readonly __entityMapConfig?: EntityConfig<E, K>;
262
+ readonly [ENTITY_MAP_BRAND]: {
263
+ __entity: E;
264
+ __key: K;
265
+ };
266
+ readonly __isEntityMap: true;
267
+ readonly __entityMapConfig?: EntityConfig<E, K>;
143
268
  }
144
- export declare function entityMap<E, K extends string | number = E extends {
269
+ export declare function entityMap<
270
+ E,
271
+ K extends string | number = E extends {
145
272
  id: infer I extends string | number;
146
- } ? I : string>(config?: EntityConfig<E, K>): EntityMapMarker<E, K>;
273
+ }
274
+ ? I
275
+ : string
276
+ >(config?: EntityConfig<E, K>): EntityMapMarker<E, K>;
147
277
  export interface MutationOptions {
148
- onError?: (error: Error) => void;
278
+ onError?: (error: Error) => void;
149
279
  }
150
280
  export interface AddOptions<E, K> extends MutationOptions {
151
- selectId?: (entity: E) => K;
281
+ selectId?: (entity: E) => K;
152
282
  }
153
283
  export interface AddManyOptions<E, K> extends AddOptions<E, K> {
154
- mode?: 'strict' | 'skip' | 'overwrite';
284
+ mode?: 'strict' | 'skip' | 'overwrite';
155
285
  }
156
286
  export interface TapHandlers<E, K extends string | number> {
157
- onAdd?: (entity: E, id: K) => void;
158
- onUpdate?: (id: K, changes: Partial<E>, entity: E) => void;
159
- onRemove?: (id: K, entity: E) => void;
160
- onChange?: () => void;
287
+ onAdd?: (entity: E, id: K) => void;
288
+ onUpdate?: (id: K, changes: Partial<E>, entity: E) => void;
289
+ onRemove?: (id: K, entity: E) => void;
290
+ onChange?: () => void;
161
291
  }
162
292
  export interface InterceptContext<T> {
163
- block(reason?: string): void;
164
- transform(value: T): void;
165
- readonly blocked: boolean;
166
- readonly blockReason: string | undefined;
293
+ block(reason?: string): void;
294
+ transform(value: T): void;
295
+ readonly blocked: boolean;
296
+ readonly blockReason: string | undefined;
167
297
  }
168
298
  export interface InterceptHandlers<E, K extends string | number> {
169
- onAdd?: (entity: E, ctx: InterceptContext<E>) => void | Promise<void>;
170
- onUpdate?: (id: K, changes: Partial<E>, ctx: InterceptContext<Partial<E>>) => void | Promise<void>;
171
- onRemove?: (id: K, entity: E, ctx: InterceptContext<void>) => void | Promise<void>;
299
+ onAdd?: (entity: E, ctx: InterceptContext<E>) => void | Promise<void>;
300
+ onUpdate?: (
301
+ id: K,
302
+ changes: Partial<E>,
303
+ ctx: InterceptContext<Partial<E>>
304
+ ) => void | Promise<void>;
305
+ onRemove?: (
306
+ id: K,
307
+ entity: E,
308
+ ctx: InterceptContext<void>
309
+ ) => void | Promise<void>;
172
310
  }
173
311
  export type EntityNode<E> = {
174
- (): E;
175
- (value: E): void;
176
- (updater: (current: E) => E): void;
312
+ (): E;
313
+ (value: E): void;
314
+ (updater: (current: E) => E): void;
177
315
  } & {
178
- [P in keyof E]: E[P] extends object ? E[P] extends readonly unknown[] ? CallableWritableSignal<E[P]> : EntityNode<E[P]> : CallableWritableSignal<E[P]>;
316
+ [P in keyof E]: E[P] extends object
317
+ ? E[P] extends readonly unknown[]
318
+ ? CallableWritableSignal<E[P]>
319
+ : EntityNode<E[P]>
320
+ : CallableWritableSignal<E[P]>;
179
321
  };
180
322
  export interface EntitySignal<E, K extends string | number = string> {
181
- byId(id: K): EntityNode<E> | undefined;
182
- byIdOrFail(id: K): EntityNode<E>;
183
- all(): Signal<E[]>;
184
- count(): Signal<number>;
185
- ids(): Signal<K[]>;
186
- has(id: K): Signal<boolean>;
187
- isEmpty(): Signal<boolean>;
188
- map(): Signal<ReadonlyMap<K, E>>;
189
- where(predicate: (entity: E) => boolean): Signal<E[]>;
190
- find(predicate: (entity: E) => boolean): Signal<E | undefined>;
191
- addOne(entity: E, opts?: AddOptions<E, K>): K;
192
- addMany(entities: E[], opts?: AddManyOptions<E, K>): K[];
193
- updateOne(id: K, changes: Partial<E>, opts?: MutationOptions): void;
194
- updateMany(ids: K[], changes: Partial<E>, opts?: MutationOptions): void;
195
- updateWhere(predicate: (entity: E) => boolean, changes: Partial<E>): number;
196
- upsertOne(entity: E, opts?: AddOptions<E, K>): K;
197
- upsertMany(entities: E[], opts?: AddOptions<E, K>): K[];
198
- removeOne(id: K, opts?: MutationOptions): void;
199
- removeMany(ids: K[], opts?: MutationOptions): void;
200
- removeWhere(predicate: (entity: E) => boolean): number;
201
- clear(): void;
202
- removeAll(): void;
203
- setAll(entities: E[], opts?: AddOptions<E, K>): void;
204
- tap(handlers: TapHandlers<E, K>): () => void;
205
- intercept(handlers: InterceptHandlers<E, K>): () => void;
323
+ byId(id: K): EntityNode<E> | undefined;
324
+ byIdOrFail(id: K): EntityNode<E>;
325
+ readonly all: Signal<E[]>;
326
+ readonly count: Signal<number>;
327
+ readonly ids: Signal<K[]>;
328
+ has(id: K): Signal<boolean>;
329
+ readonly isEmpty: Signal<boolean>;
330
+ readonly map: Signal<ReadonlyMap<K, E>>;
331
+ where(predicate: (entity: E) => boolean): Signal<E[]>;
332
+ find(predicate: (entity: E) => boolean): Signal<E | undefined>;
333
+ addOne(entity: E, opts?: AddOptions<E, K>): K;
334
+ addMany(entities: E[], opts?: AddManyOptions<E, K>): K[];
335
+ updateOne(id: K, changes: Partial<E>, opts?: MutationOptions): void;
336
+ updateMany(ids: K[], changes: Partial<E>, opts?: MutationOptions): void;
337
+ updateWhere(predicate: (entity: E) => boolean, changes: Partial<E>): number;
338
+ upsertOne(entity: E, opts?: AddOptions<E, K>): K;
339
+ upsertMany(entities: E[], opts?: AddOptions<E, K>): K[];
340
+ removeOne(id: K, opts?: MutationOptions): void;
341
+ removeMany(ids: K[], opts?: MutationOptions): void;
342
+ removeWhere(predicate: (entity: E) => boolean): number;
343
+ clear(): void;
344
+ removeAll(): void;
345
+ setAll(entities: E[], opts?: AddOptions<E, K>): void;
346
+ tap(handlers: TapHandlers<E, K>): () => void;
347
+ intercept(handlers: InterceptHandlers<E, K>): () => void;
206
348
  }
207
- export interface EntityHelpers<E extends {
349
+ export interface EntityHelpers<
350
+ E extends {
208
351
  id: string | number;
209
- }> {
210
- add(entity: E): void;
211
- update(id: E['id'], updates: Partial<E>): void;
212
- remove(id: E['id']): void;
213
- upsert(entity: E): void;
214
- selectById(id: E['id']): Signal<E | undefined>;
215
- selectBy(predicate: (entity: E) => boolean): Signal<E[]>;
216
- selectIds(): Signal<Array<string | number>>;
217
- selectAll(): Signal<E[]>;
218
- selectTotal(): Signal<number>;
219
- clear(): void;
352
+ }
353
+ > {
354
+ add(entity: E): void;
355
+ update(id: E['id'], updates: Partial<E>): void;
356
+ remove(id: E['id']): void;
357
+ upsert(entity: E): void;
358
+ selectById(id: E['id']): Signal<E | undefined>;
359
+ selectBy(predicate: (entity: E) => boolean): Signal<E[]>;
360
+ selectIds(): Signal<Array<string | number>>;
361
+ selectAll(): Signal<E[]>;
362
+ selectTotal(): Signal<number>;
363
+ clear(): void;
220
364
  }
221
365
  export interface LoggingConfig {
222
- name?: string;
223
- filter?: (path: string) => boolean;
224
- collapsed?: boolean;
225
- onLog?: (entry: LogEntry) => void;
366
+ name?: string;
367
+ filter?: (path: string) => boolean;
368
+ collapsed?: boolean;
369
+ onLog?: (entry: LogEntry) => void;
226
370
  }
227
371
  export interface LogEntry {
228
- path: string;
229
- prev: unknown;
230
- value: unknown;
231
- timestamp: number;
372
+ path: string;
373
+ prev: unknown;
374
+ value: unknown;
375
+ timestamp: number;
232
376
  }
233
377
  export interface ValidationConfig<T> {
234
- validators: Array<{
235
- match: (path: string) => boolean;
236
- validate: (value: T, path: string) => void | never;
237
- }>;
238
- onError?: (error: Error, path: string) => void;
378
+ validators: Array<{
379
+ match: (path: string) => boolean;
380
+ validate: (value: T, path: string) => void | never;
381
+ }>;
382
+ onError?: (error: Error, path: string) => void;
239
383
  }
240
384
  export interface PersistenceConfig {
241
- key: string;
242
- storage?: Storage;
243
- debounceMs?: number;
244
- filter?: (path: string) => boolean;
245
- serialize?: (state: unknown) => string;
246
- deserialize?: (json: string) => unknown;
385
+ key: string;
386
+ storage?: Storage;
387
+ debounceMs?: number;
388
+ filter?: (path: string) => boolean;
389
+ serialize?: (state: unknown) => string;
390
+ deserialize?: (json: string) => unknown;
247
391
  }
248
392
  export interface DevToolsConfig {
249
- name?: string;
250
- maxAge?: number;
251
- features?: {
252
- jump?: boolean;
253
- skip?: boolean;
254
- reorder?: boolean;
255
- };
393
+ name?: string;
394
+ maxAge?: number;
395
+ features?: {
396
+ jump?: boolean;
397
+ skip?: boolean;
398
+ reorder?: boolean;
399
+ };
256
400
  }
257
- export type EntityType<T> = T extends EntitySignal<infer E, infer K extends string | number> ? E : never;
258
- export type EntityKeyType<T> = T extends EntitySignal<unknown, infer K extends string | number> ? K : never;
259
- export type IsEntityMap<T> = T extends EntityMapMarker<unknown, infer K extends string | number> ? true : false;
401
+ export type EntityType<T> = T extends EntitySignal<
402
+ infer E,
403
+ infer K extends string | number
404
+ >
405
+ ? E
406
+ : never;
407
+ export type EntityKeyType<T> = T extends EntitySignal<
408
+ unknown,
409
+ infer K extends string | number
410
+ >
411
+ ? K
412
+ : never;
413
+ export type IsEntityMap<T> = T extends EntityMapMarker<
414
+ unknown,
415
+ infer K extends string | number
416
+ >
417
+ ? true
418
+ : false;
260
419
  export type EntityAwareTreeNode<T> = {
261
- [K in keyof T]: T[K] extends EntityMapMarker<infer E, infer Key> ? EntitySignal<E, Key> : T[K] extends object ? EntityAwareTreeNode<T[K]> : CallableWritableSignal<T[K]>;
420
+ [K in keyof T]: T[K] extends EntityMapMarker<infer E, infer Key>
421
+ ? EntitySignal<E, Key>
422
+ : T[K] extends object
423
+ ? EntityAwareTreeNode<T[K]>
424
+ : CallableWritableSignal<T[K]>;
262
425
  };
263
426
  export type PathHandler = (value: unknown, prev: unknown, path: string) => void;
264
- export type PathInterceptor = (ctx: {
427
+ export type PathInterceptor = (
428
+ ctx: {
265
429
  path: string;
266
430
  value: unknown;
267
431
  prev: unknown;
268
432
  blocked: boolean;
269
433
  blockReason?: string;
270
- }, next: () => void) => void | Promise<void>;
434
+ },
435
+ next: () => void
436
+ ) => void | Promise<void>;
271
437
  export interface TimeTravelEntry<T> {
272
- action: string;
273
- timestamp: number;
274
- state: T;
275
- payload?: unknown;
438
+ action: string;
439
+ timestamp: number;
440
+ state: T;
441
+ payload?: unknown;
276
442
  }
277
443
  export declare function isSignalTree<T>(value: unknown): value is SignalTree<T>;
278
444
  export {};