@signaltree/core 5.1.3 → 5.1.6

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,22 +1,18 @@
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
 
5
5
  function isEntityMapMarker(value) {
6
6
  return Boolean(value && typeof value === 'object' && value['__isEntityMap'] === true);
7
7
  }
8
- function isEntitySignal(value) {
9
- return !!value && typeof value === 'object' && typeof value['addOne'] === 'function' && 'all' in value;
10
- }
11
8
  function materializeEntities(tree, notifier = getPathNotifier()) {
12
- const registry = new Map();
13
9
  const state = tree.state;
14
10
  const visit = (parent, key, value, path) => {
15
11
  const nextPath = [...path, key];
16
12
  if (isEntityMapMarker(value)) {
17
13
  const basePath = nextPath.join('.');
18
14
  const config = value.__entityMapConfig ?? {};
19
- const entitySignal = new EntitySignalImpl(config, notifier, basePath);
15
+ const entitySignal = createEntitySignal(config, notifier, basePath);
20
16
  if (parent) {
21
17
  try {
22
18
  parent[key] = entitySignal;
@@ -25,7 +21,6 @@ function materializeEntities(tree, notifier = getPathNotifier()) {
25
21
  try {
26
22
  tree[key] = entitySignal;
27
23
  } catch {}
28
- registry.set(basePath, entitySignal);
29
24
  return;
30
25
  }
31
26
  if (isNodeAccessor(value)) {
@@ -44,23 +39,6 @@ function materializeEntities(tree, notifier = getPathNotifier()) {
44
39
  for (const key of Object.keys(state)) {
45
40
  visit(state, key, state[key], []);
46
41
  }
47
- return registry;
48
- }
49
- function resolveEntitySignal(tree, registry, path) {
50
- const pathStr = String(path);
51
- const existing = registry.get(pathStr);
52
- if (existing) return existing;
53
- const segments = pathStr.split('.');
54
- let current = tree.state;
55
- for (const segment of segments) {
56
- if (!current) break;
57
- current = current[segment];
58
- }
59
- if (isEntitySignal(current)) {
60
- registry.set(pathStr, current);
61
- return current;
62
- }
63
- throw new Error(`Entity path '${pathStr}' is not configured. Define it with entityMap() in your initial state.`);
64
42
  }
65
43
  function withEntities(config = {}) {
66
44
  const {
@@ -70,13 +48,8 @@ function withEntities(config = {}) {
70
48
  if (!enabled) {
71
49
  return tree;
72
50
  }
73
- const registry = materializeEntities(tree);
74
- const enhancedTree = Object.assign(tree, {
75
- entities(path) {
76
- return resolveEntitySignal(tree, registry, path);
77
- }
78
- });
79
- return enhancedTree;
51
+ materializeEntities(tree);
52
+ return tree;
80
53
  };
81
54
  }
82
55
  function enableEntities() {
@@ -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 };
@@ -449,15 +449,6 @@ function addStubMethods(tree, config) {
449
449
  }
450
450
  return engine.update(tree(), updates, options);
451
451
  };
452
- tree.getMetrics = () => {
453
- return {
454
- updates: 0,
455
- computations: 0,
456
- cacheHits: 0,
457
- cacheMisses: 0,
458
- averageUpdateTime: 0
459
- };
460
- };
461
452
  tree.entities = () => {
462
453
  console.warn('[@signaltree/core] tree.entities() is deprecated and will be removed in v6.0. ' + 'Use entityMap<E>() + withEntities() + tree.$.collectionName instead. ' + 'See https://signaltree.dev/docs/migration for migration guide.');
463
454
  if (config.debugMode) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@signaltree/core",
3
- "version": "5.1.3",
3
+ "version": "5.1.6",
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,
@@ -1,20 +1,17 @@
1
- import type { EntitySignal, SignalTree, EntityAwareTreeNode } from '../../../lib/types';
1
+ import type { SignalTree, EntityAwareTreeNode } from '../../../lib/types';
2
2
  interface EntitiesEnhancerConfig {
3
3
  enabled?: boolean;
4
4
  }
5
5
  export declare function withEntities(config?: EntitiesEnhancerConfig): <T>(tree: SignalTree<T>) => Omit<SignalTree<T>, "state" | "$"> & {
6
6
  state: EntityAwareTreeNode<T>;
7
7
  $: EntityAwareTreeNode<T>;
8
- entities<E, K extends string | number>(path: keyof T | string): EntitySignal<E, K>;
9
8
  };
10
9
  export declare function enableEntities(): <T>(tree: SignalTree<T>) => Omit<SignalTree<T>, "state" | "$"> & {
11
10
  state: EntityAwareTreeNode<T>;
12
11
  $: EntityAwareTreeNode<T>;
13
- entities<E, K extends string | number>(path: keyof T | string): EntitySignal<E, K>;
14
12
  };
15
13
  export declare function withHighPerformanceEntities(): <T>(tree: SignalTree<T>) => Omit<SignalTree<T>, "state" | "$"> & {
16
14
  state: EntityAwareTreeNode<T>;
17
15
  $: EntityAwareTreeNode<T>;
18
- entities<E, K extends string | number>(path: keyof T | string): EntitySignal<E, K>;
19
16
  };
20
17
  export {};
package/src/entities.d.ts CHANGED
@@ -1,20 +1,28 @@
1
- import type { EntitySignal, SignalTree, EntityAwareTreeNode } from '../../../lib/types';
1
+ import type {
2
+ EntitySignal,
3
+ SignalTree,
4
+ EntityAwareTreeNode,
5
+ } from '../../../lib/types';
2
6
  interface EntitiesEnhancerConfig {
3
- enabled?: boolean;
7
+ enabled?: boolean;
4
8
  }
5
- export declare function withEntities(config?: EntitiesEnhancerConfig): <T>(tree: SignalTree<T>) => Omit<SignalTree<T>, "state" | "$"> & {
6
- state: EntityAwareTreeNode<T>;
7
- $: EntityAwareTreeNode<T>;
8
- entities<E, K extends string | number>(path: keyof T | string): EntitySignal<E, K>;
9
+ export declare function withEntities(config?: EntitiesEnhancerConfig): <T>(
10
+ tree: SignalTree<T>
11
+ ) => Omit<SignalTree<T>, 'state' | '$'> & {
12
+ state: EntityAwareTreeNode<T>;
13
+ $: EntityAwareTreeNode<T>;
9
14
  };
10
- export declare function enableEntities(): <T>(tree: SignalTree<T>) => Omit<SignalTree<T>, "state" | "$"> & {
11
- state: EntityAwareTreeNode<T>;
12
- $: EntityAwareTreeNode<T>;
13
- entities<E, K extends string | number>(path: keyof T | string): EntitySignal<E, K>;
15
+ export declare function enableEntities(): <T>(tree: SignalTree<T>) => Omit<
16
+ SignalTree<T>,
17
+ 'state' | '$'
18
+ > & {
19
+ state: EntityAwareTreeNode<T>;
20
+ $: EntityAwareTreeNode<T>;
14
21
  };
15
- export declare function withHighPerformanceEntities(): <T>(tree: SignalTree<T>) => Omit<SignalTree<T>, "state" | "$"> & {
16
- state: EntityAwareTreeNode<T>;
17
- $: EntityAwareTreeNode<T>;
18
- entities<E, K extends string | number>(path: keyof T | string): EntitySignal<E, K>;
22
+ export declare function withHighPerformanceEntities(): <T>(
23
+ tree: SignalTree<T>
24
+ ) => Omit<SignalTree<T>, 'state' | '$'> & {
25
+ state: EntityAwareTreeNode<T>;
26
+ $: EntityAwareTreeNode<T>;
19
27
  };
20
28
  export {};
package/src/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { signalTree } from './lib/signal-tree';
2
- export type { SignalTree, TreeNode, CallableWritableSignal, AccessibleNode, NodeAccessor, RemoveSignalMethods, Primitive, BuiltInObject, NotFn, DeepPath, DeepAccess, TreeConfig, TreePreset, Enhancer, EnhancerMeta, EnhancerWithMeta, ChainResult, WithMethod, EntitySignal, EntityMapMarker, EntityConfig, MutationOptions, AddOptions, AddManyOptions, PerformanceMetrics, EntityHelpers, TimeTravelEntry, } from './lib/types';
2
+ export type { SignalTree, TreeNode, CallableWritableSignal, AccessibleNode, NodeAccessor, RemoveSignalMethods, Primitive, BuiltInObject, NotFn, DeepPath, DeepAccess, TreeConfig, TreePreset, Enhancer, EnhancerMeta, EnhancerWithMeta, ChainResult, WithMethod, EntitySignal, EntityMapMarker, EntityConfig, MutationOptions, AddOptions, AddManyOptions, EntityHelpers, TimeTravelEntry, } from './lib/types';
3
3
  export { entityMap } from './lib/types';
4
4
  export { equal, deepEqual, isNodeAccessor, isAnySignal, toWritableSignal, parsePath, composeEnhancers, isBuiltInObject, createLazySignalTree, } from './lib/utils';
5
5
  export { getPathNotifier } from './lib/path-notifier';
@@ -89,7 +89,6 @@ export type SignalTree<T> = NodeAccessor<T> & {
89
89
  batchedUpdates: number;
90
90
  };
91
91
  };
92
- getMetrics(): PerformanceMetrics;
93
92
  entities<E extends {
94
93
  id: string | number;
95
94
  }>(entityKey?: keyof T): EntityHelpers<E>;
@@ -117,13 +116,6 @@ export interface TreeConfig {
117
116
  useStructuralSharing?: boolean;
118
117
  security?: SecurityValidatorConfig;
119
118
  }
120
- export interface PerformanceMetrics {
121
- updates: number;
122
- computations: number;
123
- cacheHits: number;
124
- cacheMisses: number;
125
- averageUpdateTime: number;
126
- }
127
119
  export interface EntityConfig<E, K extends string | number = string> {
128
120
  selectId?: (entity: E) => K;
129
121
  hooks?: {
package/src/types.d.ts CHANGED
@@ -1,278 +1,436 @@
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
+ entities<
214
+ E extends {
215
+ id: string | number;
216
+ }
217
+ >(
218
+ entityKey?: keyof T
219
+ ): EntityHelpers<E>;
220
+ undo(): void;
221
+ redo(): void;
222
+ getHistory(): TimeTravelEntry<T>[];
223
+ resetHistory(): void;
224
+ jumpTo?: (index: number) => void;
225
+ canUndo?: () => boolean;
226
+ canRedo?: () => boolean;
227
+ getCurrentIndex?: () => number;
104
228
  };
105
229
  export type TreePreset = 'basic' | 'performance' | 'development' | 'production';
106
230
  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;
119
- }
120
- export interface PerformanceMetrics {
121
- updates: number;
122
- computations: number;
123
- cacheHits: number;
124
- cacheMisses: number;
125
- averageUpdateTime: number;
231
+ batchUpdates?: boolean;
232
+ useMemoization?: boolean;
233
+ enableTimeTravel?: boolean;
234
+ useLazySignals?: boolean;
235
+ useShallowComparison?: boolean;
236
+ maxCacheSize?: number;
237
+ trackPerformance?: boolean;
238
+ treeName?: string;
239
+ enableDevTools?: boolean;
240
+ debugMode?: boolean;
241
+ useStructuralSharing?: boolean;
242
+ security?: SecurityValidatorConfig;
126
243
  }
127
244
  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
- };
245
+ selectId?: (entity: E) => K;
246
+ hooks?: {
247
+ beforeAdd?: (entity: E) => E | false;
248
+ beforeUpdate?: (id: K, changes: Partial<E>) => Partial<E> | false;
249
+ beforeRemove?: (id: K, entity: E) => boolean;
250
+ };
134
251
  }
135
252
  declare const ENTITY_MAP_BRAND: unique symbol;
136
253
  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>;
254
+ readonly [ENTITY_MAP_BRAND]: {
255
+ __entity: E;
256
+ __key: K;
257
+ };
258
+ readonly __isEntityMap: true;
259
+ readonly __entityMapConfig?: EntityConfig<E, K>;
143
260
  }
144
- export declare function entityMap<E, K extends string | number = E extends {
261
+ export declare function entityMap<
262
+ E,
263
+ K extends string | number = E extends {
145
264
  id: infer I extends string | number;
146
- } ? I : string>(config?: EntityConfig<E, K>): EntityMapMarker<E, K>;
265
+ }
266
+ ? I
267
+ : string
268
+ >(config?: EntityConfig<E, K>): EntityMapMarker<E, K>;
147
269
  export interface MutationOptions {
148
- onError?: (error: Error) => void;
270
+ onError?: (error: Error) => void;
149
271
  }
150
272
  export interface AddOptions<E, K> extends MutationOptions {
151
- selectId?: (entity: E) => K;
273
+ selectId?: (entity: E) => K;
152
274
  }
153
275
  export interface AddManyOptions<E, K> extends AddOptions<E, K> {
154
- mode?: 'strict' | 'skip' | 'overwrite';
276
+ mode?: 'strict' | 'skip' | 'overwrite';
155
277
  }
156
278
  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;
279
+ onAdd?: (entity: E, id: K) => void;
280
+ onUpdate?: (id: K, changes: Partial<E>, entity: E) => void;
281
+ onRemove?: (id: K, entity: E) => void;
282
+ onChange?: () => void;
161
283
  }
162
284
  export interface InterceptContext<T> {
163
- block(reason?: string): void;
164
- transform(value: T): void;
165
- readonly blocked: boolean;
166
- readonly blockReason: string | undefined;
285
+ block(reason?: string): void;
286
+ transform(value: T): void;
287
+ readonly blocked: boolean;
288
+ readonly blockReason: string | undefined;
167
289
  }
168
290
  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>;
291
+ onAdd?: (entity: E, ctx: InterceptContext<E>) => void | Promise<void>;
292
+ onUpdate?: (
293
+ id: K,
294
+ changes: Partial<E>,
295
+ ctx: InterceptContext<Partial<E>>
296
+ ) => void | Promise<void>;
297
+ onRemove?: (
298
+ id: K,
299
+ entity: E,
300
+ ctx: InterceptContext<void>
301
+ ) => void | Promise<void>;
172
302
  }
173
303
  export type EntityNode<E> = {
174
- (): E;
175
- (value: E): void;
176
- (updater: (current: E) => E): void;
304
+ (): E;
305
+ (value: E): void;
306
+ (updater: (current: E) => E): void;
177
307
  } & {
178
- [P in keyof E]: E[P] extends object ? E[P] extends readonly unknown[] ? CallableWritableSignal<E[P]> : EntityNode<E[P]> : CallableWritableSignal<E[P]>;
308
+ [P in keyof E]: E[P] extends object
309
+ ? E[P] extends readonly unknown[]
310
+ ? CallableWritableSignal<E[P]>
311
+ : EntityNode<E[P]>
312
+ : CallableWritableSignal<E[P]>;
179
313
  };
180
314
  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;
315
+ byId(id: K): EntityNode<E> | undefined;
316
+ byIdOrFail(id: K): EntityNode<E>;
317
+ readonly all: Signal<E[]>;
318
+ readonly count: Signal<number>;
319
+ readonly ids: Signal<K[]>;
320
+ has(id: K): Signal<boolean>;
321
+ readonly isEmpty: Signal<boolean>;
322
+ readonly map: Signal<ReadonlyMap<K, E>>;
323
+ where(predicate: (entity: E) => boolean): Signal<E[]>;
324
+ find(predicate: (entity: E) => boolean): Signal<E | undefined>;
325
+ addOne(entity: E, opts?: AddOptions<E, K>): K;
326
+ addMany(entities: E[], opts?: AddManyOptions<E, K>): K[];
327
+ updateOne(id: K, changes: Partial<E>, opts?: MutationOptions): void;
328
+ updateMany(ids: K[], changes: Partial<E>, opts?: MutationOptions): void;
329
+ updateWhere(predicate: (entity: E) => boolean, changes: Partial<E>): number;
330
+ upsertOne(entity: E, opts?: AddOptions<E, K>): K;
331
+ upsertMany(entities: E[], opts?: AddOptions<E, K>): K[];
332
+ removeOne(id: K, opts?: MutationOptions): void;
333
+ removeMany(ids: K[], opts?: MutationOptions): void;
334
+ removeWhere(predicate: (entity: E) => boolean): number;
335
+ clear(): void;
336
+ removeAll(): void;
337
+ setAll(entities: E[], opts?: AddOptions<E, K>): void;
338
+ tap(handlers: TapHandlers<E, K>): () => void;
339
+ intercept(handlers: InterceptHandlers<E, K>): () => void;
206
340
  }
207
- export interface EntityHelpers<E extends {
341
+ export interface EntityHelpers<
342
+ E extends {
208
343
  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;
344
+ }
345
+ > {
346
+ add(entity: E): void;
347
+ update(id: E['id'], updates: Partial<E>): void;
348
+ remove(id: E['id']): void;
349
+ upsert(entity: E): void;
350
+ selectById(id: E['id']): Signal<E | undefined>;
351
+ selectBy(predicate: (entity: E) => boolean): Signal<E[]>;
352
+ selectIds(): Signal<Array<string | number>>;
353
+ selectAll(): Signal<E[]>;
354
+ selectTotal(): Signal<number>;
355
+ clear(): void;
220
356
  }
221
357
  export interface LoggingConfig {
222
- name?: string;
223
- filter?: (path: string) => boolean;
224
- collapsed?: boolean;
225
- onLog?: (entry: LogEntry) => void;
358
+ name?: string;
359
+ filter?: (path: string) => boolean;
360
+ collapsed?: boolean;
361
+ onLog?: (entry: LogEntry) => void;
226
362
  }
227
363
  export interface LogEntry {
228
- path: string;
229
- prev: unknown;
230
- value: unknown;
231
- timestamp: number;
364
+ path: string;
365
+ prev: unknown;
366
+ value: unknown;
367
+ timestamp: number;
232
368
  }
233
369
  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;
370
+ validators: Array<{
371
+ match: (path: string) => boolean;
372
+ validate: (value: T, path: string) => void | never;
373
+ }>;
374
+ onError?: (error: Error, path: string) => void;
239
375
  }
240
376
  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;
377
+ key: string;
378
+ storage?: Storage;
379
+ debounceMs?: number;
380
+ filter?: (path: string) => boolean;
381
+ serialize?: (state: unknown) => string;
382
+ deserialize?: (json: string) => unknown;
247
383
  }
248
384
  export interface DevToolsConfig {
249
- name?: string;
250
- maxAge?: number;
251
- features?: {
252
- jump?: boolean;
253
- skip?: boolean;
254
- reorder?: boolean;
255
- };
385
+ name?: string;
386
+ maxAge?: number;
387
+ features?: {
388
+ jump?: boolean;
389
+ skip?: boolean;
390
+ reorder?: boolean;
391
+ };
256
392
  }
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;
393
+ export type EntityType<T> = T extends EntitySignal<
394
+ infer E,
395
+ infer K extends string | number
396
+ >
397
+ ? E
398
+ : never;
399
+ export type EntityKeyType<T> = T extends EntitySignal<
400
+ unknown,
401
+ infer K extends string | number
402
+ >
403
+ ? K
404
+ : never;
405
+ export type IsEntityMap<T> = T extends EntityMapMarker<
406
+ unknown,
407
+ infer K extends string | number
408
+ >
409
+ ? true
410
+ : false;
260
411
  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]>;
412
+ [K in keyof T]: T[K] extends EntityMapMarker<infer E, infer Key>
413
+ ? EntitySignal<E, Key>
414
+ : T[K] extends object
415
+ ? EntityAwareTreeNode<T[K]>
416
+ : CallableWritableSignal<T[K]>;
262
417
  };
263
418
  export type PathHandler = (value: unknown, prev: unknown, path: string) => void;
264
- export type PathInterceptor = (ctx: {
419
+ export type PathInterceptor = (
420
+ ctx: {
265
421
  path: string;
266
422
  value: unknown;
267
423
  prev: unknown;
268
424
  blocked: boolean;
269
425
  blockReason?: string;
270
- }, next: () => void) => void | Promise<void>;
426
+ },
427
+ next: () => void
428
+ ) => void | Promise<void>;
271
429
  export interface TimeTravelEntry<T> {
272
- action: string;
273
- timestamp: number;
274
- state: T;
275
- payload?: unknown;
430
+ action: string;
431
+ timestamp: number;
432
+ state: T;
433
+ payload?: unknown;
276
434
  }
277
435
  export declare function isSignalTree<T>(value: unknown): value is SignalTree<T>;
278
436
  export {};