@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 +56 -1
- package/dist/index.d.ts +147 -57
- package/dist/index.js +3080 -24
- package/dist/index.js.map +1 -1
- package/dist/testing/index.js +2037 -32
- package/dist/testing/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/apex_state_wasm-WOOTVUVC.js +0 -674
- package/dist/apex_state_wasm-WOOTVUVC.js.map +0 -1
- package/dist/chunk-SLJZLVMQ.js +0 -2390
- package/dist/chunk-SLJZLVMQ.js.map +0 -1
- package/dist/chunk-VR6T6OJS.js +0 -26
- package/dist/chunk-VR6T6OJS.js.map +0 -1
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
|
-
|
|
46
|
-
|
|
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
|
-
|
|
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
|
|
341
|
+
[SCOPE in ResolvableDeepKey<DATA, Depth>]: {
|
|
268
342
|
scope: SCOPE;
|
|
269
343
|
schema: ValidationSchema<DeepValue<DATA, SCOPE>>;
|
|
270
344
|
};
|
|
271
|
-
}[
|
|
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
|
|
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
|
|
384
|
-
}[
|
|
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
|
|
411
|
-
[K in
|
|
412
|
-
}[
|
|
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
|
|
422
|
-
|
|
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
|
|
448
|
-
}[
|
|
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>] | [
|
|
472
|
-
|
|
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
|
|
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
|
-
|
|
1135
|
-
|
|
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
|
*/
|