@sladg/apex-state 3.4.1 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -58,7 +58,7 @@ const UserForm = () => {
58
58
 
59
59
  | Feature | Description | Details |
60
60
  |---|---|---|
61
- | **Type-safe paths** | `DeepKey<T>` / `DeepValue<T, P>` — compile-time path safety | |
61
+ | **Type-safe paths** | `DeepKey<T>` / `DeepValue<T, P>` — compile-time path safety with configurable depth | |
62
62
  | **Concerns** | Validation (Zod), BoolLogic conditions, dynamic text | [Concerns Guide](docs/guides/CONCERNS_GUIDE.md) |
63
63
  | **Side effects** | Sync paths, flip paths, aggregations, listeners | [Side Effects Guide](docs/SIDE_EFFECTS_GUIDE.md) |
64
64
  | **WASM mode** | Rust-powered pipeline for bulk operations (up to 367x faster) | [Architecture](docs/WASM_ARCHITECTURE.md) |
@@ -402,6 +402,61 @@ store.useConcerns('wildcards', {
402
402
  const nestedPath = `books.${_('b1')}.products.${_('p1')}.legs.${_('l1')}.notional`
403
403
  ```
404
404
 
405
+ ## Type-Safe Paths with Configurable Depth
406
+
407
+ Deeply nested state types can cause TypeScript errors like **TS2589 ("Type instantiation is excessively deep")** or slow IDE autocomplete. `DeepKey<T, Depth>` solves this with a configurable recursion limit — reduce depth for wide types to speed up compilation, increase it for deeply nested structures, and get clear feedback when the limit is reached.
408
+
409
+ ```typescript
410
+ type State = {
411
+ user: { name: string; address: { street: string; city: string } }
412
+ count: number
413
+ }
414
+
415
+ // DeepKey<State> = "user" | "count" | "user.name" | "user.address" | "user.address.street" | "user.address.city"
416
+ type AllPaths = DeepKey<State>
417
+ ```
418
+
419
+ ### Default Depth
420
+
421
+ `DefaultDepth` is set to **20** — enough for most real-world types. Uses loop unrolling (2 levels per recursion call) to stay well within TypeScript's recursion limits while supporting deeply nested structures.
422
+
423
+ ### Custom Depth
424
+
425
+ Tune the depth limit per use-site to balance **compilation speed vs path coverage**:
426
+
427
+ ```typescript
428
+ // Shallow — faster tsc for wide types with many top-level keys
429
+ type ShallowPaths = DeepKey<State, 10>
430
+
431
+ // Deeper — for types nested beyond 20 levels (e.g., recursive data models)
432
+ type DeepPaths = DeepKey<State, 30>
433
+ ```
434
+
435
+ ### Depth Limit Marker (`??`)
436
+
437
+ When `DeepKey` hits the depth limit on an object type, or encounters an **`any`-typed property** (unknowable structure), it emits a `??` marker in IDE autocomplete:
438
+
439
+ ```
440
+ some.deep.path.?? ← depth limit reached
441
+ data.payload.?? ← property typed as `any`
442
+ ```
443
+
444
+ This tells you exactly where path generation stopped. To fix it: increase depth (`DeepKey<T, 30>`), restructure your type, or replace `any` with a concrete type.
445
+
446
+ ### Depth Propagation
447
+
448
+ The `Depth` parameter propagates to all dependent types, keeping the entire type system consistent:
449
+
450
+ ```typescript
451
+ // All accept a Depth parameter (defaults to DefaultDepth = 20)
452
+ type Paths = DeepKey<State, 10>
453
+ type BoolPaths = DeepKeyFiltered<State, boolean, 10>
454
+ type Syncs = SyncPair<State, 10>
455
+ type Logic = BoolLogic<State, 10>
456
+ type Effects = SideEffects<State, Meta, 10>
457
+ type Clear = ClearPathRule<State, 10>
458
+ ```
459
+
405
460
  ## Testing with the Mock Module
406
461
 
407
462
  ```tsx
package/dist/index.d.ts CHANGED
@@ -21,6 +21,10 @@ type HASH_KEY = '[*]';
21
21
  * Supports nested objects up to depth 20 to handle deeply nested data structures.
22
22
  * Handles arrays and complex object hierarchies.
23
23
  *
24
+ * Uses loop unrolling: processes 2 nesting levels per recursion call,
25
+ * halving recursion depth while preserving bottom-up evaluation that
26
+ * TypeScript can cache (no prefix parameter).
27
+ *
24
28
  * Examples of supported paths:
25
29
  * - Simple: "name", "email"
26
30
  * - Nested: "user.address.street"
@@ -38,14 +42,84 @@ type HASH_KEY = '[*]';
38
42
  *
39
43
  * // DeepKey<User> = "name" | "address" | "address.street" | "address.city"
40
44
  * ```
45
+ *
46
+ * @example Custom depth — reduce for faster compilation on wide types
47
+ * ```typescript
48
+ * // Only traverse 10 levels deep instead of the default 20
49
+ * type ShallowPaths = DeepKey<User, 10>
50
+ * ```
51
+ *
52
+ * @example Depth limit marker — `??` appears at the cutoff point
53
+ * ```typescript
54
+ * // If a type is nested deeper than Depth, autocomplete shows:
55
+ * // "some.deep.path.??" — signaling you should increase depth or restructure
56
+ * type Paths = DeepKey<VeryDeepType, 6>
57
+ * ```
41
58
  */
42
59
 
43
60
  type Primitive = string | number | boolean | bigint | symbol | null | undefined;
44
61
  type IsAny$1<T> = 0 extends 1 & T ? true : false;
45
- type DeepKey<T, Depth extends number = 20> = Depth extends 0 ? never : IsAny$1<T> extends true ? never : T extends Primitive ? never : T extends readonly any[] ? never : string extends keyof T ? HASH_KEY | (T[string] extends Primitive ? never : T[string] extends readonly any[] ? never : DeepKey<T[string], Prev<Depth>> extends infer DK ? DK extends string ? `${HASH_KEY}.${DK}` : never : never) : {
46
- [K in keyof T & (string | number)]: `${K & string}` | (T[K] extends Primitive ? never : T[K] extends readonly any[] ? never : DeepKey<T[K], Prev<Depth>> extends infer DK ? DK extends string ? `${K & string}.${DK}` : never : never);
62
+ /**
63
+ * Default recursion depth for DeepKey and all types that depend on it.
64
+ * Change this single value to adjust the depth limit across the entire type system.
65
+ *
66
+ * Propagates to: SyncPair, FlipPair, AggregationPair, ComputationPair,
67
+ * BoolLogic, SideEffects, DeepKeyFiltered, ClearPathRule.
68
+ *
69
+ * @example Override per-call instead of changing the default
70
+ * ```typescript
71
+ * type Shallow = DeepKey<MyState, 10> // reduced depth
72
+ * type Deeper = DeepKey<MyState, 30> // increased depth
73
+ * ```
74
+ */
75
+ type DefaultDepth = 20;
76
+ /**
77
+ * Marker emitted when DeepKey reaches its depth limit on an object type,
78
+ * or when it encounters an `any`-typed property (unknowable structure).
79
+ * Appears as `some.deep.path.??` in autocomplete, signaling the cutoff point.
80
+ * Users seeing this can increase depth: `DeepKey<T, 30>` or restructure their type.
81
+ */
82
+ type DepthLimitMarker = '??';
83
+ type DeepKey<T, Depth extends number = DefaultDepth> = DeepKeyUnrolled<T, Depth> & string;
84
+ /**
85
+ * DeepKey with `??` marker paths excluded. Use in types that resolve path values
86
+ * (DeepValue, DeepKeyFiltered, PathsWithSameValueAs) where `??` would fail resolution.
87
+ * DeepKey itself keeps `??` for IDE autocomplete visibility.
88
+ */
89
+ type ResolvableDeepKey<T, Depth extends number = DefaultDepth> = Exclude<DeepKey<T, Depth>, `${string}??` | '??'>;
90
+ /**
91
+ * Loop-unrolled DeepKey: processes 2 nesting levels per recursion call.
92
+ *
93
+ * For each key K of T:
94
+ * - Emit K (level 1 path)
95
+ * - If T[K] is an object, inline level 2:
96
+ * - For each key K2 of T[K]:
97
+ * - Emit `${K}.${K2}` (level 2 path)
98
+ * - If T[K][K2] is an object, recurse with `${K}.${K2}.${DeepKeyUnrolled}`
99
+ *
100
+ * Key insight: DeepKeyUnrolled<T, Depth> has NO prefix parameter,
101
+ * so identical sub-types at the same depth are cached by TypeScript.
102
+ * Template literal distribution (`${prefix}.${union}`) is handled natively
103
+ * by TypeScript without extra type instantiation overhead.
104
+ */
105
+ type DeepKeyUnrolled<T, Depth extends number> = Depth extends 0 ? T extends Primitive | readonly any[] ? never : DepthLimitMarker : IsAny$1<T> extends true ? never : T extends Primitive ? never : T extends readonly any[] ? never : string extends keyof T ? HASH_KEY | (IsAny$1<T[string]> extends true ? `${HASH_KEY}.${DepthLimitMarker}` : NonNullable<T[string]> extends Primitive | readonly any[] ? never : RecordLevel2<NonNullable<T[string]>, HASH_KEY, Depth>) : {
106
+ [K in keyof T & (string | number)]: (K & string) | (IsAny$1<T[K]> extends true ? `${K & string}.${DepthLimitMarker}` : NonNullable<T[K]> extends Primitive | readonly any[] ? never : ConcreteLevel2<NonNullable<T[K]>, K & string, Depth>);
47
107
  }[keyof T & (string | number)];
48
- type Prev<N extends number> = N extends 20 ? 19 : N extends 19 ? 18 : N extends 18 ? 17 : N extends 17 ? 16 : N extends 16 ? 15 : N extends 15 ? 14 : N extends 14 ? 13 : N extends 13 ? 12 : N extends 12 ? 11 : N extends 11 ? 10 : N extends 10 ? 9 : N extends 9 ? 8 : N extends 8 ? 7 : N extends 7 ? 6 : N extends 6 ? 5 : N extends 5 ? 4 : N extends 4 ? 3 : N extends 3 ? 2 : N extends 2 ? 1 : N extends 1 ? 0 : never;
108
+ /**
109
+ * Level 2 expansion for concrete-key objects.
110
+ * Enumerates V's keys and uses inline template literal distribution for level 3+.
111
+ */
112
+ type ConcreteLevel2<V, ParentKey extends string, Depth extends number> = IsAny$1<V> extends true ? `${ParentKey}.${DepthLimitMarker}` : V extends Primitive ? never : V extends readonly any[] ? never : string extends keyof V ? `${ParentKey}.${HASH_KEY}` | (IsAny$1<V[string]> extends true ? `${ParentKey}.${HASH_KEY}.${DepthLimitMarker}` : NonNullable<V[string]> extends Primitive | readonly any[] ? never : `${ParentKey}.${HASH_KEY}.${DeepKeyUnrolled<NonNullable<V[string]>, Prev2<Depth>> & string}`) : {
113
+ [K2 in keyof V & (string | number)]: `${ParentKey}.${K2 & string}` | (IsAny$1<V[K2]> extends true ? `${ParentKey}.${K2 & string}.${DepthLimitMarker}` : NonNullable<V[K2]> extends Primitive | readonly any[] ? never : `${ParentKey}.${K2 & string}.${DeepKeyUnrolled<NonNullable<V[K2]>, Prev2<Depth>> & string}`);
114
+ }[keyof V & (string | number)];
115
+ /**
116
+ * Level 2 expansion for Record-value objects.
117
+ * When level 1 was a Record, its value type V is expanded at level 2.
118
+ */
119
+ type RecordLevel2<V, ParentKey extends string, Depth extends number> = IsAny$1<V> extends true ? `${ParentKey}.${DepthLimitMarker}` : V extends Primitive ? never : V extends readonly any[] ? never : string extends keyof V ? `${ParentKey}.${HASH_KEY}` | (IsAny$1<V[string]> extends true ? `${ParentKey}.${HASH_KEY}.${DepthLimitMarker}` : NonNullable<V[string]> extends Primitive | readonly any[] ? never : `${ParentKey}.${HASH_KEY}.${DeepKeyUnrolled<NonNullable<V[string]>, Prev2<Depth>> & string}`) : {
120
+ [K2 in keyof V & (string | number)]: `${ParentKey}.${K2 & string}` | (IsAny$1<V[K2]> extends true ? `${ParentKey}.${K2 & string}.${DepthLimitMarker}` : NonNullable<V[K2]> extends Primitive | readonly any[] ? never : `${ParentKey}.${K2 & string}.${DeepKeyUnrolled<NonNullable<V[K2]>, Prev2<Depth>> & string}`);
121
+ }[keyof V & (string | number)];
122
+ type Prev2<N extends number> = N extends 20 ? 18 : N extends 19 ? 17 : N extends 18 ? 16 : N extends 17 ? 15 : N extends 16 ? 14 : N extends 15 ? 13 : N extends 14 ? 12 : N extends 13 ? 11 : N extends 12 ? 10 : N extends 11 ? 9 : N extends 10 ? 8 : N extends 9 ? 7 : N extends 8 ? 6 : N extends 7 ? 5 : N extends 6 ? 4 : N extends 5 ? 3 : N extends 4 ? 2 : N extends 3 ? 1 : N extends 2 ? 0 : N extends 1 ? 0 : never;
49
123
 
50
124
  /**
51
125
  * Boolean logic DSL type definitions
@@ -90,28 +164,28 @@ type ComparableValue = string | number | boolean | null | undefined;
90
164
  * const isExpensive: BoolLogic<State> = { GT: ['product.price', 100] }
91
165
  * ```
92
166
  */
93
- type BoolLogic<STATE> = {
94
- IS_EQUAL: [DeepKey<STATE>, ComparableValue];
167
+ type BoolLogic<STATE, Depth extends number = DefaultDepth> = {
168
+ IS_EQUAL: [DeepKey<STATE, Depth>, ComparableValue];
95
169
  } | {
96
- EXISTS: DeepKey<STATE>;
170
+ EXISTS: DeepKey<STATE, Depth>;
97
171
  } | {
98
- IS_EMPTY: DeepKey<STATE>;
172
+ IS_EMPTY: DeepKey<STATE, Depth>;
99
173
  } | {
100
- AND: BoolLogic<STATE>[];
174
+ AND: BoolLogic<STATE, Depth>[];
101
175
  } | {
102
- OR: BoolLogic<STATE>[];
176
+ OR: BoolLogic<STATE, Depth>[];
103
177
  } | {
104
- NOT: BoolLogic<STATE>;
178
+ NOT: BoolLogic<STATE, Depth>;
105
179
  } | {
106
- GT: [DeepKey<STATE>, number];
180
+ GT: [DeepKey<STATE, Depth>, number];
107
181
  } | {
108
- LT: [DeepKey<STATE>, number];
182
+ LT: [DeepKey<STATE, Depth>, number];
109
183
  } | {
110
- GTE: [DeepKey<STATE>, number];
184
+ GTE: [DeepKey<STATE, Depth>, number];
111
185
  } | {
112
- LTE: [DeepKey<STATE>, number];
186
+ LTE: [DeepKey<STATE, Depth>, number];
113
187
  } | {
114
- IN: [DeepKey<STATE>, ComparableValue[]];
188
+ IN: [DeepKey<STATE, Depth>, ComparableValue[]];
115
189
  };
116
190
 
117
191
  /**
@@ -261,14 +335,14 @@ interface ValidationSchema<T = unknown> {
261
335
  * Can be a schema for the path's own value type, or a scoped schema
262
336
  * targeting a different path in the same state.
263
337
  */
264
- type ValidationStateInput<DATA, PATH extends DeepKey<DATA>> = {
338
+ type ValidationStateInput<DATA, PATH extends DeepKey<DATA, Depth>, Depth extends number = DefaultDepth> = {
265
339
  schema: ValidationSchema<DeepValue<DATA, PATH>>;
266
340
  } | {
267
- [SCOPE in DeepKey<DATA>]: {
341
+ [SCOPE in ResolvableDeepKey<DATA, Depth>]: {
268
342
  scope: SCOPE;
269
343
  schema: ValidationSchema<DeepValue<DATA, SCOPE>>;
270
344
  };
271
- }[DeepKey<DATA>];
345
+ }[ResolvableDeepKey<DATA, Depth>];
272
346
  /**
273
347
  * Get the appropriate registration config type for a concern at a given path.
274
348
  *
@@ -277,14 +351,14 @@ type ValidationStateInput<DATA, PATH extends DeepKey<DATA>> = {
277
351
  * - Template-based concerns: fixed { template: string }
278
352
  * - Custom concerns: fall back to Record<string, unknown>
279
353
  */
280
- type ConcernConfigFor<C, DATA extends object, PATH extends DeepKey<DATA>> = C extends {
354
+ type ConcernConfigFor<C, DATA extends object, PATH extends DeepKey<DATA, Depth>, Depth extends number = DefaultDepth> = C extends {
281
355
  name: 'validationState';
282
- } ? ValidationStateInput<DATA, PATH> : C extends {
356
+ } ? ValidationStateInput<DATA, PATH, Depth> : C extends {
283
357
  evaluate: (props: infer P) => any;
284
358
  } ? P extends {
285
359
  boolLogic: any;
286
360
  } ? {
287
- boolLogic: BoolLogic<DATA>;
361
+ boolLogic: BoolLogic<DATA, Depth>;
288
362
  } : Omit<P, 'state' | 'path' | 'value'> : Record<string, unknown>;
289
363
  /**
290
364
  * Extract the return type from a concern's evaluate function
@@ -340,11 +414,11 @@ type EvaluatedConcerns<CONCERNS extends readonly any[]> = {
340
414
  * }
341
415
  * ```
342
416
  */
343
- type ConcernRegistrationMap<DATA extends object, CONCERNS extends readonly any[] = readonly any[]> = Partial<{
344
- [PATH in DeepKey<DATA>]: Partial<{
417
+ type ConcernRegistrationMap<DATA extends object, CONCERNS extends readonly any[] = readonly any[], Depth extends number = DefaultDepth> = Partial<{
418
+ [PATH in DeepKey<DATA, Depth>]: Partial<{
345
419
  [C in CONCERNS[number] as C extends {
346
420
  name: string;
347
- } ? C['name'] : never]: ConcernConfigFor<C, DATA, PATH>;
421
+ } ? C['name'] : never]: ConcernConfigFor<C, DATA, PATH, Depth>;
348
422
  }>;
349
423
  }>;
350
424
 
@@ -378,10 +452,16 @@ type ConcernRegistrationMap<DATA extends object, CONCERNS extends readonly any[]
378
452
  *
379
453
  * Uses mapped type with conditional filtering - maps over all possible paths,
380
454
  * keeps those matching the target type, and extracts the union.
455
+ *
456
+ * @example Custom depth — propagates to DeepKey
457
+ * ```typescript
458
+ * // Only number paths, limited to 10 levels deep
459
+ * type ShallowNumbers = DeepKeyFiltered<State, number, 10>
460
+ * ```
381
461
  */
382
- type DeepKeyFiltered<T, U> = {
383
- [K in DeepKey<T>]: DeepValue<T, K> extends U ? K : never;
384
- }[DeepKey<T>];
462
+ type DeepKeyFiltered<T, U, Depth extends number = DefaultDepth> = {
463
+ [K in ResolvableDeepKey<T, Depth>]: NonNullable<DeepValue<T, K>> extends U ? K : never;
464
+ }[ResolvableDeepKey<T, Depth>];
385
465
 
386
466
  /**
387
467
  * PathsOfSameValue - Type-safe path tuples for side effects
@@ -407,19 +487,28 @@ type DeepKeyFiltered<T, U> = {
407
487
  /**
408
488
  * Get all paths in DATA that have the same value type as the value at PATH
409
489
  */
410
- type PathsWithSameValueAs<DATA extends object, PATH extends DeepKey<DATA>> = {
411
- [K in DeepKey<DATA>]: DeepValue<DATA, K> extends DeepValue<DATA, PATH> ? DeepValue<DATA, PATH> extends DeepValue<DATA, K> ? K : never : never;
412
- }[DeepKey<DATA>];
490
+ type PathsWithSameValueAs<DATA extends object, PATH extends ResolvableDeepKey<DATA, Depth>, Depth extends number = DefaultDepth> = {
491
+ [K in ResolvableDeepKey<DATA, Depth>]: NonNullable<DeepValue<DATA, K>> extends NonNullable<DeepValue<DATA, PATH>> ? NonNullable<DeepValue<DATA, PATH>> extends NonNullable<DeepValue<DATA, K>> ? K : never : never;
492
+ }[ResolvableDeepKey<DATA, Depth>];
413
493
  /**
414
494
  * A tuple of two paths that must have the same value type.
415
495
  * Format: [path1, path2]
416
496
  *
417
497
  * @example
418
498
  * const pair: SyncPair<State> = ['user.email', 'profile.email']
499
+ *
500
+ * @example Custom depth — propagates to DeepKey and PathsWithSameValueAs
501
+ * ```typescript
502
+ * // Limit path traversal to 10 levels
503
+ * const shallow: SyncPair<State, 10> = ['user.email', 'profile.email']
504
+ * ```
419
505
  */
420
- type SyncPair<DATA extends object> = {
421
- [P1 in DeepKey<DATA>]: [P1, PathsWithSameValueAs<DATA, P1>];
422
- }[DeepKey<DATA>];
506
+ type SyncPair<DATA extends object, Depth extends number = DefaultDepth> = {
507
+ [P1 in ResolvableDeepKey<DATA, Depth>]: [
508
+ P1,
509
+ PathsWithSameValueAs<DATA, P1, Depth>
510
+ ];
511
+ }[ResolvableDeepKey<DATA, Depth>];
423
512
  /**
424
513
  * A tuple of two paths for flip (alias for SyncPair)
425
514
  * Format: [path1, path2]
@@ -427,7 +516,7 @@ type SyncPair<DATA extends object> = {
427
516
  * @example
428
517
  * const pair: FlipPair<State> = ['isActive', 'isInactive']
429
518
  */
430
- type FlipPair<DATA extends object> = SyncPair<DATA>;
519
+ type FlipPair<DATA extends object, Depth extends number = DefaultDepth> = SyncPair<DATA, Depth>;
431
520
  /**
432
521
  * A tuple for aggregation: [target, source] or [target, source, excludeWhen]
433
522
  * First element (left) is ALWAYS the target (aggregated) path.
@@ -443,9 +532,9 @@ type FlipPair<DATA extends object> = SyncPair<DATA>;
443
532
  * ['total', 'price2', { IS_EQUAL: ['price2.disabled', true] }],
444
533
  * ]
445
534
  */
446
- type AggregationPair<DATA extends object> = {
447
- [P1 in DeepKey<DATA>]: [P1, PathsWithSameValueAs<DATA, P1>] | [P1, PathsWithSameValueAs<DATA, P1>, BoolLogic<DATA>];
448
- }[DeepKey<DATA>];
535
+ type AggregationPair<DATA extends object, Depth extends number = DefaultDepth> = {
536
+ [P1 in ResolvableDeepKey<DATA, Depth>]: [P1, PathsWithSameValueAs<DATA, P1, Depth>] | [P1, PathsWithSameValueAs<DATA, P1, Depth>, BoolLogic<DATA, Depth>];
537
+ }[ResolvableDeepKey<DATA, Depth>];
449
538
  /**
450
539
  * Supported computation operations for numeric reduction.
451
540
  */
@@ -467,9 +556,14 @@ type ComputationOp = 'SUM' | 'AVG';
467
556
  * ['AVG', 'average', 'score2'],
468
557
  * ]
469
558
  */
470
- type ComputationPair<DATA extends object> = {
471
- [TARGET in DeepKeyFiltered<DATA, number>]: [ComputationOp, TARGET, DeepKeyFiltered<DATA, number>] | [ComputationOp, TARGET, DeepKeyFiltered<DATA, number>, BoolLogic<DATA>];
472
- }[DeepKeyFiltered<DATA, number>];
559
+ type ComputationPair<DATA extends object, Depth extends number = DefaultDepth> = {
560
+ [TARGET in DeepKeyFiltered<DATA, number, Depth>]: [ComputationOp, TARGET, DeepKeyFiltered<DATA, number, Depth>] | [
561
+ ComputationOp,
562
+ TARGET,
563
+ DeepKeyFiltered<DATA, number, Depth>,
564
+ BoolLogic<DATA, Depth>
565
+ ];
566
+ }[DeepKeyFiltered<DATA, number, Depth>];
473
567
 
474
568
  /** Recursively makes all properties required, stripping undefined */
475
569
  type DeepRequired<T> = {
@@ -839,12 +933,12 @@ type OnStateListener<DATA extends object = object, SUB_STATE = DATA, META extend
839
933
  * }
840
934
  * ```
841
935
  */
842
- interface ListenerRegistration<DATA extends object = object, META extends GenericMeta = GenericMeta> {
936
+ interface ListenerRegistration<DATA extends object = object, META extends GenericMeta = GenericMeta, Depth extends number = DefaultDepth> {
843
937
  /**
844
938
  * Path to watch - only changes under this path will trigger the listener
845
939
  * null = watch all top-level paths
846
940
  */
847
- path: DeepKey<DATA> | null;
941
+ path: DeepKey<DATA, Depth> | null;
848
942
  /**
849
943
  * Scope for state and changes presentation
850
944
  * - If null: state is full DATA, changes use FULL paths
@@ -852,7 +946,7 @@ interface ListenerRegistration<DATA extends object = object, META extends Generi
852
946
  *
853
947
  * Note: Changes are filtered based on `path`, even when scope is null
854
948
  */
855
- scope: DeepKey<DATA> | null;
949
+ scope: DeepKey<DATA, Depth> | null;
856
950
  fn: OnStateListener<DATA, any, META>;
857
951
  }
858
952
  interface ListenerHandlerRef {
@@ -1075,7 +1169,7 @@ declare const prebuiltsNamespace: {
1075
1169
 
1076
1170
  type index_ValidationError = ValidationError;
1077
1171
  type index_ValidationStateConcern = ValidationStateConcern;
1078
- type index_ValidationStateInput<DATA, PATH extends DeepKey<DATA>> = ValidationStateInput<DATA, PATH>;
1172
+ type index_ValidationStateInput<DATA, PATH extends DeepKey<DATA, Depth>, Depth extends number = DefaultDepth> = ValidationStateInput<DATA, PATH, Depth>;
1079
1173
  type index_ValidationStateResult = ValidationStateResult;
1080
1174
  declare const index_disabledWhen: typeof disabledWhen;
1081
1175
  declare const index_dynamicLabel: typeof dynamicLabel;
@@ -1130,13 +1224,9 @@ declare const defaultConcerns: readonly [ValidationStateConcern, ConcernType<"di
1130
1224
  * - targets: paths to set to null
1131
1225
  * - expandMatch: if true, [*] in targets expands to ALL keys (not just matched key)
1132
1226
  */
1133
- type ClearPathRule<DATA extends object> = [
1134
- DeepKey<DATA>[],
1135
- DeepKey<DATA>[],
1136
- {
1137
- expandMatch?: boolean;
1138
- }?
1139
- ];
1227
+ type ClearPathRule<DATA extends object, Depth extends number = DefaultDepth> = [DeepKey<DATA, Depth>[], DeepKey<DATA, Depth>[], {
1228
+ expandMatch?: boolean;
1229
+ }?];
1140
1230
  /**
1141
1231
  * Side effects configuration for useSideEffects hook
1142
1232
  *
@@ -1185,37 +1275,37 @@ type ClearPathRule<DATA extends object> = [
1185
1275
  * })
1186
1276
  * ```
1187
1277
  */
1188
- interface SideEffects<DATA extends object, META extends GenericMeta = GenericMeta> {
1278
+ interface SideEffects<DATA extends object, META extends GenericMeta = GenericMeta, Depth extends number = DefaultDepth> {
1189
1279
  /**
1190
1280
  * Sync paths - keeps specified paths synchronized
1191
1281
  * Format: [path1, path2] - both paths stay in sync
1192
1282
  */
1193
- syncPaths?: SyncPair<DATA>[];
1283
+ syncPaths?: SyncPair<DATA, Depth>[];
1194
1284
  /**
1195
1285
  * Flip paths - keeps specified paths with opposite values
1196
1286
  * Format: [path1, path2] - paths have inverse boolean values
1197
1287
  */
1198
- flipPaths?: FlipPair<DATA>[];
1288
+ flipPaths?: FlipPair<DATA, Depth>[];
1199
1289
  /**
1200
1290
  * Aggregations - aggregates sources into target
1201
1291
  * Format: [target, source] - target is ALWAYS first (left)
1202
1292
  * Multiple pairs can point to same target for multi-source aggregation
1203
1293
  */
1204
- aggregations?: AggregationPair<DATA>[];
1294
+ aggregations?: AggregationPair<DATA, Depth>[];
1205
1295
  /**
1206
1296
  * Clear paths - "when X changes, set Y to null"
1207
1297
  * Format: [triggers[], targets[], { expandMatch?: boolean }?]
1208
1298
  * - Default: [*] in target correlates with trigger's [*] (same key)
1209
1299
  * - expandMatch: true → [*] in target expands to ALL keys
1210
1300
  */
1211
- clearPaths?: ClearPathRule<DATA>[];
1301
+ clearPaths?: ClearPathRule<DATA, Depth>[];
1212
1302
  /**
1213
1303
  * Computations - numeric reduction operations (SUM, AVG)
1214
1304
  * Format: [operation, target, source] - target is computed from sources
1215
1305
  * Multiple pairs can point to same target for multi-source computation
1216
1306
  * Unidirectional: source → target only (writes to target are no-op)
1217
1307
  */
1218
- computations?: ComputationPair<DATA>[];
1308
+ computations?: ComputationPair<DATA, Depth>[];
1219
1309
  /**
1220
1310
  * Listeners - react to state changes with scoped state
1221
1311
  */