@signaltree/core 7.1.5 → 7.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/dist/constants.js +6 -0
- package/dist/deep-equal.js +41 -0
- package/dist/enhancers/batching/batching.js +230 -0
- package/dist/enhancers/devtools/devtools.js +318 -0
- package/dist/enhancers/effects/effects.js +66 -0
- package/dist/enhancers/entities/entities.js +7 -0
- package/dist/enhancers/index.js +72 -0
- package/dist/enhancers/memoization/memoization.js +420 -0
- package/dist/enhancers/presets/lib/presets.js +27 -0
- package/dist/enhancers/serialization/constants.js +15 -0
- package/dist/enhancers/serialization/serialization.js +656 -0
- package/dist/enhancers/time-travel/time-travel.js +283 -0
- package/dist/enhancers/time-travel/utils.js +11 -0
- package/dist/enhancers/utils/copy-tree-properties.js +20 -0
- package/dist/index.js +26 -0
- package/dist/is-built-in-object.js +23 -0
- package/dist/lib/async-helpers.js +77 -0
- package/dist/lib/constants.js +56 -0
- package/dist/lib/edit-session.js +84 -0
- package/dist/lib/entity-signal.js +544 -0
- package/dist/lib/internals/batch-scope.js +8 -0
- package/dist/lib/internals/derived-types.js +6 -0
- package/dist/lib/internals/materialize-markers.js +72 -0
- package/dist/lib/internals/merge-derived.js +59 -0
- package/dist/lib/markers/derived.js +6 -0
- package/dist/lib/markers/entity-map.js +20 -0
- package/dist/lib/markers/status.js +71 -0
- package/dist/lib/markers/stored.js +111 -0
- package/dist/lib/memory/memory-manager.js +164 -0
- package/dist/lib/path-notifier.js +178 -0
- package/dist/lib/presets.js +20 -0
- package/dist/lib/security/security-validator.js +121 -0
- package/dist/lib/signal-tree.js +415 -0
- package/dist/lib/types.js +3 -0
- package/dist/lib/utils.js +264 -0
- package/dist/lru-cache.js +64 -0
- package/dist/parse-path.js +13 -0
- package/package.json +1 -1
- package/src/enhancers/batching/batching.d.ts +10 -0
- package/src/enhancers/batching/batching.types.d.ts +1 -0
- package/src/enhancers/batching/index.d.ts +1 -0
- package/src/enhancers/batching/test-setup.d.ts +3 -0
- package/src/enhancers/devtools/devtools.d.ts +68 -0
- package/src/enhancers/devtools/devtools.types.d.ts +1 -0
- package/src/enhancers/devtools/index.d.ts +1 -0
- package/src/enhancers/devtools/test-setup.d.ts +3 -0
- package/src/enhancers/effects/effects.d.ts +9 -0
- package/src/enhancers/effects/effects.types.d.ts +1 -0
- package/src/enhancers/effects/index.d.ts +1 -0
- package/src/enhancers/entities/entities.d.ts +7 -0
- package/src/enhancers/entities/entities.types.d.ts +1 -0
- package/src/enhancers/entities/index.d.ts +1 -0
- package/src/enhancers/entities/test-setup.d.ts +3 -0
- package/src/enhancers/index.d.ts +3 -0
- package/src/enhancers/memoization/index.d.ts +1 -0
- package/src/enhancers/memoization/memoization.d.ts +54 -0
- package/src/enhancers/memoization/memoization.types.d.ts +1 -0
- package/src/enhancers/memoization/test-setup.d.ts +3 -0
- package/src/enhancers/presets/index.d.ts +1 -0
- package/src/enhancers/presets/lib/presets.d.ts +8 -0
- package/src/enhancers/serialization/constants.d.ts +14 -0
- package/src/enhancers/serialization/index.d.ts +2 -0
- package/src/enhancers/serialization/serialization.d.ts +68 -0
- package/src/enhancers/serialization/test-setup.d.ts +3 -0
- package/src/enhancers/test-helpers/types-equals.d.ts +2 -0
- package/src/enhancers/time-travel/index.d.ts +1 -0
- package/src/enhancers/time-travel/test-setup.d.ts +3 -0
- package/src/enhancers/time-travel/time-travel.d.ts +10 -0
- package/src/enhancers/time-travel/time-travel.types.d.ts +1 -0
- package/src/enhancers/time-travel/utils.d.ts +2 -0
- package/src/enhancers/types.d.ts +1 -0
- package/src/enhancers/typing/helpers-types.d.ts +2 -0
- package/src/enhancers/utils/copy-tree-properties.d.ts +1 -0
- package/src/index.d.ts +26 -0
- package/src/lib/async-helpers.d.ts +8 -0
- package/src/lib/constants.d.ts +41 -0
- package/src/lib/dev-proxy.d.ts +3 -0
- package/src/lib/edit-session.d.ts +21 -0
- package/src/lib/entity-signal.d.ts +1 -0
- package/src/lib/internals/batch-scope.d.ts +3 -0
- package/src/lib/internals/builder-types.d.ts +13 -0
- package/src/lib/internals/derived-types.d.ts +19 -0
- package/src/lib/internals/materialize-markers.d.ts +5 -0
- package/src/lib/internals/merge-derived.d.ts +4 -0
- package/src/lib/markers/derived.d.ts +9 -0
- package/src/lib/markers/entity-map.d.ts +4 -0
- package/src/lib/markers/index.d.ts +3 -0
- package/src/lib/markers/status.d.ts +32 -0
- package/src/lib/markers/stored.d.ts +23 -0
- package/src/lib/memory/memory-manager.d.ts +30 -0
- package/src/lib/path-notifier.d.ts +34 -0
- package/src/lib/performance/diff-engine.d.ts +33 -0
- package/src/lib/performance/path-index.d.ts +25 -0
- package/src/lib/performance/update-engine.d.ts +32 -0
- package/src/lib/presets.d.ts +34 -0
- package/src/lib/security/security-validator.d.ts +33 -0
- package/src/lib/signal-tree.d.ts +6 -0
- package/src/lib/types.d.ts +300 -0
- package/src/lib/utils.d.ts +25 -0
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { isDerivedMarker, getDerivedMarkerSymbol, type DerivedMarker, type DerivedType, } from './derived';
|
|
2
|
+
export { status, isStatusMarker, createStatusSignal, LoadingState, STATUS_MARKER, type StatusMarker, type StatusSignal, type StatusConfig, } from './status';
|
|
3
|
+
export { stored, isStoredMarker, createStoredSignal, STORED_MARKER, type StoredMarker, type StoredSignal, type StoredOptions, } from './stored';
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Signal, WritableSignal } from '@angular/core';
|
|
2
|
+
export declare const STATUS_MARKER: unique symbol;
|
|
3
|
+
export declare enum LoadingState {
|
|
4
|
+
NotLoaded = "NOT_LOADED",
|
|
5
|
+
Loading = "LOADING",
|
|
6
|
+
Loaded = "LOADED",
|
|
7
|
+
Error = "ERROR"
|
|
8
|
+
}
|
|
9
|
+
export interface StatusConfig {
|
|
10
|
+
initialState?: LoadingState;
|
|
11
|
+
}
|
|
12
|
+
export interface StatusMarker<E = Error> {
|
|
13
|
+
[STATUS_MARKER]: true;
|
|
14
|
+
initialState: LoadingState;
|
|
15
|
+
readonly __errorType?: E;
|
|
16
|
+
}
|
|
17
|
+
export interface StatusSignal<E = Error> {
|
|
18
|
+
state: WritableSignal<LoadingState>;
|
|
19
|
+
error: WritableSignal<E | null>;
|
|
20
|
+
isNotLoaded: Signal<boolean>;
|
|
21
|
+
isLoading: Signal<boolean>;
|
|
22
|
+
isLoaded: Signal<boolean>;
|
|
23
|
+
isError: Signal<boolean>;
|
|
24
|
+
setNotLoaded(): void;
|
|
25
|
+
setLoading(): void;
|
|
26
|
+
setLoaded(): void;
|
|
27
|
+
setError(error: E): void;
|
|
28
|
+
reset(): void;
|
|
29
|
+
}
|
|
30
|
+
export declare function status<E = Error>(initialState?: LoadingState): StatusMarker<E>;
|
|
31
|
+
export declare function isStatusMarker(value: unknown): value is StatusMarker;
|
|
32
|
+
export declare function createStatusSignal<E = Error>(marker: StatusMarker<E>): StatusSignal<E>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export declare const STORED_MARKER: unique symbol;
|
|
2
|
+
export interface StoredOptions<T> {
|
|
3
|
+
serialize?: (value: T) => string;
|
|
4
|
+
deserialize?: (stored: string) => T;
|
|
5
|
+
storage?: Storage | null;
|
|
6
|
+
debounceMs?: number;
|
|
7
|
+
}
|
|
8
|
+
export interface StoredMarker<T> {
|
|
9
|
+
[STORED_MARKER]: true;
|
|
10
|
+
key: string;
|
|
11
|
+
defaultValue: T;
|
|
12
|
+
options: StoredOptions<T>;
|
|
13
|
+
}
|
|
14
|
+
export interface StoredSignal<T> {
|
|
15
|
+
(): T;
|
|
16
|
+
set(value: T): void;
|
|
17
|
+
update(fn: (current: T) => T): void;
|
|
18
|
+
clear(): void;
|
|
19
|
+
reload(): void;
|
|
20
|
+
}
|
|
21
|
+
export declare function stored<T>(key: string, defaultValue: T, options?: StoredOptions<T>): StoredMarker<T>;
|
|
22
|
+
export declare function isStoredMarker(value: unknown): value is StoredMarker<unknown>;
|
|
23
|
+
export declare function createStoredSignal<T>(marker: StoredMarker<T>): StoredSignal<T>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { WritableSignal } from '@angular/core';
|
|
2
|
+
export interface MemoryStats {
|
|
3
|
+
cachedSignals: number;
|
|
4
|
+
cleanedUpSignals: number;
|
|
5
|
+
peakCachedSignals: number;
|
|
6
|
+
manualDisposes: number;
|
|
7
|
+
estimatedMemoryBytes: number;
|
|
8
|
+
}
|
|
9
|
+
export interface MemoryManagerConfig {
|
|
10
|
+
enableAutoCleanup?: boolean;
|
|
11
|
+
debugMode?: boolean;
|
|
12
|
+
onCleanup?: (path: string, stats: MemoryStats) => void;
|
|
13
|
+
}
|
|
14
|
+
export declare class SignalMemoryManager {
|
|
15
|
+
private cache;
|
|
16
|
+
private registry;
|
|
17
|
+
private config;
|
|
18
|
+
private stats;
|
|
19
|
+
constructor(config?: MemoryManagerConfig);
|
|
20
|
+
cacheSignal<T>(path: string, signal: WritableSignal<T>): void;
|
|
21
|
+
getSignal(path: string): WritableSignal<unknown> | undefined;
|
|
22
|
+
hasSignal(path: string): boolean;
|
|
23
|
+
removeSignal(path: string): boolean;
|
|
24
|
+
private handleCleanup;
|
|
25
|
+
getStats(): MemoryStats;
|
|
26
|
+
dispose(): void;
|
|
27
|
+
getCachedPaths(): string[];
|
|
28
|
+
clearStale(): number;
|
|
29
|
+
resetStats(): void;
|
|
30
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type PathNotifierInterceptor = (value: unknown, prev: unknown, path: string) => {
|
|
2
|
+
block?: boolean;
|
|
3
|
+
transform?: unknown;
|
|
4
|
+
};
|
|
5
|
+
export declare class PathNotifier {
|
|
6
|
+
private subscribers;
|
|
7
|
+
private interceptors;
|
|
8
|
+
private batchingEnabled;
|
|
9
|
+
private pendingFlush;
|
|
10
|
+
private pending;
|
|
11
|
+
private firstValues;
|
|
12
|
+
private flushCallbacks;
|
|
13
|
+
constructor(options?: {
|
|
14
|
+
batching?: boolean;
|
|
15
|
+
});
|
|
16
|
+
setBatchingEnabled(enabled: boolean): void;
|
|
17
|
+
isBatchingEnabled(): boolean;
|
|
18
|
+
subscribe(pattern: string, handler: PathNotifierHandler): () => void;
|
|
19
|
+
intercept(pattern: string, interceptor: PathNotifierInterceptor): () => void;
|
|
20
|
+
notify(path: string, value: unknown, prev: unknown): {
|
|
21
|
+
blocked: boolean;
|
|
22
|
+
value: unknown;
|
|
23
|
+
};
|
|
24
|
+
private _runNotify;
|
|
25
|
+
private flush;
|
|
26
|
+
flushSync(): void;
|
|
27
|
+
onFlush(callback: () => void): () => void;
|
|
28
|
+
hasPending(): boolean;
|
|
29
|
+
private matches;
|
|
30
|
+
clear(): void;
|
|
31
|
+
getSubscriberCount(): number;
|
|
32
|
+
getInterceptorCount(): number;
|
|
33
|
+
}
|
|
34
|
+
export declare function getPathNotifier(): PathNotifier;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Path } from './path-index';
|
|
2
|
+
export declare enum ChangeType {
|
|
3
|
+
ADD = "add",
|
|
4
|
+
UPDATE = "update",
|
|
5
|
+
DELETE = "delete",
|
|
6
|
+
REPLACE = "replace"
|
|
7
|
+
}
|
|
8
|
+
export interface Change {
|
|
9
|
+
type: ChangeType;
|
|
10
|
+
path: Path;
|
|
11
|
+
value?: unknown;
|
|
12
|
+
oldValue?: unknown;
|
|
13
|
+
}
|
|
14
|
+
export interface Diff {
|
|
15
|
+
changes: Change[];
|
|
16
|
+
hasChanges: boolean;
|
|
17
|
+
}
|
|
18
|
+
export interface DiffOptions {
|
|
19
|
+
maxDepth?: number;
|
|
20
|
+
detectDeletions?: boolean;
|
|
21
|
+
ignoreArrayOrder?: boolean;
|
|
22
|
+
equalityFn?: (a: unknown, b: unknown) => boolean;
|
|
23
|
+
keyValidator?: (key: string) => boolean;
|
|
24
|
+
}
|
|
25
|
+
export declare class DiffEngine {
|
|
26
|
+
private defaultOptions;
|
|
27
|
+
diff(current: unknown, updates: unknown, options?: DiffOptions): Diff;
|
|
28
|
+
private traverse;
|
|
29
|
+
private diffArrays;
|
|
30
|
+
private diffArraysOrdered;
|
|
31
|
+
private diffArraysUnordered;
|
|
32
|
+
private stringify;
|
|
33
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { WritableSignal } from '@angular/core';
|
|
2
|
+
export type PathSegment = string | number;
|
|
3
|
+
export type Path = PathSegment[];
|
|
4
|
+
export declare class PathIndex<T extends object = WritableSignal<any>> {
|
|
5
|
+
private root;
|
|
6
|
+
private pathCache;
|
|
7
|
+
private stats;
|
|
8
|
+
set(path: Path, signal: T): void;
|
|
9
|
+
get(path: Path): T | null;
|
|
10
|
+
has(path: Path): boolean;
|
|
11
|
+
getByPrefix(prefix: Path): Map<string, T>;
|
|
12
|
+
delete(path: Path): boolean;
|
|
13
|
+
clear(): void;
|
|
14
|
+
getStats(): {
|
|
15
|
+
hits: number;
|
|
16
|
+
misses: number;
|
|
17
|
+
sets: number;
|
|
18
|
+
cleanups: number;
|
|
19
|
+
hitRate: number;
|
|
20
|
+
cacheSize: number;
|
|
21
|
+
};
|
|
22
|
+
buildFromTree(tree: unknown, path?: Path): void;
|
|
23
|
+
private pathToString;
|
|
24
|
+
private collectDescendants;
|
|
25
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { PathIndex } from './path-index';
|
|
2
|
+
import type { DiffOptions } from './diff-engine';
|
|
3
|
+
export interface UpdateOptions extends DiffOptions {
|
|
4
|
+
batch?: boolean;
|
|
5
|
+
batchSize?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface UpdateResult {
|
|
8
|
+
changed: boolean;
|
|
9
|
+
duration: number;
|
|
10
|
+
changedPaths: string[];
|
|
11
|
+
stats?: {
|
|
12
|
+
totalPaths: number;
|
|
13
|
+
optimizedPaths: number;
|
|
14
|
+
batchedUpdates: number;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export declare class OptimizedUpdateEngine {
|
|
18
|
+
private pathIndex;
|
|
19
|
+
private diffEngine;
|
|
20
|
+
constructor(tree: unknown);
|
|
21
|
+
update(tree: unknown, updates: unknown, options?: UpdateOptions): UpdateResult;
|
|
22
|
+
rebuildIndex(tree: unknown): void;
|
|
23
|
+
getIndexStats(): ReturnType<PathIndex['getStats']>;
|
|
24
|
+
private createPatches;
|
|
25
|
+
private createPatch;
|
|
26
|
+
private calculatePriority;
|
|
27
|
+
private sortPatches;
|
|
28
|
+
private applyPatches;
|
|
29
|
+
private batchApplyPatches;
|
|
30
|
+
private applyPatch;
|
|
31
|
+
private isEqual;
|
|
32
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ISignalTree, TreeConfig, BatchingConfig, MemoizationConfig, TimeTravelConfig, DevToolsConfig, BatchingMethods, MemoizationMethods, TimeTravelMethods, DevToolsMethods, EffectsMethods, EntitiesEnabled } from './types';
|
|
2
|
+
export interface DevTreeConfig extends TreeConfig {
|
|
3
|
+
effects?: Record<string, never>;
|
|
4
|
+
batching?: BatchingConfig;
|
|
5
|
+
memoization?: MemoizationConfig;
|
|
6
|
+
timeTravel?: TimeTravelConfig;
|
|
7
|
+
devTools?: DevToolsConfig;
|
|
8
|
+
entities?: Record<string, never>;
|
|
9
|
+
}
|
|
10
|
+
export interface ProdTreeConfig extends TreeConfig {
|
|
11
|
+
effects?: Record<string, never>;
|
|
12
|
+
batching?: BatchingConfig;
|
|
13
|
+
memoization?: MemoizationConfig;
|
|
14
|
+
entities?: Record<string, never>;
|
|
15
|
+
}
|
|
16
|
+
export interface MinimalTreeConfig extends TreeConfig {
|
|
17
|
+
effects?: Record<string, never>;
|
|
18
|
+
}
|
|
19
|
+
export type FullSignalTree<T> = ISignalTree<T> & EffectsMethods<T> & BatchingMethods & MemoizationMethods<T> & EntitiesEnabled & TimeTravelMethods & DevToolsMethods;
|
|
20
|
+
export type ProdSignalTree<T> = ISignalTree<T> & EffectsMethods<T> & BatchingMethods & MemoizationMethods<T> & EntitiesEnabled;
|
|
21
|
+
export type MinimalSignalTree<T> = ISignalTree<T> & EffectsMethods<T>;
|
|
22
|
+
export declare function createDevTree<T extends object>(initialState: T, config?: DevTreeConfig): FullSignalTree<T>;
|
|
23
|
+
export declare function createDevTree(): {
|
|
24
|
+
enhancer: <T>(tree: ISignalTree<T>) => ISignalTree<T> & EffectsMethods<T> & BatchingMethods<T> & MemoizationMethods<T> & EntitiesEnabled & TimeTravelMethods<T> & DevToolsMethods;
|
|
25
|
+
};
|
|
26
|
+
export declare function createProdTree<T extends object>(initialState: T, config?: ProdTreeConfig): ProdSignalTree<T>;
|
|
27
|
+
export declare function createMinimalTree<T extends object>(initialState: T, config?: MinimalTreeConfig): MinimalSignalTree<T>;
|
|
28
|
+
export declare const devTree: typeof createDevTree;
|
|
29
|
+
export declare const prodTree: typeof createProdTree;
|
|
30
|
+
export declare const minimalTree: typeof createMinimalTree;
|
|
31
|
+
export declare function buildTree<T extends object>(initialState: T, config?: TreeConfig): {
|
|
32
|
+
add<R>(enhancer: (t: ISignalTree<T>) => R): any;
|
|
33
|
+
done(): ISignalTree<T>;
|
|
34
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export type SecurityEventType = 'dangerous-key-blocked' | 'xss-attempt-blocked' | 'function-value-blocked' | 'validation-error';
|
|
2
|
+
export interface SecurityEvent {
|
|
3
|
+
type: SecurityEventType;
|
|
4
|
+
key?: string;
|
|
5
|
+
value?: unknown;
|
|
6
|
+
reason: string;
|
|
7
|
+
timestamp: number;
|
|
8
|
+
}
|
|
9
|
+
export interface SecurityValidatorConfig {
|
|
10
|
+
preventPrototypePollution?: boolean;
|
|
11
|
+
preventXSS?: boolean;
|
|
12
|
+
preventFunctions?: boolean;
|
|
13
|
+
customDangerousKeys?: string[];
|
|
14
|
+
onSecurityEvent?: (event: SecurityEvent) => void;
|
|
15
|
+
sanitizationMode?: 'strict' | 'permissive';
|
|
16
|
+
}
|
|
17
|
+
export declare class SecurityValidator {
|
|
18
|
+
private readonly config;
|
|
19
|
+
private readonly dangerousKeys;
|
|
20
|
+
constructor(config?: SecurityValidatorConfig);
|
|
21
|
+
validateKey(key: string): void;
|
|
22
|
+
validateValue<T>(value: T): T;
|
|
23
|
+
private sanitize;
|
|
24
|
+
validateKeyValue<T>(key: string, value: T): T;
|
|
25
|
+
isDangerousKey(key: string): boolean;
|
|
26
|
+
getConfig(): Readonly<Required<SecurityValidatorConfig>>;
|
|
27
|
+
}
|
|
28
|
+
export declare const SecurityPresets: {
|
|
29
|
+
strict: () => SecurityValidator;
|
|
30
|
+
standard: () => SecurityValidator;
|
|
31
|
+
permissive: () => SecurityValidator;
|
|
32
|
+
disabled: () => SecurityValidator;
|
|
33
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { SignalTreeBuilder } from './internals/builder-types';
|
|
2
|
+
import { ProcessDerived } from './internals/derived-types';
|
|
3
|
+
import type { TreeNode, TreeConfig, NodeAccessor } from './types';
|
|
4
|
+
export declare function isNodeAccessor(value: unknown): value is NodeAccessor<unknown>;
|
|
5
|
+
export declare function signalTree<T extends object, TDerived extends object>(initialState: T, derivedFactory: ($: TreeNode<T>) => TDerived): SignalTreeBuilder<T, TreeNode<T> & ProcessDerived<TDerived>>;
|
|
6
|
+
export declare function signalTree<T extends object>(initialState: T, config?: TreeConfig): SignalTreeBuilder<T, TreeNode<T>>;
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { Signal, WritableSignal } from '@angular/core';
|
|
2
|
+
import { StatusMarker, StatusSignal } from './markers/status';
|
|
3
|
+
import { StoredMarker, StoredSignal } from './markers/stored';
|
|
4
|
+
import { SecurityValidatorConfig } from './security/security-validator';
|
|
5
|
+
export interface TimeTravelConfig {
|
|
6
|
+
enabled?: boolean;
|
|
7
|
+
maxHistorySize?: number;
|
|
8
|
+
includePayload?: boolean;
|
|
9
|
+
actionNames?: {
|
|
10
|
+
update?: string;
|
|
11
|
+
set?: string;
|
|
12
|
+
batch?: string;
|
|
13
|
+
[key: string]: string | undefined;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export interface MemoizationConfig {
|
|
17
|
+
enabled?: boolean;
|
|
18
|
+
maxCacheSize?: number;
|
|
19
|
+
ttl?: number;
|
|
20
|
+
enableLRU?: boolean;
|
|
21
|
+
equality?: 'deep' | 'shallow' | 'reference';
|
|
22
|
+
}
|
|
23
|
+
export type Primitive = string | number | boolean | null | undefined | bigint | symbol;
|
|
24
|
+
export type NotFn<T> = T extends (...args: unknown[]) => unknown ? never : T;
|
|
25
|
+
declare module '@angular/core' {
|
|
26
|
+
interface WritableSignal<T> {
|
|
27
|
+
(value: NotFn<T>): void;
|
|
28
|
+
(updater: (current: T) => T): void;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export interface NodeAccessor<T> {
|
|
32
|
+
(): T;
|
|
33
|
+
(value: Partial<T>): void;
|
|
34
|
+
(updater: (current: T) => T): void;
|
|
35
|
+
}
|
|
36
|
+
export type TreeNode<T> = {
|
|
37
|
+
[K in keyof T]: T[K] extends EntityMapMarker<infer E, infer Key> ? EntitySignal<E, Key> : T[K] extends StatusMarker<infer Err> ? StatusSignal<Err> : T[K] extends StoredMarker<infer V> ? StoredSignal<V> : T[K] extends Primitive ? CallableWritableSignal<T[K]> : T[K] extends readonly unknown[] ? CallableWritableSignal<T[K]> : T[K] extends Date | RegExp | Map<any, any> | Set<any> | Error | ((...args: unknown[]) => unknown) ? CallableWritableSignal<T[K]> : T[K] extends object ? NodeAccessor<T[K]> & TreeNode<T[K]> : CallableWritableSignal<T[K]>;
|
|
38
|
+
};
|
|
39
|
+
export interface ISignalTree<T> extends NodeAccessor<T> {
|
|
40
|
+
readonly state: TreeNode<T>;
|
|
41
|
+
readonly $: TreeNode<T>;
|
|
42
|
+
with<TAdded>(enhancer: (tree: ISignalTree<T>) => ISignalTree<T> & TAdded): this & TAdded;
|
|
43
|
+
bind(thisArg?: unknown): NodeAccessor<T>;
|
|
44
|
+
destroy(): void;
|
|
45
|
+
}
|
|
46
|
+
export interface EffectsMethods<T> {
|
|
47
|
+
effect(fn: (state: T) => void | (() => void)): () => void;
|
|
48
|
+
subscribe(fn: (state: T) => void): () => void;
|
|
49
|
+
}
|
|
50
|
+
export interface BatchingConfig {
|
|
51
|
+
enabled?: boolean;
|
|
52
|
+
notificationDelayMs?: number;
|
|
53
|
+
}
|
|
54
|
+
export interface BatchingMethods<T = unknown> {
|
|
55
|
+
batch(fn: () => void): void;
|
|
56
|
+
coalesce(fn: () => void): void;
|
|
57
|
+
hasPendingNotifications(): boolean;
|
|
58
|
+
flushNotifications(): void;
|
|
59
|
+
}
|
|
60
|
+
export interface MemoizationMethods<T> {
|
|
61
|
+
memoize<R>(fn: (state: T) => R, cacheKey?: string): Signal<R>;
|
|
62
|
+
memoizedUpdate?: (updater: (current: T) => Partial<T>, cacheKey?: string) => void;
|
|
63
|
+
clearMemoCache(key?: string): void;
|
|
64
|
+
clearCache?: (key?: string) => void;
|
|
65
|
+
getCacheStats(): CacheStats;
|
|
66
|
+
}
|
|
67
|
+
export type CacheStats = {
|
|
68
|
+
size: number;
|
|
69
|
+
hitRate: number;
|
|
70
|
+
totalHits: number;
|
|
71
|
+
totalMisses: number;
|
|
72
|
+
keys: string[];
|
|
73
|
+
};
|
|
74
|
+
export interface TimeTravelMethods<T = unknown> {
|
|
75
|
+
undo(): void;
|
|
76
|
+
redo(): void;
|
|
77
|
+
canUndo(): boolean;
|
|
78
|
+
canRedo(): boolean;
|
|
79
|
+
getHistory(): TimeTravelEntry<T>[];
|
|
80
|
+
resetHistory(): void;
|
|
81
|
+
jumpTo(index: number): void;
|
|
82
|
+
getCurrentIndex(): number;
|
|
83
|
+
readonly __timeTravel?: {
|
|
84
|
+
undo(): void;
|
|
85
|
+
redo(): void;
|
|
86
|
+
canUndo(): boolean;
|
|
87
|
+
canRedo(): boolean;
|
|
88
|
+
getHistory(): TimeTravelEntry<T>[];
|
|
89
|
+
resetHistory(): void;
|
|
90
|
+
jumpTo(index: number): void;
|
|
91
|
+
getCurrentIndex(): number;
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
export interface DevToolsMethods {
|
|
95
|
+
connectDevTools(): void;
|
|
96
|
+
disconnectDevTools(): void;
|
|
97
|
+
}
|
|
98
|
+
export interface EntitiesEnabled {
|
|
99
|
+
}
|
|
100
|
+
export interface OptimizedUpdateMethods<T> {
|
|
101
|
+
updateOptimized(updates: Partial<T>, options?: {
|
|
102
|
+
batch?: boolean;
|
|
103
|
+
batchSize?: number;
|
|
104
|
+
maxDepth?: number;
|
|
105
|
+
ignoreArrayOrder?: boolean;
|
|
106
|
+
equalityFn?: (a: unknown, b: unknown) => boolean;
|
|
107
|
+
}): {
|
|
108
|
+
changed: boolean;
|
|
109
|
+
duration: number;
|
|
110
|
+
changedPaths: string[];
|
|
111
|
+
stats?: {
|
|
112
|
+
totalPaths: number;
|
|
113
|
+
optimizedPaths: number;
|
|
114
|
+
batchedUpdates: number;
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
export interface TimeTravelEntry<T> {
|
|
119
|
+
action: string;
|
|
120
|
+
timestamp: number;
|
|
121
|
+
state: T;
|
|
122
|
+
payload?: unknown;
|
|
123
|
+
}
|
|
124
|
+
export type TreePreset = 'basic' | 'performance' | 'development' | 'production';
|
|
125
|
+
export interface TreeConfig {
|
|
126
|
+
batchUpdates?: boolean;
|
|
127
|
+
useMemoization?: boolean;
|
|
128
|
+
enableTimeTravel?: boolean;
|
|
129
|
+
useLazySignals?: boolean;
|
|
130
|
+
useShallowComparison?: boolean;
|
|
131
|
+
maxCacheSize?: number;
|
|
132
|
+
trackPerformance?: boolean;
|
|
133
|
+
treeName?: string;
|
|
134
|
+
enableDevTools?: boolean;
|
|
135
|
+
debugMode?: boolean;
|
|
136
|
+
useStructuralSharing?: boolean;
|
|
137
|
+
security?: SecurityValidatorConfig;
|
|
138
|
+
}
|
|
139
|
+
export interface EntityConfig<E, K extends string | number = string> {
|
|
140
|
+
selectId?: (entity: E) => K;
|
|
141
|
+
hooks?: {
|
|
142
|
+
beforeAdd?: (entity: E) => E | false;
|
|
143
|
+
beforeUpdate?: (id: K, changes: Partial<E>) => Partial<E> | false;
|
|
144
|
+
beforeRemove?: (id: K, entity: E) => boolean;
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
declare const ENTITY_MAP_BRAND: unique symbol;
|
|
148
|
+
export interface EntityMapMarker<E, K extends string | number> {
|
|
149
|
+
readonly [ENTITY_MAP_BRAND]: {
|
|
150
|
+
__entity: E;
|
|
151
|
+
__key: K;
|
|
152
|
+
};
|
|
153
|
+
readonly __isEntityMap: true;
|
|
154
|
+
readonly __entityMapConfig?: EntityConfig<E, K>;
|
|
155
|
+
}
|
|
156
|
+
export { entityMap } from './markers/entity-map';
|
|
157
|
+
export interface MutationOptions {
|
|
158
|
+
onError?: (error: Error) => void;
|
|
159
|
+
}
|
|
160
|
+
export interface AddOptions<E, K> extends MutationOptions {
|
|
161
|
+
selectId?: (entity: E) => K;
|
|
162
|
+
}
|
|
163
|
+
export interface AddManyOptions<E, K> extends AddOptions<E, K> {
|
|
164
|
+
mode?: 'strict' | 'skip' | 'overwrite';
|
|
165
|
+
}
|
|
166
|
+
export interface TapHandlers<E, K extends string | number> {
|
|
167
|
+
onAdd?: (entity: E, id: K) => void;
|
|
168
|
+
onUpdate?: (id: K, changes: Partial<E>, entity: E) => void;
|
|
169
|
+
onRemove?: (id: K, entity: E) => void;
|
|
170
|
+
onChange?: () => void;
|
|
171
|
+
}
|
|
172
|
+
export interface InterceptContext<T> {
|
|
173
|
+
block(reason?: string): void;
|
|
174
|
+
transform(value: T): void;
|
|
175
|
+
readonly blocked: boolean;
|
|
176
|
+
readonly blockReason: string | undefined;
|
|
177
|
+
}
|
|
178
|
+
export interface InterceptHandlers<E, K extends string | number> {
|
|
179
|
+
onAdd?: (entity: E, ctx: InterceptContext<E>) => void | Promise<void>;
|
|
180
|
+
onUpdate?: (id: K, changes: Partial<E>, ctx: InterceptContext<Partial<E>>) => void | Promise<void>;
|
|
181
|
+
onRemove?: (id: K, entity: E, ctx: InterceptContext<void>) => void | Promise<void>;
|
|
182
|
+
}
|
|
183
|
+
export type EntityNode<E> = {
|
|
184
|
+
(): E;
|
|
185
|
+
(value: E): void;
|
|
186
|
+
(updater: (current: E) => E): void;
|
|
187
|
+
} & {
|
|
188
|
+
[P in keyof E]: E[P] extends object ? E[P] extends readonly unknown[] ? CallableWritableSignal<E[P]> : EntityNode<E[P]> : CallableWritableSignal<E[P]>;
|
|
189
|
+
};
|
|
190
|
+
export interface EntitySignal<E, K extends string | number = string> {
|
|
191
|
+
byId(id: K): EntityNode<E> | undefined;
|
|
192
|
+
byIdOrFail(id: K): EntityNode<E>;
|
|
193
|
+
readonly all: Signal<E[]>;
|
|
194
|
+
readonly count: Signal<number>;
|
|
195
|
+
readonly ids: Signal<K[]>;
|
|
196
|
+
has(id: K): Signal<boolean>;
|
|
197
|
+
readonly isEmpty: Signal<boolean>;
|
|
198
|
+
readonly map: Signal<ReadonlyMap<K, E>>;
|
|
199
|
+
where(predicate: (entity: E) => boolean): Signal<E[]>;
|
|
200
|
+
find(predicate: (entity: E) => boolean): Signal<E | undefined>;
|
|
201
|
+
addOne(entity: E, opts?: AddOptions<E, K>): K;
|
|
202
|
+
addMany(entities: E[], opts?: AddManyOptions<E, K>): K[];
|
|
203
|
+
updateOne(id: K, changes: Partial<E>, opts?: MutationOptions): void;
|
|
204
|
+
updateMany(ids: K[], changes: Partial<E>, opts?: MutationOptions): void;
|
|
205
|
+
updateWhere(predicate: (entity: E) => boolean, changes: Partial<E>): number;
|
|
206
|
+
upsertOne(entity: E, opts?: AddOptions<E, K>): K;
|
|
207
|
+
upsertMany(entities: E[], opts?: AddOptions<E, K>): K[];
|
|
208
|
+
removeOne(id: K, opts?: MutationOptions): void;
|
|
209
|
+
removeMany(ids: K[], opts?: MutationOptions): void;
|
|
210
|
+
removeWhere(predicate: (entity: E) => boolean): number;
|
|
211
|
+
clear(): void;
|
|
212
|
+
removeAll(): void;
|
|
213
|
+
setAll(entities: E[], opts?: AddOptions<E, K>): void;
|
|
214
|
+
tap(handlers: TapHandlers<E, K>): () => void;
|
|
215
|
+
intercept(handlers: InterceptHandlers<E, K>): () => void;
|
|
216
|
+
}
|
|
217
|
+
export interface LoggingConfig {
|
|
218
|
+
name?: string;
|
|
219
|
+
filter?: (path: string) => boolean;
|
|
220
|
+
collapsed?: boolean;
|
|
221
|
+
onLog?: (entry: LogEntry) => void;
|
|
222
|
+
}
|
|
223
|
+
export interface LogEntry {
|
|
224
|
+
path: string;
|
|
225
|
+
prev: unknown;
|
|
226
|
+
value: unknown;
|
|
227
|
+
timestamp: number;
|
|
228
|
+
}
|
|
229
|
+
export interface ValidationConfig<T> {
|
|
230
|
+
validators: Array<{
|
|
231
|
+
match: (path: string) => boolean;
|
|
232
|
+
validate: (value: T, path: string) => void | never;
|
|
233
|
+
}>;
|
|
234
|
+
onError?: (error: Error, path: string) => void;
|
|
235
|
+
}
|
|
236
|
+
export interface PersistenceConfig {
|
|
237
|
+
key: string;
|
|
238
|
+
storage?: Storage;
|
|
239
|
+
debounceMs?: number;
|
|
240
|
+
filter?: (path: string) => boolean;
|
|
241
|
+
serialize?: (state: unknown) => string;
|
|
242
|
+
deserialize?: (json: string) => unknown;
|
|
243
|
+
}
|
|
244
|
+
export interface DevToolsConfig {
|
|
245
|
+
enableBrowserDevTools?: boolean;
|
|
246
|
+
enableLogging?: boolean;
|
|
247
|
+
performanceThreshold?: number;
|
|
248
|
+
name?: string;
|
|
249
|
+
treeName?: string;
|
|
250
|
+
enabled?: boolean;
|
|
251
|
+
logActions?: boolean;
|
|
252
|
+
maxAge?: number;
|
|
253
|
+
features?: {
|
|
254
|
+
jump?: boolean;
|
|
255
|
+
skip?: boolean;
|
|
256
|
+
reorder?: boolean;
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
export type EntityType<T> = T extends EntitySignal<infer E, infer K extends string | number> ? E : never;
|
|
260
|
+
export type EntityKeyType<T> = T extends EntitySignal<unknown, infer K extends string | number> ? K : never;
|
|
261
|
+
export type IsEntityMap<T> = T extends EntityMapMarker<unknown, infer K extends string | number> ? true : false;
|
|
262
|
+
export type DeepEntityAwareTreeNode<T> = {
|
|
263
|
+
[K in keyof T]: T[K] extends EntityMapMarker<infer E, infer Key> ? EntitySignal<E, Key> : T[K] extends object ? DeepEntityAwareTreeNode<T[K]> : CallableWritableSignal<T[K]>;
|
|
264
|
+
};
|
|
265
|
+
export type EntityAwareTreeNode<T> = {
|
|
266
|
+
[K in keyof T]: T[K] extends EntityMapMarker<infer E, infer Key> ? EntitySignal<E, Key> : CallableWritableSignal<T[K]>;
|
|
267
|
+
};
|
|
268
|
+
export type TypedSignalTree<T> = ISignalTree<T> & {
|
|
269
|
+
$: DeepEntityAwareTreeNode<T>;
|
|
270
|
+
};
|
|
271
|
+
export type PathHandler = (value: unknown, prev: unknown, path: string) => void;
|
|
272
|
+
export type PathInterceptor = (ctx: {
|
|
273
|
+
path: string;
|
|
274
|
+
value: unknown;
|
|
275
|
+
prev: unknown;
|
|
276
|
+
blocked: boolean;
|
|
277
|
+
blockReason?: string;
|
|
278
|
+
}, next: () => void) => void | Promise<void>;
|
|
279
|
+
export type CallableWritableSignal<T> = WritableSignal<T> & {
|
|
280
|
+
(value: NotFn<T>): void;
|
|
281
|
+
(updater: (current: T) => T): void;
|
|
282
|
+
};
|
|
283
|
+
export type AccessibleNode<T> = NodeAccessor<T> & TreeNode<T>;
|
|
284
|
+
export declare const ENHANCER_META: unique symbol;
|
|
285
|
+
export type Enhancer<TAdded> = (tree: ISignalTree<any>) => ISignalTree<any> & TAdded;
|
|
286
|
+
export type EnhancerWithMeta<TAdded> = Enhancer<TAdded> & {
|
|
287
|
+
metadata?: EnhancerMeta;
|
|
288
|
+
};
|
|
289
|
+
export interface EnhancerMeta {
|
|
290
|
+
name?: string;
|
|
291
|
+
requires?: string[];
|
|
292
|
+
provides?: string[];
|
|
293
|
+
description?: string;
|
|
294
|
+
}
|
|
295
|
+
export type FullSignalTree<T> = ISignalTree<T> & EffectsMethods<T> & BatchingMethods<T> & MemoizationMethods<T> & TimeTravelMethods<T> & DevToolsMethods & EntitiesEnabled & OptimizedUpdateMethods<T>;
|
|
296
|
+
export type ProdSignalTree<T> = ISignalTree<T> & EffectsMethods<T> & BatchingMethods<T> & MemoizationMethods<T> & EntitiesEnabled & OptimizedUpdateMethods<T>;
|
|
297
|
+
export type MinimalSignalTree<T> = ISignalTree<T> & EffectsMethods<T>;
|
|
298
|
+
export type SignalTree<T> = ISignalTree<T> & TreeNode<T>;
|
|
299
|
+
export type SignalTreeBase<T> = ISignalTree<T> & TreeNode<T>;
|
|
300
|
+
export declare function isSignalTree<T>(value: unknown): value is ISignalTree<T>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { WritableSignal } from '@angular/core';
|
|
2
|
+
import { deepEqual, isBuiltInObject, parsePath } from '@signaltree/shared';
|
|
3
|
+
export { deepEqual };
|
|
4
|
+
export { deepEqual as equal };
|
|
5
|
+
export { isBuiltInObject };
|
|
6
|
+
export { parsePath };
|
|
7
|
+
export declare function isEntityMapMarker(value: unknown): boolean;
|
|
8
|
+
export interface MemoryManager {
|
|
9
|
+
getSignal(path: string): WritableSignal<unknown> | undefined;
|
|
10
|
+
cacheSignal(path: string, signal: WritableSignal<unknown>): void;
|
|
11
|
+
dispose(): void;
|
|
12
|
+
}
|
|
13
|
+
import type { NodeAccessor, TreeNode } from './types';
|
|
14
|
+
export declare function isNodeAccessor(value: unknown): value is NodeAccessor<unknown>;
|
|
15
|
+
export declare function isAnySignal(value: unknown): boolean;
|
|
16
|
+
export declare function toWritableSignal<T>(node: NodeAccessor<T>, injector?: unknown): WritableSignal<T>;
|
|
17
|
+
export declare function composeEnhancers<T>(...enhancers: Array<(tree: T) => T>): (tree: T) => T;
|
|
18
|
+
export declare function createLazySignalTree<T extends object>(obj: T, equalityFn: (a: unknown, b: unknown) => boolean, basePath?: string, memoryManager?: MemoryManager): TreeNode<T>;
|
|
19
|
+
export declare function unwrap<T>(node: TreeNode<T>): T;
|
|
20
|
+
export declare function unwrap<T>(node: NodeAccessor<T> & TreeNode<T>): T;
|
|
21
|
+
export declare function unwrap<T>(node: NodeAccessor<T>): T;
|
|
22
|
+
export declare function unwrap<T>(node: unknown): T;
|
|
23
|
+
export declare function snapshotState<T>(state: TreeNode<T>): T;
|
|
24
|
+
export declare function applyState<T>(stateNode: TreeNode<T>, snapshot: T): void;
|
|
25
|
+
export declare function deepCloneJSON<T>(value: T): T;
|